mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-25 18:33:44 +03:00
Merge branch 'master' into encrypt-pass
This commit is contained in:
commit
0161d54dbb
|
@ -6,12 +6,14 @@ language: python
|
||||||
|
|
||||||
python:
|
python:
|
||||||
- 2.7
|
- 2.7
|
||||||
|
- 3.7-dev
|
||||||
- 3.6
|
- 3.6
|
||||||
- 3.5
|
- 3.5
|
||||||
- 3.4
|
- 3.4
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- python setup.py install
|
- pip install -U pip setuptools wheel
|
||||||
|
- pip install .
|
||||||
- rm -rf psycopg2.egg-info
|
- rm -rf psycopg2.egg-info
|
||||||
- sudo scripts/travis_prepare.sh
|
- sudo scripts/travis_prepare.sh
|
||||||
|
|
||||||
|
|
10
LICENSE
10
LICENSE
|
@ -1,5 +1,5 @@
|
||||||
psycopg2 and the LGPL
|
psycopg2 and the LGPL
|
||||||
=====================
|
---------------------
|
||||||
|
|
||||||
psycopg2 is free software: you can redistribute it and/or modify it
|
psycopg2 is free software: you can redistribute it and/or modify it
|
||||||
under the terms of the GNU Lesser General Public License as published
|
under the terms of the GNU Lesser General Public License as published
|
||||||
|
@ -29,15 +29,15 @@ If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
Alternative licenses
|
Alternative licenses
|
||||||
====================
|
--------------------
|
||||||
|
|
||||||
If you prefer you can use the Zope Database Adapter ZPsycopgDA (i.e.,
|
If you prefer you can use the Zope Database Adapter ZPsycopgDA (i.e.,
|
||||||
every file inside the ZPsycopgDA directory) user the ZPL license as
|
every file inside the ZPsycopgDA directory) using the ZPL license as
|
||||||
published on the Zope web site, http://www.zope.org/Resources/ZPL.
|
published on the Zope web site, http://www.zope.org/Resources/ZPL.
|
||||||
|
|
||||||
Also, the following BSD-like license applies (at your option) to the
|
Also, the following BSD-like license applies (at your option) to the
|
||||||
files following the pattern psycopg/adapter*.{h,c} and
|
files following the pattern ``psycopg/adapter*.{h,c}`` and
|
||||||
psycopg/microprotocol*.{h,c}:
|
``psycopg/microprotocol*.{h,c}``:
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose,
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
including commercial applications, and to alter it and redistribute it
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
|
4
NEWS
4
NEWS
|
@ -17,10 +17,14 @@ Other changes:
|
||||||
What's new in psycopg 2.7.5
|
What's new in psycopg 2.7.5
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- Allow non-ascii chars in namedtuple fields (regression introduced fixing
|
||||||
|
:ticket:`#211`).
|
||||||
|
- Fixed adaptation of arrays of arrays of nulls (:ticket:`#325`).
|
||||||
- Fixed building on Solaris 11 and derivatives such as SmartOS and illumos
|
- Fixed building on Solaris 11 and derivatives such as SmartOS and illumos
|
||||||
(:ticket:`#677`).
|
(:ticket:`#677`).
|
||||||
- Maybe fixed building on MSYS2 (as reported in :ticket:`#658`).
|
- Maybe fixed building on MSYS2 (as reported in :ticket:`#658`).
|
||||||
- Allow string subclasses in connection and other places (:ticket:`#679`).
|
- Allow string subclasses in connection and other places (:ticket:`#679`).
|
||||||
|
- Don't raise an exception closing an unused named cursor (:ticket:`#716`).
|
||||||
|
|
||||||
|
|
||||||
What's new in psycopg 2.7.4
|
What's new in psycopg 2.7.4
|
||||||
|
|
|
@ -54,8 +54,8 @@ external libraries, by installing the `psycopg2-binary`_ package from PyPI::
|
||||||
The binary package is a practical choice for development and testing but in
|
The binary package is a practical choice for development and testing but in
|
||||||
production it is advised to use the package built from sources.
|
production it is advised to use the package built from sources.
|
||||||
|
|
||||||
.. _PyPI: https://pypi.python.org/pypi/psycopg2
|
.. _PyPI: https://pypi.org/project/psycopg2/
|
||||||
.. _psycopg2-binary: https://pypi.python.org/pypi/psycopg2-binary
|
.. _psycopg2-binary: https://pypi.org/project/psycopg2-binary/
|
||||||
.. _install: http://initd.org/psycopg/docs/install.html#install-from-source
|
.. _install: http://initd.org/psycopg/docs/install.html#install-from-source
|
||||||
.. _faq: http://initd.org/psycopg/docs/faq.html#faq-compile
|
.. _faq: http://initd.org/psycopg/docs/faq.html#faq-compile
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,6 @@ doctest:
|
||||||
upload:
|
upload:
|
||||||
# this command requires ssh configured to the proper target
|
# this command requires ssh configured to the proper target
|
||||||
tar czf - -C html . | ssh psycoweb tar xzvf - -C docs/current
|
tar czf - -C html . | ssh psycoweb tar xzvf - -C docs/current
|
||||||
# this command requires a .pypirc with the right privileges
|
|
||||||
# python src/tools/pypi_docs_upload.py psycopg2 $$(pwd)/html
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(MAKE) $(SPHOPTS) -C src $@
|
$(MAKE) $(SPHOPTS) -C src $@
|
||||||
|
|
|
@ -100,5 +100,5 @@ Test packages may be uploaded on the `PyPI testing site`__ using::
|
||||||
|
|
||||||
assuming `proper configuration`__ of ``~/.pypirc``.
|
assuming `proper configuration`__ of ``~/.pypirc``.
|
||||||
|
|
||||||
.. __: https://testpypi.python.org/pypi/psycopg2
|
.. __: https://test.pypi.org/project/psycopg2/
|
||||||
.. __: https://wiki.python.org/moin/TestPyPI
|
.. __: https://wiki.python.org/moin/TestPyPI
|
||||||
|
|
|
@ -485,7 +485,7 @@ details. You can check the `psycogreen`_ project for further informations and
|
||||||
resources about the topic.
|
resources about the topic.
|
||||||
|
|
||||||
.. _coroutine: http://en.wikipedia.org/wiki/Coroutine
|
.. _coroutine: http://en.wikipedia.org/wiki/Coroutine
|
||||||
.. _greenlet: http://pypi.python.org/pypi/greenlet
|
.. _greenlet: https://pypi.org/project/greenlet/
|
||||||
.. _green threads: http://en.wikipedia.org/wiki/Green_threads
|
.. _green threads: http://en.wikipedia.org/wiki/Green_threads
|
||||||
.. _Eventlet: http://eventlet.net/
|
.. _Eventlet: http://eventlet.net/
|
||||||
.. _gevent: http://www.gevent.org/
|
.. _gevent: http://www.gevent.org/
|
||||||
|
|
|
@ -61,8 +61,8 @@ except ImportError:
|
||||||
release = version
|
release = version
|
||||||
|
|
||||||
intersphinx_mapping = {
|
intersphinx_mapping = {
|
||||||
'py': ('http://docs.python.org/2', None),
|
'py': ('https://docs.python.org/2', None),
|
||||||
'py3': ('http://docs.python.org/3', None),
|
'py3': ('https://docs.python.org/3', None),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Pattern to generate links to the bug tracker
|
# Pattern to generate links to the bug tracker
|
||||||
|
|
|
@ -48,6 +48,7 @@ Psycopg 2 is both Unicode and Python 3 friendly.
|
||||||
errorcodes
|
errorcodes
|
||||||
faq
|
faq
|
||||||
news
|
news
|
||||||
|
license
|
||||||
|
|
||||||
|
|
||||||
.. ifconfig:: builder != 'text'
|
.. ifconfig:: builder != 'text'
|
||||||
|
|
|
@ -45,7 +45,9 @@ Build prerequisites
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The build prerequisites are to be met in order to install Psycopg from source
|
The build prerequisites are to be met in order to install Psycopg from source
|
||||||
code, either from a source distribution package or from PyPI.
|
code, from a source distribution package, GitHub_ or from PyPI.
|
||||||
|
|
||||||
|
.. _GitHub: https://github.com/psycopg/psycopg2
|
||||||
|
|
||||||
Psycopg is a C wrapper around the libpq_ PostgreSQL client library. To install
|
Psycopg is a C wrapper around the libpq_ PostgreSQL client library. To install
|
||||||
it from sources you will need:
|
it from sources you will need:
|
||||||
|
@ -140,7 +142,7 @@ Make sure to use an up-to-date version of :program:`pip` (you can upgrade it
|
||||||
using something like ``pip install -U pip``)
|
using something like ``pip install -U pip``)
|
||||||
|
|
||||||
.. __: PyPI-binary_
|
.. __: PyPI-binary_
|
||||||
.. _PyPI-binary: https://pypi.python.org/pypi/psycopg2-binary/
|
.. _PyPI-binary: https://pypi.org/project/psycopg2-binary/
|
||||||
.. _wheel: http://pythonwheels.com/
|
.. _wheel: http://pythonwheels.com/
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -302,10 +304,14 @@ Try the following. *In order:*
|
||||||
- Google for `!psycopg2` *your error message*. Especially useful the week
|
- Google for `!psycopg2` *your error message*. Especially useful the week
|
||||||
after the release of a new OS X version.
|
after the release of a new OS X version.
|
||||||
|
|
||||||
- Write to the `Mailing List`__.
|
- Write to the `Mailing List`_.
|
||||||
|
|
||||||
|
- If you think that you have discovered a bug, test failure or missing feature
|
||||||
|
please raise a ticket in the `bug tracker`_.
|
||||||
|
|
||||||
- Complain on your blog or on Twitter that `!psycopg2` is the worst package
|
- Complain on your blog or on Twitter that `!psycopg2` is the worst package
|
||||||
ever and about the quality time you have wasted figuring out the correct
|
ever and about the quality time you have wasted figuring out the correct
|
||||||
:envvar:`ARCHFLAGS`. Especially useful from the Starbucks near you.
|
:envvar:`ARCHFLAGS`. Especially useful from the Starbucks near you.
|
||||||
|
|
||||||
.. __: https://lists.postgresql.org/mj/mj_wwwusr?func=lists-long-full&extra=psycopg
|
.. _mailing list: https://lists.postgresql.org/mj/mj_wwwusr?func=lists-long-full&extra=psycopg
|
||||||
|
.. _bug tracker: https://github.com/psycopg/psycopg2/issues
|
||||||
|
|
7
doc/src/license.rst
Normal file
7
doc/src/license.rst
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.. index::
|
||||||
|
single: License
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: ../../LICENSE
|
|
@ -1,3 +1,7 @@
|
||||||
|
.. index::
|
||||||
|
single: Release notes
|
||||||
|
single: News
|
||||||
|
|
||||||
Release notes
|
Release notes
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
|
@ -363,12 +363,15 @@ class NamedTupleCursor(_cursor):
|
||||||
return
|
return
|
||||||
|
|
||||||
def _make_nt(self):
|
def _make_nt(self):
|
||||||
|
# ascii except alnum and underscore
|
||||||
|
nochars = ' !"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~'
|
||||||
|
re_clean = _re.compile('[' + _re.escape(nochars) + ']')
|
||||||
|
|
||||||
def f(s):
|
def f(s):
|
||||||
# NOTE: Python 3 actually allows unicode chars in fields
|
s = re_clean.sub('_', s)
|
||||||
s = _re.sub('[^a-zA-Z0-9_]', '_', s)
|
|
||||||
# Python identifier cannot start with numbers, namedtuple fields
|
# Python identifier cannot start with numbers, namedtuple fields
|
||||||
# cannot start with underscore. So...
|
# cannot start with underscore. So...
|
||||||
if _re.match('^[0-9_]', s):
|
if s[0] == '_' or '0' <= s[0] <= '9':
|
||||||
s = 'f' + s
|
s = 'f' + s
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
|
@ -38,13 +38,14 @@ list_quote(listObject *self)
|
||||||
{
|
{
|
||||||
/* adapt the list by calling adapt() recursively and then wrapping
|
/* adapt the list by calling adapt() recursively and then wrapping
|
||||||
everything into "ARRAY[]" */
|
everything into "ARRAY[]" */
|
||||||
PyObject *tmp = NULL, *str = NULL, *joined = NULL, *res = NULL;
|
PyObject *res = NULL;
|
||||||
|
PyObject **qs = NULL;
|
||||||
|
Py_ssize_t bufsize = 0;
|
||||||
|
char *buf = NULL, *ptr;
|
||||||
|
|
||||||
/* list consisting of only NULL don't work with the ARRAY[] construct
|
/* list consisting of only NULL don't work with the ARRAY[] construct
|
||||||
* so we use the {NULL,...} syntax. Note however that list of lists where
|
* so we use the {NULL,...} syntax. The same syntax is also necessary
|
||||||
* some element is a list of only null still fails: for that we should use
|
* to convert array of arrays containing only nulls. */
|
||||||
* the '{...}' syntax uniformly but we cannot do it in the current
|
|
||||||
* infrastructure. TODO in psycopg3 */
|
|
||||||
int all_nulls = 1;
|
int all_nulls = 1;
|
||||||
|
|
||||||
Py_ssize_t i, len;
|
Py_ssize_t i, len;
|
||||||
|
@ -53,47 +54,95 @@ list_quote(listObject *self)
|
||||||
|
|
||||||
/* empty arrays are converted to NULLs (still searching for a way to
|
/* empty arrays are converted to NULLs (still searching for a way to
|
||||||
insert an empty array in postgresql */
|
insert an empty array in postgresql */
|
||||||
if (len == 0) return Bytes_FromString("'{}'");
|
if (len == 0) {
|
||||||
|
res = Bytes_FromString("'{}'");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
tmp = PyTuple_New(len);
|
if (!(qs = PyMem_New(PyObject *, len))) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
memset(qs, 0, len * sizeof(PyObject *));
|
||||||
|
|
||||||
for (i=0; i<len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
PyObject *quoted;
|
|
||||||
PyObject *wrapped = PyList_GET_ITEM(self->wrapped, i);
|
PyObject *wrapped = PyList_GET_ITEM(self->wrapped, i);
|
||||||
if (wrapped == Py_None) {
|
if (wrapped == Py_None) {
|
||||||
Py_INCREF(psyco_null);
|
Py_INCREF(psyco_null);
|
||||||
quoted = psyco_null;
|
qs[i] = psyco_null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
quoted = microprotocol_getquoted(wrapped,
|
if (!(qs[i] = microprotocol_getquoted(
|
||||||
(connectionObject*)self->connection);
|
wrapped, (connectionObject*)self->connection))) {
|
||||||
if (quoted == NULL) goto error;
|
goto exit;
|
||||||
all_nulls = 0;
|
}
|
||||||
|
|
||||||
|
/* Lists of arrays containing only nulls are also not supported
|
||||||
|
* by the ARRAY construct so we should do some special casing */
|
||||||
|
if (!PyList_Check(wrapped) || Bytes_AS_STRING(qs[i])[0] == 'A') {
|
||||||
|
all_nulls = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
bufsize += Bytes_GET_SIZE(qs[i]) + 1; /* this, and a comma */
|
||||||
/* here we don't loose a refcnt: SET_ITEM does not change the
|
|
||||||
reference count and we are just transferring ownership of the tmp
|
|
||||||
object to the tuple */
|
|
||||||
PyTuple_SET_ITEM(tmp, i, quoted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now that we have a tuple of adapted objects we just need to join them
|
/* Create an array literal, usually ARRAY[...] but if the contents are
|
||||||
and put "ARRAY[] around the result */
|
* all NULL or array of NULL we must use the '{...}' syntax
|
||||||
str = Bytes_FromString(", ");
|
*/
|
||||||
joined = PyObject_CallMethod(str, "join", "(O)", tmp);
|
if (!(ptr = buf = PyMem_Malloc(bufsize + 8))) {
|
||||||
if (joined == NULL) goto error;
|
PyErr_NoMemory();
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
/* PG doesn't like ARRAY[NULL..] */
|
|
||||||
if (!all_nulls) {
|
if (!all_nulls) {
|
||||||
res = Bytes_FromFormat("ARRAY[%s]", Bytes_AsString(joined));
|
strcpy(ptr, "ARRAY[");
|
||||||
} else {
|
ptr += 6;
|
||||||
res = Bytes_FromFormat("'{%s}'", Bytes_AsString(joined));
|
for (i = 0; i < len; i++) {
|
||||||
|
Py_ssize_t sl;
|
||||||
|
sl = Bytes_GET_SIZE(qs[i]);
|
||||||
|
memcpy(ptr, Bytes_AS_STRING(qs[i]), sl);
|
||||||
|
ptr += sl;
|
||||||
|
*ptr++ = ',';
|
||||||
|
}
|
||||||
|
*(ptr - 1) = ']';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*ptr++ = '\'';
|
||||||
|
*ptr++ = '{';
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
/* in case all the adapted things are nulls (or array of nulls),
|
||||||
|
* the quoted string is either NULL or an array of the form
|
||||||
|
* '{NULL,...}', in which case we have to strip the extra quotes */
|
||||||
|
char *s;
|
||||||
|
Py_ssize_t sl;
|
||||||
|
s = Bytes_AS_STRING(qs[i]);
|
||||||
|
sl = Bytes_GET_SIZE(qs[i]);
|
||||||
|
if (s[0] != '\'') {
|
||||||
|
memcpy(ptr, s, sl);
|
||||||
|
ptr += sl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(ptr, s + 1, sl - 2);
|
||||||
|
ptr += sl - 2;
|
||||||
|
}
|
||||||
|
*ptr++ = ',';
|
||||||
|
}
|
||||||
|
*(ptr - 1) = '}';
|
||||||
|
*ptr++ = '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
res = Bytes_FromStringAndSize(buf, ptr - buf);
|
||||||
Py_XDECREF(tmp);
|
|
||||||
Py_XDECREF(str);
|
exit:
|
||||||
Py_XDECREF(joined);
|
if (qs) {
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
PyObject *q = qs[i];
|
||||||
|
Py_XDECREF(q);
|
||||||
|
}
|
||||||
|
PyMem_Free(qs);
|
||||||
|
}
|
||||||
|
PyMem_Free(buf);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,11 @@ psyco_curs_close(cursorObject *self)
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
PGTransactionStatusType status;
|
PGTransactionStatusType status;
|
||||||
|
|
||||||
|
if (!self->query) {
|
||||||
|
Dprintf("skipping named cursor close because unused");
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
|
||||||
if (self->conn) {
|
if (self->conn) {
|
||||||
status = PQtransactionStatus(self->conn->pgconn);
|
status = PQtransactionStatus(self->conn->pgconn);
|
||||||
}
|
}
|
||||||
|
@ -66,17 +71,18 @@ psyco_curs_close(cursorObject *self)
|
||||||
status = PQTRANS_UNKNOWN;
|
status = PQTRANS_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(status == PQTRANS_UNKNOWN || status == PQTRANS_INERROR)) {
|
if (status == PQTRANS_UNKNOWN || status == PQTRANS_INERROR) {
|
||||||
EXC_IF_NO_MARK(self);
|
|
||||||
PyOS_snprintf(buffer, 127, "CLOSE %s", self->qname);
|
|
||||||
if (pq_execute(self, buffer, 0, 0, 1) == -1) return NULL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Dprintf("skipping named curs close because tx status %d",
|
Dprintf("skipping named curs close because tx status %d",
|
||||||
(int)status);
|
(int)status);
|
||||||
|
goto close;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXC_IF_NO_MARK(self);
|
||||||
|
PyOS_snprintf(buffer, 127, "CLOSE %s", self->qname);
|
||||||
|
if (pq_execute(self, buffer, 0, 0, 1) == -1) return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close:
|
||||||
self->closed = 1;
|
self->closed = 1;
|
||||||
Dprintf("psyco_curs_close: cursor at %p closed", self);
|
Dprintf("psyco_curs_close: cursor at %p closed", self);
|
||||||
|
|
||||||
|
|
|
@ -406,6 +406,11 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'P':
|
||||||
|
PyErr_SetString(NotSupportedError,
|
||||||
|
"iso_8601 intervalstyle currently not supported");
|
||||||
|
return NULL;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,15 +56,15 @@ fi
|
||||||
# Unsupported postgres versions that we still support
|
# Unsupported postgres versions that we still support
|
||||||
# Images built by https://github.com/psycopg/psycopg2-wheels/tree/build-dinosaurs
|
# Images built by https://github.com/psycopg/psycopg2-wheels/tree/build-dinosaurs
|
||||||
if [[ -n "$TEST_PAST" ]]; then
|
if [[ -n "$TEST_PAST" ]]; then
|
||||||
run_test 7.4
|
|
||||||
run_test 8.0
|
|
||||||
run_test 8.1
|
|
||||||
run_test 8.2
|
|
||||||
run_test 8.3
|
|
||||||
run_test 8.4
|
|
||||||
run_test 9.0
|
|
||||||
run_test 9.1
|
|
||||||
run_test 9.2
|
run_test 9.2
|
||||||
|
run_test 9.1
|
||||||
|
run_test 9.0
|
||||||
|
run_test 8.4
|
||||||
|
run_test 8.3
|
||||||
|
run_test 8.2
|
||||||
|
run_test 8.1
|
||||||
|
run_test 8.0
|
||||||
|
run_test 7.4
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Postgres built from master
|
# Postgres built from master
|
||||||
|
|
|
@ -435,6 +435,11 @@ class CursorTests(ConnectingTestCase):
|
||||||
self.assertEqual([(2,), (3,), (4,)], cur2.fetchmany(3))
|
self.assertEqual([(2,), (3,), (4,)], cur2.fetchmany(3))
|
||||||
self.assertEqual([(5,), (6,), (7,)], cur2.fetchall())
|
self.assertEqual([(5,), (6,), (7,)], cur2.fetchall())
|
||||||
|
|
||||||
|
@skip_before_postgres(8, 0)
|
||||||
|
def test_named_noop_close(self):
|
||||||
|
cur = self.conn.cursor('test')
|
||||||
|
cur.close()
|
||||||
|
|
||||||
@skip_before_postgres(8, 0)
|
@skip_before_postgres(8, 0)
|
||||||
def test_scroll(self):
|
def test_scroll(self):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
|
|
|
@ -438,6 +438,14 @@ class DatetimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
|
||||||
r = cur.fetchone()[0]
|
r = cur.fetchone()[0]
|
||||||
self.assertEqual(r, v, "%s -> %s != %s" % (s, r, v))
|
self.assertEqual(r, v, "%s -> %s != %s" % (s, r, v))
|
||||||
|
|
||||||
|
@skip_before_postgres(8, 4)
|
||||||
|
def test_interval_iso_8601_not_supported(self):
|
||||||
|
# We may end up supporting, but no pressure for it
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
cur.execute("set local intervalstyle to iso_8601")
|
||||||
|
cur.execute("select '1 day 2 hours'::interval")
|
||||||
|
self.assertRaises(psycopg2.NotSupportedError, cur.fetchone)
|
||||||
|
|
||||||
|
|
||||||
# Only run the datetime tests if psycopg was compiled with support.
|
# Only run the datetime tests if psycopg was compiled with support.
|
||||||
if not hasattr(psycopg2.extensions, 'PYDATETIME'):
|
if not hasattr(psycopg2.extensions, 'PYDATETIME'):
|
||||||
|
|
|
@ -19,7 +19,7 @@ from datetime import timedelta
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.extras
|
import psycopg2.extras
|
||||||
import unittest
|
import unittest
|
||||||
from .testutils import ConnectingTestCase, skip_before_postgres
|
from .testutils import ConnectingTestCase, skip_before_postgres, skip_before_python
|
||||||
|
|
||||||
|
|
||||||
class ExtrasDictCursorTests(ConnectingTestCase):
|
class ExtrasDictCursorTests(ConnectingTestCase):
|
||||||
|
@ -357,6 +357,14 @@ class NamedTupleCursorTest(ConnectingTestCase):
|
||||||
self.assertEqual(rv.f_column_, 2)
|
self.assertEqual(rv.f_column_, 2)
|
||||||
self.assertEqual(rv.f3, 3)
|
self.assertEqual(rv.f3, 3)
|
||||||
|
|
||||||
|
@skip_before_python(3)
|
||||||
|
@skip_before_postgres(8)
|
||||||
|
def test_nonascii_name(self):
|
||||||
|
curs = self.conn.cursor()
|
||||||
|
curs.execute('select 1 as \xe5h\xe9')
|
||||||
|
rv = curs.fetchone()
|
||||||
|
self.assertEqual(getattr(rv, '\xe5h\xe9'), 1)
|
||||||
|
|
||||||
def test_minimal_generation(self):
|
def test_minimal_generation(self):
|
||||||
# Instrument the class to verify it gets called the minimum number of times.
|
# Instrument the class to verify it gets called the minimum number of times.
|
||||||
from psycopg2.extras import NamedTupleCursor
|
from psycopg2.extras import NamedTupleCursor
|
||||||
|
|
|
@ -224,16 +224,31 @@ class TypesBasicTests(ConnectingTestCase):
|
||||||
curs.execute("insert into na (boola) values (%s)", ([True, None],))
|
curs.execute("insert into na (boola) values (%s)", ([True, None],))
|
||||||
curs.execute("insert into na (boola) values (%s)", ([None, None],))
|
curs.execute("insert into na (boola) values (%s)", ([None, None],))
|
||||||
|
|
||||||
# TODO: array of array of nulls are not supported yet
|
curs.execute("insert into na (textaa) values (%s)", ([[None]],))
|
||||||
# curs.execute("insert into na (textaa) values (%s)", ([[None]],))
|
|
||||||
curs.execute("insert into na (textaa) values (%s)", ([['a', None]],))
|
curs.execute("insert into na (textaa) values (%s)", ([['a', None]],))
|
||||||
# curs.execute("insert into na (textaa) values (%s)", ([[None, None]],))
|
curs.execute("insert into na (textaa) values (%s)", ([[None, None]],))
|
||||||
# curs.execute("insert into na (intaa) values (%s)", ([[None]],))
|
|
||||||
|
curs.execute("insert into na (intaa) values (%s)", ([[None]],))
|
||||||
curs.execute("insert into na (intaa) values (%s)", ([[42, None]],))
|
curs.execute("insert into na (intaa) values (%s)", ([[42, None]],))
|
||||||
# curs.execute("insert into na (intaa) values (%s)", ([[None, None]],))
|
curs.execute("insert into na (intaa) values (%s)", ([[None, None]],))
|
||||||
# curs.execute("insert into na (boolaa) values (%s)", ([[None]],))
|
|
||||||
|
curs.execute("insert into na (boolaa) values (%s)", ([[None]],))
|
||||||
curs.execute("insert into na (boolaa) values (%s)", ([[True, None]],))
|
curs.execute("insert into na (boolaa) values (%s)", ([[True, None]],))
|
||||||
# curs.execute("insert into na (boolaa) values (%s)", ([[None, None]],))
|
curs.execute("insert into na (boolaa) values (%s)", ([[None, None]],))
|
||||||
|
|
||||||
|
@testutils.skip_before_postgres(8, 2)
|
||||||
|
def testNestedArrays(self):
|
||||||
|
curs = self.conn.cursor()
|
||||||
|
for a in [
|
||||||
|
[[1]],
|
||||||
|
[[None]],
|
||||||
|
[[None, None, None]],
|
||||||
|
[[None, None], [1, None]],
|
||||||
|
[[None, None], [None, None]],
|
||||||
|
[[[None, None], [None, None]]],
|
||||||
|
]:
|
||||||
|
curs.execute("select %s::int[]", (a,))
|
||||||
|
self.assertEqual(curs.fetchone()[0], a)
|
||||||
|
|
||||||
@testutils.skip_from_python(3)
|
@testutils.skip_from_python(3)
|
||||||
def testTypeRoundtripBuffer(self):
|
def testTypeRoundtripBuffer(self):
|
||||||
|
|
|
@ -179,8 +179,8 @@ class HstoreTestCase(ConnectingTestCase):
|
||||||
m = re.match(br'hstore\(ARRAY\[([^\]]+)\], ARRAY\[([^\]]+)\]\)', q)
|
m = re.match(br'hstore\(ARRAY\[([^\]]+)\], ARRAY\[([^\]]+)\]\)', q)
|
||||||
self.assert_(m, repr(q))
|
self.assert_(m, repr(q))
|
||||||
|
|
||||||
kk = m.group(1).split(b", ")
|
kk = m.group(1).split(b",")
|
||||||
vv = m.group(2).split(b", ")
|
vv = m.group(2).split(b",")
|
||||||
ii = list(zip(kk, vv))
|
ii = list(zip(kk, vv))
|
||||||
ii.sort()
|
ii.sort()
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import psycopg2
|
||||||
import psycopg2.extensions as ext
|
import psycopg2.extensions as ext
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
from .testutils import ConnectingTestCase
|
from .testutils import ConnectingTestCase, skip_before_postgres
|
||||||
|
|
||||||
|
|
||||||
class WithTestCase(ConnectingTestCase):
|
class WithTestCase(ConnectingTestCase):
|
||||||
|
@ -215,6 +215,11 @@ class WithCursorTestCase(WithTestCase):
|
||||||
else:
|
else:
|
||||||
self.fail("where is my exception?")
|
self.fail("where is my exception?")
|
||||||
|
|
||||||
|
@skip_before_postgres(8, 0)
|
||||||
|
def test_named_with_noop(self):
|
||||||
|
with self.conn.cursor('named') as cur:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
8
tox.ini
8
tox.ini
|
@ -1,13 +1,9 @@
|
||||||
# Tox (http://tox.testrun.org/) is a tool for running tests
|
|
||||||
# in multiple virtualenvs. This configuration file will run the
|
|
||||||
# test suite on all supported python versions. To use it, "pip install tox"
|
|
||||||
# and then run "tox" from this directory.
|
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py27
|
envlist = py{27,34,35,36}
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands = make check
|
commands = make check
|
||||||
|
whitelist_externals = make
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
max-line-length = 85
|
max-line-length = 85
|
||||||
|
|
Loading…
Reference in New Issue
Block a user