diff options
author | Hugo van Kemenade <hugovk@users.noreply.github.com> | 2020-11-17 20:39:39 +0200 |
---|---|---|
committer | Hugo van Kemenade <hugovk@users.noreply.github.com> | 2020-11-17 22:22:11 +0200 |
commit | 6c48b63ae4a70da6dbbc5b6eef20806d7f505950 (patch) | |
tree | 6976b00090753bf0f64a5b4f3745e71f96f0d2c0 | |
parent | b05a5819317ab19fc7749e34fb67f4604d9bd10a (diff) | |
download | psycopg2-6c48b63ae4a70da6dbbc5b6eef20806d7f505950.tar.gz |
Drop support for EOL Python 2.7
34 files changed, 100 insertions, 674 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 0a1603b..a6c0777 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -19,8 +19,6 @@ environment: - {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "37", PY_ARCH: "64"} - {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "36", PY_ARCH: "32"} - {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "36", PY_ARCH: "64"} - - {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "27", PY_ARCH: "32"} - - {APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015, PY_VER: "27", PY_ARCH: "64"} OPENSSL_VERSION: "1_1_1h" POSTGRES_VERSION: "11_4" diff --git a/.travis.yml b/.travis.yml index 84dd605..dc615b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,10 +13,6 @@ python: - 3.7 - 3.8 -matrix: - include: - - python: 2.7 - install: - sudo apt-get install -y bc - pip install -U pip setuptools wheel diff --git a/lib/_ipaddress.py b/lib/_ipaddress.py index 9350c8d..4094c27 100644 --- a/lib/_ipaddress.py +++ b/lib/_ipaddress.py @@ -26,7 +26,6 @@ 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 @@ -78,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(text_type(s)) + return ipaddress.ip_interface(str(s)) def cast_network(s, cur=None): if s is None: return None - return ipaddress.ip_network(text_type(s)) + return ipaddress.ip_network(str(s)) def adapt_ipaddress(obj): diff --git a/lib/_json.py b/lib/_json.py index eac3797..e5958b9 100644 --- a/lib/_json.py +++ b/lib/_json.py @@ -32,7 +32,6 @@ import json from psycopg2._psycopg import ISQLQuote, QuotedString from psycopg2._psycopg import new_type, new_array_type, register_type -from psycopg2.compat import PY2 # oids from PostgreSQL 9.2 @@ -82,13 +81,9 @@ class Json(object): qs.prepare(self._conn) return qs.getquoted() - if PY2: - def __str__(self): - return self.getquoted() - else: - def __str__(self): - # getquoted is binary in Py3 - return self.getquoted().decode('ascii', 'replace') + def __str__(self): + # getquoted is binary + return self.getquoted().decode('ascii', 'replace') def register_json(conn_or_curs=None, globally=False, loads=None, diff --git a/lib/_range.py b/lib/_range.py index b668fb6..499f501 100644 --- a/lib/_range.py +++ b/lib/_range.py @@ -30,7 +30,6 @@ 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): @@ -315,7 +314,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, string_types): + if isinstance(pgrange, str): self.adapter = type(pgrange, (RangeAdapter,), {}) self.adapter.name = pgrange else: @@ -332,7 +331,7 @@ class RangeCaster(object): self.range = None try: - if isinstance(pyrange, string_types): + if isinstance(pyrange, str): self.range = type(pyrange, (Range,), {}) if issubclass(pyrange, Range) and pyrange is not Range: self.range = pyrange diff --git a/lib/compat.py b/lib/compat.py deleted file mode 100644 index 54606a8..0000000 --- a/lib/compat.py +++ /dev/null @@ -1,19 +0,0 @@ -import sys - -__all__ = ['string_types', 'text_type', 'lru_cache'] - -if sys.version_info[0] == 2: - # Python 2 - PY2 = True - PY3 = False - string_types = basestring, - text_type = unicode - from ._lru_cache import lru_cache - -else: - # Python 3 - PY2 = False - PY3 = True - string_types = str, - text_type = str - from functools import lru_cache diff --git a/lib/extras.py b/lib/extras.py index 135a3fb..3b42be7 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -38,7 +38,7 @@ from psycopg2 import extensions as _ext from .extensions import cursor as _cursor from .extensions import connection as _connection from .extensions import adapt as _A, quote_ident -from .compat import PY2, PY3, lru_cache +from functools import lru_cache from psycopg2._psycopg import ( # noqa REPLICATION_PHYSICAL, REPLICATION_LOGICAL, @@ -210,21 +210,6 @@ class DictRow(list): self[:] = data[0] self._index = data[1] - if PY2: - iterkeys = keys - itervalues = values - iteritems = items - has_key = __contains__ - - def keys(self): - return list(self.iterkeys()) - - def values(self): - return tuple(self.itervalues()) - - def items(self): - return list(self.iteritems()) - class RealDictConnection(_connection): """A connection that uses `RealDictCursor` automatically.""" @@ -436,7 +421,7 @@ class LoggingConnection(_connection): def _logtofile(self, msg, curs): msg = self.filter(msg, curs) if msg: - if PY3 and isinstance(msg, bytes): + if isinstance(msg, bytes): msg = msg.decode(_ext.encodings[self.encoding], 'replace') self._logobj.write(msg + _os.linesep) @@ -490,7 +475,7 @@ class MinTimeLoggingConnection(LoggingConnection): def filter(self, msg, curs): t = (_time.time() - curs.timestamp) * 1000 if t > self._mintime: - if PY3 and isinstance(msg, bytes): + if isinstance(msg, bytes): msg = msg.decode(_ext.encodings[self.encoding], 'replace') return msg + _os.linesep + " (execution time: %d ms)" % t @@ -993,12 +978,7 @@ def register_hstore(conn_or_curs, globally=False, unicode=False, array_oid = tuple([x for x in array_oid if x]) # create and register the typecaster - if PY2 and unicode: - cast = HstoreAdapter.parse_unicode - else: - cast = HstoreAdapter.parse - - HSTORE = _ext.new_type(oid, "HSTORE", cast) + HSTORE = _ext.new_type(oid, "HSTORE", HstoreAdapter.parse) _ext.register_type(HSTORE, not globally and conn_or_curs or None) _ext.register_adapter(dict, HstoreAdapter) @@ -27,7 +27,6 @@ import string from psycopg2 import extensions as ext -from psycopg2.compat import PY3, string_types _formatter = string.Formatter() @@ -148,7 +147,7 @@ class Composed(Composable): "foo", "bar" """ - if isinstance(joiner, string_types): + if isinstance(joiner, str): joiner = SQL(joiner) elif not isinstance(joiner, SQL): raise TypeError( @@ -180,7 +179,7 @@ class SQL(Composable): select "foo", "bar" from "table" """ def __init__(self, string): - if not isinstance(string, string_types): + if not isinstance(string, str): raise TypeError("SQL values must be strings") super(SQL, self).__init__(string) @@ -324,7 +323,7 @@ class Identifier(Composable): raise TypeError("Identifier cannot be empty") for s in strings: - if not isinstance(s, string_types): + if not isinstance(s, str): raise TypeError("SQL identifier parts must be strings") super(Identifier, self).__init__(strings) @@ -392,7 +391,7 @@ class Literal(Composable): a.prepare(conn) rv = a.getquoted() - if PY3 and isinstance(rv, bytes): + if isinstance(rv, bytes): rv = rv.decode(ext.encodings[conn.encoding]) return rv @@ -426,7 +425,7 @@ class Placeholder(Composable): """ def __init__(self, name=None): - if isinstance(name, string_types): + if isinstance(name, str): if ')' in name: raise ValueError("invalid name: %r" % name) diff --git a/psycopg/adapter_asis.c b/psycopg/adapter_asis.c index d0cb505..49240bd 100644 --- a/psycopg/adapter_asis.c +++ b/psycopg/adapter_asis.c @@ -45,14 +45,12 @@ asis_getquoted(asisObject *self, PyObject *args) } else { rv = PyObject_Str(self->wrapped); -#if PY_3 - /* unicode to bytes in Py3 */ + /* unicode to bytes */ if (rv) { PyObject *tmp = PyUnicode_AsUTF8String(rv); Py_DECREF(rv); rv = tmp; } -#endif } return rv; diff --git a/psycopg/adapter_binary.c b/psycopg/adapter_binary.c index 693ce31..f6f7cdd 100644 --- a/psycopg/adapter_binary.c +++ b/psycopg/adapter_binary.c @@ -76,15 +76,6 @@ binary_quote(binaryObject *self) buffer_len = view.len; } -#if PY_2 - if (!buffer && (Bytes_Check(self->wrapped) || PyBuffer_Check(self->wrapped))) { - if (PyObject_AsReadBuffer(self->wrapped, (const void **)&buffer, - &buffer_len) < 0) { - goto exit; - } - } -#endif - if (!buffer) { goto exit; } diff --git a/psycopg/adapter_pdecimal.c b/psycopg/adapter_pdecimal.c index f42fdef..a178f88 100644 --- a/psycopg/adapter_pdecimal.c +++ b/psycopg/adapter_pdecimal.c @@ -81,8 +81,7 @@ pdecimal_getquoted(pdecimalObject *self, PyObject *args) /* res may be unicode and may suffer for issue #57 */ output: -#if PY_3 - /* unicode to bytes in Py3 */ + /* unicode to bytes */ { PyObject *tmp = PyUnicode_AsUTF8String(res); Py_DECREF(res); @@ -90,7 +89,6 @@ output: goto end; } } -#endif if ('-' == Bytes_AS_STRING(res)[0]) { /* Prepend a space in front of negative numbers (ticket #57) */ diff --git a/psycopg/adapter_pfloat.c b/psycopg/adapter_pfloat.c index 2243633..e72e7f1 100644 --- a/psycopg/adapter_pfloat.c +++ b/psycopg/adapter_pfloat.c @@ -54,8 +54,7 @@ pfloat_getquoted(pfloatObject *self, PyObject *args) goto exit; } -#if PY_3 - /* unicode to bytes in Py3 */ + /* unicode to bytes */ { PyObject *tmp = PyUnicode_AsUTF8String(rv); Py_DECREF(rv); @@ -63,7 +62,6 @@ pfloat_getquoted(pfloatObject *self, PyObject *args) goto exit; } } -#endif if ('-' == Bytes_AS_STRING(rv)[0]) { /* Prepend a space in front of negative numbers (ticket #57) */ diff --git a/psycopg/adapter_pint.c b/psycopg/adapter_pint.c index 2784759..63bfae5 100644 --- a/psycopg/adapter_pint.c +++ b/psycopg/adapter_pint.c @@ -40,11 +40,7 @@ pint_getquoted(pintObject *self, PyObject *args) /* Convert subclass to int to handle IntEnum and other subclasses * whose str() is not the number. */ - if (PyLong_CheckExact(self->wrapped) -#if PY_2 - || PyInt_CheckExact(self->wrapped) -#endif - ) { + if (PyLong_CheckExact(self->wrapped)) { res = PyObject_Str(self->wrapped); } else { PyObject *tmp; @@ -60,8 +56,7 @@ pint_getquoted(pintObject *self, PyObject *args) goto exit; } -#if PY_3 - /* unicode to bytes in Py3 */ + /* unicode to bytes */ { PyObject *tmp = PyUnicode_AsUTF8String(res); Py_DECREF(res); @@ -69,7 +64,6 @@ pint_getquoted(pintObject *self, PyObject *args) goto exit; } } -#endif if ('-' == Bytes_AS_STRING(res)[0]) { /* Prepend a space in front of negative numbers (ticket #57) */ diff --git a/psycopg/lobject_int.c b/psycopg/lobject_int.c index 71a8bfb..cf17721 100644 --- a/psycopg/lobject_int.c +++ b/psycopg/lobject_int.c @@ -85,11 +85,7 @@ _lobject_parse_mode(const char *mode) pos += 1; break; default: -#if PY_2 - rv |= LOBJECT_BINARY; -#else rv |= LOBJECT_TEXT; -#endif break; } diff --git a/psycopg/microprotocols.c b/psycopg/microprotocols.c index 19408fe..4600bd4 100644 --- a/psycopg/microprotocols.c +++ b/psycopg/microprotocols.c @@ -92,11 +92,7 @@ _get_superclass_adapter(PyObject *obj, PyObject *proto) Py_ssize_t i, ii; type = Py_TYPE(obj); - if (!( -#if PY_2 - (Py_TPFLAGS_HAVE_CLASS & type->tp_flags) && -#endif - type->tp_mro)) { + if (!(type->tp_mro)) { /* has no mro */ return Py_None; } diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index 36fd1f9..9aebfb8 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -309,11 +309,6 @@ adapters_init(PyObject *module) if (0 > microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType)) { goto exit; } -#if PY_2 - if (0 > microprotocols_add(&PyInt_Type, NULL, (PyObject*)&pintType)) { - goto exit; - } -#endif if (0 > microprotocols_add(&PyLong_Type, NULL, (PyObject*)&pintType)) { goto exit; } @@ -322,25 +317,14 @@ adapters_init(PyObject *module) } /* strings */ -#if PY_2 - if (0 > microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType)) { - goto exit; - } -#endif if (0 > microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType)) { goto exit; } /* binary */ -#if PY_2 - if (0 > microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType)) { - goto exit; - } -#else if (0 > microprotocols_add(&PyBytes_Type, NULL, (PyObject*)&binaryType)) { goto exit; } -#endif if (0 > microprotocols_add(&PyByteArray_Type, NULL, (PyObject*)&binaryType)) { goto exit; @@ -1049,7 +1033,6 @@ static PyMethodDef psycopgMethods[] = { {NULL, NULL, 0, NULL} /* Sentinel */ }; -#if PY_3 static struct PyModuleDef psycopgmodule = { PyModuleDef_HEAD_INIT, "_psycopg", @@ -1061,7 +1044,6 @@ static struct PyModuleDef psycopgmodule = { NULL, NULL }; -#endif #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void @@ -1095,11 +1077,7 @@ INIT_MODULE(_psycopg)(void) if (!(psyco_null = Bytes_FromString("NULL"))) { goto exit; } /* initialize the module */ -#if PY_2 - module = Py_InitModule("_psycopg", psycopgMethods); -#else module = PyModule_Create(&psycopgmodule); -#endif if (!module) { goto exit; } if (0 > add_module_constants(module)) { goto exit; } @@ -1115,9 +1093,5 @@ INIT_MODULE(_psycopg)(void) Dprintf("psycopgmodule: module initialization complete"); exit: -#if PY_3 return module; -#else - return; -#endif } diff --git a/psycopg/python.h b/psycopg/python.h index c142de4..7be45bd 100644 --- a/psycopg/python.h +++ b/psycopg/python.h @@ -27,30 +27,11 @@ #ifndef PSYCOPG_PYTHON_H #define PSYCOPG_PYTHON_H 1 -#define PY_2 (PY_MAJOR_VERSION == 2) -#define PY_3 (PY_MAJOR_VERSION == 3) - -#if PY_2 && PY_VERSION_HEX < 0x02070000 -#error "psycopg requires Python 2.7" -#endif - -#if PY_3 && PY_VERSION_HEX < 0x03060000 +#if PY_VERSION_HEX < 0x03060000 #error "psycopg requires Python 3.6" #endif #include <structmember.h> -#if PY_2 -#include <stringobject.h> -#endif - -/* hash() return size changed around version 3.2a4 on 64bit platforms. Before - * this, the return size was always a long, regardless of arch. ~3.2 - * introduced the Py_hash_t & Py_uhash_t typedefs with the resulting sizes - * based upon arch. */ -#if PY_VERSION_HEX < 0x030200A4 -typedef long Py_hash_t; -typedef unsigned long Py_uhash_t; -#endif /* Since Py_TYPE() is changed to the inline static function, * Py_TYPE(obj) = new_type must be replaced with Py_SET_TYPE(obj, new_type) @@ -72,43 +53,6 @@ typedef unsigned long Py_uhash_t; #define FORMAT_CODE_SIZE_T "%zu" #endif -#if PY_2 - -#define Text_Type PyString_Type -#define Text_Check(s) PyString_Check(s) -#define Text_Format(f,a) PyString_Format(f,a) -#define Text_FromUTF8(s) PyString_FromString(s) -#define Text_FromUTF8AndSize(s,n) PyString_FromStringAndSize(s,n) - -#define Bytes_Type PyString_Type -#define Bytes_Check PyString_Check -#define Bytes_CheckExact PyString_CheckExact -#define Bytes_AS_STRING PyString_AS_STRING -#define Bytes_GET_SIZE PyString_GET_SIZE -#define Bytes_Size PyString_Size -#define Bytes_AsString PyString_AsString -#define Bytes_AsStringAndSize PyString_AsStringAndSize -#define Bytes_FromString PyString_FromString -#define Bytes_FromStringAndSize PyString_FromStringAndSize -#define Bytes_FromFormat PyString_FromFormat -#define Bytes_ConcatAndDel PyString_ConcatAndDel -#define _Bytes_Resize _PyString_Resize - -#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) -#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) -#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) - -#define INIT_MODULE(m) init ## m - -/* fix #961, but don't change all types to longs. Sure someone will complain. */ -#define PyLong_FromOid(x) (((x) & 0x80000000) ? \ - PyLong_FromUnsignedLong((unsigned long)(x)) : \ - PyInt_FromLong((x))) - -#endif /* PY_2 */ - -#if PY_3 - #define Text_Type PyUnicode_Type #define Text_Check(s) PyUnicode_Check(s) #define Text_Format(f,a) PyUnicode_Format(f,a) @@ -149,8 +93,6 @@ typedef unsigned long Py_uhash_t; #define PyLong_FromOid(x) (PyLong_FromUnsignedLong((unsigned long)(x))) -#endif /* PY_3 */ - /* expose Oid attributes in Python C objects */ #define T_OID T_UINT diff --git a/psycopg/typecast.c b/psycopg/typecast.c index 021fb0c..4f713b1 100644 --- a/psycopg/typecast.c +++ b/psycopg/typecast.c @@ -475,11 +475,7 @@ PyTypeObject typecastType = { 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ -#if PY_VERSION_HEX < 0x03000000 - typecast_cmp, /*tp_compare*/ -#else 0, /*tp_reserved*/ -#endif typecast_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ @@ -651,11 +647,7 @@ typecast_cast(PyObject *obj, const char *str, Py_ssize_t len, PyObject *curs) * Notice that this way it is about impossible to create a python * typecaster on a binary type. */ if (str) { -#if PY_2 - s = PyString_FromStringAndSize(str, len); -#else s = conn_decode(((cursorObject *)curs)->conn, str, len); -#endif } else { Py_INCREF(Py_None); diff --git a/psycopg/typecast_basic.c b/psycopg/typecast_basic.c index f3fac98..9363cf1 100644 --- a/psycopg/typecast_basic.c +++ b/psycopg/typecast_basic.c @@ -26,22 +26,7 @@ /** INTEGER - cast normal integers (4 bytes) to python int **/ -#if PY_2 -static PyObject * -typecast_INTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs) -{ - char buffer[12]; - - if (s == NULL) { Py_RETURN_NONE; } - if (s[len] != '\0') { - strncpy(buffer, s, (size_t) len); buffer[len] = '\0'; - s = buffer; - } - return PyInt_FromString((char *)s, NULL, 0); -} -#else #define typecast_INTEGER_cast typecast_LONGINTEGER_cast -#endif /** LONGINTEGER - cast long integers (8 bytes) to python long **/ @@ -67,11 +52,7 @@ typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs) if (s == NULL) { Py_RETURN_NONE; } if (!(str = Text_FromUTF8AndSize(s, len))) { return NULL; } -#if PY_2 - flo = PyFloat_FromString(str, NULL); -#else flo = PyFloat_FromString(str); -#endif Py_DECREF(str); return flo; } @@ -103,11 +84,7 @@ typecast_UNICODE_cast(const char *s, Py_ssize_t len, PyObject *curs) /** STRING - cast strings of any type to python string **/ -#if PY_2 -#define typecast_STRING_cast typecast_BYTES_cast -#else #define typecast_STRING_cast typecast_UNICODE_cast -#endif /** BOOLEAN - cast boolean value into right python object **/ diff --git a/psycopg/typecast_binary.c b/psycopg/typecast_binary.c index 032d0f1..9dd2194 100644 --- a/psycopg/typecast_binary.c +++ b/psycopg/typecast_binary.c @@ -54,39 +54,6 @@ chunk_repr(chunkObject *self) ); } -#if PY_2 - -static Py_ssize_t -chunk_getreadbuffer(chunkObject *self, Py_ssize_t segment, void **ptr) -{ - if (segment != 0) - { - PyErr_SetString(PyExc_SystemError, - "accessing non-existant buffer segment"); - return -1; - } - *ptr = self->base; - return self->len; -} - -static Py_ssize_t -chunk_getsegcount(chunkObject *self, Py_ssize_t *lenp) -{ - if (lenp != NULL) - *lenp = self->len; - return 1; -} - -static PyBufferProcs chunk_as_buffer = -{ - (readbufferproc) chunk_getreadbuffer, - (writebufferproc) NULL, - (segcountproc) chunk_getsegcount, - (charbufferproc) NULL -}; - -#else - /* 3.0 buffer interface */ int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags) { @@ -105,8 +72,6 @@ static PyBufferProcs chunk_as_buffer = NULL, }; -#endif - #define chunk_doc "memory chunk" PyTypeObject chunkType = { @@ -183,13 +148,8 @@ typecast_BINARY_cast(const char *s, Py_ssize_t l, PyObject *curs) buffer = NULL; chunk->len = (Py_ssize_t)len; -#if PY_2 - if ((res = PyBuffer_FromObject((PyObject *)chunk, 0, chunk->len)) == NULL) - goto exit; -#else if ((res = PyMemoryView_FromObject((PyObject*)chunk)) == NULL) goto exit; -#endif exit: Py_XDECREF((PyObject *)chunk); diff --git a/psycopg/utils.c b/psycopg/utils.c index 33b3aa8..d585b2c 100644 --- a/psycopg/utils.c +++ b/psycopg/utils.c @@ -190,7 +190,7 @@ psyco_ensure_bytes(PyObject *obj) /* Take a Python object and return text from it. * - * On Py3 this means converting bytes to unicode. On Py2 bytes are fine. + * This means converting bytes to unicode. * * The function is ref neutral: steals a ref from obj and adds one to the * return value. It is safe to call it on NULL. @@ -198,9 +198,6 @@ psyco_ensure_bytes(PyObject *obj) STEALS(1) PyObject * psyco_ensure_text(PyObject *obj) { -#if PY_2 - return obj; -#else if (obj) { /* bytes to unicode in Py3 */ PyObject *rv = PyUnicode_FromEncodedObject(obj, "utf8", "replace"); @@ -210,7 +207,6 @@ psyco_ensure_text(PyObject *obj) else { return NULL; } -#endif } /* Check if a file derives from TextIOBase. @@ -309,24 +305,13 @@ exit: /* Convert a C string into Python Text using a specified codec. * - * The codec is the python function codec.getdecoder(enc). It is only used on - * Python 3 to return unicode: in Py2 the function returns a string. + * The codec is the python function codec.getdecoder(enc). * * len is optional: use -1 to have it calculated by the function. */ PyObject * psyco_text_from_chars_safe(const char *str, Py_ssize_t len, PyObject *decoder) { -#if PY_2 - - if (!str) { Py_RETURN_NONE; } - - if (len < 0) { len = strlen(str); } - - return PyString_FromStringAndSize(str, len); - -#else - static PyObject *replace = NULL; PyObject *rv = NULL; PyObject *b = NULL; @@ -356,8 +341,6 @@ exit: Py_XDECREF(t); Py_XDECREF(b); return rv; - -#endif } @@ -26,8 +26,6 @@ UPDATEs. psycopg2 also provide full asynchronous operations and support for coroutine libraries. """ -# Note: The setup.py must be compatible with both Python 2 and 3 - import os import sys @@ -38,7 +36,6 @@ from distutils.command.build_ext import build_ext from distutils.sysconfig import get_python_inc from distutils.ccompiler import get_default_compiler from distutils.errors import CompileError -from distutils.util import get_platform try: import configparser @@ -58,13 +55,12 @@ Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Programming Language :: Python -Programming Language :: Python :: 2 -Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 +Programming Language :: Python :: 3 :: Only Programming Language :: Python :: Implementation :: CPython Programming Language :: C Programming Language :: SQL @@ -201,12 +197,6 @@ For further information please check the 'doc/src/install.rst' file (also at 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: - pg_config_path = pg_config_path.encode( - sys.getfilesystemencoding()) - return pg_config_path @@ -307,30 +297,6 @@ For further information please check the 'doc/src/install.rst' file (also at """) raise - sysVer = sys.version_info[:2] - - # For Python versions that use MSVC compiler 2008, re-insert the - # manifest into the resulting .pyd file. - if self.compiler_is_msvc() and sysVer == (2, 7): - platform = get_platform() - # Default to the x86 manifest - manifest = '_psycopg.vc9.x86.manifest' - if platform == 'win-amd64': - manifest = '_psycopg.vc9.amd64.manifest' - try: - ext_path = self.get_ext_fullpath(extension.name) - except AttributeError: - ext_path = os.path.join(self.build_lib, - 'psycopg2', '_psycopg.pyd') - # Make sure spawn() will work if compile() was never - # called. https://github.com/psycopg/psycopg2/issues/380 - if not self.compiler.initialized: - self.compiler.initialize() - self.compiler.spawn( - ['mt.exe', '-nologo', '-manifest', - os.path.join('psycopg', manifest), - '-outputresource:%s;2' % ext_path]) - def finalize_win32(self): """Finalize build system configuration on win32 platform.""" @@ -598,7 +564,7 @@ setup(name="psycopg2", url="https://psycopg.org/", license="LGPL with exceptions", platforms=["any"], - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*', + python_requires='>=3.6', 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/__init__.py b/tests/__init__.py index f5c422f..76a01ee 100755 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -58,9 +58,6 @@ from . import test_types_basic from . import test_types_extras from . import test_with -if sys.version_info[:2] < (3, 6): - from . import test_async_keyword - def test_suite(): # If connection to test db fails, bail out early. @@ -76,8 +73,6 @@ 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/dbapi20.py b/tests/dbapi20.py index fe89bb0..d2f87da 100644 --- a/tests/dbapi20.py +++ b/tests/dbapi20.py @@ -185,13 +185,8 @@ class DatabaseAPI20Test(unittest.TestCase): def test_Exceptions(self): # Make sure required exceptions exist, and are in the # defined hierarchy. - if sys.version[0] == '3': #under Python 3 StardardError no longer exists - self.failUnless(issubclass(self.driver.Warning,Exception)) - self.failUnless(issubclass(self.driver.Error,Exception)) - else: - self.failUnless(issubclass(self.driver.Warning,StandardError)) - self.failUnless(issubclass(self.driver.Error,StandardError)) - + self.failUnless(issubclass(self.driver.Warning,Exception)) + self.failUnless(issubclass(self.driver.Error,Exception)) self.failUnless( issubclass(self.driver.InterfaceError,self.driver.Error) ) diff --git a/tests/test_connection.py b/tests/test_connection.py index d822689..f4ea436 100755 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -42,7 +42,7 @@ import psycopg2.extras from psycopg2 import extensions as ext from .testutils import ( - PY2, unittest, skip_if_no_superuser, skip_before_postgres, + unittest, skip_if_no_superuser, skip_before_postgres, skip_after_postgres, skip_before_libpq, skip_after_libpq, ConnectingTestCase, skip_if_tpc_disabled, skip_if_windows, slow, skip_if_crdb, crdb_version) @@ -466,10 +466,7 @@ class ParseDsnTestCase(ConnectingTestCase): def test_unicode_value(self): snowman = u"\u2603" d = ext.parse_dsn('dbname=' + snowman) - if PY2: - self.assertEqual(d['dbname'], snowman.encode('utf8')) - else: - self.assertEqual(d['dbname'], snowman) + self.assertEqual(d['dbname'], snowman) def test_unicode_key(self): snowman = u"\u2603" diff --git a/tests/test_copy.py b/tests/test_copy.py index 9274f1d..bbf68a6 100755 --- a/tests/test_copy.py +++ b/tests/test_copy.py @@ -34,7 +34,7 @@ from subprocess import Popen, PIPE import psycopg2 import psycopg2.extensions -from .testutils import skip_copy_if_green, PY2, TextIOBase +from .testutils import skip_copy_if_green, TextIOBase from .testconfig import dsn @@ -133,14 +133,9 @@ class CopyTests(ConnectingTestCase): self.conn.set_client_encoding('latin1') self._create_temp_table() # the above call closed the xn - if PY2: - abin = ''.join(map(chr, range(32, 127) + range(160, 256))) - about = abin.decode('latin1').replace('\\', '\\\\') - - else: - abin = bytes(list(range(32, 127)) - + list(range(160, 256))).decode('latin1') - about = abin.replace('\\', '\\\\') + abin = bytes(list(range(32, 127)) + + list(range(160, 256))).decode('latin1') + about = abin.replace('\\', '\\\\') curs = self.conn.cursor() curs.execute('insert into tcopy values (%s, %s)', @@ -155,13 +150,9 @@ class CopyTests(ConnectingTestCase): self.conn.set_client_encoding('latin1') self._create_temp_table() # the above call closed the xn - if PY2: - abin = ''.join(map(chr, range(32, 127) + range(160, 255))) - about = abin.replace('\\', '\\\\') - else: - abin = bytes(list(range(32, 127)) - + list(range(160, 255))).decode('latin1') - about = abin.replace('\\', '\\\\').encode('latin1') + abin = bytes(list(range(32, 127)) + + list(range(160, 255))).decode('latin1') + about = abin.replace('\\', '\\\\').encode('latin1') curs = self.conn.cursor() curs.execute('insert into tcopy values (%s, %s)', @@ -176,15 +167,9 @@ class CopyTests(ConnectingTestCase): self.conn.set_client_encoding('latin1') self._create_temp_table() # the above call closed the xn - if PY2: - abin = ''.join(map(chr, range(32, 127) + range(160, 256))) - abin = abin.decode('latin1') - about = abin.replace('\\', '\\\\') - - else: - abin = bytes(list(range(32, 127)) - + list(range(160, 256))).decode('latin1') - about = abin.replace('\\', '\\\\') + abin = bytes(list(range(32, 127)) + + list(range(160, 256))).decode('latin1') + about = abin.replace('\\', '\\\\') f = io.StringIO() f.write(about) diff --git a/tests/test_cursor.py b/tests/test_cursor.py index d293b2b..1e6b250 100755 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -39,7 +39,6 @@ from .testutils import (ConnectingTestCase, skip_before_postgres, skip_if_windows, skip_if_crdb, crdb_version) import psycopg2.extras -from psycopg2.compat import text_type class CursorTests(ConnectingTestCase): @@ -83,7 +82,7 @@ class CursorTests(ConnectingTestCase): snowman = u"\u2603" def b(s): - if isinstance(s, text_type): + if isinstance(s, str): return s.encode('utf8') else: return s diff --git a/tests/test_extras_dictcursor.py b/tests/test_extras_dictcursor.py index a3c553e..e52ad4a 100755 --- a/tests/test_extras_dictcursor.py +++ b/tests/test_extras_dictcursor.py @@ -20,14 +20,14 @@ import time import pickle import unittest from datetime import timedelta +from functools import lru_cache import psycopg2 -from psycopg2.compat import lru_cache import psycopg2.extras from psycopg2.extras import NamedTupleConnection, NamedTupleCursor from .testutils import ConnectingTestCase, skip_before_postgres, \ - skip_before_python, skip_from_python, crdb_version, skip_if_crdb + crdb_version, skip_if_crdb class _DictCursorBase(ConnectingTestCase): @@ -179,27 +179,7 @@ class ExtrasDictCursorTests(_DictCursorBase): self.assertEqual(len(rv3), 2) self.assertEqual(len(rv), 2) - @skip_from_python(3) - def test_iter_methods_2(self): - curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) - curs.execute("select 10 as a, 20 as b") - r = curs.fetchone() - self.assert_(isinstance(r.keys(), list)) - self.assertEqual(len(r.keys()), 2) - self.assert_(isinstance(r.values(), tuple)) # sic? - self.assertEqual(len(r.values()), 2) - self.assert_(isinstance(r.items(), list)) - self.assertEqual(len(r.items()), 2) - - self.assert_(not isinstance(r.iterkeys(), list)) - self.assertEqual(len(list(r.iterkeys())), 2) - self.assert_(not isinstance(r.itervalues(), list)) - self.assertEqual(len(list(r.itervalues())), 2) - self.assert_(not isinstance(r.iteritems(), list)) - self.assertEqual(len(list(r.iteritems())), 2) - - @skip_before_python(3) - def test_iter_methods_3(self): + def test_iter_methods(self): curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) curs.execute("select 10 as a, 20 as b") r = curs.fetchone() @@ -226,21 +206,6 @@ class ExtrasDictCursorTests(_DictCursorBase): self.assertEqual(list(r1.values()), list(r.values())) self.assertEqual(list(r1.items()), list(r.items())) - @skip_from_python(3) - def test_order_iter(self): - curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) - curs.execute("select 5 as foo, 4 as bar, 33 as baz, 2 as qux") - r = curs.fetchone() - self.assertEqual(list(r.iterkeys()), ['foo', 'bar', 'baz', 'qux']) - self.assertEqual(list(r.itervalues()), [5, 4, 33, 2]) - self.assertEqual(list(r.iteritems()), - [('foo', 5), ('bar', 4), ('baz', 33), ('qux', 2)]) - - r1 = pickle.loads(pickle.dumps(r)) - self.assertEqual(list(r1.iterkeys()), list(r.iterkeys())) - self.assertEqual(list(r1.itervalues()), list(r.itervalues())) - self.assertEqual(list(r1.iteritems()), list(r.iteritems())) - class ExtrasDictCursorRealTests(_DictCursorBase): def testRealMeansReal(self): @@ -340,27 +305,7 @@ class ExtrasDictCursorRealTests(_DictCursorBase): row = getter(curs) self.failUnless(row['foo'] == 'bar') - @skip_from_python(3) - def test_iter_methods_2(self): - curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) - curs.execute("select 10 as a, 20 as b") - r = curs.fetchone() - self.assert_(isinstance(r.keys(), list)) - self.assertEqual(len(r.keys()), 2) - self.assert_(isinstance(r.values(), list)) - self.assertEqual(len(r.values()), 2) - self.assert_(isinstance(r.items(), list)) - self.assertEqual(len(r.items()), 2) - - self.assert_(not isinstance(r.iterkeys(), list)) - self.assertEqual(len(list(r.iterkeys())), 2) - self.assert_(not isinstance(r.itervalues(), list)) - self.assertEqual(len(list(r.itervalues())), 2) - self.assert_(not isinstance(r.iteritems(), list)) - self.assertEqual(len(list(r.iteritems())), 2) - - @skip_before_python(3) - def test_iter_methods_3(self): + def test_iter_methods(self): curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) curs.execute("select 10 as a, 20 as b") r = curs.fetchone() @@ -387,21 +332,6 @@ class ExtrasDictCursorRealTests(_DictCursorBase): self.assertEqual(list(r1.values()), list(r.values())) self.assertEqual(list(r1.items()), list(r.items())) - @skip_from_python(3) - def test_order_iter(self): - curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) - curs.execute("select 5 as foo, 4 as bar, 33 as baz, 2 as qux") - r = curs.fetchone() - self.assertEqual(list(r.iterkeys()), ['foo', 'bar', 'baz', 'qux']) - self.assertEqual(list(r.itervalues()), [5, 4, 33, 2]) - self.assertEqual(list(r.iteritems()), - [('foo', 5), ('bar', 4), ('baz', 33), ('qux', 2)]) - - r1 = pickle.loads(pickle.dumps(r)) - self.assertEqual(list(r1.iterkeys()), list(r.iterkeys())) - self.assertEqual(list(r1.itervalues()), list(r.itervalues())) - self.assertEqual(list(r1.iteritems()), list(r.iteritems())) - def test_pop(self): curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) curs.execute("select 1 as a, 2 as b, 3 as c") @@ -564,7 +494,6 @@ class NamedTupleCursorTest(ConnectingTestCase): self.assertEqual(rv.f_column_, 2) self.assertEqual(rv.f3, 3) - @skip_before_python(3) @skip_before_postgres(8) def test_nonascii_name(self): curs = self.conn.cursor() diff --git a/tests/test_quote.py b/tests/test_quote.py index dfe3219..8a1f06a 100755 --- a/tests/test_quote.py +++ b/tests/test_quote.py @@ -25,7 +25,7 @@ from . import testutils import unittest -from .testutils import ConnectingTestCase, skip_if_crdb, unichr, PY2 +from .testutils import ConnectingTestCase, skip_if_crdb import psycopg2 import psycopg2.extensions @@ -79,17 +79,11 @@ class QuotingTestCase(ConnectingTestCase): data = b"""some data with \000\013 binary stuff into, 'quotes' and \\ a backslash too. """ - if PY2: - data += "".join(map(chr, range(256))) - else: - data += bytes(list(range(256))) + data += bytes(list(range(256))) curs = self.conn.cursor() curs.execute("SELECT %s::bytea;", (psycopg2.Binary(data),)) - if PY2: - res = str(curs.fetchone()[0]) - else: - res = curs.fetchone()[0].tobytes() + res = curs.fetchone()[0].tobytes() if res[0] in (b'x', ord(b'x')) and self.conn.info.server_version >= 90000: return self.skipTest( @@ -110,7 +104,7 @@ class QuotingTestCase(ConnectingTestCase): data = u"""some data with \t chars to escape into, 'quotes', \u20ac euro sign and \\ a backslash too. """ - data += u"".join(map(unichr, [u for u in range(1, 65536) + data += u"".join(map(chr, [u for u in range(1, 65536) if not 0xD800 <= u <= 0xDFFF])) # surrogate area self.conn.set_client_encoding('UNICODE') @@ -125,11 +119,8 @@ class QuotingTestCase(ConnectingTestCase): def test_latin1(self): self.conn.set_client_encoding('LATIN1') curs = self.conn.cursor() - if PY2: - data = ''.join(map(chr, range(32, 127) + range(160, 256))) - else: - data = bytes(list(range(32, 127)) - + list(range(160, 256))).decode('latin1') + data = bytes(list(range(32, 127)) + + list(range(160, 256))).decode('latin1') # as string curs.execute("SELECT %s::text;", (data,)) @@ -137,25 +128,13 @@ class QuotingTestCase(ConnectingTestCase): self.assertEqual(res, data) self.assert_(not self.conn.notices) - # as unicode - if PY2: - psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn) - data = data.decode('latin1') - - curs.execute("SELECT %s::text;", (data,)) - res = curs.fetchone()[0] - self.assertEqual(res, data) - self.assert_(not self.conn.notices) @skip_if_crdb("encoding") def test_koi8(self): self.conn.set_client_encoding('KOI8') curs = self.conn.cursor() - if PY2: - data = ''.join(map(chr, range(32, 127) + range(128, 256))) - else: - data = bytes(list(range(32, 127)) - + list(range(128, 256))).decode('koi8_r') + data = bytes(list(range(32, 127)) + + list(range(128, 256))).decode('koi8_r') # as string curs.execute("SELECT %s::text;", (data,)) @@ -163,16 +142,6 @@ class QuotingTestCase(ConnectingTestCase): self.assertEqual(res, data) self.assert_(not self.conn.notices) - # as unicode - if PY2: - psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn) - data = data.decode('koi8_r') - - curs.execute("SELECT %s::text;", (data,)) - res = curs.fetchone()[0] - self.assertEqual(res, data) - self.assert_(not self.conn.notices) - def test_bytes(self): snowman = u"\u2603" conn = self.connect() @@ -204,10 +173,7 @@ class TestQuotedIdentifier(ConnectingTestCase): def test_unicode_ident(self): snowman = u"\u2603" quoted = '"' + snowman + '"' - if PY2: - self.assertEqual(quote_ident(snowman, self.conn), quoted.encode('utf8')) - else: - self.assertEqual(quote_ident(snowman, self.conn), quoted) + self.assertEqual(quote_ident(snowman, self.conn), quoted) class TestStringAdapter(ConnectingTestCase): @@ -248,7 +214,6 @@ class TestStringAdapter(ConnectingTestCase): self.assertEqual(a.encoding, 'utf_8') self.assertQuotedEqual(a.getquoted(), b"'\xe2\x98\x83'") - @testutils.skip_before_python(3) def test_adapt_bytes(self): snowman = u"\u2603" self.conn.set_client_encoding('utf8') diff --git a/tests/test_sql.py b/tests/test_sql.py index 7818ee8..730fa18 100755 --- a/tests/test_sql.py +++ b/tests/test_sql.py @@ -31,7 +31,6 @@ from .testutils import ( import psycopg2 from psycopg2 import sql -from psycopg2.compat import text_type class SqlFormatTests(ConnectingTestCase): @@ -62,13 +61,6 @@ class SqlFormatTests(ConnectingTestCase): self.assert_(isinstance(s1, str)) self.assertEqual(s1, 'select "field" from "table"') - def test_unicode(self): - s = sql.SQL(u"select {0} from {1}").format( - sql.Identifier(u'field'), sql.Identifier('table')) - s1 = s.as_string(self.conn) - self.assert_(isinstance(s1, text_type)) - self.assertEqual(s1, u'select "field" from "table"') - def test_compose_literal(self): s = sql.SQL("select {0};").format(sql.Literal(dt.date(2016, 12, 31))) s1 = s.as_string(self.conn) diff --git a/tests/test_types_basic.py b/tests/test_types_basic.py index efdff73..9317571 100755 --- a/tests/test_types_basic.py +++ b/tests/test_types_basic.py @@ -31,7 +31,7 @@ import platform from . import testutils import unittest -from .testutils import PY2, long, text_type, ConnectingTestCase, restore_types +from .testutils import ConnectingTestCase, restore_types from .testutils import skip_if_crdb import psycopg2 @@ -59,8 +59,6 @@ class TypesBasicTests(ConnectingTestCase): def testNumber(self): s = self.execute("SELECT %s AS foo", (1971,)) self.failUnless(s == 1971, "wrong integer quoting: " + str(s)) - s = self.execute("SELECT %s AS foo", (long(1971),)) - self.failUnless(s == long(1971), "wrong integer quoting: " + str(s)) def testBoolean(self): x = self.execute("SELECT %s as foo", (False,)) @@ -110,16 +108,10 @@ class TypesBasicTests(ConnectingTestCase): self.failUnless(str(s) == "-inf", "wrong float quoting: " + str(s)) def testBinary(self): - if PY2: - s = ''.join([chr(x) for x in range(256)]) - b = psycopg2.Binary(s) - buf = self.execute("SELECT %s::bytea AS foo", (b,)) - self.assertEqual(s, str(buf)) - else: - s = bytes(range(256)) - b = psycopg2.Binary(s) - buf = self.execute("SELECT %s::bytea AS foo", (b,)) - self.assertEqual(s, buf.tobytes()) + s = bytes(range(256)) + b = psycopg2.Binary(s) + buf = self.execute("SELECT %s::bytea AS foo", (b,)) + self.assertEqual(s, buf.tobytes()) def testBinaryNone(self): b = psycopg2.Binary(None) @@ -128,26 +120,16 @@ class TypesBasicTests(ConnectingTestCase): def testBinaryEmptyString(self): # test to make sure an empty Binary is converted to an empty string - if PY2: - b = psycopg2.Binary('') - self.assertEqual(str(b), "''::bytea") - else: - b = psycopg2.Binary(bytes([])) - self.assertEqual(str(b), "''::bytea") + b = psycopg2.Binary(bytes([])) + self.assertEqual(str(b), "''::bytea") def testBinaryRoundTrip(self): # test to make sure buffers returned by psycopg2 are # understood by execute: - if PY2: - s = ''.join([chr(x) for x in range(256)]) - buf = self.execute("SELECT %s::bytea AS foo", (psycopg2.Binary(s),)) - buf2 = self.execute("SELECT %s::bytea AS foo", (buf,)) - self.assertEqual(s, str(buf2)) - else: - s = bytes(range(256)) - buf = self.execute("SELECT %s::bytea AS foo", (psycopg2.Binary(s),)) - buf2 = self.execute("SELECT %s::bytea AS foo", (buf,)) - self.assertEqual(s, buf2.tobytes()) + s = bytes(range(256)) + buf = self.execute("SELECT %s::bytea AS foo", (psycopg2.Binary(s),)) + buf2 = self.execute("SELECT %s::bytea AS foo", (buf,)) + self.assertEqual(s, buf2.tobytes()) @skip_if_crdb("nested array") def testArray(self): @@ -227,7 +209,7 @@ class TypesBasicTests(ConnectingTestCase): curs = self.conn.cursor() curs.execute("select '{a,b,c}'::text[]") x = curs.fetchone()[0] - self.assert_(isinstance(x[0], text_type)) + self.assert_(isinstance(x[0], str)) self.assertEqual(x, [u'a', u'b', u'c']) def testBytesArray(self): @@ -291,27 +273,6 @@ class TypesBasicTests(ConnectingTestCase): curs.execute("select %s::int[]", (a,)) self.assertEqual(curs.fetchone()[0], a) - @testutils.skip_from_python(3) - def testTypeRoundtripBuffer(self): - o1 = buffer("".join(map(chr, range(256)))) - o2 = self.execute("select %s;", (o1,)) - self.assertEqual(type(o1), type(o2)) - - # Test with an empty buffer - o1 = buffer("") - o2 = self.execute("select %s;", (o1,)) - self.assertEqual(type(o1), type(o2)) - self.assertEqual(str(o1), str(o2)) - - @testutils.skip_from_python(3) - def testTypeRoundtripBufferArray(self): - o1 = buffer("".join(map(chr, range(256)))) - o1 = [o1] - o2 = self.execute("select %s;", (o1,)) - self.assertEqual(type(o1[0]), type(o2[0])) - self.assertEqual(str(o1[0]), str(o2[0])) - - @testutils.skip_before_python(3) def testTypeRoundtripBytes(self): o1 = bytes(range(256)) o2 = self.execute("select %s;", (o1,)) @@ -322,7 +283,6 @@ class TypesBasicTests(ConnectingTestCase): o2 = self.execute("select %s;", (o1,)) self.assertEqual(memoryview, type(o2)) - @testutils.skip_before_python(3) def testTypeRoundtripBytesArray(self): o1 = bytes(range(256)) o1 = [o1] @@ -332,12 +292,7 @@ class TypesBasicTests(ConnectingTestCase): def testAdaptBytearray(self): o1 = bytearray(range(256)) o2 = self.execute("select %s;", (o1,)) - - if PY2: - self.assertEqual(buffer, type(o2)) - else: - self.assertEqual(memoryview, type(o2)) - + self.assertEqual(memoryview, type(o2)) self.assertEqual(len(o1), len(o2)) for c1, c2 in zip(o1, o2): self.assertEqual(c1, ord(c2)) @@ -345,28 +300,18 @@ class TypesBasicTests(ConnectingTestCase): # Test with an empty buffer o1 = bytearray([]) o2 = self.execute("select %s;", (o1,)) - self.assertEqual(len(o2), 0) - if PY2: - self.assertEqual(buffer, type(o2)) - else: - self.assertEqual(memoryview, type(o2)) + self.assertEqual(memoryview, type(o2)) def testAdaptMemoryview(self): o1 = memoryview(bytearray(range(256))) o2 = self.execute("select %s;", (o1,)) - if PY2: - self.assertEqual(buffer, type(o2)) - else: - self.assertEqual(memoryview, type(o2)) + self.assertEqual(memoryview, type(o2)) # Test with an empty buffer o1 = memoryview(bytearray([])) o2 = self.execute("select %s;", (o1,)) - if PY2: - self.assertEqual(buffer, type(o2)) - else: - self.assertEqual(memoryview, type(o2)) + self.assertEqual(memoryview, type(o2)) def testByteaHexCheckFalsePositive(self): # the check \x -> x to detect bad bytea decode @@ -382,8 +327,6 @@ class TypesBasicTests(ConnectingTestCase): self.assertEqual(1, f1) i1 = self.execute("select -%s;", (-1,)) self.assertEqual(1, i1) - l1 = self.execute("select -%s;", (long(-1),)) - self.assertEqual(1, l1) def testGenericArray(self): a = self.execute("select '{1, 2, 3}'::int4[]") @@ -417,7 +360,6 @@ class TypesBasicTests(ConnectingTestCase): a = self.execute("select '{10:20:30:40:50:60}'::macaddr[]") self.assertEqual(a, ['10:20:30:40:50:60']) - @testutils.skip_before_python(3, 4) def testIntEnum(self): from enum import IntEnum @@ -453,19 +395,6 @@ class AdaptSubclassTest(unittest.TestCase): register_adapter(B, lambda b: AsIs("b")) self.assertEqual(b'b', adapt(C()).getquoted()) - @testutils.skip_from_python(3) - @restore_types - def test_no_mro_no_joy(self): - class A: - pass - - class B(A): - pass - - register_adapter(A, lambda a: AsIs("a")) - self.assertRaises(psycopg2.ProgrammingError, adapt, B()) - - @testutils.skip_before_python(3) @restore_types def test_adapt_subtype_3(self): class A: @@ -512,10 +441,7 @@ class ByteaParserTest(unittest.TestCase): if rv is None: return None - if PY2: - return str(rv) - else: - return rv.tobytes() + return rv.tobytes() def test_null(self): rv = self.cast(None) @@ -536,10 +462,7 @@ class ByteaParserTest(unittest.TestCase): buf = buf.upper() buf = '\\x' + buf rv = self.cast(buf.encode('utf8')) - if PY2: - self.assertEqual(rv, ''.join(map(chr, range(256)))) - else: - self.assertEqual(rv, bytes(range(256))) + self.assertEqual(rv, bytes(range(256))) def test_full_hex_upper(self): return self.test_full_hex(upper=True) @@ -547,10 +470,7 @@ class ByteaParserTest(unittest.TestCase): def test_full_escaped_octal(self): buf = ''.join(("\\%03o" % i) for i in range(256)) rv = self.cast(buf.encode('utf8')) - if PY2: - self.assertEqual(rv, ''.join(map(chr, range(256)))) - else: - self.assertEqual(rv, bytes(range(256))) + self.assertEqual(rv, bytes(range(256))) def test_escaped_mixed(self): buf = ''.join(("\\%03o" % i) for i in range(32)) @@ -558,12 +478,8 @@ class ByteaParserTest(unittest.TestCase): buf += ''.join('\\' + c for c in string.ascii_letters) buf += '\\\\' rv = self.cast(buf.encode('utf8')) - if PY2: - tgt = ''.join(map(chr, range(32))) \ - + string.ascii_letters * 2 + '\\' - else: - tgt = bytes(range(32)) + \ - (string.ascii_letters * 2 + '\\').encode('ascii') + tgt = bytes(range(32)) + \ + (string.ascii_letters * 2 + '\\').encode('ascii') self.assertEqual(rv, tgt) diff --git a/tests/test_types_extras.py b/tests/test_types_extras.py index b3819c3..a126994 100755 --- a/tests/test_types_extras.py +++ b/tests/test_types_extras.py @@ -25,8 +25,8 @@ from functools import wraps from pickle import dumps, loads import unittest -from .testutils import (PY2, text_type, skip_if_no_uuid, skip_before_postgres, - ConnectingTestCase, py3_raises_typeerror, slow, skip_from_python, +from .testutils import (skip_if_no_uuid, skip_before_postgres, + ConnectingTestCase, raises_typeerror, slow, restore_types, skip_if_crdb, crdb_version) import psycopg2 @@ -250,19 +250,6 @@ class HstoreTestCase(ConnectingTestCase): self.assertEqual(t[2], {'a': 'b'}) @skip_if_no_hstore - @skip_from_python(3) - def test_register_unicode(self): - register_hstore(self.conn, unicode=True) - cur = self.conn.cursor() - cur.execute("select null::hstore, ''::hstore, 'a => b'::hstore") - t = cur.fetchone() - self.assert_(t[0] is None) - self.assertEqual(t[1], {}) - self.assertEqual(t[2], {u'a': u'b'}) - self.assert_(isinstance(t[2].keys()[0], unicode)) - self.assert_(isinstance(t[2].values()[0], unicode)) - - @skip_if_no_hstore @restore_types def test_register_globally(self): HstoreAdapter.get_oids(self.conn) @@ -297,38 +284,12 @@ class HstoreTestCase(ConnectingTestCase): ok({''.join(ab): ''.join(ab)}) self.conn.set_client_encoding('latin1') - if PY2: - ab = map(chr, range(32, 127) + range(160, 255)) - else: - ab = bytes(list(range(32, 127)) + list(range(160, 255))).decode('latin1') + ab = bytes(list(range(32, 127)) + list(range(160, 255))).decode('latin1') ok({''.join(ab): ''.join(ab)}) ok(dict(zip(ab, ab))) @skip_if_no_hstore - @skip_from_python(3) - def test_roundtrip_unicode(self): - register_hstore(self.conn, unicode=True) - cur = self.conn.cursor() - - def ok(d): - cur.execute("select %s", (d,)) - d1 = cur.fetchone()[0] - self.assertEqual(len(d), len(d1)) - for k, v in d1.iteritems(): - self.assert_(k in d, k) - self.assertEqual(d[k], v) - self.assert_(isinstance(k, unicode)) - self.assert_(v is None or isinstance(v, unicode)) - - ok({}) - ok({'a': 'b', 'c': None, 'd': u'\u20ac', u'\u2603': 'e'}) - - ab = map(unichr, range(1, 1024)) - ok({u''.join(ab): u''.join(ab)}) - ok(dict(zip(ab, ab))) - - @skip_if_no_hstore @restore_types def test_oid(self): cur = self.conn.cursor() @@ -356,10 +317,7 @@ class HstoreTestCase(ConnectingTestCase): ds.append({''.join(ab): ''.join(ab)}) self.conn.set_client_encoding('latin1') - if PY2: - ab = map(chr, range(32, 127) + range(160, 255)) - else: - ab = bytes(list(range(32, 127)) + list(range(160, 255))).decode('latin1') + ab = bytes(list(range(32, 127)) + list(range(160, 255))).decode('latin1') ds.append({''.join(ab): ''.join(ab)}) ds.append(dict(zip(ab, ab))) @@ -1238,9 +1196,9 @@ class RangeTestCase(unittest.TestCase): self.assert_(not Range() < Range()) self.assert_(not Range(empty=True) < Range(empty=True)) self.assert_(not Range(1, 2) < Range(1, 2)) - with py3_raises_typeerror(): + with raises_typeerror(): self.assert_(1 < Range(1, 2)) - with py3_raises_typeerror(): + with raises_typeerror(): self.assert_(not Range(1, 2) < 1) def test_gt_ordering(self): @@ -1253,9 +1211,9 @@ class RangeTestCase(unittest.TestCase): self.assert_(not Range() > Range()) self.assert_(not Range(empty=True) > Range(empty=True)) self.assert_(not Range(1, 2) > Range(1, 2)) - with py3_raises_typeerror(): + with raises_typeerror(): self.assert_(not 1 > Range(1, 2)) - with py3_raises_typeerror(): + with raises_typeerror(): self.assert_(Range(1, 2) > 1) def test_le_ordering(self): @@ -1268,9 +1226,9 @@ class RangeTestCase(unittest.TestCase): self.assert_(Range() <= Range()) self.assert_(Range(empty=True) <= Range(empty=True)) self.assert_(Range(1, 2) <= Range(1, 2)) - with py3_raises_typeerror(): + with raises_typeerror(): self.assert_(1 <= Range(1, 2)) - with py3_raises_typeerror(): + with raises_typeerror(): self.assert_(not Range(1, 2) <= 1) def test_ge_ordering(self): @@ -1283,9 +1241,9 @@ class RangeTestCase(unittest.TestCase): self.assert_(Range() >= Range()) self.assert_(Range(empty=True) >= Range(empty=True)) self.assert_(Range(1, 2) >= Range(1, 2)) - with py3_raises_typeerror(): + with raises_typeerror(): self.assert_(not 1 >= Range(1, 2)) - with py3_raises_typeerror(): + with raises_typeerror(): self.assert_(Range(1, 2) >= 1) def test_pickling(self): @@ -1313,10 +1271,10 @@ class RangeTestCase(unittest.TestCase): for bounds in ('()', '[]', '(]', '[)'): r = Range(0, 4, bounds=bounds) - results.append(text_type(r)) + results.append(str(r)) r = Range(empty=True) - results.append(text_type(r)) + results.append(str(r)) self.assertEqual(results, expected) def test_str_datetime(self): @@ -1328,7 +1286,7 @@ class RangeTestCase(unittest.TestCase): r = DateTimeTZRange(datetime(2010, 1, 1, tzinfo=tz), datetime(2011, 1, 1, tzinfo=tz)) expected = u'[2010-01-01 00:00:00-05:00, 2011-01-01 00:00:00-05:00)' - result = text_type(r) + result = str(r) self.assertEqual(result, expected) diff --git a/tests/testutils.py b/tests/testutils.py index 2b5bbf9..b64d4fc 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -34,32 +34,16 @@ import platform import unittest from functools import wraps from ctypes.util import find_library +from io import StringIO # noqa +from io import TextIOBase # noqa +from importlib import reload # noqa import psycopg2 import psycopg2.errors import psycopg2.extensions -from psycopg2.compat import PY2, PY3, string_types, text_type from .testconfig import green, dsn, repl_dsn -# Python 2/3 compatibility - -if PY2: - # Python 2 - from StringIO import StringIO - TextIOBase = object - long = long - reload = reload - unichr = unichr - -else: - # Python 3 - from io import StringIO # noqa - from io import TextIOBase # noqa - from importlib import reload # noqa - long = int - unichr = chr - # Silence warnings caused by the stubbornness of the Python unittest # maintainers @@ -102,7 +86,7 @@ class ConnectingTestCase(unittest.TestCase): def assertQuotedEqual(self, first, second, msg=None): """Compare two quoted strings disregarding eventual E'' quotes""" def f(s): - if isinstance(s, text_type): + if isinstance(s, str): return re.sub(r"\bE'", "'", s) elif isinstance(first, bytes): return re.sub(br"\bE'", b"'", s) @@ -454,7 +438,7 @@ def skip_if_crdb(reason, conn=None, version=None): "== 20.1.3": the test will be skipped only if the version matches. """ - if not isinstance(reason, string_types): + if not isinstance(reason, str): raise TypeError("reason should be a string, got %r instead" % reason) if conn is not None: @@ -518,14 +502,13 @@ def _crdb_match_version(version, pattern): return op(version, ref) -class py3_raises_typeerror(object): +class raises_typeerror(object): def __enter__(self): pass def __exit__(self, type, exc, tb): - if PY3: - assert type is TypeError - return True + assert type is TypeError + return True def slow(f): @@ -1,5 +1,5 @@ [tox] -envlist = py{27,36,37,38,39} +envlist = py{36,37,38,39} [testenv] commands = make check |