mirror of
				https://github.com/psycopg/psycopg2.git
				synced 2025-10-31 07:47:30 +03:00 
			
		
		
		
	Merge branch 'python2' into python3
Conflicts: NEWS-2.3 psycopg/connection_type.c tests/test_connection.py tests/types_basic.py
This commit is contained in:
		
						commit
						80bd6e2794
					
				
							
								
								
									
										2
									
								
								NEWS-2.3
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								NEWS-2.3
									
									
									
									
									
								
							|  | @ -5,6 +5,7 @@ What's new in psycopg 2.3.3 | ||||||
| 
 | 
 | ||||||
|   - Added `register_composite()` function to cast PostgreSQL composite types |   - Added `register_composite()` function to cast PostgreSQL composite types | ||||||
|     into Python tuples/namedtuples. |     into Python tuples/namedtuples. | ||||||
|  |   - Connections and cursors are weakly referenceable. | ||||||
|   - The build script refuses to guess values if pg_config is not found. |   - The build script refuses to guess values if pg_config is not found. | ||||||
|   - Improved PostgreSQL-Python encodings mapping. Added a few |   - Improved PostgreSQL-Python encodings mapping. Added a few | ||||||
|     missing encodings: EUC_CN, EUC_JIS_2004, ISO885910, ISO885916, |     missing encodings: EUC_CN, EUC_JIS_2004, ISO885910, ISO885916, | ||||||
|  | @ -16,6 +17,7 @@ What's new in psycopg 2.3.3 | ||||||
| 
 | 
 | ||||||
