summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo van Kemenade <hugovk@users.noreply.github.com>2020-11-17 20:39:39 +0200
committerHugo van Kemenade <hugovk@users.noreply.github.com>2020-11-17 22:22:11 +0200
commit6c48b63ae4a70da6dbbc5b6eef20806d7f505950 (patch)
tree6976b00090753bf0f64a5b4f3745e71f96f0d2c0
parentb05a5819317ab19fc7749e34fb67f4604d9bd10a (diff)
downloadpsycopg2-6c48b63ae4a70da6dbbc5b6eef20806d7f505950.tar.gz
Drop support for EOL Python 2.7
-rw-r--r--.appveyor.yml2
-rw-r--r--.travis.yml4
-rw-r--r--lib/_ipaddress.py5
-rw-r--r--lib/_json.py11
-rw-r--r--lib/_range.py5
-rw-r--r--lib/compat.py19
-rw-r--r--lib/extras.py28
-rw-r--r--lib/sql.py11
-rw-r--r--psycopg/adapter_asis.c4
-rw-r--r--psycopg/adapter_binary.c9
-rw-r--r--psycopg/adapter_pdecimal.c4
-rw-r--r--psycopg/adapter_pfloat.c4
-rw-r--r--psycopg/adapter_pint.c10
-rw-r--r--psycopg/lobject_int.c4
-rw-r--r--psycopg/microprotocols.c6
-rw-r--r--psycopg/psycopgmodule.c26
-rw-r--r--psycopg/python.h60
-rw-r--r--psycopg/typecast.c8
-rw-r--r--psycopg/typecast_basic.c23
-rw-r--r--psycopg/typecast_binary.c40
-rw-r--r--psycopg/utils.c21
-rw-r--r--setup.py38
-rwxr-xr-xtests/__init__.py5
-rw-r--r--tests/dbapi20.py9
-rwxr-xr-xtests/test_connection.py7
-rwxr-xr-xtests/test_copy.py35
-rwxr-xr-xtests/test_cursor.py3
-rwxr-xr-xtests/test_extras_dictcursor.py79
-rwxr-xr-xtests/test_quote.py53
-rwxr-xr-xtests/test_sql.py8
-rwxr-xr-xtests/test_types_basic.py126
-rwxr-xr-xtests/test_types_extras.py72
-rw-r--r--tests/testutils.py33
-rw-r--r--tox.ini2
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)
diff --git a/lib/sql.py b/lib/sql.py
index 6883452..2077267 100644
--- a/lib/sql.py
+++ b/lib/sql.py
@@ -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
}
diff --git a/setup.py b/setup.py
index a413f56..0731cff 100644
--- a/setup.py
+++ b/setup.py
@@ -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):
diff --git a/tox.ini b/tox.ini
index b8d7d6d..a2ab53c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py{27,36,37,38,39}
+envlist = py{36,37,38,39}
[testenv]
commands = make check