Merge branch 'rm-eol'

Fix #626, close #628.
This commit is contained in:
Daniele Varrazzo 2017-11-28 16:27:41 +00:00
commit 3e52b5445b
29 changed files with 89 additions and 156 deletions

View File

@ -15,7 +15,7 @@ environment:
# http://www.appveyor.com/docs/installed-software#python
# Py 2.7 = VS Ver. 9.0 (VS 2008)
# Py 3.3, 3.4 = VS Ver. 10.0 (VS 2010)
# Py 3.4 = VS Ver. 10.0 (VS 2010)
# Py 3.5, 3.6 = VS Ver. 14.0 (VS 2015)
- PYTHON: C:\Python27-x64
@ -51,15 +51,6 @@ environment:
PYTHON_ARCH: 32
VS_VER: 10.0
- PYTHON: C:\Python33-x64
DISTUTILS_USE_SDK: '1'
PYTHON_ARCH: 64
VS_VER: 10.0
- PYTHON: C:\Python33
PYTHON_ARCH: 32
VS_VER: 10.0
PSYCOPG2_TESTDB: psycopg2_test
PSYCOPG2_TESTDB_USER: postgres
PSYCOPG2_TESTDB_PASSWORD: Password12!

1
.gitignore vendored
View File

@ -11,6 +11,7 @@ doc/html/*
doc/psycopg2.txt
scripts/pypi_docs_upload.py
env
.idea
.tox
/rel
/wheels

View File

@ -7,11 +7,8 @@ language: python
python:
- 2.7
- 3.6
- 2.6
- 3.5
- 3.4
- 3.3
- 3.2
install:
- python setup.py install

8
NEWS
View File

@ -1,6 +1,14 @@
Current release
---------------
What's new in psycopg 2.8
-------------------------
Other changes:
- Dropped support for Python 2.6, 3.2, 3.3.
What's new in psycopg 2.7.4
^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -59,7 +59,7 @@ between 8.1 and 10 are included in the module.
>>> try:
... cur.execute("SELECT ouch FROM aargh;")
... except Exception, e:
... except Exception as e:
... pass
...
>>> errorcodes.lookup(e.pgcode[:2])

View File

@ -17,8 +17,8 @@ The current `!psycopg2` implementation supports:
..
NOTE: keep consistent with setup.py and the /features/ page.
- Python 2 versions from 2.6 to 2.7
- Python 3 versions from 3.2 to 3.6
- Python version 2.7
- Python 3 versions from 3.4 to 3.6
- PostgreSQL server versions from 7.4 to 10
- PostgreSQL client library version from 9.1

View File

@ -457,14 +457,12 @@ the connection or globally: see the function
Binary adaptation
^^^^^^^^^^^^^^^^^
Python types representing binary objects are converted into
PostgreSQL binary string syntax, suitable for :sql:`bytea` fields. Such
types are `buffer` (only available in Python 2), `memoryview` (available
from Python 2.7), `bytearray` (available from Python 2.6) and `bytes`
(only from Python 3: the name is available from Python 2.6 but it's only an
alias for the type `!str`). Any object implementing the `Revised Buffer
Protocol`__ should be usable as binary type where the protocol is supported
(i.e. from Python 2.6). Received data is returned as `!buffer` (in Python 2)
Python types representing binary objects are converted into PostgreSQL binary
string syntax, suitable for :sql:`bytea` fields. Such types are `buffer`
(only available in Python 2), `memoryview`, `bytearray`, and `bytes` (only in
Python 3: the name is available in Python 2 but it's only an alias for the
type `!str`). Any object implementing the `Revised Buffer Protocol`__ should
be usable as binary type. Received data is returned as `!buffer` (in Python 2)
or `!memoryview` (in Python 3).
.. __: http://www.python.org/dev/peps/pep-3118/

View File

@ -27,9 +27,9 @@ import psycopg2
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dsn:", DSN
print("Opening connection using dsn:", DSN)
conn = psycopg2.connect(DSN)
print "Encoding for this connection is", conn.encoding
print("Encoding for this connection is", conn.encoding)
curs = conn.cursor()
try:
@ -51,16 +51,16 @@ io.close()
io = open('copy_from.txt', 'r')
curs.copy_from(io, 'test_copy')
print "1) Copy %d records from file object " % len(data) + \
"using defaults (sep: \\t and null = \\N)"
print("1) Copy %d records from file object " % len(data) +
"using defaults (sep: \\t and null = \\N)")
io.close()
curs.execute("SELECT * FROM test_copy")
rows = curs.fetchall()
print " Select returned %d rows" % len(rows)
print(" Select returned %d rows" % len(rows))
for r in rows:
print " %s %s\t%s" % (r[0], r[1], r[2])
print(" %s %s\t%s" % (r[0], r[1], r[2]))
curs.execute("delete from test_copy")
conn.commit()
@ -75,15 +75,15 @@ io.close()
io = open('copy_from.txt', 'r')
curs.copy_from(io, 'test_copy', ':')
print "2) Copy %d records from file object using sep = :" % len(data)
print("2) Copy %d records from file object using sep = :" % len(data))
io.close()
curs.execute("SELECT * FROM test_copy")
rows = curs.fetchall()
print " Select returned %d rows" % len(rows)
print(" Select returned %d rows" % len(rows))
for r in rows:
print " %s %s\t%s" % (r[0], r[1], r[2])
print(" %s %s\t%s" % (r[0], r[1], r[2]))
curs.execute("delete from test_copy")
conn.commit()
@ -98,15 +98,15 @@ io.close()
io = open('copy_from.txt', 'r')
curs.copy_from(io, 'test_copy', null='NULL')
print "3) Copy %d records from file object using null = NULL" % len(data)
print("3) Copy %d records from file object using null = NULL" % len(data))
io.close()
curs.execute("SELECT * FROM test_copy")
rows = curs.fetchall()
print " Select using cursor returned %d rows" % len(rows)
print(" Select using cursor returned %d rows" % len(rows))
for r in rows:
print " %s %s\t%s" % (r[0], r[1], r[2])
print(" %s %s\t%s" % (r[0], r[1], r[2]))
curs.execute("delete from test_copy")
conn.commit()
@ -119,16 +119,16 @@ io.close()
io = open('copy_from.txt', 'r')
curs.copy_from(io, 'test_copy', ':', 'NULL')
print "4) Copy %d records from file object " % len(data) + \
"using sep = : and null = NULL"
print("4) Copy %d records from file object " % len(data) +
"using sep = : and null = NULL")
io.close()
curs.execute("SELECT * FROM test_copy")
rows = curs.fetchall()
print " Select using cursor returned %d rows" % len(rows)
print(" Select using cursor returned %d rows" % len(rows))
for r in rows:
print " %s %s\t%s" % (r[0], r[1], r[2])
print(" %s %s\t%s" % (r[0], r[1], r[2]))
curs.execute("delete from test_copy")
conn.commit()
@ -141,20 +141,20 @@ data.write('\n'.join(['Tom\tJenkins\t37',
data.seek(0)
curs.copy_from(data, 'test_copy')
print "5) Copy 3 records from StringIO object using defaults"
print("5) Copy 3 records from StringIO object using defaults")
curs.execute("SELECT * FROM test_copy")
rows = curs.fetchall()
print " Select using cursor returned %d rows" % len(rows)
print(" Select using cursor returned %d rows" % len(rows))
for r in rows:
print " %s %s\t%s" % (r[0], r[1], r[2])
print(" %s %s\t%s" % (r[0], r[1], r[2]))
curs.execute("delete from test_copy")
conn.commit()
# simple error test
print "6) About to raise an error"
print("6) About to raise an error")
data = StringIO.StringIO()
data.write('\n'.join(['Tom\tJenkins\t37',
'Madonna\t\N\t45',
@ -163,9 +163,9 @@ data.seek(0)
try:
curs.copy_from(data, 'test_copy')
except StandardError, err:
except StandardError as err:
conn.rollback()
print " Caught error (as expected):\n", err
print(" Caught error (as expected):\n", err)
conn.rollback()

View File

@ -25,9 +25,9 @@ import psycopg2.extensions
if len(sys.argv) > 1:
DSN = sys.argv[1]
print "Opening connection using dsn:", DSN
print("Opening connection using dsn:", DSN)
conn = psycopg2.connect(DSN)
print "Encoding for this connection is", conn.encoding
print("Encoding for this connection is", conn.encoding)
class NoDataError(psycopg2.ProgrammingError):
@ -52,12 +52,12 @@ class Cursor(psycopg2.extensions.cursor):
curs = conn.cursor(cursor_factory=Cursor)
curs.execute("SELECT 1 AS foo")
print "Result of fetchone():", curs.fetchone()
print("Result of fetchone():", curs.fetchone())
# now let's raise the exception
try:
curs.fetchone()
except NoDataError, err:
print "Exception caught:", err
except NoDataError as err:
print("Exception caught:", err)
conn.rollback()

View File

@ -84,7 +84,7 @@ def insert_func(conn_or_pool, rows):
try:
c.execute("INSERT INTO test_threads VALUES (%s, %s, %s)",
(str(i), i, float(i)))
except psycopg2.ProgrammingError, err:
except psycopg2.ProgrammingError as err:
print name, ": an error occurred; skipping this insert"
print err
conn.commit()
@ -112,10 +112,10 @@ def select_func(conn_or_pool, z):
if MODE == 1:
conn_or_pool.putconn(conn)
s = name + ": number of rows fetched: " + str(len(l))
print s
except psycopg2.ProgrammingError, err:
print name, ": an error occurred; skipping this select"
print err
print(s)
except psycopg2.ProgrammingError as err:
print(name, ": an error occurred; skipping this select")
print(err)
## create the connection pool or the connections
if MODE == 0:
@ -129,14 +129,14 @@ else:
## create the threads
threads = []
print "Creating INSERT threads:"
print("Creating INSERT threads:")
for name in INSERT_THREADS:
t = threading.Thread(None, insert_func, 'Thread-'+name,
(conn_insert, ROWS))
t.setDaemon(0)
threads.append(t)
print "Creating SELECT threads:"
print("Creating SELECT threads:")
for name in SELECT_THREADS:
t = threading.Thread(None, select_func, 'Thread-'+name,
(conn_select, SELECT_DIV))
@ -150,12 +150,12 @@ for t in threads:
# and wait for them to finish
for t in threads:
t.join()
print t.getName(), "exited OK"
print(t.getName(), "exited OK")
conn.commit()
curs.execute("SELECT count(name) FROM test_threads")
print "Inserted", curs.fetchone()[0], "rows."
print("Inserted", curs.fetchone()[0], "rows.")
curs.execute("DROP TABLE test_threads")
conn.commit()

View File

@ -194,7 +194,7 @@ def _get_json_oids(conn_or_curs, name='json'):
r = curs.fetchone()
# revert the status of the connection as before the command
if (conn_status != STATUS_IN_TRANSACTION and not conn.autocommit):
if conn_status != STATUS_IN_TRANSACTION and not conn.autocommit:
conn.rollback()
if not r:

View File

@ -181,11 +181,8 @@ class Range(object):
return self.__gt__(other)
def __getstate__(self):
return dict(
(slot, getattr(self, slot))
for slot in self.__slots__
if hasattr(self, slot)
)
return {slot: getattr(self, slot)
for slot in self.__slots__ if hasattr(self, slot)}
def __setstate__(self, state):
for slot, value in state.items():

View File

@ -163,7 +163,7 @@ def make_dsn(dsn=None, **kwargs):
kwargs['dbname'] = kwargs.pop('database')
# Drop the None arguments
kwargs = dict((k, v) for (k, v) in kwargs.iteritems() if v is not None)
kwargs = {k: v for (k, v) in kwargs.iteritems() if v is not None}
if dsn is not None:
tmp = parse_dsn(dsn)

View File

@ -264,7 +264,7 @@ class RealDictCursor(DictCursorBase):
class RealDictRow(dict):
"""A `!dict` subclass representing a data record."""
__slots__ = ('_column_mapping')
__slots__ = ('_column_mapping',)
def __init__(self, cursor):
dict.__init__(self)
@ -280,7 +280,7 @@ class RealDictRow(dict):
return dict.__setitem__(self, name, value)
def __getstate__(self):
return (self.copy(), self._column_mapping[:])
return self.copy(), self._column_mapping[:]
def __setstate__(self, data):
self.update(data[0])

View File

@ -203,12 +203,12 @@ class SQL(Composable):
:rtype: `Composed`
The method is similar to the Python `str.format()` method: the string
template supports auto-numbered (``{}``, only available from Python
2.7), numbered (``{0}``, ``{1}``...), and named placeholders
(``{name}``), with positional arguments replacing the numbered
placeholders and keywords replacing the named ones. However placeholder
modifiers (``{0!r}``, ``{0:<10}``) are not supported. Only
`!Composable` objects can be passed to the template.
template supports auto-numbered (``{}``), numbered (``{0}``,
``{1}``...), and named placeholders (``{name}``), with positional
arguments replacing the numbered placeholders and keywords replacing
the named ones. However placeholder modifiers (``{0!r}``, ``{0:<10}``)
are not supported. Only `!Composable` objects can be passed to the
template.
Example::

View File

@ -75,7 +75,7 @@ class FixedOffsetTimezone(datetime.tzinfo):
def __getinitargs__(self):
offset_mins = self._offset.seconds // 60 + self._offset.days * 24 * 60
return (offset_mins, self._name)
return offset_mins, self._name
def utcoffset(self, dt):
return self._offset

View File

@ -31,8 +31,10 @@
#include <stringobject.h>
#endif
#if PY_VERSION_HEX < 0x02060000
# error "psycopg requires Python >= 2.6"
#if ((PY_VERSION_HEX < 0x02070000) \
|| ((PY_VERSION_HEX >= 0x03000000) \
&& (PY_VERSION_HEX < 0x03040000)) )
# error "psycopg requires Python 2.7 or 3.4+"
#endif
/* hash() return size changed around version 3.2a4 on 64bit platforms. Before
@ -44,14 +46,6 @@ typedef long Py_hash_t;
typedef unsigned long Py_uhash_t;
#endif
/* Macros defined in Python 2.6 */
#ifndef Py_REFCNT
#define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt)
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size)
#define PyVarObject_HEAD_INIT(x,n) PyObject_HEAD_INIT(x) n,
#endif
/* FORMAT_CODE_PY_SSIZE_T is for Py_ssize_t: */
#define FORMAT_CODE_PY_SSIZE_T "%" PY_FORMAT_SIZE_T "d"