|   - Fixed adaptation of None in composite types (ticket #26). Bug report by |   - Fixed adaptation of None in composite types (ticket #26). Bug report by | ||||||
|     Karsten Hilbert. |     Karsten Hilbert. | ||||||
|  |   - Fixed several reference leaks in less common code paths. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| What's new in psycopg 2.3.2 | What's new in psycopg 2.3.2 | ||||||
|  |  | ||||||
|  | @ -769,7 +769,7 @@ class CompositeCaster(object): | ||||||
| 
 | 
 | ||||||
|         self.attnames = [ a[0] for a in attrs ] |         self.attnames = [ a[0] for a in attrs ] | ||||||
|         self.atttypes = [ a[1] for a in attrs ] |         self.atttypes = [ a[1] for a in attrs ] | ||||||
|         self.type = self._create_type(name, self.attnames) |         self._create_type(name, self.attnames) | ||||||
|         self.typecaster = _ext.new_type((oid,), name, self.parse) |         self.typecaster = _ext.new_type((oid,), name, self.parse) | ||||||
| 
 | 
 | ||||||
|     def parse(self, s, curs): |     def parse(self, s, curs): | ||||||
|  | @ -784,7 +784,7 @@ class CompositeCaster(object): | ||||||
| 
 | 
 | ||||||
|         attrs = [ curs.cast(oid, token) |         attrs = [ curs.cast(oid, token) | ||||||
|             for oid, token in zip(self.atttypes, tokens) ] |             for oid, token in zip(self.atttypes, tokens) ] | ||||||
|         return self.type(*attrs) |         return self._ctor(*attrs) | ||||||
| 
 | 
 | ||||||
|     _re_tokenize = regex.compile(r""" |     _re_tokenize = regex.compile(r""" | ||||||
|   \(? ([,\)])                       # an empty token, representing NULL |   \(? ([,\)])                       # an empty token, representing NULL | ||||||
|  | @ -813,9 +813,11 @@ class CompositeCaster(object): | ||||||
|         try: |         try: | ||||||
|             from collections import namedtuple |             from collections import namedtuple | ||||||
|         except ImportError: |         except ImportError: | ||||||
|             return tuple |             self.type = tuple | ||||||
|  |             self._ctor = lambda *args: tuple(args) | ||||||
|         else: |         else: | ||||||
|             return namedtuple(name, attnames) |             self.type = namedtuple(name, attnames) | ||||||
|  |             self._ctor = self.type | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _from_db(self, name, conn_or_curs): |     def _from_db(self, name, conn_or_curs): | ||||||
|  |  | ||||||
|  | @ -362,20 +362,13 @@ psyco_Time(PyObject *self, PyObject *args) | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PyObject * | static PyObject * | ||||||
| psyco_Timestamp(PyObject *self, PyObject *args) | _psyco_Timestamp(int year, int month, int day, | ||||||
|  |                  int hour, int minute, double second, PyObject *tzinfo) | ||||||
| { | { | ||||||
|  |     double micro; | ||||||
|  |     PyObject *obj; | ||||||
|     PyObject *res = NULL; |     PyObject *res = NULL; | ||||||
|     PyObject *tzinfo = NULL; |  | ||||||
|     int year, month, day; |  | ||||||
|     int hour=0, minute=0; /* default to midnight */ |  | ||||||
|     double micro, second=0.0; |  | ||||||
| 
 |  | ||||||
|     PyObject* obj = NULL; |  | ||||||
| 
 |  | ||||||
|     if (!PyArg_ParseTuple(args, "lii|iidO", &year, &month, &day, |  | ||||||
|                           &hour, &minute, &second, &tzinfo)) |  | ||||||
|         return NULL; |  | ||||||
| 
 | 
 | ||||||
|     micro = (second - floor(second)) * 1000000.0; |     micro = (second - floor(second)) * 1000000.0; | ||||||
|     second = floor(second); |     second = floor(second); | ||||||
|  | @ -400,6 +393,21 @@ psyco_Timestamp(PyObject *self, PyObject *args) | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | PyObject * | ||||||
|  | psyco_Timestamp(PyObject *self, PyObject *args) | ||||||
|  | { | ||||||
|  |     PyObject *tzinfo = NULL; | ||||||
|  |     int year, month, day; | ||||||
|  |     int hour=0, minute=0; /* default to midnight */ | ||||||
|  |     double second=0.0; | ||||||
|  | 
 | ||||||
|  |     if (!PyArg_ParseTuple(args, "lii|iidO", &year, &month, &day, | ||||||
|  |                           &hour, &minute, &second, &tzinfo)) | ||||||
|  |         return NULL; | ||||||
|  | 
 | ||||||
|  |     return _psyco_Timestamp(year, month, day, hour, minute, second, tzinfo); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| PyObject * | PyObject * | ||||||
| psyco_DateFromTicks(PyObject *self, PyObject *args) | psyco_DateFromTicks(PyObject *self, PyObject *args) | ||||||
| { | { | ||||||
|  | @ -460,18 +468,10 @@ psyco_TimestampFromTicks(PyObject *self, PyObject *args) | ||||||
|     t = (time_t)floor(ticks); |     t = (time_t)floor(ticks); | ||||||
|     ticks -= (double)t; |     ticks -= (double)t; | ||||||
|     if (localtime_r(&t, &tm)) { |     if (localtime_r(&t, &tm)) { | ||||||
|         PyObject *value = Py_BuildValue("iiiiidO", |         res = _psyco_Timestamp( | ||||||
|             tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, |             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, | ||||||
|             tm.tm_hour, tm.tm_min, |             tm.tm_hour, tm.tm_min, (double)tm.tm_sec + ticks, | ||||||
|             (double)tm.tm_sec + ticks, |  | ||||||
|             pyPsycopgTzLOCAL); |             pyPsycopgTzLOCAL); | ||||||
|         if (value) { |  | ||||||
|             /* FIXME: not decref'ing the value here is a memory leak
 |  | ||||||
| 	       but, on the other hand, if we decref we get a clean nice |  | ||||||
| 	       segfault (on my 64 bit Python 2.4 box). So this leaks |  | ||||||
| 	       will stay until after 2.0.7 when we'll try to plug it */ |  | ||||||
|             res = psyco_Timestamp(self, value); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return res; |     return res; | ||||||
|  |  | ||||||
|  | @ -99,7 +99,7 @@ typedef struct { | ||||||
|     PGconn *pgconn;           /* the postgresql connection */ |     PGconn *pgconn;           /* the postgresql connection */ | ||||||
|     PGcancel *cancel;         /* the cancellation structure */ |     PGcancel *cancel;         /* the cancellation structure */ | ||||||
| 
 | 
 | ||||||
|     PyObject *async_cursor;   /* a cursor executing an asynchronous query */ |     PyObject *async_cursor;   /* weakref to a cursor executing an asynchronous query */ | ||||||
|     int async_status;         /* asynchronous execution status */ |     int async_status;         /* asynchronous execution status */ | ||||||
| 
 | 
 | ||||||
|     /* notice processing */ |     /* notice processing */ | ||||||
|  | @ -115,6 +115,7 @@ typedef struct { | ||||||
|     PyObject *binary_types;   /* a set of typecasters for binary types */ |     PyObject *binary_types;   /* a set of typecasters for binary types */ | ||||||
| 
 | 
 | ||||||
|     int equote;               /* use E''-style quotes for escaped strings */ |     int equote;               /* use E''-style quotes for escaped strings */ | ||||||
|  |     PyObject *weakreflist;    /* list of weak references */ | ||||||
| 
 | 
 | ||||||
| } connectionObject; | } connectionObject; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -823,7 +823,17 @@ conn_poll(connectionObject *self) | ||||||
|         if (res == PSYCO_POLL_OK && self->async_cursor) { |         if (res == PSYCO_POLL_OK && self->async_cursor) { | ||||||
|             /* An async query has just finished: parse the tuple in the
 |             /* An async query has just finished: parse the tuple in the
 | ||||||
|              * target cursor. */ |              * target cursor. */ | ||||||
|             cursorObject *curs = (cursorObject *)self->async_cursor; |             cursorObject *curs; | ||||||
|  |             PyObject *py_curs = PyWeakref_GetObject(self->async_cursor); | ||||||
|  |             if (Py_None == py_curs) { | ||||||
|  |                 pq_clear_async(self); | ||||||
|  |                 PyErr_SetString(InterfaceError, | ||||||
|  |                     "the asynchronous cursor has disappeared"); | ||||||
|  |                 res = PSYCO_POLL_ERROR; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             curs = (cursorObject *)py_curs; | ||||||
|             IFCLEARPGRES(curs->pgres); |             IFCLEARPGRES(curs->pgres); | ||||||
|             curs->pgres = pq_get_last_result(self); |             curs->pgres = pq_get_last_result(self); | ||||||
| 
 | 
 | ||||||
|  | @ -835,8 +845,7 @@ conn_poll(connectionObject *self) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             /* We have finished with our async_cursor */ |             /* We have finished with our async_cursor */ | ||||||
|             Py_XDECREF(self->async_cursor); |             Py_CLEAR(self->async_cursor); | ||||||
|             self->async_cursor = NULL; |  | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -300,8 +300,6 @@ _psyco_conn_tpc_finish(connectionObject *self, PyObject *args, | ||||||
|             goto exit; |             goto exit; | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         PyObject *tmp; |  | ||||||
| 
 |  | ||||||
|         /* committing/aborting our own transaction. */ |         /* committing/aborting our own transaction. */ | ||||||
|         if (!self->tpc_xid) { |         if (!self->tpc_xid) { | ||||||
|             PyErr_SetString(ProgrammingError, |             PyErr_SetString(ProgrammingError, | ||||||
|  | @ -327,11 +325,10 @@ _psyco_conn_tpc_finish(connectionObject *self, PyObject *args, | ||||||
|             goto exit; |             goto exit; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         Py_CLEAR(self->tpc_xid); | ||||||
|  | 
 | ||||||
|         /* connection goes ready */ |         /* connection goes ready */ | ||||||
|         self->status = CONN_STATUS_READY; |         self->status = CONN_STATUS_READY; | ||||||
|         tmp = (PyObject *)self->tpc_xid; |  | ||||||
|         self->tpc_xid = NULL; |  | ||||||
|         Py_DECREF(tmp); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Py_INCREF(Py_None); |     Py_INCREF(Py_None); | ||||||
|  | @ -888,6 +885,10 @@ connection_dealloc(PyObject* obj) | ||||||
| { | { | ||||||
|     connectionObject *self = (connectionObject *)obj; |     connectionObject *self = (connectionObject *)obj; | ||||||
| 
 | 
 | ||||||
|  |     if (self->weakreflist) { | ||||||
|  |         PyObject_ClearWeakRefs(obj); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     PyObject_GC_UnTrack(self); |     PyObject_GC_UnTrack(self); | ||||||
| 
 | 
 | ||||||
|     if (self->closed == 0) conn_close(self); |     if (self->closed == 0) conn_close(self); | ||||||
|  | @ -899,6 +900,7 @@ connection_dealloc(PyObject* obj) | ||||||
|     PyMem_Free(self->codec); |     PyMem_Free(self->codec); | ||||||
|     if (self->critical) free(self->critical); |     if (self->critical) free(self->critical); | ||||||
| 
 | 
 | ||||||
|  |     Py_CLEAR(self->tpc_xid); | ||||||
|     Py_CLEAR(self->async_cursor); |     Py_CLEAR(self->async_cursor); | ||||||
|     Py_CLEAR(self->notice_list); |     Py_CLEAR(self->notice_list); | ||||||
|     Py_CLEAR(self->notice_filter); |     Py_CLEAR(self->notice_filter); | ||||||
|  | @ -952,6 +954,7 @@ connection_repr(connectionObject *self) | ||||||
| static int | static int | ||||||
| connection_traverse(connectionObject *self, visitproc visit, void *arg) | connection_traverse(connectionObject *self, visitproc visit, void *arg) | ||||||
| { | { | ||||||
|  |     Py_VISIT(self->tpc_xid); | ||||||
|     Py_VISIT(self->async_cursor); |     Py_VISIT(self->async_cursor); | ||||||
|     Py_VISIT(self->notice_list); |     Py_VISIT(self->notice_list); | ||||||
|     Py_VISIT(self->notice_filter); |     Py_VISIT(self->notice_filter); | ||||||
|  | @ -993,14 +996,16 @@ PyTypeObject connectionType = { | ||||||
|     0,          /*tp_setattro*/ |     0,          /*tp_setattro*/ | ||||||
|     0,          /*tp_as_buffer*/ |     0,          /*tp_as_buffer*/ | ||||||
| 
 | 
 | ||||||
|     Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ |     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | | ||||||
|  |         Py_TPFLAGS_HAVE_WEAKREFS, | ||||||
|  |                 /*tp_flags*/ | ||||||
|     connectionType_doc, /*tp_doc*/ |     connectionType_doc, /*tp_doc*/ | ||||||
| 
 | 
 | ||||||
|     (traverseproc)connection_traverse, /*tp_traverse*/ |     (traverseproc)connection_traverse, /*tp_traverse*/ | ||||||
|     0,          /*tp_clear*/ |     0,          /*tp_clear*/ | ||||||
| 
 | 
 | ||||||
|     0,          /*tp_richcompare*/ |     0,          /*tp_richcompare*/ | ||||||
|     0,          /*tp_weaklistoffset*/ |     offsetof(connectionObject, weakreflist), /* tp_weaklistoffset */ | ||||||
| 
 | 
 | ||||||
|     0,          /*tp_iter*/ |     0,          /*tp_iter*/ | ||||||
|     0,          /*tp_iternext*/ |     0,          /*tp_iternext*/ | ||||||
|  |  | ||||||
|  | @ -76,6 +76,8 @@ typedef struct { | ||||||
|     PyObject *string_types;   /* a set of typecasters for string types */ |     PyObject *string_types;   /* a set of typecasters for string types */ | ||||||
|     PyObject *binary_types;   /* a set of typecasters for binary types */ |     PyObject *binary_types;   /* a set of typecasters for binary types */ | ||||||
| 
 | 
 | ||||||
|  |     PyObject *weakreflist;    /* list of weak references */ | ||||||
|  | 
 | ||||||
| } cursorObject; | } cursorObject; | ||||||
| 
 | 
 | ||||||
| /* C-callable functions in cursor_int.c and cursor_ext.c */ | /* C-callable functions in cursor_int.c and cursor_ext.c */ | ||||||
|  |  | ||||||
|  | @ -779,7 +779,8 @@ psyco_curs_fetchone(cursorObject *self, PyObject *args) | ||||||
|     /* if the query was async aggresively free pgres, to allow
 |     /* if the query was async aggresively free pgres, to allow
 | ||||||
|        successive requests to reallocate it */ |        successive requests to reallocate it */ | ||||||
|     if (self->row >= self->rowcount |     if (self->row >= self->rowcount | ||||||
|         && self->conn->async_cursor == (PyObject*)self) |         && self->conn->async_cursor | ||||||
|  |         && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) | ||||||
|         IFCLEARPGRES(self->pgres); |         IFCLEARPGRES(self->pgres); | ||||||
| 
 | 
 | ||||||
|     return res; |     return res; | ||||||
|  | @ -855,7 +856,8 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords) | ||||||
|     /* if the query was async aggresively free pgres, to allow
 |     /* if the query was async aggresively free pgres, to allow
 | ||||||
|        successive requests to reallocate it */ |        successive requests to reallocate it */ | ||||||
|     if (self->row >= self->rowcount |     if (self->row >= self->rowcount | ||||||
|         && self->conn->async_cursor == (PyObject*)self) |         && self->conn->async_cursor | ||||||
|  |         && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) | ||||||
|         IFCLEARPGRES(self->pgres); |         IFCLEARPGRES(self->pgres); | ||||||
| 
 | 
 | ||||||
|     return list; |     return list; | ||||||
|  | @ -919,7 +921,8 @@ psyco_curs_fetchall(cursorObject *self, PyObject *args) | ||||||
|     /* if the query was async aggresively free pgres, to allow
 |     /* if the query was async aggresively free pgres, to allow
 | ||||||
|        successive requests to reallocate it */ |        successive requests to reallocate it */ | ||||||
|     if (self->row >= self->rowcount |     if (self->row >= self->rowcount | ||||||
|         && self->conn->async_cursor == (PyObject*)self) |         && self->conn->async_cursor | ||||||
|  |         && PyWeakref_GetObject(self->conn->async_cursor) == (PyObject*)self) | ||||||
|         IFCLEARPGRES(self->pgres); |         IFCLEARPGRES(self->pgres); | ||||||
| 
 | 
 | ||||||
|     return list; |     return list; | ||||||
|  | @ -1626,6 +1629,7 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name) | ||||||
| 
 | 
 | ||||||
|     self->string_types = NULL; |     self->string_types = NULL; | ||||||
|     self->binary_types = NULL; |     self->binary_types = NULL; | ||||||
|  |     self->weakreflist = NULL; | ||||||
| 
 | 
 | ||||||
|     Py_INCREF(Py_None); |     Py_INCREF(Py_None); | ||||||
|     self->description = Py_None; |     self->description = Py_None; | ||||||
|  | @ -1652,6 +1656,10 @@ cursor_dealloc(PyObject* obj) | ||||||
| { | { | ||||||
|     cursorObject *self = (cursorObject *)obj; |     cursorObject *self = (cursorObject *)obj; | ||||||
| 
 | 
 | ||||||
|  |     if (self->weakreflist) { | ||||||
|  |         PyObject_ClearWeakRefs(obj); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     PyObject_GC_UnTrack(self); |     PyObject_GC_UnTrack(self); | ||||||
| 
 | 
 | ||||||
|     if (self->name) PyMem_Free(self->name); |     if (self->name) PyMem_Free(self->name); | ||||||
|  | @ -1752,14 +1760,15 @@ PyTypeObject cursorType = { | ||||||
|     0,          /*tp_as_buffer*/ |     0,          /*tp_as_buffer*/ | ||||||
| 
 | 
 | ||||||
|     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER | |     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER | | ||||||
|       Py_TPFLAGS_HAVE_GC, /*tp_flags*/ |       Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_WEAKREFS , | ||||||
|  |                 /*tp_flags*/ | ||||||
|     cursorType_doc, /*tp_doc*/ |     cursorType_doc, /*tp_doc*/ | ||||||
| 
 | 
 | ||||||
|     (traverseproc)cursor_traverse, /*tp_traverse*/ |     (traverseproc)cursor_traverse, /*tp_traverse*/ | ||||||
|     0,          /*tp_clear*/ |     0,          /*tp_clear*/ | ||||||
| 
 | 
 | ||||||
|     0,          /*tp_richcompare*/ |     0,          /*tp_richcompare*/ | ||||||
|     0,          /*tp_weaklistoffset*/ |     offsetof(cursorObject, weakreflist), /*tp_weaklistoffset*/ | ||||||
| 
 | 
 | ||||||
|     cursor_iter, /*tp_iter*/ |     cursor_iter, /*tp_iter*/ | ||||||
|     cursor_next, /*tp_iternext*/ |     cursor_next, /*tp_iternext*/ | ||||||
|  |  | ||||||
|  | @ -275,8 +275,7 @@ pq_clear_async(connectionObject *conn) | ||||||
|         Dprintf("pq_clear_async: clearing PGresult at %p", pgres); |         Dprintf("pq_clear_async: clearing PGresult at %p", pgres); | ||||||
|         CLEARPGRES(pgres); |         CLEARPGRES(pgres); | ||||||
|     } |     } | ||||||
|     Py_XDECREF(conn->async_cursor); |     Py_CLEAR(conn->async_cursor); | ||||||
|     conn->async_cursor = NULL; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -820,8 +819,11 @@ pq_execute(cursorObject *curs, const char *query, int async) | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         curs->conn->async_status = async_status; |         curs->conn->async_status = async_status; | ||||||
|         Py_INCREF(curs); |         curs->conn->async_cursor = PyWeakref_NewRef((PyObject *)curs, NULL); | ||||||
|         curs->conn->async_cursor = (PyObject*)curs; |         if (!curs->conn->async_cursor) { | ||||||
|  |             /* weakref creation failed */ | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return 1-async; |     return 1-async; | ||||||
|  |  | ||||||
							
								
								
									
										111
									
								
								scripts/refcounter.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										111
									
								
								scripts/refcounter.py
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,111 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | """Detect reference leaks after several unit test runs. | ||||||
|  | 
 | ||||||
|  | The script runs the unit test and counts the objects alive after the run. If | ||||||
|  | the object count differs between the last two runs, a report is printed and the | ||||||
|  | script exits with error 1. | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | # Copyright (C) 2011 Daniele Varrazzo <daniele.varrazzo@gmail.com> | ||||||
|  | # | ||||||
|  | # This program is free software; you can redistribute it and/or modify | ||||||
|  | # it under the terms of the GNU General Public License as published by | ||||||
|  | # the Free Software Foundation; either version 2 of the License, or | ||||||
|  | # (at your option) any later version. | ||||||
|  | # | ||||||
|  | # This program is distributed in the hope that it will be useful, | ||||||
|  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | # GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | # You should have received a copy of the GNU General Public License | ||||||
|  | # along with this program; if not, write to the Free Software | ||||||
|  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||||
|  | 
 | ||||||
|  | import gc | ||||||
|  | import sys | ||||||
|  | import difflib | ||||||
|  | import unittest | ||||||
|  | from pprint import pprint | ||||||
|  | from collections import defaultdict | ||||||
|  | 
 | ||||||
|  | def main(): | ||||||
|  |     opt = parse_args() | ||||||
|  | 
 | ||||||
|  |     import psycopg2.tests | ||||||
|  |     test = psycopg2.tests | ||||||
|  |     if opt.suite: | ||||||
|  |         test = getattr(test, opt.suite) | ||||||
|  | 
 | ||||||
|  |     sys.stdout.write("test suite %s\n" % test.__name__) | ||||||
|  | 
 | ||||||
|  |     for i in range(1, opt.nruns + 1): | ||||||
|  |         sys.stdout.write("test suite run %d of %d\n" % (i, opt.nruns)) | ||||||
|  |         runner = unittest.TextTestRunner() | ||||||
|  |         runner.run(test.test_suite()) | ||||||
|  |         dump(i, opt) | ||||||
|  | 
 | ||||||
|  |     f1 = open('debug-%02d.txt' % (opt.nruns - 1)).readlines() | ||||||
|  |     f2 = open('debug-%02d.txt' % (opt.nruns)).readlines() | ||||||
|  |     for line in difflib.unified_diff(f1, f2, | ||||||
|  |             "run %d" % (opt.nruns - 1), "run %d" % opt.nruns): | ||||||
|  |         sys.stdout.write(line) | ||||||
|  | 
 | ||||||
|  |     rv = f1 != f2 and 1 or 0 | ||||||
|  | 
 | ||||||
|  |     if opt.objs: | ||||||
|  |         f1 = open('objs-%02d.txt' % (opt.nruns - 1)).readlines() | ||||||
|  |         f2 = open('objs-%02d.txt' % (opt.nruns)).readlines() | ||||||
|  |         for line in difflib.unified_diff(f1, f2, | ||||||
|  |                 "run %d" % (opt.nruns - 1), "run %d" % opt.nruns): | ||||||
|  |             sys.stdout.write(line) | ||||||
|  | 
 | ||||||
|  |     return rv | ||||||
|  | 
 | ||||||
|  | def parse_args(): | ||||||
|  |     import optparse | ||||||
|  | 
 | ||||||
|  |     parser = optparse.OptionParser(description=__doc__) | ||||||
|  |     parser.add_option('--nruns', type='int', metavar="N", default=3, | ||||||
|  |         help="number of test suite runs [default: %default]") | ||||||
|  |     parser.add_option('--suite', metavar="NAME", | ||||||
|  |         help="the test suite to run (e.g. 'test_cursor'). [default: all]") | ||||||
|  |     parser.add_option('--objs', metavar="TYPE", | ||||||
|  |         help="in case of leaks, print a report of object TYPE " | ||||||
|  |             "(support still incomplete)") | ||||||
|  | 
 | ||||||
|  |     opt, args = parser.parse_args() | ||||||
|  |     return opt | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def dump(i, opt): | ||||||
|  |     gc.collect() | ||||||
|  |     objs = gc.get_objects() | ||||||
|  | 
 | ||||||
|  |     c = defaultdict(int) | ||||||
|  |     for o in objs: | ||||||
|  |         c[type(o)] += 1 | ||||||
|  | 
 | ||||||
|  |     pprint( | ||||||
|  |         sorted(((v,str(k)) for k,v in c.items()), reverse=True), | ||||||
|  |         stream=open("debug-%02d.txt" % i, "w")) | ||||||
|  | 
 | ||||||
|  |     if opt.objs: | ||||||
|  |         co = [] | ||||||
|  |         t = getattr(__builtins__, opt.objs) | ||||||
|  |         for o in objs: | ||||||
|  |             if type(o) is t: | ||||||
|  |                 co.append(o) | ||||||
|  | 
 | ||||||
|  |         # TODO: very incomplete | ||||||
|  |         if t is dict: | ||||||
|  |             co.sort(key = lambda d: d.items()) | ||||||
|  |         else: | ||||||
|  |             co.sort() | ||||||
|  | 
 | ||||||
|  |         pprint(co, stream=open("objs-%02d.txt" % i, "w")) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     sys.exit(main()) | ||||||
|  | 
 | ||||||
|  | @ -410,6 +410,18 @@ class AsyncTests(unittest.TestCase): | ||||||
|         self.assertEqual("CREATE TABLE", cur.statusmessage) |         self.assertEqual("CREATE TABLE", cur.statusmessage) | ||||||
|         self.assert_(self.conn.notices) |         self.assert_(self.conn.notices) | ||||||
| 
 | 
 | ||||||
|  |     def test_async_cursor_gone(self): | ||||||
|  |         cur = self.conn.cursor() | ||||||
|  |         cur.execute("select 42;"); | ||||||
|  |         del cur | ||||||
|  |         self.assertRaises(psycopg2.InterfaceError, self.wait, self.conn) | ||||||
|  | 
 | ||||||
|  |         # The connection is still usable | ||||||
|  |         cur = self.conn.cursor() | ||||||
|  |         cur.execute("select 42;"); | ||||||
|  |         self.wait(self.conn) | ||||||
|  |         self.assertEqual(cur.fetchone(), (42,)) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| def test_suite(): | def test_suite(): | ||||||
|     return unittest.TestLoader().loadTestsFromName(__name__) |     return unittest.TestLoader().loadTestsFromName(__name__) | ||||||
|  |  | ||||||
|  | @ -119,6 +119,14 @@ class ConnectionTests(unittest.TestCase): | ||||||
|         cur.execute("select 'foo'::text;") |         cur.execute("select 'foo'::text;") | ||||||
|         self.assertEqual(cur.fetchone()[0], u'foo') |         self.assertEqual(cur.fetchone()[0], u'foo') | ||||||
| 
 | 
 | ||||||
|  |     def test_weakref(self): | ||||||
|  |         from weakref import ref | ||||||
|  |         conn = psycopg2.connect(self.conn.dsn) | ||||||
|  |         w = ref(conn) | ||||||
|  |         conn.close() | ||||||
|  |         del conn | ||||||
|  |         self.assert_(w() is None) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class IsolationLevelsTestCase(unittest.TestCase): | class IsolationLevelsTestCase(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -100,6 +100,13 @@ class CursorTests(unittest.TestCase): | ||||||
|         curs2 = self.conn.cursor() |         curs2 = self.conn.cursor() | ||||||
|         self.assertEqual("foofoo", curs2.cast(705, 'foo')) |         self.assertEqual("foofoo", curs2.cast(705, 'foo')) | ||||||
| 
 | 
 | ||||||
|  |     def test_weakref(self): | ||||||
|  |         from weakref import ref | ||||||
|  |         curs = self.conn.cursor() | ||||||
|  |         w = ref(curs) | ||||||
|  |         del curs | ||||||
|  |         self.assert_(w() is None) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| def test_suite(): | def test_suite(): | ||||||
|     return unittest.TestLoader().loadTestsFromName(__name__) |     return unittest.TestLoader().loadTestsFromName(__name__) | ||||||
|  |  | ||||||
|  | @ -79,7 +79,7 @@ class QuotingTestCase(unittest.TestCase): | ||||||
|             if not 0xD800 <= u <= 0xDFFF ]))    # surrogate area |             if not 0xD800 <= u <= 0xDFFF ]))    # surrogate area | ||||||
|         self.conn.set_client_encoding('UNICODE') |         self.conn.set_client_encoding('UNICODE') | ||||||
| 
 | 
 | ||||||
|         psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) |         psycopg2.extensions.register_type(psycopg2.extensions.UNICODE, self.conn) | ||||||
|         curs.execute("SELECT %s::text;", (data,)) |         curs.execute("SELECT %s::text;", (data,)) | ||||||
|         res = curs.fetchone()[0] |         res = curs.fetchone()[0] | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -232,7 +232,11 @@ class AdaptSubclassTest(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|         register_adapter(A, lambda a: AsIs("a")) |         register_adapter(A, lambda a: AsIs("a")) | ||||||
|         register_adapter(B, lambda b: AsIs("b")) |         register_adapter(B, lambda b: AsIs("b")) | ||||||
|  |         try: | ||||||
|             self.assertEqual(b('b'), adapt(C()).getquoted()) |             self.assertEqual(b('b'), adapt(C()).getquoted()) | ||||||
|  |         finally: | ||||||
|  |            del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote] | ||||||
|  |            del psycopg2.extensions.adapters[B, psycopg2.extensions.ISQLQuote] | ||||||
| 
 | 
 | ||||||
|     @testutils.skip_on_python3 |     @testutils.skip_on_python3 | ||||||
|     def test_no_mro_no_joy(self): |     def test_no_mro_no_joy(self): | ||||||
|  | @ -242,7 +246,11 @@ class AdaptSubclassTest(unittest.TestCase): | ||||||
|         class B(A): pass |         class B(A): pass | ||||||
| 
 | 
 | ||||||
|         register_adapter(A, lambda a: AsIs("a")) |         register_adapter(A, lambda a: AsIs("a")) | ||||||
|  |         try: | ||||||
|             self.assertRaises(psycopg2.ProgrammingError, adapt, B()) |             self.assertRaises(psycopg2.ProgrammingError, adapt, B()) | ||||||
|  |         finally: | ||||||
|  |            del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote] | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     @testutils.skip_on_python2 |     @testutils.skip_on_python2 | ||||||
|     def test_adapt_subtype_3(self): |     def test_adapt_subtype_3(self): | ||||||
|  | @ -252,7 +260,10 @@ class AdaptSubclassTest(unittest.TestCase): | ||||||
|         class B(A): pass |         class B(A): pass | ||||||
| 
 | 
 | ||||||
|         register_adapter(A, lambda a: AsIs("a")) |         register_adapter(A, lambda a: AsIs("a")) | ||||||
|  |         try: | ||||||
|             self.assertEqual(b("a"), adapt(B()).getquoted()) |             self.assertEqual(b("a"), adapt(B()).getquoted()) | ||||||
|  |         finally: | ||||||
|  |            del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_suite(): | def test_suite(): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user