From 8baf6aa37293c75e8ffbf150f6504faa43a08045 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Thu, 2 Feb 2017 19:03:22 +0000 Subject: [PATCH 1/3] Convert warnings into errors on test --- setup.py | 6 +++++- tests/__init__.py | 5 +++++ tests/test_errcodes.py | 5 ++++- tests/test_types_extras.py | 11 +++++++++-- tests/testutils.py | 8 ++++++-- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 1c5c8597..d251f988 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,11 @@ else: # workaround subclass for ticket #153 pass - sys.path.insert(0, 'scripts') + # 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 diff --git a/tests/__init__.py b/tests/__init__.py index 1a240994..5382afe8 100755 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -22,6 +22,11 @@ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. +# Convert warnings into errors here. We can't do it with -W because on +# Travis importing site raises a warning. +import warnings +warnings.simplefilter('error') # noqa + import sys from testconfig import dsn from testutils import unittest diff --git a/tests/test_errcodes.py b/tests/test_errcodes.py index 6865194f..3e7ed81a 100755 --- a/tests/test_errcodes.py +++ b/tests/test_errcodes.py @@ -27,7 +27,10 @@ from testutils import unittest, ConnectingTestCase try: reload except NameError: - from imp import reload + try: + from importlib import reload + except ImportError: + from imp import reload from threading import Thread from psycopg2 import errorcodes diff --git a/tests/test_types_extras.py b/tests/test_types_extras.py index 8e615616..7249ae04 100755 --- a/tests/test_types_extras.py +++ b/tests/test_types_extras.py @@ -17,6 +17,7 @@ from __future__ import with_statement import re import sys +import warnings from decimal import Decimal from datetime import date, datetime from functools import wraps @@ -77,7 +78,10 @@ class TypesExtrasTests(ConnectingTestCase): self.failUnless(type(s) == list and len(s) == 0) def testINET(self): - psycopg2.extras.register_inet() + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + psycopg2.extras.register_inet() + i = psycopg2.extras.Inet("192.168.1.0/24") s = self.execute("SELECT %s AS foo", (i,)) self.failUnless(i.addr == s.addr) @@ -86,7 +90,10 @@ class TypesExtrasTests(ConnectingTestCase): self.failUnless(s is None) def testINETARRAY(self): - psycopg2.extras.register_inet() + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + psycopg2.extras.register_inet() + i = psycopg2.extras.Inet("192.168.1.0/24") s = self.execute("SELECT %s AS foo", ([i],)) self.failUnless(i.addr == s[0].addr) diff --git a/tests/testutils.py b/tests/testutils.py index 93477357..d63dc00b 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -50,7 +50,9 @@ else: @wraps(f) def skipIf__(self): if cond: - warnings.warn(msg) + with warnings.catch_warnings(): + warnings.simplefilter('always', UserWarning) + warnings.warn(msg) return else: return f(self) @@ -61,7 +63,9 @@ else: return skipIf(True, msg) def skipTest(self, msg): - warnings.warn(msg) + with warnings.catch_warnings(): + warnings.simplefilter('always', UserWarning) + warnings.warn(msg) return unittest.TestCase.skipTest = skipTest From ce9be69615c700a0472098c5db171d33091c0b5a Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Fri, 3 Feb 2017 04:28:27 +0000 Subject: [PATCH 2/3] Added async_ as an alias for async Added in argument for psycopg2.connect() and connection.__init__, and for the connection.async attribute. --- lib/__init__.py | 14 ++- psycopg/connection_type.c | 10 +- psycopg/psycopgmodule.c | 10 +- tests/__init__.py | 5 + tests/test_async.py | 16 +-- tests/test_async_keyword.py | 217 ++++++++++++++++++++++++++++++++++++ tests/test_cancel.py | 4 +- tests/test_connection.py | 13 +-- tests/test_module.py | 35 +++--- tests/test_replication.py | 2 +- tests/testutils.py | 6 +- 11 files changed, 282 insertions(+), 50 deletions(-) create mode 100755 tests/test_async_keyword.py diff --git a/lib/__init__.py b/lib/__init__.py index fb22b4c0..492b924d 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -82,8 +82,7 @@ else: del Decimal, Adapter -def connect(dsn=None, connection_factory=None, cursor_factory=None, - async=False, **kwargs): +def connect(dsn=None, connection_factory=None, cursor_factory=None, **kwargs): """ Create a new database connection. @@ -111,17 +110,24 @@ def connect(dsn=None, connection_factory=None, cursor_factory=None, Using the *cursor_factory* parameter, a new default cursor factory will be used by cursor(). - Using *async*=True an asynchronous connection will be created. + Using *async*=True an asynchronous connection will be created. *async_* is + a valid alias (for Python versions where ``async`` is a keyword). Any other keyword parameter will be passed to the underlying client library: the list of supported parameters depends on the library version. """ + kwasync = {} + if 'async' in kwargs: + kwasync['async'] = kwargs.pop('async') + if 'async_' in kwargs: + kwasync['async_'] = kwargs.pop('async_') + if dsn is None and not kwargs: raise TypeError('missing dsn and no parameters') dsn = _ext.make_dsn(dsn, **kwargs) - conn = _connect(dsn, connection_factory=connection_factory, async=async) + conn = _connect(dsn, connection_factory=connection_factory, **kwasync) if cursor_factory is not None: conn.cursor_factory = cursor_factory diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 7401bc14..2066579e 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -1040,6 +1040,8 @@ static struct PyMemberDef connectionObject_members[] = { "The current connection string."}, {"async", T_LONG, offsetof(connectionObject, async), READONLY, "True if the connection is asynchronous."}, + {"async_", T_LONG, offsetof(connectionObject, async), READONLY, + "True if the connection is asynchronous."}, {"status", T_INT, offsetof(connectionObject, status), READONLY, "The current transaction status."}, @@ -1186,12 +1188,14 @@ static int connection_init(PyObject *obj, PyObject *args, PyObject *kwds) { const char *dsn; - long int async = 0; - static char *kwlist[] = {"dsn", "async", NULL}; + long int async = 0, async_ = 0; + static char *kwlist[] = {"dsn", "async", "async_", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|l", kwlist, &dsn, &async)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ll", kwlist, + &dsn, &async, &async_)) return -1; + if (async_) { async = async_; } return connection_setup((connectionObject *)obj, dsn, async); } diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index c4d1517a..6c95bd69 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -82,15 +82,17 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds) PyObject *conn = NULL; PyObject *factory = NULL; const char *dsn = NULL; - int async = 0; + int async = 0, async_ = 0; - static char *kwlist[] = {"dsn", "connection_factory", "async", NULL}; + static char *kwlist[] = {"dsn", "connection_factory", "async", "async_", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|Oi", kwlist, - &dsn, &factory, &async)) { + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|Oii", kwlist, + &dsn, &factory, &async, &async_)) { return NULL; } + if (async_) { async = async_; } + Dprintf("psyco_connect: dsn = '%s', async = %d", dsn, async); /* allocate connection, fill with errors and return it */ diff --git a/tests/__init__.py b/tests/__init__.py index 5382afe8..2b3a6a4d 100755 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -54,6 +54,9 @@ import test_types_basic import test_types_extras import test_with +if sys.version_info[:2] < (3, 6): + import test_async_keyword + def test_suite(): # If connection to test db fails, bail out early. @@ -69,6 +72,8 @@ def test_suite(): suite = unittest.TestSuite() suite.addTest(test_async.test_suite()) + if sys.version_info[:2] < (3, 6): + suite.addTest(test_async_keyword.test_suite()) suite.addTest(test_bugX000.test_suite()) suite.addTest(test_bug_gc.test_suite()) suite.addTest(test_cancel.test_suite()) diff --git a/tests/test_async.py b/tests/test_async.py index 6f8fed58..cdf17225 100755 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -55,7 +55,7 @@ class AsyncTests(ConnectingTestCase): ConnectingTestCase.setUp(self) self.sync_conn = self.conn - self.conn = self.connect(async=True) + self.conn = self.connect(async_=True) self.wait(self.conn) @@ -71,8 +71,8 @@ class AsyncTests(ConnectingTestCase): sync_cur = self.sync_conn.cursor() del cur, sync_cur - self.assert_(self.conn.async) - self.assert_(not self.sync_conn.async) + self.assert_(self.conn.async_) + self.assert_(not self.sync_conn.async_) # the async connection should be in isolevel 0 self.assertEquals(self.conn.isolation_level, 0) @@ -310,12 +310,12 @@ class AsyncTests(ConnectingTestCase): def test_async_subclass(self): class MyConn(psycopg2.extensions.connection): - def __init__(self, dsn, async=0): - psycopg2.extensions.connection.__init__(self, dsn, async=async) + def __init__(self, dsn, async_=0): + psycopg2.extensions.connection.__init__(self, dsn, async_=async_) - conn = self.connect(connection_factory=MyConn, async=True) + conn = self.connect(connection_factory=MyConn, async_=True) self.assert_(isinstance(conn, MyConn)) - self.assert_(conn.async) + self.assert_(conn.async_) conn.close() def test_flush_on_write(self): @@ -438,7 +438,7 @@ class AsyncTests(ConnectingTestCase): def test_async_connection_error_message(self): try: - cnn = psycopg2.connect('dbname=thisdatabasedoesntexist', async=True) + cnn = psycopg2.connect('dbname=thisdatabasedoesntexist', async_=True) self.wait(cnn) except psycopg2.Error, e: self.assertNotEqual(str(e), "asynchronous connection failed", diff --git a/tests/test_async_keyword.py b/tests/test_async_keyword.py new file mode 100755 index 00000000..3b20e8d6 --- /dev/null +++ b/tests/test_async_keyword.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# test_async_keyword.py - test for objects using 'async' as attribute/param +# +# Copyright (C) 2017 Daniele Varrazzo +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. + +import psycopg2 +from psycopg2 import extras + +from testconfig import dsn +from testutils import (ConnectingTestCase, unittest, skip_before_postgres, + assertDsnEqual) +from test_replication import ReplicationTestCase, skip_repl_if_green +from psycopg2.extras import LogicalReplicationConnection, StopReplication + + +class AsyncTests(ConnectingTestCase): + def setUp(self): + ConnectingTestCase.setUp(self) + + self.sync_conn = self.conn + self.conn = self.connect(async=True) + + self.wait(self.conn) + + curs = self.conn.cursor() + curs.execute(''' + CREATE TEMPORARY TABLE table1 ( + id int PRIMARY KEY + )''') + self.wait(curs) + + def test_connection_setup(self): + cur = self.conn.cursor() + sync_cur = self.sync_conn.cursor() + del cur, sync_cur + + self.assert_(self.conn.async) + self.assert_(not self.sync_conn.async) + + # the async connection should be in isolevel 0 + self.assertEquals(self.conn.isolation_level, 0) + + # check other properties to be found on the connection + self.assert_(self.conn.server_version) + self.assert_(self.conn.protocol_version in (2, 3)) + self.assert_(self.conn.encoding in psycopg2.extensions.encodings) + + def test_async_subclass(self): + class MyConn(psycopg2.extensions.connection): + def __init__(self, dsn, async=0): + psycopg2.extensions.connection.__init__(self, dsn, async=async) + + conn = self.connect(connection_factory=MyConn, async=True) + self.assert_(isinstance(conn, MyConn)) + self.assert_(conn.async) + conn.close() + + def test_async_connection_error_message(self): + try: + cnn = psycopg2.connect('dbname=thisdatabasedoesntexist', async=True) + self.wait(cnn) + except psycopg2.Error, e: + self.assertNotEqual(str(e), "asynchronous connection failed", + "connection error reason lost") + else: + self.fail("no exception raised") + + +class CancelTests(ConnectingTestCase): + def setUp(self): + ConnectingTestCase.setUp(self) + + cur = self.conn.cursor() + cur.execute(''' + CREATE TEMPORARY TABLE table1 ( + id int PRIMARY KEY + )''') + self.conn.commit() + + @skip_before_postgres(8, 2) + def test_async_cancel(self): + async_conn = psycopg2.connect(dsn, async=True) + self.assertRaises(psycopg2.OperationalError, async_conn.cancel) + extras.wait_select(async_conn) + cur = async_conn.cursor() + cur.execute("select pg_sleep(10000)") + self.assertTrue(async_conn.isexecuting()) + async_conn.cancel() + self.assertRaises(psycopg2.extensions.QueryCanceledError, + extras.wait_select, async_conn) + cur.execute("select 1") + extras.wait_select(async_conn) + self.assertEqual(cur.fetchall(), [(1, )]) + + def test_async_connection_cancel(self): + async_conn = psycopg2.connect(dsn, async=True) + async_conn.close() + self.assertTrue(async_conn.closed) + + +class ConnectTestCase(unittest.TestCase): + def setUp(self): + self.args = None + + def connect_stub(dsn, connection_factory=None, async=False): + self.args = (dsn, connection_factory, async) + + self._connect_orig = psycopg2._connect + psycopg2._connect = connect_stub + + def tearDown(self): + psycopg2._connect = self._connect_orig + + def test_there_has_to_be_something(self): + self.assertRaises(TypeError, psycopg2.connect) + self.assertRaises(TypeError, psycopg2.connect, + connection_factory=lambda dsn, async=False: None) + self.assertRaises(TypeError, psycopg2.connect, + async=True) + + def test_factory(self): + def f(dsn, async=False): + pass + + psycopg2.connect(database='foo', host='baz', connection_factory=f) + assertDsnEqual(self, self.args[0], 'dbname=foo host=baz') + self.assertEqual(self.args[1], f) + self.assertEqual(self.args[2], False) + + psycopg2.connect("dbname=foo host=baz", connection_factory=f) + assertDsnEqual(self, self.args[0], 'dbname=foo host=baz') + self.assertEqual(self.args[1], f) + self.assertEqual(self.args[2], False) + + def test_async(self): + psycopg2.connect(database='foo', host='baz', async=1) + assertDsnEqual(self, self.args[0], 'dbname=foo host=baz') + self.assertEqual(self.args[1], None) + self.assert_(self.args[2]) + + psycopg2.connect("dbname=foo host=baz", async=True) + assertDsnEqual(self, self.args[0], 'dbname=foo host=baz') + self.assertEqual(self.args[1], None) + self.assert_(self.args[2]) + + +class AsyncReplicationTest(ReplicationTestCase): + @skip_before_postgres(9, 4) # slots require 9.4 + @skip_repl_if_green + def test_async_replication(self): + conn = self.repl_connect( + connection_factory=LogicalReplicationConnection, async=1) + if conn is None: + return + + cur = conn.cursor() + + self.create_replication_slot(cur, output_plugin='test_decoding') + self.wait(cur) + + cur.start_replication(self.slot) + self.wait(cur) + + self.make_replication_events() + + self.msg_count = 0 + + def consume(msg): + # just check the methods + "%s: %s" % (cur.io_timestamp, repr(msg)) + + self.msg_count += 1 + if self.msg_count > 3: + cur.send_feedback(reply=True) + raise StopReplication() + + cur.send_feedback(flush_lsn=msg.data_start) + + # cannot be used in asynchronous mode + self.assertRaises(psycopg2.ProgrammingError, cur.consume_stream, consume) + + def process_stream(): + from select import select + while True: + msg = cur.read_message() + if msg: + consume(msg) + else: + select([cur], [], []) + self.assertRaises(StopReplication, process_stream) + + +def test_suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_cancel.py b/tests/test_cancel.py index a8eb7506..6f66ef77 100755 --- a/tests/test_cancel.py +++ b/tests/test_cancel.py @@ -87,7 +87,7 @@ class CancelTests(ConnectingTestCase): @skip_before_postgres(8, 2) def test_async_cancel(self): - async_conn = psycopg2.connect(dsn, async=True) + async_conn = psycopg2.connect(dsn, async_=True) self.assertRaises(psycopg2.OperationalError, async_conn.cancel) extras.wait_select(async_conn) cur = async_conn.cursor() @@ -101,7 +101,7 @@ class CancelTests(ConnectingTestCase): self.assertEqual(cur.fetchall(), [(1, )]) def test_async_connection_cancel(self): - async_conn = psycopg2.connect(dsn, async=True) + async_conn = psycopg2.connect(dsn, async_=True) async_conn.close() self.assertTrue(async_conn.closed) diff --git a/tests/test_connection.py b/tests/test_connection.py index 833751b9..68e4da8c 100755 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -33,7 +33,7 @@ import psycopg2.errorcodes from psycopg2 import extensions as ext from testutils import ( - unittest, decorate_all_tests, skip_if_no_superuser, + unittest, assertDsnEqual, decorate_all_tests, skip_if_no_superuser, skip_before_postgres, skip_after_postgres, skip_before_libpq, ConnectingTestCase, skip_if_tpc_disabled, skip_if_windows) @@ -392,9 +392,6 @@ class ParseDsnTestCase(ConnectingTestCase): class MakeDsnTestCase(ConnectingTestCase): - def assertDsnEqual(self, dsn1, dsn2): - self.assertEqual(set(dsn1.split()), set(dsn2.split())) - def test_empty_arguments(self): self.assertEqual(ext.make_dsn(), '') @@ -412,7 +409,7 @@ class MakeDsnTestCase(ConnectingTestCase): def test_empty_param(self): dsn = ext.make_dsn(dbname='sony', password='') - self.assertDsnEqual(dsn, "dbname=sony password=''") + assertDsnEqual(self, dsn, "dbname=sony password=''") def test_escape(self): dsn = ext.make_dsn(dbname='hello world') @@ -435,10 +432,10 @@ class MakeDsnTestCase(ConnectingTestCase): def test_params_merging(self): dsn = ext.make_dsn('dbname=foo host=bar', host='baz') - self.assertDsnEqual(dsn, 'dbname=foo host=baz') + assertDsnEqual(self, dsn, 'dbname=foo host=baz') dsn = ext.make_dsn('dbname=foo', user='postgres') - self.assertDsnEqual(dsn, 'dbname=foo user=postgres') + assertDsnEqual(self, dsn, 'dbname=foo user=postgres') def test_no_dsn_munging(self): dsnin = 'dbname=a host=b user=c password=d' @@ -452,7 +449,7 @@ class MakeDsnTestCase(ConnectingTestCase): self.assertEqual(dsn, url) dsn = ext.make_dsn(url, application_name='woot') - self.assertDsnEqual(dsn, + assertDsnEqual(self, dsn, 'dbname=test user=tester password=secret application_name=woot') self.assertRaises(psycopg2.ProgrammingError, diff --git a/tests/test_module.py b/tests/test_module.py index 6a1606d6..a5d36e65 100755 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -26,8 +26,8 @@ import os import sys from subprocess import Popen -from testutils import unittest, skip_before_python, skip_before_postgres -from testutils import ConnectingTestCase, skip_copy_if_green, script_to_py3 +from testutils import (unittest, skip_before_python, skip_before_postgres, + ConnectingTestCase, skip_copy_if_green, script_to_py3, assertDsnEqual) import psycopg2 @@ -36,24 +36,21 @@ class ConnectTestCase(unittest.TestCase): def setUp(self): self.args = None - def conect_stub(dsn, connection_factory=None, async=False): - self.args = (dsn, connection_factory, async) + def connect_stub(dsn, connection_factory=None, async_=False): + self.args = (dsn, connection_factory, async_) self._connect_orig = psycopg2._connect - psycopg2._connect = conect_stub + psycopg2._connect = connect_stub def tearDown(self): psycopg2._connect = self._connect_orig - def assertDsnEqual(self, dsn1, dsn2): - self.assertEqual(set(dsn1.split()), set(dsn2.split())) - def test_there_has_to_be_something(self): self.assertRaises(TypeError, psycopg2.connect) self.assertRaises(TypeError, psycopg2.connect, - connection_factory=lambda dsn, async=False: None) + connection_factory=lambda dsn, async_=False: None) self.assertRaises(TypeError, psycopg2.connect, - async=True) + async_=True) def test_no_keywords(self): psycopg2.connect('') @@ -92,27 +89,27 @@ class ConnectTestCase(unittest.TestCase): self.assertEqual(self.args[0], 'options=stuff') def test_factory(self): - def f(dsn, async=False): + def f(dsn, async_=False): pass psycopg2.connect(database='foo', host='baz', connection_factory=f) - self.assertDsnEqual(self.args[0], 'dbname=foo host=baz') + assertDsnEqual(self, self.args[0], 'dbname=foo host=baz') self.assertEqual(self.args[1], f) self.assertEqual(self.args[2], False) psycopg2.connect("dbname=foo host=baz", connection_factory=f) - self.assertDsnEqual(self.args[0], 'dbname=foo host=baz') + assertDsnEqual(self, self.args[0], 'dbname=foo host=baz') self.assertEqual(self.args[1], f) self.assertEqual(self.args[2], False) def test_async(self): - psycopg2.connect(database='foo', host='baz', async=1) - self.assertDsnEqual(self.args[0], 'dbname=foo host=baz') + psycopg2.connect(database='foo', host='baz', async_=1) + assertDsnEqual(self, self.args[0], 'dbname=foo host=baz') self.assertEqual(self.args[1], None) self.assert_(self.args[2]) - psycopg2.connect("dbname=foo host=baz", async=True) - self.assertDsnEqual(self.args[0], 'dbname=foo host=baz') + psycopg2.connect("dbname=foo host=baz", async_=True) + assertDsnEqual(self, self.args[0], 'dbname=foo host=baz') self.assertEqual(self.args[1], None) self.assert_(self.args[2]) @@ -124,7 +121,7 @@ class ConnectTestCase(unittest.TestCase): def test_empty_param(self): psycopg2.connect(database='sony', password='') - self.assertDsnEqual(self.args[0], "dbname=sony password=''") + assertDsnEqual(self, self.args[0], "dbname=sony password=''") def test_escape(self): psycopg2.connect(database='hello world') @@ -147,7 +144,7 @@ class ConnectTestCase(unittest.TestCase): self.assertEqual(self.args[0], 'dbname=bar') psycopg2.connect('dbname=foo', user='postgres') - self.assertDsnEqual(self.args[0], 'dbname=foo user=postgres') + assertDsnEqual(self, self.args[0], 'dbname=foo user=postgres') class ExceptionsTestCase(ConnectingTestCase): diff --git a/tests/test_replication.py b/tests/test_replication.py index 79d1295d..0aed578f 100755 --- a/tests/test_replication.py +++ b/tests/test_replication.py @@ -183,7 +183,7 @@ class AsyncReplicationTest(ReplicationTestCase): @skip_repl_if_green def test_async_replication(self): conn = self.repl_connect( - connection_factory=LogicalReplicationConnection, async=1) + connection_factory=LogicalReplicationConnection, async_=1) if conn is None: return diff --git a/tests/testutils.py b/tests/testutils.py index d63dc00b..69dfe85c 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -134,7 +134,7 @@ class ConnectingTestCase(unittest.TestCase): import psycopg2 try: conn = self.connect(**kwargs) - if conn.async == 1: + if conn.async_ == 1: self.wait(conn) except psycopg2.OperationalError, e: # If pgcode is not set it is a genuine connection error @@ -459,3 +459,7 @@ class py3_raises_typeerror(object): if sys.version_info[0] >= 3: assert type is TypeError return True + + +def assertDsnEqual(testsuite, dsn1, dsn2): + testsuite.assertEqual(set(dsn1.split()), set(dsn2.split())) From 44c3b776917444ca98667f5d6f4470c767f820bf Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Fri, 3 Feb 2017 04:39:20 +0000 Subject: [PATCH 3/3] Added docs for async_ alias --- NEWS | 2 ++ doc/src/connection.rst | 4 ++++ doc/src/extensions.rst | 2 ++ doc/src/module.rst | 6 +++++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index b4d11e64..e51b706c 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,8 @@ New features: unicode encoding/decoding (:ticket:`#473`). - Fixed error caused by missing decoding `~psycopg2.extras.LoggingConnection` (:ticket:`#483`). +- Added ``async_`` as an alias for ``async`` to support Python 3.7 where + ``async`` will become a keyword (:ticket:`#495`). Other changes: diff --git a/doc/src/connection.rst b/doc/src/connection.rst index c99c8bd8..0bc584ca 100644 --- a/doc/src/connection.rst +++ b/doc/src/connection.rst @@ -706,9 +706,13 @@ The ``connection`` class .. attribute:: async + async_ Read only attribute: 1 if the connection is asynchronous, 0 otherwise. + .. versionchanged:: 2.7 added the `!async_` alias for Python versions + where `!async` is a keyword. + .. method:: poll() diff --git a/doc/src/extensions.rst b/doc/src/extensions.rst index 9c5a8538..8d70ba38 100644 --- a/doc/src/extensions.rst +++ b/doc/src/extensions.rst @@ -29,6 +29,8 @@ introspection etc. For a complete description of the class, see `connection`. + .. versionchanged:: 2.7 + *async_* can be used as alias for *async*. .. class:: cursor(conn, name=None) diff --git a/doc/src/module.rst b/doc/src/module.rst index 97fbdf19..2bd259e3 100644 --- a/doc/src/module.rst +++ b/doc/src/module.rst @@ -64,7 +64,8 @@ The module interface respects the standard defined in the |DBAPI|_. cursors you can use this parameter instead of subclassing a connection. Using *async*\=\ `!True` an asynchronous connection will be created: see - :ref:`async-support` to know about advantages and limitations. + :ref:`async-support` to know about advantages and limitations. *async_* is + a valid alias for the Python version where ``async`` is a keyword. .. versionchanged:: 2.4.3 any keyword argument is passed to the connection. Previously only the @@ -76,6 +77,9 @@ The module interface respects the standard defined in the |DBAPI|_. .. versionchanged:: 2.7 both *dsn* and keyword arguments can be specified. + .. versionchanged:: 2.7 + added *async_* alias. + .. seealso:: - `~psycopg2.extensions.parse_dsn`