Merge branch 'setup-cleanup' into devel

This commit is contained in:
Daniele Varrazzo 2011-06-07 23:58:58 +01:00
commit 178698f9e8
2 changed files with 190 additions and 175 deletions

2
NEWS
View File

@ -18,6 +18,8 @@ What's new in psycopg 2.4.2
- Trying to execute concurrent operations on the same connection - Trying to execute concurrent operations on the same connection
through concurrent green thread results in an error instead of a through concurrent green thread results in an error instead of a
deadlock. deadlock.
- Fixed detection of pg_config on Window. Report and fix, plus some
long needed setup.py cleanup by Steve Lacy: thanks!
What's new in psycopg 2.4.1 What's new in psycopg 2.4.1

363
setup.py
View File

@ -45,21 +45,15 @@ Operating System :: Unix
# Note: The setup.py must be compatible with both Python 2 and 3 # Note: The setup.py must be compatible with both Python 2 and 3
import os import os
import os.path
import sys import sys
import re import re
import subprocess import subprocess
from distutils.core import setup, Extension from distutils.core import setup, Extension
from distutils.errors import DistutilsFileError
from distutils.command.build_ext import build_ext from distutils.command.build_ext import build_ext
from distutils.sysconfig import get_python_inc from distutils.sysconfig import get_python_inc
from distutils.ccompiler import get_default_compiler from distutils.ccompiler import get_default_compiler
from distutils.dep_util import newer_group
from distutils.util import get_platform from distutils.util import get_platform
try:
from distutils.msvc9compiler import MSVCCompiler
except ImportError:
MSVCCompiler = None
try: try:
from distutils.command.build_py import build_py_2to3 as build_py from distutils.command.build_py import build_py_2to3 as build_py
except ImportError: except ImportError:
@ -85,21 +79,129 @@ version_flags = ['dt', 'dec']
PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win') PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win')
def get_pg_config(kind, pg_config):
try: class PostgresConfig:
p = subprocess.Popen([pg_config, "--" + kind], def __init__(self, build_ext):
stdin=subprocess.PIPE, self.build_ext = build_ext
stdout=subprocess.PIPE, self.pg_config_exe = self.build_ext.pg_config
stderr=subprocess.PIPE) if not self.pg_config_exe:
except OSError: self.pg_config_exe = self.autodetect_pg_config_path()
raise Warning("Unable to find 'pg_config' file in '%s'" % pg_config) if self.pg_config_exe is None:
p.stdin.close() sys.stderr.write("""\
r = p.stdout.readline().strip() Error: pg_config executable not found.
if not r:
raise Warning(p.stderr.readline()) Please add the directory containing pg_config to the PATH
if not isinstance(r, str): or specify the full executable path with the option:
r = r.decode('ascii')
return r python setup.py build_ext --pg-config /path/to/pg_config build ...
or with the pg_config option in 'setup.cfg'.
""")
sys.exit(1)
def query(self, attr_name):
"""Spawn the pg_config executable, querying for the given config
name, and return the printed value, sanitized. """
try:
pg_config_process = subprocess.Popen(
[self.pg_config_exe, "--" + attr_name],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
except OSError:
raise Warning("Unable to find 'pg_config' file in '%s'" %
self.pg_config_exe)
pg_config_process.stdin.close()
result = pg_config_process.stdout.readline().strip()
if not result:
raise Warning(pg_config_process.stderr.readline())
if not isinstance(result, str):
result = result.decode('ascii')
return result
def find_on_path(self, exename, path_directories=None):
if not path_directories:
path_directories = os.environ['PATH'].split(os.pathsep)
for dir_name in path_directories:
fullpath = os.path.join(dir_name, exename)
if os.path.isfile(fullpath):
return fullpath
return None
def autodetect_pg_config_path(self):
"""Find and return the path to the pg_config executable."""
if PLATFORM_IS_WINDOWS:
return self.autodetect_pg_config_path_windows()
else:
return self.find_on_path('pg_config')
def autodetect_pg_config_path_windows(self):
"""Attempt several different ways of finding the pg_config
executable on Windows, and return its full path, if found."""
# This code only runs if they have not specified a pg_config option
# in the config file or via the commandline.
# First, check for pg_config.exe on the PATH, and use that if found.
pg_config_exe = self.find_on_path('pg_config.exe')
if pg_config_exe:
return pg_config_exe
# Now, try looking in the Windows Registry to find a PostgreSQL
# installation, and infer the path from that.
pg_config_exe = self._get_pg_config_from_registry()
if pg_config_exe:
return pg_config_exe
return None
def _get_pg_config_from_registry(self):
try:
import winreg
except ImportError:
import _winreg as winreg
reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
try:
pg_inst_list_key = winreg.OpenKey(reg,
'SOFTWARE\\PostgreSQL\\Installations')
except EnvironmentError:
# No PostgreSQL installation, as best as we can tell.
return None
try:
# Determine the name of the first subkey, if any:
try:
first_sub_key_name = winreg.EnumKey(pg_inst_list_key, 0)
except EnvironmentError:
return None
pg_first_inst_key = winreg.OpenKey(reg,
'SOFTWARE\\PostgreSQL\\Installations\\'
+ first_sub_key_name)
try:
pg_inst_base_dir = winreg.QueryValueEx(
pg_first_inst_key, 'Base Directory')[0]
finally:
winreg.CloseKey(pg_first_inst_key)
finally:
winreg.CloseKey(pg_inst_list_key)
pg_config_path = os.path.join(
pg_inst_base_dir, 'bin', 'pg_config.exe')
if not os.path.exists(pg_config_path):
return None
# Support unicode paths, if this version of Python provides the
# necessary infrastructure:
if sys.version_info[0] < 3 \
and hasattr(sys, 'getfilesystemencoding'):
pg_config_path = pg_config_path.encode(
sys.getfilesystemencoding())
return pg_config_path
class psycopg_build_ext(build_ext): class psycopg_build_ext(build_ext):
"""Conditionally complement the setup.cfg options file. """Conditionally complement the setup.cfg options file.
@ -126,6 +228,9 @@ class psycopg_build_ext(build_ext):
boolean_options = build_ext.boolean_options[:] boolean_options = build_ext.boolean_options[:]
boolean_options.extend(('use-pydatetime', 'have-ssl', 'static-libpq')) boolean_options.extend(('use-pydatetime', 'have-ssl', 'static-libpq'))
def __init__(self, *args, **kwargs):
build_ext.__init__(self, *args, **kwargs)
def initialize_options(self): def initialize_options(self):
build_ext.initialize_options(self) build_ext.initialize_options(self)
self.use_pg_dll = 1 self.use_pg_dll = 1
@ -136,7 +241,13 @@ class psycopg_build_ext(build_ext):
self.static_libpq = static_libpq self.static_libpq = static_libpq
self.pg_config = None self.pg_config = None
def get_compiler(self): def compiler_is_msvc(self):
return self.get_compiler_name().lower().startswith('msvc')
def compiler_is_mingw(self):
return self.get_compiler_name().lower().startswith('mingw')
def get_compiler_name(self):
"""Return the name of the C compiler used to compile extensions. """Return the name of the C compiler used to compile extensions.
If a compiler was not explicitly set (on the command line, for If a compiler was not explicitly set (on the command line, for
@ -153,43 +264,39 @@ class psycopg_build_ext(build_ext):
name = get_default_compiler() name = get_default_compiler()
return name return name
def get_pg_config(self, kind): def get_export_symbols(self, extension):
return get_pg_config(kind, self.pg_config)
def get_export_symbols(self, ext):
# Fix MSVC seeing two of the same export symbols. # Fix MSVC seeing two of the same export symbols.
if self.get_compiler().lower().startswith('msvc'): if self.compiler_is_msvc():
return [] return []
else: else:
return build_ext.get_export_symbols(self, ext) return build_ext.get_export_symbols(self, extension)
def build_extension(self, ext): def build_extension(self, extension):
build_ext.build_extension(self, ext) build_ext.build_extension(self, extension)
# For Python versions that use MSVC compiler 2008, re-insert the # For Python versions that use MSVC compiler 2008, re-insert the
# manifest into the resulting .pyd file. # manifest into the resulting .pyd file.
if MSVCCompiler and isinstance(self.compiler, MSVCCompiler): if self.compiler_is_msvc():
platform = get_platform() platform = get_platform()
# Default to the x86 manifest # Default to the x86 manifest
manifest = '_psycopg.vc9.x86.manifest' manifest = '_psycopg.vc9.x86.manifest'
if platform == 'win-amd64': if platform == 'win-amd64':
manifest = '_psycopg.vc9.amd64.manifest' manifest = '_psycopg.vc9.amd64.manifest'
self.compiler.spawn(['mt.exe', '-nologo', '-manifest', self.compiler.spawn(
os.path.join('psycopg', manifest), ['mt.exe', '-nologo', '-manifest',
'-outputresource:%s;2' % (os.path.join(self.build_lib, 'psycopg2', '_psycopg.pyd'))]) os.path.join('psycopg', manifest),
'-outputresource:%s;2' % (
os.path.join(self.build_lib,
'psycopg2', '_psycopg.pyd'))])
def finalize_win32(self): def finalize_win32(self):
"""Finalize build system configuration on win32 platform.""" """Finalize build system configuration on win32 platform."""
import struct
sysVer = sys.version_info[:2] sysVer = sys.version_info[:2]
# Add compiler-specific arguments: # Add compiler-specific arguments:
extra_compiler_args = [] extra_compiler_args = []
compiler_name = self.get_compiler().lower() if self.compiler_is_mingw():
compiler_is_msvc = compiler_name.startswith('msvc')
compiler_is_mingw = compiler_name.startswith('mingw')
if compiler_is_mingw:
# Default MinGW compilation of Python extensions on Windows uses # Default MinGW compilation of Python extensions on Windows uses
# only -O: # only -O:
extra_compiler_args.append('-O3') extra_compiler_args.append('-O3')
@ -201,23 +308,23 @@ class psycopg_build_ext(build_ext):
extra_compiler_args.append('-fno-strict-aliasing') extra_compiler_args.append('-fno-strict-aliasing')
# Force correct C runtime library linkage: # Force correct C runtime library linkage:
if sysVer <= (2,3): if sysVer <= (2, 3):
# Yes: 'msvcr60', rather than 'msvcrt', is the correct value # Yes: 'msvcr60', rather than 'msvcrt', is the correct value
# on the line below: # on the line below:
self.libraries.append('msvcr60') self.libraries.append('msvcr60')
elif sysVer in ((2,4), (2,5)): elif sysVer in ((2, 4), (2, 5)):
self.libraries.append('msvcr71') self.libraries.append('msvcr71')
# Beyond Python 2.5, we take our chances on the default C runtime # Beyond Python 2.5, we take our chances on the default C runtime
# library, because we don't know what compiler those future # library, because we don't know what compiler those future
# versions of Python will use. # versions of Python will use.
for exten in ext: # ext is a global list of Extension objects for extension in ext: # ext is a global list of Extension objects
exten.extra_compile_args.extend(extra_compiler_args) extension.extra_compile_args.extend(extra_compiler_args)
# End of add-compiler-specific arguments section. # End of add-compiler-specific arguments section.
self.libraries.append("ws2_32") self.libraries.append("ws2_32")
self.libraries.append("advapi32") self.libraries.append("advapi32")
if compiler_is_msvc: if self.compiler_is_msvc():
# MSVC requires an explicit "libpq" # MSVC requires an explicit "libpq"
self.libraries.remove("pq") self.libraries.remove("pq")
self.libraries.append("secur32") self.libraries.append("secur32")
@ -242,48 +349,39 @@ class psycopg_build_ext(build_ext):
def finalize_linux2(self): def finalize_linux2(self):
"""Finalize build system configuration on GNU/Linux platform.""" """Finalize build system configuration on GNU/Linux platform."""
# tell piro that GCC is fine and dandy, but not so MS compilers # tell piro that GCC is fine and dandy, but not so MS compilers
for ext in self.extensions: for extension in self.extensions:
ext.extra_compile_args.append('-Wdeclaration-after-statement') extension.extra_compile_args.append(
'-Wdeclaration-after-statement')
def finalize_options(self): def finalize_options(self):
"""Complete the build system configuation.""" """Complete the build system configuation."""
build_ext.finalize_options(self) build_ext.finalize_options(self)
if self.pg_config is None:
self.pg_config = self.autodetect_pg_config_path()
if self.pg_config is None:
sys.stderr.write("""\
Error: pg_config executable not found.
Please add the directory containing pg_config to the PATH pg_config_helper = PostgresConfig(self)
or specify the full executable path with the option:
python setup.py build_ext --pg-config /path/to/pg_config build ...
or with the pg_config option in 'setup.cfg'.
""")
sys.exit(1)
self.include_dirs.append(".") self.include_dirs.append(".")
if self.static_libpq: if self.static_libpq:
if not self.link_objects: self.link_objects = [] if not hasattr(self, 'link_objects'):
self.link_objects = []
self.link_objects.append( self.link_objects.append(
os.path.join(self.get_pg_config("libdir"), "libpq.a")) os.path.join(pg_config_helper.query("libdir"), "libpq.a"))
else: else:
self.libraries.append("pq") self.libraries.append("pq")
try: try:
self.library_dirs.append(self.get_pg_config("libdir")) self.library_dirs.append(pg_config_helper.query("libdir"))
self.include_dirs.append(self.get_pg_config("includedir")) self.include_dirs.append(pg_config_helper.query("includedir"))
self.include_dirs.append(self.get_pg_config("includedir-server")) self.include_dirs.append(pg_config_helper.query("includedir-server"))
try: try:
# Here we take a conservative approach: we suppose that # Here we take a conservative approach: we suppose that
# *at least* PostgreSQL 7.4 is available (this is the only # *at least* PostgreSQL 7.4 is available (this is the only
# 7.x series supported by psycopg 2) # 7.x series supported by psycopg 2)
pgversion = self.get_pg_config("version").split()[1] pgversion = pg_config_helper.query("version").split()[1]
except: except:
pgversion = "7.4.0" pgversion = "7.4.0"
verre = re.compile(r"(\d+)\.(\d+)(?:(?:\.(\d+))|(devel|(alpha|beta|rc)\d+))") verre = re.compile(
r"(\d+)\.(\d+)(?:(?:\.(\d+))|(devel|(alpha|beta|rc)\d+))")
m = verre.match(pgversion) m = verre.match(pgversion)
if m: if m:
pgmajor, pgminor, pgpatch = m.group(1, 2, 3) pgmajor, pgminor, pgpatch = m.group(1, 2, 3)
@ -298,108 +396,21 @@ or with the pg_config option in 'setup.cfg'.
define_macros.append(("PG_VERSION_HEX", "0x%02X%02X%02X" % define_macros.append(("PG_VERSION_HEX", "0x%02X%02X%02X" %
(int(pgmajor), int(pgminor), int(pgpatch)))) (int(pgmajor), int(pgminor), int(pgpatch))))
except Warning: except Warning:
w = sys.exc_info()[1] # work around py 2/3 different syntax w = sys.exc_info()[1] # work around py 2/3 different syntax
sys.stderr.write("Error: %s\n" % w) sys.stderr.write("Error: %s\n" % w)
sys.exit(1) sys.exit(1)
if hasattr(self, "finalize_" + sys.platform): if hasattr(self, "finalize_" + sys.platform):
getattr(self, "finalize_" + sys.platform)() getattr(self, "finalize_" + sys.platform)()
def autodetect_pg_config_path(self):
if PLATFORM_IS_WINDOWS:
return self.autodetect_pg_config_path_windows()
else:
return self.autodetect_pg_config_path_posix()
def autodetect_pg_config_path_posix(self):
exename = 'pg_config'
for dir in os.environ['PATH'].split(os.pathsep):
fn = os.path.join(dir, exename)
if os.path.isfile(fn):
return fn
def autodetect_pg_config_path_windows(self):
# Find the first PostgreSQL installation listed in the registry and
# return the full path to its pg_config utility.
#
# This autodetection is performed *only* if the following conditions
# hold:
#
# 1) The pg_config utility is not already available on the PATH:
if os.popen('pg_config').close() is None: # .close()->None == success
return None
# 2) The user has not specified any of the following settings in
# setup.cfg:
# - pg_config
# - include_dirs
# - library_dirs
for settingName in ('pg_config', 'include_dirs', 'library_dirs'):
try:
val = parser.get('build_ext', settingName)
except configparser.NoOptionError:
pass
else:
if val.strip() != '':
return None
# end of guard conditions
try:
import winreg
except ImportError:
import _winreg as winreg
pg_inst_base_dir = None
pg_config_path = None
reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
try:
pg_inst_list_key = winreg.OpenKey(reg,
'SOFTWARE\\PostgreSQL\\Installations'
)
except EnvironmentError:
pg_inst_list_key = None
if pg_inst_list_key is not None:
try:
# Determine the name of the first subkey, if any:
try:
first_sub_key_name = winreg.EnumKey(pg_inst_list_key, 0)
except EnvironmentError:
first_sub_key_name = None
if first_sub_key_name is not None:
pg_first_inst_key = winreg.OpenKey(reg,
'SOFTWARE\\PostgreSQL\\Installations\\'
+ first_sub_key_name
)
try:
pg_inst_base_dir = winreg.QueryValueEx(
pg_first_inst_key, 'Base Directory'
)[0]
finally:
winreg.CloseKey(pg_first_inst_key)
finally:
winreg.CloseKey(pg_inst_list_key)
if pg_inst_base_dir and os.path.exists(pg_inst_base_dir):
pg_config_path = os.path.join(pg_inst_base_dir, 'bin',
'pg_config.exe'
)
# Support unicode paths, if this version of Python provides the
# necessary infrastructure:
if sys.version_info[0] < 3 \
and hasattr(sys, 'getfilesystemencoding'):
pg_config_path = pg_config_path.encode(
sys.getfilesystemencoding())
return pg_config_path
# let's start with macro definitions (the ones not already in setup.cfg) # let's start with macro definitions (the ones not already in setup.cfg)
define_macros = [] define_macros = []
include_dirs = [] include_dirs = []
# gather information to build the extension module # gather information to build the extension module
ext = [] ; data_files = [] ext = []
data_files = []
# sources # sources
@ -452,7 +463,7 @@ else:
if os.path.exists(mxincludedir): if os.path.exists(mxincludedir):
# Build the support for mx: we will check at runtime if it can be imported # Build the support for mx: we will check at runtime if it can be imported
include_dirs.append(mxincludedir) include_dirs.append(mxincludedir)
define_macros.append(('HAVE_MXDATETIME','1')) define_macros.append(('HAVE_MXDATETIME', '1'))
sources.append('adapter_mxdatetime.c') sources.append('adapter_mxdatetime.c')
depends.extend(['adapter_mxdatetime.h', 'typecast_mxdatetime.c']) depends.extend(['adapter_mxdatetime.h', 'typecast_mxdatetime.c'])
have_mxdatetime = True have_mxdatetime = True
@ -460,18 +471,21 @@ if os.path.exists(mxincludedir):
# now decide which package will be the default for date/time typecasts # now decide which package will be the default for date/time typecasts
if have_pydatetime and (use_pydatetime or not have_mxdatetime): if have_pydatetime and (use_pydatetime or not have_mxdatetime):
define_macros.append(('PSYCOPG_DEFAULT_PYDATETIME','1')) define_macros.append(('PSYCOPG_DEFAULT_PYDATETIME', '1'))
elif have_mxdatetime: elif have_mxdatetime:
define_macros.append(('PSYCOPG_DEFAULT_MXDATETIME','1')) define_macros.append(('PSYCOPG_DEFAULT_MXDATETIME', '1'))
else: else:
def e(msg): error_message = """\
sys.stderr.write("error: " + msg + "\n") psycopg requires a datetime module:
e("psycopg requires a datetime module:") mx.DateTime module not found
e(" mx.DateTime module not found") python datetime module not found
e(" python datetime module not found")
e("Note that psycopg needs the module headers and not just the module") Note that psycopg needs the module headers and not just the module
e("itself. If you installed Python or mx.DateTime from a binary package") itself. If you installed Python or mx.DateTime from a binary package
e("you probably need to install its companion -dev or -devel package.") you probably need to install its companion -dev or -devel package."""
for line in error_message.split("\n"):
sys.stderr.write("error: " + line)
sys.exit(1) sys.exit(1)
# generate a nice version string to avoid confusion when users report bugs # generate a nice version string to avoid confusion when users report bugs
@ -485,9 +499,9 @@ else:
PSYCOPG_VERSION_EX = PSYCOPG_VERSION PSYCOPG_VERSION_EX = PSYCOPG_VERSION
if not PLATFORM_IS_WINDOWS: if not PLATFORM_IS_WINDOWS:
define_macros.append(('PSYCOPG_VERSION', '"'+PSYCOPG_VERSION_EX+'"')) define_macros.append(('PSYCOPG_VERSION', '"' + PSYCOPG_VERSION_EX + '"'))
else: else:
define_macros.append(('PSYCOPG_VERSION', '\\"'+PSYCOPG_VERSION_EX+'\\"')) define_macros.append(('PSYCOPG_VERSION', '\\"' + PSYCOPG_VERSION_EX + '\\"'))
if parser.has_option('build_ext', 'have_ssl'): if parser.has_option('build_ext', 'have_ssl'):
have_ssl = int(parser.get('build_ext', 'have_ssl')) have_ssl = int(parser.get('build_ext', 'have_ssl'))
@ -525,17 +539,16 @@ setup(name="psycopg2",
author="Federico Di Gregorio", author="Federico Di Gregorio",
author_email="fog@initd.org", author_email="fog@initd.org",
url="http://initd.org/psycopg/", url="http://initd.org/psycopg/",
download_url = download_url, download_url=download_url,
license="GPL with exceptions or ZPL", license="GPL with exceptions or ZPL",
platforms = ["any"], platforms=["any"],
description=__doc__.split("\n")[0], description=__doc__.split("\n")[0],
long_description="\n".join(__doc__.split("\n")[2:]), long_description="\n".join(__doc__.split("\n")[2:]),
classifiers=[x for x in classifiers.split("\n") if x], classifiers=[x for x in classifiers.split("\n") if x],
data_files=data_files, data_files=data_files,
package_dir={'psycopg2':'lib', 'psycopg2.tests': 'tests'}, package_dir={'psycopg2': 'lib', 'psycopg2.tests': 'tests'},
packages=['psycopg2', 'psycopg2.tests'], packages=['psycopg2', 'psycopg2.tests'],
cmdclass={ cmdclass={
'build_ext': psycopg_build_ext, 'build_ext': psycopg_build_ext,
'build_py': build_py, }, 'build_py': build_py, },
ext_modules=ext) ext_modules=ext)