mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-10 19:16:34 +03:00
Drop 2to3 build step; make all code compatible with all Pythons
Make all library code compatible with both Python 2 and Python 3. Helps move to modern Python idioms. Can now write for Python 3 (with workarounds for Python 2) instead of the other way around. In the future, when it is eventually time to drop Python 2, the library will be in a better position to remove workarounds Added a very small comparability module compat.py where required. It includes definitions for: - text_type -- A type. str on Python 3. unicode on Python 2. - string_types -- A tuple. Contains only str on Python 3. Contains str & unicode on Python 2.
This commit is contained in:
parent
f35465231f
commit
8ad2098b74
5
NEWS
5
NEWS
|
@ -9,9 +9,10 @@ Other changes:
|
|||
- Dropped support for Python 2.6, 3.2, 3.3.
|
||||
- Dropped `psycopg1` module.
|
||||
- Dropped deprecated ``register_tstz_w_secs()`` (was previously a no-op).
|
||||
- No longer use 2to3 during installation for Python 2 & 3 compatability. All
|
||||
source files are now compatible with Python 2 & 3 as is.
|
||||
- The ``psycopg2.test`` package is no longer installed by ``python setup.py
|
||||
install``. The test source files now are compatible with Python 2 and 3
|
||||
without using 2to3.
|
||||
install``.
|
||||
|
||||
|
||||
What's new in psycopg 2.7.4
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
from psycopg2.extensions import (
|
||||
new_type, new_array_type, register_type, register_adapter, QuotedString)
|
||||
from psycopg2.compat import text_type
|
||||
|
||||
# The module is imported on register_ipaddress
|
||||
ipaddress = None
|
||||
|
@ -76,13 +77,13 @@ def cast_interface(s, cur=None):
|
|||
if s is None:
|
||||
return None
|
||||
# Py2 version force the use of unicode. meh.
|
||||
return ipaddress.ip_interface(unicode(s))
|
||||
return ipaddress.ip_interface(text_type(s))
|
||||
|
||||
|
||||
def cast_network(s, cur=None):
|
||||
if s is None:
|
||||
return None
|
||||
return ipaddress.ip_network(unicode(s))
|
||||
return ipaddress.ip_network(text_type(s))
|
||||
|
||||
|
||||
def adapt_ipaddress(obj):
|
||||
|
|
|
@ -29,6 +29,7 @@ import re
|
|||
from psycopg2._psycopg import ProgrammingError, InterfaceError
|
||||
from psycopg2.extensions import ISQLQuote, adapt, register_adapter
|
||||
from psycopg2.extensions import new_type, new_array_type, register_type
|
||||
from psycopg2.compat import string_types
|
||||
|
||||
|
||||
class Range(object):
|
||||
|
@ -126,9 +127,13 @@ class Range(object):
|
|||
|
||||
return True
|
||||
|
||||
def __nonzero__(self):
|
||||
def __bool__(self):
|
||||
return self._bounds is not None
|
||||
|
||||
def __nonzero__(self):
|
||||
# Python 2 compatibility
|
||||
return type(self).__bool__(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Range):
|
||||
return False
|
||||
|
@ -296,7 +301,7 @@ class RangeCaster(object):
|
|||
# an implementation detail and is not documented. It is currently used
|
||||
# for the numeric ranges.
|
||||
self.adapter = None
|
||||
if isinstance(pgrange, basestring):
|
||||
if isinstance(pgrange, string_types):
|
||||
self.adapter = type(pgrange, (RangeAdapter,), {})
|
||||
self.adapter.name = pgrange
|
||||
else:
|
||||
|
@ -313,7 +318,7 @@ class RangeCaster(object):
|
|||
|
||||
self.range = None
|
||||
try:
|
||||
if isinstance(pyrange, basestring):
|
||||
if isinstance(pyrange, string_types):
|
||||
self.range = type(pyrange, (Range,), {})
|
||||
if issubclass(pyrange, Range) and pyrange is not Range:
|
||||
self.range = pyrange
|
||||
|
|
10
lib/compat.py
Normal file
10
lib/compat.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
import sys
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
# Python 2
|
||||
string_types = basestring,
|
||||
text_type = unicode
|
||||
else:
|
||||
# Python 3
|
||||
string_types = str,
|
||||
text_type = str
|
|
@ -40,7 +40,7 @@ def lookup(code, _cache={}):
|
|||
|
||||
# Generate the lookup map at first usage.
|
||||
tmp = {}
|
||||
for k, v in globals().iteritems():
|
||||
for k, v in globals().items():
|
||||
if isinstance(v, str) and len(v) in (2, 5):
|
||||
tmp[v] = k
|
||||
|
||||
|
|
|
@ -163,7 +163,7 @@ def make_dsn(dsn=None, **kwargs):
|
|||
kwargs['dbname'] = kwargs.pop('database')
|
||||
|
||||
# Drop the None arguments
|
||||
kwargs = {k: v for (k, v) in kwargs.iteritems() if v is not None}
|
||||
kwargs = {k: v for (k, v) in kwargs.items() if v is not None}
|
||||
|
||||
if dsn is not None:
|
||||
tmp = parse_dsn(dsn)
|
||||
|
@ -171,7 +171,7 @@ def make_dsn(dsn=None, **kwargs):
|
|||
kwargs = tmp
|
||||
|
||||
dsn = " ".join(["%s=%s" % (k, _param_escape(str(v)))
|
||||
for (k, v) in kwargs.iteritems()])
|
||||
for (k, v) in kwargs.items()])
|
||||
|
||||
# verify that the returned dsn is valid
|
||||
parse_dsn(dsn)
|
||||
|
@ -216,7 +216,7 @@ del Range
|
|||
# When the encoding is set its name is cleaned up from - and _ and turned
|
||||
# uppercase, so an encoding not respecting these rules wouldn't be found in the
|
||||
# encodings keys and would raise an exception with the unicode typecaster
|
||||
for k, v in encodings.items():
|
||||
for k, v in list(encodings.items()):
|
||||
k = k.replace('_', '').replace('-', '').upper()
|
||||
encodings[k] = v
|
||||
|
||||
|
|
|
@ -318,14 +318,14 @@ class NamedTupleCursor(_cursor):
|
|||
nt = self.Record
|
||||
if nt is None:
|
||||
nt = self.Record = self._make_nt()
|
||||
return map(nt._make, ts)
|
||||
return list(map(nt._make, ts))
|
||||
|
||||
def fetchall(self):
|
||||
ts = super(NamedTupleCursor, self).fetchall()
|
||||
nt = self.Record
|
||||
if nt is None:
|
||||
nt = self.Record = self._make_nt()
|
||||
return map(nt._make, ts)
|
||||
return list(map(nt._make, ts))
|
||||
|
||||
def __iter__(self):
|
||||
try:
|
||||
|
@ -566,7 +566,7 @@ class ReplicationCursor(_replicationCursor):
|
|||
"cannot specify output plugin options for physical replication")
|
||||
|
||||
command += " ("
|
||||
for k, v in options.iteritems():
|
||||
for k, v in options.items():
|
||||
if not command.endswith('('):
|
||||
command += ", "
|
||||
command += "%s %s" % (quote_ident(k, self), _A(str(v)))
|
||||
|
@ -762,7 +762,7 @@ class HstoreAdapter(object):
|
|||
|
||||
adapt = _ext.adapt
|
||||
rv = []
|
||||
for k, v in self.wrapped.iteritems():
|
||||
for k, v in self.wrapped.items():
|
||||
k = adapt(k)
|
||||
k.prepare(self.conn)
|
||||
k = k.getquoted()
|
||||
|
@ -784,9 +784,9 @@ class HstoreAdapter(object):
|
|||
if not self.wrapped:
|
||||
return b"''::hstore"
|
||||
|
||||
k = _ext.adapt(self.wrapped.keys())
|
||||
k = _ext.adapt(list(self.wrapped.keys()))
|
||||
k.prepare(self.conn)
|
||||
v = _ext.adapt(self.wrapped.values())
|
||||
v = _ext.adapt(list(self.wrapped.values()))
|
||||
v.prepare(self.conn)
|
||||
return b"hstore(" + k.getquoted() + b", " + v.getquoted() + b")"
|
||||
|
||||
|
@ -1112,7 +1112,7 @@ def _paginate(seq, page_size):
|
|||
it = iter(seq)
|
||||
while 1:
|
||||
try:
|
||||
for i in xrange(page_size):
|
||||
for i in range(page_size):
|
||||
page.append(next(it))
|
||||
yield page
|
||||
page = []
|
||||
|
|
|
@ -209,8 +209,8 @@ class PersistentConnectionPool(AbstractConnectionPool):
|
|||
|
||||
# we we'll need the thread module, to determine thread ids, so we
|
||||
# import it here and copy it in an instance variable
|
||||
import thread as _thread # work around for 2to3 bug - see ticket #348
|
||||
self.__thread = _thread
|
||||
import thread
|
||||
self.__thread = thread
|
||||
|
||||
def getconn(self):
|
||||
"""Generate thread id and return a connection."""
|
||||
|
|
|
@ -27,6 +27,7 @@ import sys
|
|||
import string
|
||||
|
||||
from psycopg2 import extensions as ext
|
||||
from psycopg2.compat import string_types
|
||||
|
||||
|
||||
_formatter = string.Formatter()
|
||||
|
@ -147,7 +148,7 @@ class Composed(Composable):
|
|||
"foo", "bar"
|
||||
|
||||
"""
|
||||
if isinstance(joiner, basestring):
|
||||
if isinstance(joiner, string_types):
|
||||
joiner = SQL(joiner)
|
||||
elif not isinstance(joiner, SQL):
|
||||
raise TypeError(
|
||||
|
@ -179,7 +180,7 @@ class SQL(Composable):
|
|||
select "foo", "bar" from "table"
|
||||
"""
|
||||
def __init__(self, string):
|
||||
if not isinstance(string, basestring):
|
||||
if not isinstance(string, string_types):
|
||||
raise TypeError("SQL values must be strings")
|
||||
super(SQL, self).__init__(string)
|
||||
|
||||
|
@ -308,7 +309,7 @@ class Identifier(Composable):
|
|||
|
||||
"""
|
||||
def __init__(self, string):
|
||||
if not isinstance(string, basestring):
|
||||
if not isinstance(string, string_types):
|
||||
raise TypeError("SQL identifiers must be strings")
|
||||
|
||||
super(Identifier, self).__init__(string)
|
||||
|
@ -395,7 +396,7 @@ class Placeholder(Composable):
|
|||
"""
|
||||
|
||||
def __init__(self, name=None):
|
||||
if isinstance(name, basestring):
|
||||
if isinstance(name, string_types):
|
||||
if ')' in name:
|
||||
raise ValueError("invalid name: %r" % name)
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ def fetch_errors(versions):
|
|||
# https://github.com/postgres/postgres/commit/28e0727076
|
||||
errors['55']['55P04'] = 'UNSAFE_NEW_ENUM_VALUE_USAGE'
|
||||
|
||||
for c, cerrs in e1.iteritems():
|
||||
for c, cerrs in e1.items():
|
||||
errors[c].update(cerrs)
|
||||
|
||||
return classes, errors
|
||||
|
|
19
setup.py
19
setup.py
|
@ -41,21 +41,6 @@ from distutils.sysconfig import get_python_inc
|
|||
from distutils.ccompiler import get_default_compiler
|
||||
from distutils.util import get_platform
|
||||
|
||||
try:
|
||||
from distutils.command.build_py import build_py_2to3
|
||||
except ImportError:
|
||||
from distutils.command.build_py import build_py
|
||||
else:
|
||||
class build_py(build_py_2to3):
|
||||
# workaround subclass for ticket #153
|
||||
pass
|
||||
|
||||
# Configure distutils to run our custom 2to3 fixers as well
|
||||
from lib2to3.refactor import get_fixers_from_package
|
||||
build_py.fixer_names = [f for f in get_fixers_from_package('lib2to3.fixes')
|
||||
# creates a pending deprecation warning on py 3.4
|
||||
if not f.endswith('.fix_reload')]
|
||||
|
||||
try:
|
||||
import configparser
|
||||
except ImportError:
|
||||
|
@ -626,7 +611,5 @@ setup(name="psycopg2",
|
|||
data_files=data_files,
|
||||
package_dir={'psycopg2': 'lib'},
|
||||
packages=['psycopg2'],
|
||||
cmdclass={
|
||||
'build_ext': psycopg_build_ext,
|
||||
'build_py': build_py, },
|
||||
cmdclass={'build_ext': psycopg_build_ext},
|
||||
ext_modules=ext)
|
||||
|
|
Loading…
Reference in New Issue
Block a user