View File

@ -22,15 +22,15 @@ def sleep(curs):
# DECLARE zz INSENSITIVE SCROLL CURSOR WITH HOLD FOR
# SELECT now();
# FOR READ ONLY;""", async = 1)
curs.execute("SELECT now() AS foo", async=1);
curs.execute("SELECT now() AS foo", async=1)
sleep(curs)
print curs.fetchall()
#curs.execute("""
# FETCH FORWARD 1 FROM zz;""", async = 1)
curs.execute("SELECT now() AS bar", async=1);
curs.execute("SELECT now() AS bar", async=1)
print curs.fetchall()
curs.execute("SELECT now() AS bar");
curs.execute("SELECT now() AS bar")
sleep(curs)

View File

@ -6,7 +6,7 @@ class B(object):
print "ga called", attr
return object.__getattribute__(self, attr)
def _sqlquote(self):
if self._o == True:
if self._o:
return 'It is True'
else:
return 'It is False'

View File

@ -43,7 +43,7 @@ def main():
dump(i, opt)
f1 = open('debug-%02d.txt' % (opt.nruns - 1)).readlines()
f2 = open('debug-%02d.txt' % (opt.nruns)).readlines()
f2 = open('debug-%02d.txt' % opt.nruns).readlines()
for line in difflib.unified_diff(f1, f2,
"run %d" % (opt.nruns - 1), "run %d" % opt.nruns):
sys.stdout.write(line)
@ -52,7 +52,7 @@ def main():
if opt.objs:
f1 = open('objs-%02d.txt' % (opt.nruns - 1)).readlines()
f2 = open('objs-%02d.txt' % (opt.nruns)).readlines()
f2 = open('objs-%02d.txt' % opt.nruns).readlines()
for line in difflib.unified_diff(f1, f2,
"run %d" % (opt.nruns - 1), "run %d" % opt.nruns):
sys.stdout.write(line)

