From ffcc65d4f04190551de5c71c6f106cb92f9235ab Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 10:11:42 +0200 Subject: [PATCH 01/14] Drop support for EOL Python 2.6 --- .gitignore | 1 + .travis.yml | 1 - doc/src/install.rst | 2 +- psycopg/python.h | 12 ++---------- setup.py | 20 ++++---------------- tests/test_connection.py | 3 +-- tests/test_sql.py | 2 -- tests/test_types_basic.py | 1 - tox.ini | 2 +- 9 files changed, 10 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index 409bb3a7..d78118b8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ doc/html/* doc/psycopg2.txt scripts/pypi_docs_upload.py env +.idea .tox /rel /wheels diff --git a/.travis.yml b/.travis.yml index 4d558f17..6152a4bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ language: python python: - 2.7 - 3.6 - - 2.6 - 3.5 - 3.4 - 3.3 diff --git a/doc/src/install.rst b/doc/src/install.rst index 2993c7cb..c2c245a9 100644 --- a/doc/src/install.rst +++ b/doc/src/install.rst @@ -17,7 +17,7 @@ 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 version 2.7 - Python 3 versions from 3.2 to 3.6 - PostgreSQL server versions from 7.4 to 10 - PostgreSQL client library version from 9.1 diff --git a/psycopg/python.h b/psycopg/python.h index cfb8dad3..b16c4b9d 100644 --- a/psycopg/python.h +++ b/psycopg/python.h @@ -31,8 +31,8 @@ #include #endif -#if PY_VERSION_HEX < 0x02060000 -# error "psycopg requires Python >= 2.6" +#if PY_VERSION_HEX < 0x02070000 +# error "psycopg requires Python >= 2.7" #endif /* hash() return size changed around version 3.2a4 on 64bit platforms. Before @@ -44,14 +44,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" diff --git a/setup.py b/setup.py index 6ffae80a..f010ef5b 100644 --- a/setup.py +++ b/setup.py @@ -76,7 +76,6 @@ 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 @@ -297,7 +296,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 in ((2, 7), (3, 0), (3, 1), (3, 2)): platform = get_platform() # Default to the x86 manifest manifest = '_psycopg.vc9.x86.manifest' @@ -319,7 +318,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 +333,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 +401,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 +606,7 @@ try: f = open("README.rst") readme = f.read() f.close() -except: +except Exception: print("failed to read readme: ignoring...") readme = __doc__ @@ -633,6 +620,7 @@ setup(name="psycopg2", download_url=download_url, license="LGPL with exceptions or ZPL", platforms=["any"], + python_requires='>=2.7,!=3.0.*,!=3.1.*', description=readme.split("\n")[0], long_description="\n".join(readme.split("\n")[2:]).lstrip(), classifiers=[x for x in classifiers.split("\n") if x], diff --git a/tests/test_connection.py b/tests/test_connection.py index eff10650..f13df18d 100755 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -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: diff --git a/tests/test_sql.py b/tests/test_sql.py index e35bf32d..2e12ba63 100755 --- a/tests/test_sql.py +++ b/tests/test_sql.py @@ -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) diff --git a/tests/test_types_basic.py b/tests/test_types_basic.py index 8d624c1a..b0af6daa 100755 --- a/tests/test_types_basic.py +++ b/tests/test_types_basic.py @@ -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,)) diff --git a/tox.ini b/tox.ini index 4a1129d5..17612e25 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27 +envlist = py27 [testenv] commands = make check From 53c1c5dcc1da6aa36ee9f53c5276739053b85897 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 10:22:02 +0200 Subject: [PATCH 02/14] Remove redundant hasattr checks --- setup.py | 3 +-- tests/testutils.py | 33 ++------------------------------- 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/setup.py b/setup.py index f010ef5b..efcf81d8 100644 --- a/setup.py +++ b/setup.py @@ -214,8 +214,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()) diff --git a/tests/testutils.py b/tests/testutils.py index ba31e3b0..03401891 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -32,37 +32,8 @@ 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 +skip = unittest.skip +skipIf = unittest.skipIf # Silence warnings caused by the stubbornness of the Python unittest # maintainers From 2f3c233f389fc895f65cca8168b2dd6da016a8bb Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 10:25:09 +0200 Subject: [PATCH 03/14] Simplify Boolean --- sandbox/pbool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox/pbool.py b/sandbox/pbool.py index 35ca760c..817e3ea0 100644 --- a/sandbox/pbool.py +++ b/sandbox/pbool.py @@ -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' From b69457ccdf072b20797689fe8b03a318769a96fb Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 10:30:15 +0200 Subject: [PATCH 04/14] Update to Exception as e, print() --- doc/src/errorcodes.rst | 2 +- examples/copy_from.py | 44 +++++++++++++++++++++--------------------- examples/cursor.py | 10 +++++----- examples/threads.py | 18 ++++++++--------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/doc/src/errorcodes.rst b/doc/src/errorcodes.rst index f5f2fa8a..ab49afb6 100644 --- a/doc/src/errorcodes.rst +++ b/doc/src/errorcodes.rst @@ -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]) diff --git a/examples/copy_from.py b/examples/copy_from.py index fed6bf15..e258a077 100644 --- a/examples/copy_from.py +++ b/examples/copy_from.py @@ -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() diff --git a/examples/cursor.py b/examples/cursor.py index 2d56fd73..465967c3 100644 --- a/examples/cursor.py +++ b/examples/cursor.py @@ -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() diff --git a/examples/threads.py b/examples/threads.py index d73730ce..d24b0a5d 100644 --- a/examples/threads.py +++ b/examples/threads.py @@ -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() From 955526b200f998ce00d0dadfd8fa8049ac1df284 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 10:31:03 +0200 Subject: [PATCH 05/14] Replace comparison with None with equality operator --- tests/dbapi20.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/dbapi20.py b/tests/dbapi20.py index f707c090..ff98ddc6 100644 --- a/tests/dbapi20.py +++ b/tests/dbapi20.py @@ -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) From 7282ef0d14f54812aefb47806b949ee7209812b9 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 10:32:04 +0200 Subject: [PATCH 06/14] Rewrite list creation as list literal --- tests/test_types_extras.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_types_extras.py b/tests/test_types_extras.py index 6d842d80..f50b1b5e 100755 --- a/tests/test_types_extras.py +++ b/tests/test_types_extras.py @@ -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))) From 08b479bc105af9ee50ce3d67d7661bd85fc6437f Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 15:40:25 +0200 Subject: [PATCH 07/14] __slots__ should be a tuple --- lib/extras.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/extras.py b/lib/extras.py index 8b430535..67e50b29 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -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) From 283de270987dbd17a3cf2285da60722d4b5cc001 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 10:32:55 +0200 Subject: [PATCH 08/14] Remove redundant parentheses --- lib/_json.py | 2 +- lib/extras.py | 2 +- lib/tz.py | 2 +- scripts/refcounter.py | 4 ++-- tests/test_lobject.py | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/_json.py b/lib/_json.py index 30910eba..71c2b76c 100644 --- a/lib/_json.py +++ b/lib/_json.py @@ -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: diff --git a/lib/extras.py b/lib/extras.py index 67e50b29..3308f78f 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -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]) diff --git a/lib/tz.py b/lib/tz.py index 92a16041..d593175c 100644 --- a/lib/tz.py +++ b/lib/tz.py @@ -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 diff --git a/scripts/refcounter.py b/scripts/refcounter.py index 9e900cf7..305dd1bc 100755 --- a/scripts/refcounter.py +++ b/scripts/refcounter.py @@ -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) diff --git a/tests/test_lobject.py b/tests/test_lobject.py index cf8a556d..8eafabc9 100755 --- a/tests/test_lobject.py +++ b/tests/test_lobject.py @@ -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): From ea76504cd1f8c32c0eb15d337d5e6b44af1cb301 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 10:33:31 +0200 Subject: [PATCH 09/14] Remove trailing semicolons --- sandbox/async.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sandbox/async.py b/sandbox/async.py index 17e96d01..3766fbb9 100644 --- a/sandbox/async.py +++ b/sandbox/async.py @@ -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) From c2d082e896e7bcb81231603404e7d4789e56cf00 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 11:13:38 +0200 Subject: [PATCH 10/14] Drop support for EOL Python 3.0-3.3 --- .appveyor.yml | 11 +---------- .travis.yml | 2 -- doc/src/install.rst | 2 +- setup.py | 6 ++---- 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 4f9a40c5..46f54dfe 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -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! diff --git a/.travis.yml b/.travis.yml index 6152a4bc..e6182a75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,6 @@ python: - 3.6 - 3.5 - 3.4 - - 3.3 - - 3.2 install: - python setup.py install diff --git a/doc/src/install.rst b/doc/src/install.rst index c2c245a9..8b6651da 100644 --- a/doc/src/install.rst +++ b/doc/src/install.rst @@ -18,7 +18,7 @@ The current `!psycopg2` implementation supports: NOTE: keep consistent with setup.py and the /features/ page. - Python version 2.7 -- Python 3 versions from 3.2 to 3.6 +- Python 3 versions from 3.4 to 3.6 - PostgreSQL server versions from 7.4 to 10 - PostgreSQL client library version from 9.1 diff --git a/setup.py b/setup.py index efcf81d8..264078a9 100644 --- a/setup.py +++ b/setup.py @@ -78,8 +78,6 @@ Programming Language :: Python Programming Language :: Python :: 2 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 @@ -295,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, 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' @@ -619,7 +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.*', + 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], From 60b1517c5590ec0addc60d50174d0e96566582e0 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 28 Nov 2017 13:59:54 +0200 Subject: [PATCH 11/14] Add news and update version check --- NEWS | 8 ++++++++ psycopg/python.h | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 65f4936a..7d99ed32 100644 --- a/NEWS +++ b/NEWS @@ -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/psycopg/python.h b/psycopg/python.h index b16c4b9d..fc8c2fed 100644 --- a/psycopg/python.h +++ b/psycopg/python.h @@ -31,8 +31,10 @@ #include #endif -#if PY_VERSION_HEX < 0x02070000 -# error "psycopg requires Python >= 2.7" +#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 From a0229cff8252c07eddf22626a65e3f2967e2b037 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Tue, 28 Nov 2017 15:38:27 +0000 Subject: [PATCH 12/14] Documentation tweaked to omit Python 2.6 distinctions --- doc/src/usage.rst | 14 ++++++-------- lib/sql.py | 12 ++++++------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/doc/src/usage.rst b/doc/src/usage.rst index 79823b11..5dcab8fe 100644 --- a/doc/src/usage.rst +++ b/doc/src/usage.rst @@ -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/ diff --git a/lib/sql.py b/lib/sql.py index d89e1176..5dfe7c8b 100644 --- a/lib/sql.py +++ b/lib/sql.py @@ -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:: From c3ee9cac41a7703b3f75049403621edd0702c330 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Tue, 28 Nov 2017 15:38:56 +0000 Subject: [PATCH 13/14] Dropped unused test functions --- tests/testutils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/testutils.py b/tests/testutils.py index 03401891..bd08d6c7 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -32,9 +32,6 @@ from functools import wraps from testconfig import dsn, repl_dsn -skip = unittest.skip -skipIf = unittest.skipIf - # Silence warnings caused by the stubbornness of the Python unittest # maintainers # http://bugs.python.org/issue9424 From f939f39580c46e1601e2f7c491e2afa9fdabbfbe Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Tue, 28 Nov 2017 15:47:35 +0000 Subject: [PATCH 14/14] Use dict comprehensions --- lib/_range.py | 7 ++----- lib/extensions.py | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/_range.py b/lib/_range.py index ee9c329e..c1facc0f 100644 --- a/lib/_range.py +++ b/lib/_range.py @@ -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(): diff --git a/lib/extensions.py b/lib/extensions.py index 91b81331..d15f76c9 100644 --- a/lib/extensions.py +++ b/lib/extensions.py @@ -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)