View File

@ -76,11 +76,8 @@ License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
License :: OSI Approved :: Zope Public License
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.6
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.2
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
@ -215,8 +212,7 @@ or with the pg_config option in 'setup.cfg'.
# Support unicode paths, if this version of Python provides the
# necessary infrastructure:
if sys.version_info[0] < 3 \
and hasattr(sys, 'getfilesystemencoding'):
if sys.version_info[0] < 3:
pg_config_path = pg_config_path.encode(
sys.getfilesystemencoding())
@ -297,7 +293,7 @@ class psycopg_build_ext(build_ext):
# For Python versions that use MSVC compiler 2008, re-insert the
# manifest into the resulting .pyd file.
if self.compiler_is_msvc() and sysVer in ((2, 6), (2, 7), (3, 0), (3, 1), (3, 2)):
if self.compiler_is_msvc() and sysVer == (2, 7):
platform = get_platform()
# Default to the x86 manifest
manifest = '_psycopg.vc9.x86.manifest'
@ -319,7 +315,6 @@ class psycopg_build_ext(build_ext):
def finalize_win32(self):
"""Finalize build system configuration on win32 platform."""
sysVer = sys.version_info[:2]
# Add compiler-specific arguments:
extra_compiler_args = []
@ -335,17 +330,6 @@ class psycopg_build_ext(build_ext):
# API code.
extra_compiler_args.append('-fno-strict-aliasing')
# Force correct C runtime library linkage:
if sysVer <= (2, 3):
# Yes: 'msvcr60', rather than 'msvcrt', is the correct value
# on the line below:
self.libraries.append('msvcr60')
elif sysVer in ((2, 4), (2, 5)):
self.libraries.append('msvcr71')
# Beyond Python 2.5, we take our chances on the default C runtime
# library, because we don't know what compiler those future
# versions of Python will use.
for extension in ext: # ext is a global list of Extension objects
extension.extra_compile_args.extend(extra_compiler_args)
# End of add-compiler-specific arguments section.
@ -414,7 +398,7 @@ class psycopg_build_ext(build_ext):
# *at least* PostgreSQL 7.4 is available (this is the only
# 7.x series supported by psycopg 2)
pgversion = pg_config_helper.query("version").split()[1]
except:
except Exception:
pgversion = "7.4.0"
verre = re.compile(
@ -619,7 +603,7 @@ try:
f = open("README.rst")
readme = f.read()
f.close()
except:
except Exception:
print("failed to read readme: ignoring...")
readme = __doc__
@ -633,6 +617,7 @@ setup(name="psycopg2",
download_url=download_url,
license="LGPL with exceptions or ZPL",
platforms=["any"],
python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*',
description=readme.split("\n")[0],
long_description="\n".join(readme.split("\n")[2:]).lstrip(),
classifiers=[x for x in classifiers.split("\n") if x],

View File

@ -762,7 +762,7 @@ class DatabaseAPI20Test(unittest.TestCase):
names=cur.fetchall()
assert len(names) == len(self.samples)
s=cur.nextset()
assert s == None,'No more return sets, should return None'
assert s is None, 'No more return sets, should return None'
finally:
self.help_nextset_tearDown(cur)

View File

@ -352,8 +352,7 @@ class ParseDsnTestCase(ConnectingTestCase):
"DSN with quoting parsed")
# Can't really use assertRaisesRegexp() here since we need to
# make sure that secret is *not* exposed in the error messgage
# (and it also requires python >= 2.7).
# make sure that secret is *not* exposed in the error message.
raised = False
try:
# unterminated quote after dbname:

View File

@ -463,9 +463,9 @@ def _has_lo64(conn):
% conn.server_version)
if 'lo64' not in psycopg2.__version__:
return (False, "this psycopg build doesn't support the lo64 API")
return False, "this psycopg build doesn't support the lo64 API"
return (True, "this server and build support the lo64 API")
return True, "this server and build support the lo64 API"
def skip_if_no_lo64(f):

View File

@ -32,7 +32,6 @@ from psycopg2 import sql
class SqlFormatTests(ConnectingTestCase):
@skip_before_python(2, 7)
def test_pos(self):
s = sql.SQL("select {} from {}").format(
sql.Identifier('field'), sql.Identifier('table'))
@ -91,7 +90,6 @@ class SqlFormatTests(ConnectingTestCase):
def test_compose_badnargs(self):
self.assertRaises(IndexError, sql.SQL("select {0};").format)
@skip_before_python(2, 7)
def test_compose_badnargs_auto(self):
self.assertRaises(IndexError, sql.SQL("select {};").format)
self.assertRaises(ValueError, sql.SQL("select {} {1};").format, 10, 20)

View File

@ -295,7 +295,6 @@ class TypesBasicTests(ConnectingTestCase):
else:
self.assertEqual(memoryview, type(o2))
@testutils.skip_before_python(2, 7)
def testAdaptMemoryview(self):
o1 = memoryview(bytearray(range(256)))
o2 = self.execute("select %s;", (o1,))

View File

@ -365,9 +365,7 @@ class HstoreTestCase(ConnectingTestCase):
from psycopg2.extras import register_hstore
register_hstore(self.conn)
ds = []
ds.append({})
ds.append({'a': 'b', 'c': None})
ds = [{}, {'a': 'b', 'c': None}]
ab = map(chr, range(32, 128))
ds.append(dict(zip(ab, ab)))

View File

@ -32,38 +32,6 @@ from functools import wraps
from testconfig import dsn, repl_dsn
if hasattr(unittest, 'skipIf'):
skip = unittest.skip
skipIf = unittest.skipIf
else:
import warnings
def skipIf(cond, msg):
def skipIf_(f):
@wraps(f)
def skipIf__(self):
if cond:
with warnings.catch_warnings():
warnings.simplefilter('always', UserWarning)
warnings.warn(msg)
return
else:
return f(self)
return skipIf__
return skipIf_
def skip(msg):
return skipIf(True, msg)
def skipTest(self, msg):
with warnings.catch_warnings():
warnings.simplefilter('always', UserWarning)
warnings.warn(msg)
return
unittest.TestCase.skipTest = skipTest
# Silence warnings caused by the stubbornness of the Python unittest
# maintainers
# http://bugs.python.org/issue9424

View File

@ -4,7 +4,7 @@
# and then run "tox" from this directory.
[tox]
envlist = py26, py27
envlist = py27
[testenv]
commands = make check