Merge branch 'dead-bytes' into devel

This commit is contained in:
Daniele Varrazzo 2013-03-16 16:25:17 +00:00
commit e1ff432f4c
45 changed files with 3 additions and 5420 deletions

2455
ChangeLog

File diff suppressed because it is too large Load Diff

2
NEWS
View File

@ -15,6 +15,8 @@ What's new in psycopg 2.5
supporting it. supporting it.
- Properly cleanup memory of broken connections (ticket #142). - Properly cleanup memory of broken connections (ticket #142).
- 'errorcodes' map updated to PostgreSQL 9.2. - 'errorcodes' map updated to PostgreSQL 9.2.
- Dropped Zope adapter from source repository. ZPsycopgDA can now be
found at <http://github.com/psycopg/ZPsycopgDA>.
What's new in psycopg 2.4.6 What's new in psycopg 2.4.6

View File

@ -1,360 +0,0 @@
# ZPsycopgDA/DA.py - ZPsycopgDA Zope product: Database Connection
#
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
# Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies.
ALLOWED_PSYCOPG_VERSIONS = ('2.4', '2.4.1', '2.4.4', '2.4.5', '2.4.6')
import sys
import time
import db
import re
import Acquisition
import Shared.DC.ZRDB.Connection
from db import DB
from Globals import HTMLFile
from ExtensionClass import Base
from App.Dialogs import MessageDialog
from DateTime import DateTime
# ImageFile is deprecated in Zope >= 2.9
try:
from App.ImageFile import ImageFile
except ImportError:
# Zope < 2.9. If PIL's installed with a .pth file, we're probably
# hosed.
from ImageFile import ImageFile
# import psycopg and functions/singletons needed for date/time conversions
import psycopg2
from psycopg2 import NUMBER, STRING, ROWID, DATETIME
from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE
from psycopg2.extensions import TIME, INTERVAL
from psycopg2.extensions import new_type, register_type
# add a new connection to a folder
manage_addZPsycopgConnectionForm = HTMLFile('dtml/add',globals())
def manage_addZPsycopgConnection(self, id, title, connection_string,
zdatetime=None, tilevel=2,
encoding='', check=None, REQUEST=None):
"""Add a DB connection to a folder."""
self._setObject(id, Connection(id, title, connection_string,
zdatetime, check, tilevel, encoding))
if REQUEST is not None: return self.manage_main(self, REQUEST)
# the connection object
class Connection(Shared.DC.ZRDB.Connection.Connection):
"""ZPsycopg Connection."""
_isAnSQLConnection = 1
id = 'Psycopg2_database_connection'
database_type = 'Psycopg2'
meta_type = title = 'Z Psycopg 2 Database Connection'
icon = 'misc_/conn'
def __init__(self, id, title, connection_string,
zdatetime, check=None, tilevel=2, encoding='UTF-8'):
self.zdatetime = zdatetime
self.id = str(id)
self.edit(title, connection_string, zdatetime,
check=check, tilevel=tilevel, encoding=encoding)
def factory(self):
return DB
## connection parameters editing ##
def edit(self, title, connection_string,
zdatetime, check=None, tilevel=2, encoding='UTF-8'):
self.title = title
self.connection_string = connection_string
self.zdatetime = zdatetime
self.tilevel = tilevel
self.encoding = encoding
if check: self.connect(self.connection_string)
manage_properties = HTMLFile('dtml/edit', globals())
def manage_edit(self, title, connection_string,
zdatetime=None, check=None, tilevel=2, encoding='UTF-8',
REQUEST=None):
"""Edit the DB connection."""
self.edit(title, connection_string, zdatetime,
check=check, tilevel=tilevel, encoding=encoding)
if REQUEST is not None:
msg = "Connection edited."
return self.manage_main(self,REQUEST,manage_tabs_message=msg)
def connect(self, s):
try:
self._v_database_connection.close()
except:
pass
# check psycopg version and raise exception if does not match
if psycopg2.__version__.split(' ')[0] not in ALLOWED_PSYCOPG_VERSIONS:
raise ImportError("psycopg version mismatch (imported %s)" %
psycopg2.__version__)
self._v_connected = ''
dbf = self.factory()
# TODO: let the psycopg exception propagate, or not?
self._v_database_connection = dbf(
self.connection_string, self.tilevel, self.get_type_casts(), self.encoding)
self._v_database_connection.open()
self._v_connected = DateTime()
return self
def get_type_casts(self):
# note that in both cases order *is* important
if self.zdatetime:
return ZDATETIME, ZDATE, ZTIME
else:
return DATETIME, DATE, TIME
## browsing and table/column management ##
manage_options = Shared.DC.ZRDB.Connection.Connection.manage_options
# + (
# {'label': 'Browse', 'action':'manage_browse'},)
#manage_tables = HTMLFile('dtml/tables', globals())
#manage_browse = HTMLFile('dtml/browse', globals())
info = None
def table_info(self):
return self._v_database_connection.table_info()
def __getitem__(self, name):
if name == 'tableNamed':
if not hasattr(self, '_v_tables'): self.tpValues()
return self._v_tables.__of__(self)
raise KeyError, name
def tpValues(self):
res = []
conn = self._v_database_connection
for d in conn.tables(rdb=0):
try:
name = d['TABLE_NAME']
b = TableBrowser()
b.__name__ = name
b._d = d
b._c = c
try:
b.icon = table_icons[d['TABLE_TYPE']]
except:
pass
r.append(b)
except:
pass
return res
## database connection registration data ##
classes = (Connection,)
meta_types = ({'name':'Z Psycopg 2 Database Connection',
'action':'manage_addZPsycopgConnectionForm'},)
folder_methods = {
'manage_addZPsycopgConnection': manage_addZPsycopgConnection,
'manage_addZPsycopgConnectionForm': manage_addZPsycopgConnectionForm}
__ac_permissions__ = (
('Add Z Psycopg Database Connections',
('manage_addZPsycopgConnectionForm', 'manage_addZPsycopgConnection')),)
# add icons
misc_={'conn': ImageFile('icons/DBAdapterFolder_icon.gif', globals())}
for icon in ('table', 'view', 'stable', 'what', 'field', 'text', 'bin',
'int', 'float', 'date', 'time', 'datetime'):
misc_[icon] = ImageFile('icons/%s.gif' % icon, globals())
## zope-specific psycopg typecasters ##
# convert an ISO timestamp string from postgres to a Zope DateTime object
def _cast_DateTime(iso, curs):
if iso:
if iso in ['-infinity', 'infinity']:
return iso
else:
return DateTime(iso)
# convert an ISO date string from postgres to a Zope DateTime object
def _cast_Date(iso, curs):
if iso:
if iso in ['-infinity', 'infinity']:
return iso
else:
return DateTime(iso)
# Convert a time string from postgres to a Zope DateTime object.
# NOTE: we set the day as today before feeding to DateTime so
# that it has the same DST settings.
def _cast_Time(iso, curs):
if iso:
if iso in ['-infinity', 'infinity']:
return iso
else:
return DateTime(time.strftime('%Y-%m-%d %H:%M:%S',
time.localtime(time.time())[:3]+
time.strptime(iso[:8], "%H:%M:%S")[3:]))
# NOTE: we don't cast intervals anymore because they are passed
# untouched to Zope.
def _cast_Interval(iso, curs):
return iso
ZDATETIME = new_type((1184, 1114), "ZDATETIME", _cast_DateTime)
ZINTERVAL = new_type((1186,), "ZINTERVAL", _cast_Interval)
ZDATE = new_type((1082,), "ZDATE", _cast_Date)
ZTIME = new_type((1083,), "ZTIME", _cast_Time)
## table browsing helpers ##
class TableBrowserCollection(Acquisition.Implicit):
pass
class Browser(Base):
def __getattr__(self, name):
try:
return self._d[name]
except KeyError:
raise AttributeError, name
class values:
def len(self):
return 1
def __getitem__(self, i):
try:
return self._d[i]
except AttributeError:
pass
self._d = self._f()
return self._d[i]
class TableBrowser(Browser, Acquisition.Implicit):
icon = 'what'
Description = check = ''
info = HTMLFile('table_info', globals())
menu = HTMLFile('table_menu', globals())
def tpValues(self):
v = values()
v._f = self.tpValues_
return v
def tpValues_(self):
r=[]
tname=self.__name__
for d in self._c.columns(tname):
b=ColumnBrowser()
b._d=d
try: b.icon=field_icons[d['Type']]
except: pass
b.TABLE_NAME=tname
r.append(b)
return r
def tpId(self): return self._d['TABLE_NAME']
def tpURL(self): return "Table/%s" % self._d['TABLE_NAME']
def Name(self): return self._d['TABLE_NAME']
def Type(self): return self._d['TABLE_TYPE']
manage_designInput=HTMLFile('designInput',globals())
def manage_buildInput(self, id, source, default, REQUEST=None):
"Create a database method for an input form"
args=[]
values=[]
names=[]
columns=self._columns
for i in range(len(source)):
s=source[i]
if s=='Null': continue
c=columns[i]
d=default[i]
t=c['Type']
n=c['Name']
names.append(n)
if s=='Argument':
values.append("<dtml-sqlvar %s type=%s>'" %
(n, vartype(t)))
a='%s%s' % (n, boboType(t))
if d: a="%s=%s" % (a,d)
args.append(a)
elif s=='Property':
values.append("<dtml-sqlvar %s type=%s>'" %
(n, vartype(t)))
else:
if isStringType(t):
if find(d,"\'") >= 0: d=join(split(d,"\'"),"''")
values.append("'%s'" % d)
elif d:
values.append(str(d))
else:
raise ValueError, (
'no default was given for <em>%s</em>' % n)
class ColumnBrowser(Browser):
icon='field'
def check(self):
return ('\t<input type=checkbox name="%s.%s">' %
(self.TABLE_NAME, self._d['Name']))
def tpId(self): return self._d['Name']
def tpURL(self): return "Column/%s" % self._d['Name']
def Description(self):
d=self._d
if d['Scale']:
return " %(Type)s(%(Precision)s,%(Scale)s) %(Nullable)s" % d
else:
return " %(Type)s(%(Precision)s) %(Nullable)s" % d
table_icons={
'TABLE': 'table',
'VIEW':'view',
'SYSTEM_TABLE': 'stable',
}
field_icons={
NUMBER.name: 'i',
STRING.name: 'text',
DATETIME.name: 'date',
INTEGER.name: 'int',
FLOAT.name: 'float',
BOOLEAN.name: 'bin',
ROWID.name: 'int'
}

View File

@ -1,29 +0,0 @@
# ZPsycopgDA/__init__.py - ZPsycopgDA Zope product
#
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
# Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies.
__doc__ = "ZPsycopg Database Adapter Registration."
__version__ = '2.0'
import DA
def initialize(context):
context.registerClass(
DA.Connection,
permission = 'Add Z Psycopg 2 Database Connections',
constructors = (DA.manage_addZPsycopgConnectionForm,
DA.manage_addZPsycopgConnection),
icon = 'icons/DBAdapterFolder_icon.gif')

View File

@ -1,209 +0,0 @@
# ZPsycopgDA/db.py - query execution
#
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
# Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies.
from Shared.DC.ZRDB.TM import TM
from Shared.DC.ZRDB import dbi_db
from ZODB.POSException import ConflictError
import site
import pool
import psycopg2
from psycopg2.extensions import INTEGER, LONGINTEGER, FLOAT, BOOLEAN, DATE, TIME
from psycopg2.extensions import TransactionRollbackError, register_type
from psycopg2 import NUMBER, STRING, ROWID, DATETIME
# the DB object, managing all the real query work
class DB(TM, dbi_db.DB):
_p_oid = _p_changed = _registered = None
def __init__(self, dsn, tilevel, typecasts, enc='utf-8'):
self.dsn = dsn
self.tilevel = tilevel
self.typecasts = typecasts
if enc is None or enc == "":
self.encoding = "utf-8"
else:
self.encoding = enc
self.failures = 0
self.calls = 0
self.make_mappings()
def getconn(self, init=True):
# if init is False we are trying to get hold on an already existing
# connection, so we avoid to (re)initialize it risking errors.
conn = pool.getconn(self.dsn)
if init:
# use set_session where available as in these versions
# set_isolation_level generates an extra query.
if psycopg2.__version__ >= '2.4.2':
conn.set_session(isolation_level=int(self.tilevel))
else:
conn.set_isolation_level(int(self.tilevel))
conn.set_client_encoding(self.encoding)
for tc in self.typecasts:
register_type(tc, conn)
return conn
def putconn(self, close=False):
try:
conn = pool.getconn(self.dsn, False)
except AttributeError:
pass
pool.putconn(self.dsn, conn, close)
def getcursor(self):
conn = self.getconn(False)
return conn.cursor()
def _finish(self, *ignored):
try:
conn = self.getconn(False)
conn.commit()
self.putconn()
except AttributeError:
pass
def _abort(self, *ignored):
try:
conn = self.getconn(False)
conn.rollback()
self.putconn()
except AttributeError:
pass
def open(self):
# this will create a new pool for our DSN if not already existing,
# then get and immediately release a connection
self.getconn()
self.putconn()
def close(self):
# FIXME: if this connection is closed we flush all the pool associated
# with the current DSN; does this makes sense?
pool.flushpool(self.dsn)
def sortKey(self):
return 1
def make_mappings(self):
"""Generate the mappings used later by self.convert_description()."""
self.type_mappings = {}
for t, s in [(INTEGER,'i'), (LONGINTEGER, 'i'), (NUMBER, 'n'),
(BOOLEAN,'n'), (ROWID, 'i'),
(DATETIME, 'd'), (DATE, 'd'), (TIME, 'd')]:
for v in t.values:
self.type_mappings[v] = (t, s)
def convert_description(self, desc, use_psycopg_types=False):
"""Convert DBAPI-2.0 description field to Zope format."""
items = []
for name, typ, width, ds, p, scale, null_ok in desc:
m = self.type_mappings.get(typ, (STRING, 's'))
items.append({
'name': name,
'type': use_psycopg_types and m[0] or m[1],
'width': width,
'precision': p,
'scale': scale,
'null': null_ok,
})
return items
## tables and rows ##
def tables(self, rdb=0, _care=('TABLE', 'VIEW')):
self._register()
c = self.getcursor()
c.execute(
"SELECT t.tablename AS NAME, 'TABLE' AS TYPE "
" FROM pg_tables t WHERE tableowner <> 'postgres' "
"UNION SELECT v.viewname AS NAME, 'VIEW' AS TYPE "
" FROM pg_views v WHERE viewowner <> 'postgres' "
"UNION SELECT t.tablename AS NAME, 'SYSTEM_TABLE\' AS TYPE "
" FROM pg_tables t WHERE tableowner = 'postgres' "
"UNION SELECT v.viewname AS NAME, 'SYSTEM_TABLE' AS TYPE "
"FROM pg_views v WHERE viewowner = 'postgres'")
res = []
for name, typ in c.fetchall():
if typ in _care:
res.append({'TABLE_NAME': name, 'TABLE_TYPE': typ})
self.putconn()
return res
def columns(self, table_name):
self._register()
c = self.getcursor()
try:
r = c.execute('SELECT * FROM "%s" WHERE 1=0' % table_name)
except:
return ()
self.putconn()
return self.convert_description(c.description, True)
## query execution ##
def query(self, query_string, max_rows=None, query_data=None):
self._register()
self.calls = self.calls+1
desc = ()
res = []
nselects = 0
c = self.getcursor()
try:
for qs in [x for x in query_string.split('\0') if x]:
try:
if query_data:
c.execute(qs, query_data)
else:
c.execute(qs)
except TransactionRollbackError:
# Ha, here we have to look like we are the ZODB raising conflict errrors, raising ZPublisher.Publish.Retry just doesn't work
#logging.debug("Serialization Error, retrying transaction", exc_info=True)
raise ConflictError("TransactionRollbackError from psycopg2")
except psycopg2.OperationalError:
#logging.exception("Operational error on connection, closing it.")
try:
# Only close our connection
self.putconn(True)
except:
#logging.debug("Something went wrong when we tried to close the pool", exc_info=True)
pass
if c.description is not None:
nselects += 1
if c.description != desc and nselects > 1:
raise psycopg2.ProgrammingError(
'multiple selects in single query not allowed')
if max_rows:
res = c.fetchmany(max_rows)
else:
res = c.fetchall()
desc = c.description
self.failures = 0
except StandardError, err:
self._abort()
raise err
return self.convert_description(desc), res

View File

@ -1,108 +0,0 @@
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add Z Psycopg 2 Database Connection',
help_product='ZPsycopgDA',
help_topic='ZPsycopgDA-Method-Add.stx'
)">
<p class="form-help">
A Zope Psycopg 2 Database Connection is used to connect and execute
queries on a PostgreSQL database.
</p>
<p class="form-help">
In the form below <em>Connection String</em> (also called the Data Source Name
or DSN for short) is a string... (TODO: finish docs)
</p>
<form action="manage_addZPsycopgConnection" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40"
value="Psycopg2_database_connection" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40"
value="Z Psycopg 2 Database Connection"/>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connection string
</div>
</td>
<td align="left" valign="top">
<input type="text" name="connection_string" size="40" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connect immediately
</div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="check" value="YES" checked="YES" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Use Zope's internal DateTime
</div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="zdatetime" value="YES" checked="YES" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Transaction isolation level
</div>
</td>
<td align="left" valign="top">
<select name="tilevel:int">
<option value="4">Read uncommitted</option>
<option value="1">Read committed</option>
<option value="2" selected="YES">Repeatable read</option>
<option value="3">Serializable</option>
</select>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Encoding
</div>
</td>
<td align="left" valign="top">
<input type="text" name="encoding" size="40" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<input class="form-element" type="submit" name="submit" value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>

View File

@ -1,11 +0,0 @@
<html>
<head><title><dtml-var title_or_id >tables</title></head>
<body bgcolor="#FFFFFF" link="#000099" vlink="#555555" alink="#77003B">
<dtml-var manage_tabs>
<dtml-tree header="info">
<IMG SRC="<dtml-var SCRIPT_NAME >/misc_/ZPsycopgDA/<dtml-var icon>"
ALT="<dtml-var Type>" BORDER="0">
<dtml-var Name><dtml-var Description>
</dtml-tree>
</body>
</html>

View File

@ -1,84 +0,0 @@
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<form action="manage_edit" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40"
value="&dtml-title;"/>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connection string
</div>
</td>
<td align="left" valign="top">
<input type="text" name="connection_string" size="40"
value="&dtml-connection_string;" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Use Zope's internal DateTime
</div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="zdatetime" value="YES"
<dtml-if expr="zdatetime">checked="YES"</dtml-if> />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Transaction isolation level
</div>
</td>
<td align="left" valign="top">
<select name="tilevel:int">
<option value="4"
<dtml-if expr="tilevel==4">selected="YES"</dtml-if>>
Read uncommitted</option>
<option value="1"
<dtml-if expr="tilevel==1">selected="YES"</dtml-if>>
Read committed</option>
<option value="2"
<dtml-if expr="tilevel==2">selected="YES"</dtml-if>>
Repeatable read</option>
<option value="3"
<dtml-if expr="tilevel==3">selected="YES"</dtml-if>>
Serializable</option>
</select>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Encoding
</div>
</td>
<td align="left" valign="top">
<input type="text" name="encoding" size="40"
value="&dtml-encoding;" />
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Save Changes " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>

View File

@ -1,7 +0,0 @@
<dtml-var standard_html_header>
<dtml-var TABLE_TYPE><dtml-if TABLE_OWNER>
owned by <dtml-var TABLE_OWNER></dtml-if>
<dtml-if REMARKS><br><dtml-var REMARKS></dtml-if>
<dtml-var standard_html_footer>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 894 B

View File

@ -1,193 +0,0 @@
# ZPsycopgDA/pool.py - ZPsycopgDA Zope product: connection pooling
#
# Copyright (C) 2004-2010 Federico Di Gregorio <fog@debian.org>
#
# psycopg2 is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
# Import modules needed by _psycopg to allow tools like py2exe to do
# their work without bothering about the module dependencies.
# All the connections are held in a pool of pools, directly accessible by the
# ZPsycopgDA code in db.py.
import threading
import psycopg2
from psycopg2.pool import PoolError
class AbstractConnectionPool(object):
"""Generic key-based pooling code."""
def __init__(self, minconn, maxconn, *args, **kwargs):
"""Initialize the connection pool.
New 'minconn' connections are created immediately calling 'connfunc'
with given parameters. The connection pool will support a maximum of
about 'maxconn' connections.
"""
self.minconn = minconn
self.maxconn = maxconn
self.closed = False
self._args = args
self._kwargs = kwargs
self._pool = []
self._used = {}
self._rused = {} # id(conn) -> key map
self._keys = 0
for i in range(self.minconn):
self._connect()
def _connect(self, key=None):
"""Create a new connection and assign it to 'key' if not None."""
conn = psycopg2.connect(*self._args, **self._kwargs)
if key is not None:
self._used[key] = conn
self._rused[id(conn)] = key
else:
self._pool.append(conn)
return conn
def _getkey(self):
"""Return a new unique key."""
self._keys += 1
return self._keys
def _getconn(self, key=None):
"""Get a free connection and assign it to 'key' if not None."""
if self.closed: raise PoolError("connection pool is closed")
if key is None: key = self._getkey()
if key in self._used:
return self._used[key]
if self._pool:
self._used[key] = conn = self._pool.pop()
self._rused[id(conn)] = key
return conn
else:
if len(self._used) == self.maxconn:
raise PoolError("connection pool exausted")
return self._connect(key)
def _putconn(self, conn, key=None, close=False):
"""Put away a connection."""
if self.closed: raise PoolError("connection pool is closed")
if key is None: key = self._rused[id(conn)]
if not key:
raise PoolError("trying to put unkeyed connection")
if len(self._pool) < self.minconn and not close:
self._pool.append(conn)
else:
conn.close()
# here we check for the presence of key because it can happen that a
# thread tries to put back a connection after a call to close
if not self.closed or key in self._used:
del self._used[key]
del self._rused[id(conn)]
def _closeall(self):
"""Close all connections.
Note that this can lead to some code fail badly when trying to use
an already closed connection. If you call .closeall() make sure
your code can deal with it.
"""
if self.closed: raise PoolError("connection pool is closed")
for conn in self._pool + list(self._used.values()):
try:
conn.close()
except:
pass
self.closed = True
class PersistentConnectionPool(AbstractConnectionPool):
"""A pool that assigns persistent connections to different threads.
Note that this connection pool generates by itself the required keys
using the current thread id. This means that until a thread puts away
a connection it will always get the same connection object by successive
`!getconn()` calls. This also means that a thread can't use more than one
single connection from the pool.
"""
def __init__(self, minconn, maxconn, *args, **kwargs):
"""Initialize the threading lock."""
import threading
AbstractConnectionPool.__init__(
self, minconn, maxconn, *args, **kwargs)
self._lock = threading.Lock()
# we we'll need the thread module, to determine thread ids, so we
# import it here and copy it in an instance variable
import thread
self.__thread = thread
def getconn(self):
"""Generate thread id and return a connection."""
key = self.__thread.get_ident()
self._lock.acquire()
try:
return self._getconn(key)
finally:
self._lock.release()
def putconn(self, conn=None, close=False):
"""Put away an unused connection."""
key = self.__thread.get_ident()
self._lock.acquire()
try:
if not conn: conn = self._used[key]
self._putconn(conn, key, close)
finally:
self._lock.release()
def closeall(self):
"""Close all connections (even the one currently in use.)"""
self._lock.acquire()
try:
self._closeall()
finally:
self._lock.release()
_connections_pool = {}
_connections_lock = threading.Lock()
def getpool(dsn, create=True):
_connections_lock.acquire()
try:
if not _connections_pool.has_key(dsn) and create:
_connections_pool[dsn] = \
PersistentConnectionPool(4, 200, dsn)
finally:
_connections_lock.release()
return _connections_pool[dsn]
def flushpool(dsn):
_connections_lock.acquire()
try:
_connections_pool[dsn].closeall()
del _connections_pool[dsn]
finally:
_connections_lock.release()
def getconn(dsn, create=True):
return getpool(dsn, create=create).getconn()
def putconn(dsn, conn, close=False):
getpool(dsn).putconn(conn, close=close)

View File

@ -1,7 +0,0 @@
ZPsycopgDA (in the Debian zope-psycopgda package) is a Zope Database
Adapter based on the psycopg Python/PostgreSQL driver. You'll find
more information and documentation in the pythonX.Y-psycopg package,
where X.Y is the version of your installed Python.
Details for ZPsycopgDA for Zope are found in the documentation of
the python2.3-psycopg package.

676
debian/changelog vendored
View File

@ -1,676 +0,0 @@
psycopg2 (2.0.7-2) unstable; urgency=medium
* ZPsycopgDA/DA.py: updated the patch. (Closes: #478860)
-- Fabio Tranchitella <kobold@debian.org> Thu, 01 May 2008 17:43:54 +0200
psycopg2 (2.0.7-1) unstable; urgency=low
* New upstream release. (Closes: #476101)
* debian/control: bumped Standard-Versions to 3.7.3.
-- Fabio Tranchitella <kobold@debian.org> Tue, 15 Apr 2008 10:05:51 +0200
psycopg2 (2.0.6-4) unstable; urgency=low
[ Sandro Tosi ]
* debian/control
- uniforming Vcs-Browser field
[ Fabio Tranchitella ]
* Provides a encoding parameter when adding a ZPsycopgDA instance using the
ZMI. (Closes: #475123)
-- Fabio Tranchitella <kobold@debian.org> Wed, 09 Apr 2008 19:51:10 +0200
psycopg2 (2.0.6-3) unstable; urgency=low
[ Piotr Ożarowski ]
* XS-Vcs-Svn field renamed to Vcs-Svn
* Vcs-Browser field added
[ Fabio Tranchitella ]
* Mention DB-API 2.0 compatibility in the long description.
(Closes: #430763)
-- Fabio Tranchitella <kobold@debian.org> Thu, 08 Nov 2007 15:07:05 +0100
psycopg2 (2.0.6-2) unstable; urgency=low
* Build a python-psycopg-dbg package
-- Scott Kitterman <scott@kitterman.com> Tue, 03 Jul 2007 16:55:48 -0400
psycopg2 (2.0.6-1) unstable; urgency=low
* New upstream release.
* psycopg2da: removed, merged upstream.
-- Fabio Tranchitella <kobold@debian.org> Sat, 09 Jun 2007 22:38:23 +0200
psycopg2 (2.0.5.1-7) UNRELEASED; urgency=low
* debian/watch: added.
-- Fabio Tranchitella <kobold@debian.org> Fri, 9 Feb 2007 12:35:55 +0100
psycopg2 (2.0.5.1-6) unstable; urgency=high
* debian/zope-psycopgda2.dzproduct: requires Zope 2.9 or higher: previous
versions use python2.3 which is not supported anymore in psycopg.
-- Fabio Tranchitella <kobold@debian.org> Mon, 15 Jan 2007 11:39:15 +0100
psycopg2 (2.0.5.1-5) unstable; urgency=medium
* Do not run dh_pycentral on zope-psycopgda2. (Closes: #400846)
-- Fabio Tranchitella <kobold@debian.org> Wed, 29 Nov 2006 09:04:09 +0100
psycopg2 (2.0.5.1-4) unstable; urgency=medium
* Fixed a bug in psycopg2da.
* debian/control: bumped build-dependency on zope-debhelper.
* Added XS-Vcs-Svn field
-- Fabio Tranchitella <kobold@debian.org> Fri, 24 Nov 2006 13:50:11 +0100
psycopg2 (2.0.5.1-3) unstable; urgency=low
* psycopgda: imported upstream psycopg2da database adapter from SVN, which
builds a new binary package, python-psycopg2da.
-- Fabio Tranchitella <kobold@debian.org> Fri, 10 Nov 2006 08:56:05 +0100
psycopg2 (2.0.5.1-2) unstable; urgency=low
* debian/control: added again a dependency on python-egenix-mxdatetime.
(Closes: #389636)
-- Fabio Tranchitella <kobold@debian.org> Tue, 3 Oct 2006 10:25:22 +0200
psycopg2 (2.0.5.1-1) unstable; urgency=low
* New upstream release.
-- Fabio Tranchitella <kobold@debian.org> Tue, 19 Sep 2006 08:22:36 +0200
psycopg2 (2.0.4-1) unstable; urgency=low
* New upstream release.
* debian/control:
+ removed dependency on python-egenix-mxdatetime.
+ added ${shlibs:Depends} for the python-psycopg2 package.
(Closes: #381462)
-- Fabio Tranchitella <kobold@debian.org> Wed, 9 Aug 2006 10:28:30 +0200
psycopg2 (2.0.2-1) unstable; urgency=low
* New upstream major release, new source package. (Closes: #377956)
-- Fabio Tranchitella <kobold@debian.org> Sun, 16 Jul 2006 21:43:41 +0200
psycopg (1.1.21-8) unstable; urgency=low
* debian/zope-psycopgda.dzproduct: added 2.9 to the list of supported
zope versions. (Closes: #376538)
-- Fabio Tranchitella <kobold@debian.org> Fri, 14 Jul 2006 10:19:54 +0200
psycopg (1.1.21-7) unstable; urgency=low
* Moved dh_installzope within an arch-indep target. (Closes: #373842)
-- Fabio Tranchitella <kobold@debian.org> Fri, 16 Jun 2006 09:37:23 +0200
psycopg (1.1.21-6) unstable; urgency=low
* Python policy transition. (Closes: #373482)
-- Fabio Tranchitella <kobold@debian.org> Thu, 15 Jun 2006 19:09:36 +0200
psycopg (1.1.21-5) unstable; urgency=high
* ypemod.c, new_psyco_bufferobject():
- Escape quotes psycopg.Binary() results as '', not as \', since the
latter does not work any more with some client encodings with the latest
PostgreSQL (in some multi-byte encodings you can exploit \' escaping to
inject SQL code, see CVE-2006-2314). (Closes: #369230)
Thanks to Martin Pitt and Ubuntu security team for the patch.
-- Fabio Tranchitella <kobold@debian.org> Tue, 30 May 2006 22:15:06 +0200
psycopg (1.1.21-4) unstable; urgency=low
* debian/rules: remove *.o in the clean target. (Closes: #352835)
-- Fabio Tranchitella <kobold@debian.org> Thu, 16 Feb 2006 12:06:53 +0000
psycopg (1.1.21-3) unstable; urgency=low
* debian/control: removed build-dependency on postgresql-server-dev-8.0,
as suggested by Martin Pitt. (Closes: #339640)
-- Fabio Tranchitella <kobold@debian.org> Fri, 18 Nov 2005 08:44:26 +0000
psycopg (1.1.21-2) unstable; urgency=low
* debian/control: zope-psycopgda should depend on the same version of the
psycopg python module. (Closes: #336765)
-- Fabio Tranchitella <kobold@debian.org> Wed, 2 Nov 2005 12:07:33 +0000
psycopg (1.1.21-1) unstable; urgency=low
* New maintainer; Thanks Federico for your work, and be sure that I'll
take care of this package.
* New upstream release (Closes: #321592, #320618, #333638)
* debian/python2.4-psycopg.dirs: added. (Closes: #319509, #329115)
* debian/control: dropped support for python2.1 and
python2.2. (Closes: #333639)
* debian/control: Standards-Version bumped to 3.6.2, no changes required.
* debian/rules: make use of dh_installzope from zope-debhelper to build the
zope-psycopgda package.
(Closes: #158669, #323599, #268975, #292247, #327415)
* debian/control: added build-depends on postgresql-server-dev-8.0.
(Closes: #333638)
* Re-packaged upstream tarball replacing some broken images.
(Closes: #292008, #305392)
-- Fabio Tranchitella <kobold@debian.org> Fri, 28 Oct 2005 11:24:37 +0000
psycopg (1.1.19-1) unstable; urgency=low
* New upstream release.
* Applied patch from Martin Krafft to build Zope 2.7 packages.
* Modified to use the new PostgreSQL packages.
* Added python 2.4 package (Closes: #301403).
* Upstream applied various Ubuntu patches (Closes: #301947, #300893).
-- Federico Di Gregorio <fog@debian.org> Sat, 16 Jul 2005 20:47:08 +0200
psycopg (1.1.18-1) unstable; urgency=low
* New upstream release.
* 1.1.16 fixed rowcount bug (closes: #266299).
-- Federico Di Gregorio <fog@debian.org> Wed, 5 Jan 2005 21:05:15 +0100
psycopg (1.1.17-1) unstable; urgency=high
* Urgency is still high because 1.1.16 was never uploaded.
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Thu, 19 Nov 2004 01:14:30 +0200
psycopg (1.1.16-1) unstable; urgency=high
* New upstream release.
* Tagged with urgency=high because fix a grave bug (rowcount) introduced
in 1.1.15.
* Upstream fix: does not segfault when using COPY TO/COPY FROM in
.execute() (closes: #279222).
-- Federico Di Gregorio <fog@debian.org> Sat, 30 Oct 2004 02:35:30 +0200
psycopg (1.1.15-1) unstable; urgency=low
* New upstream release.
* Definitely fixed (ah ah) time interval problems (closes: #259213).
-- Federico Di Gregorio <fog@initd.org> Thu, 29 Jul 2004 23:43:59 +0200
psycopg (1.1.14-1) unstable; urgency=low
* New upstream release.
* Don't put two copies of changelog in every package anymore
(closes: #256662).
* Updated test script works as expected (closes: #231391).
* Changes from NMU incorporated in 1.1.12:
- zpsycopgda depends on python2.2-psycopg (closes: #227420, #227147).
- compiled with postgresql in unstable (close: #220527).
-- Federico Di Gregorio <fog@initd.org> Fri, 9 Jul 2004 23:01:40 +0200
psycopg (1.1.13-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Fri, 21 May 2004 10:33:54 +0200
psycopg (1.1.12-1) unstable; urgency=low
* New upstream release (the "martin you won't have this package"
release.)
* Integrated changes from NMU releases.
-- Federico Di Gregorio <fog@debian.org> Sun, 16 May 2004 10:14:47 +0200
psycopg (1.1.10-1.2) unstable; urgency=low
* Non-maintainer upload. Thinking about taking this package over...
* Changed dependency on pyscopgda Python module to Python version 2.2.
(closes: #227147, #227420)
* Added Lintian overrides for image-in-/usr/lib warnings -- Zope needs
these images...
-- martin f. krafft <madduck@debian.org> Thu, 15 Apr 2004 23:30:40 +0200
psycopg (1.1.10-1.1) unstable; urgency=low
* Non-maintainer upload.
* No changes - this upload is simply a rebuild against the current unstable
instead of experimental postgresql-dev.
(closes: #219927, #220141, #220173, #220527)
-- Peter Hawkins <peterh@debian.org> Sun, 28 Dec 2003 10:57:30 +1100
psycopg (1.1.10-1) unstable; urgency=low
* Added download location to debian/copyright file (Closes: #215880).
-- Federico Di Gregorio <fog@initd.org> Sat, 8 Nov 2003 23:32:40 +0100
psycopg (1.1.9-1) unstable; urgency=low
* New upstream release.
* Bug was agains an old 1.0.x version of psycopg (Closes: #208702).
-- Federico Di Gregorio <fog@initd.org> Wed, 10 Sep 2003 13:04:42 +0200
psycopg (1.1.8-1) unstable; urgency=low
* New upstream release.
* Integrated NMU from Matthias Klose (closes: #205746).
-- Federico Di Gregorio <fog@debian.org> Fri, 1 Aug 2003 11:50:57 +0200
psycopg (1.1.5.1-1.1) unstable; urgency=low
* NMU
* Update for python2.3 as the default python version (closes: #205746).
-- Matthias Klose <doko@debian.org> Fri, 22 Aug 2003 00:02:25 +0200
psycopg (1.1.7-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Sat, 26 Jul 2003 15:03:39 +0200
psycopg (1.1.6-1) unstable; urgency=low
* New upstream release.
* Upstream applied patch from BTS (Closes: #200161).
-- Federico Di Gregorio <fog@initd.org> Sun, 13 Jul 2003 23:36:04 +0200
psycopg (1.1.5.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@initd.org> Mon, 23 Jun 2003 00:37:33 +0200
psycopg (1.1.5-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@initd.org> Sun, 22 Jun 2003 21:30:01 +0200
psycopg (1.1.4-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Wed, 7 May 2003 15:21:31 +0200
psycopg (1.1.3-1) unstable; urgency=low
* New upstream release.
* Changed section in debian/control (-> python).
-- Federico Di Gregorio <fog@debian.org> Wed, 2 Apr 2003 10:33:36 +0200
psycopg (1.1.2-1) unstable; urgency=low
* New upstream release.
* Started to track the 1.1.x branch.
-- Federico Di Gregorio <fog@debian.org> Tue, 25 Feb 2003 01:06:08 +0100
psycopg (1.0.15.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Fri, 14 Feb 2003 16:09:50 +0100
psycopg (1.0.15-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Wed, 12 Feb 2003 23:49:51 +0100
psycopg (1.0.14-1) unstable; urgency=low
* Applied patch from John Goerzen to fix memory leak in executemany()
and callproc() (Closes: #169284).
* New upstream release (Closes: #170297).
-- Federico Di Gregorio <fog@debian.org> Mon, 25 Nov 2002 16:50:37 +0100
psycopg (1.0.13-1) unstable; urgency=low
* New upstream release.
* Python 2.3 package added (Closes: #160831)
* IntegrityError raised when needed (upstream, Closes: #165791)
* Packages are lintian clean again.
-- Federico Di Gregorio <fog@debian.org> Fri, 25 Oct 2002 11:54:19 +0200
psycopg (1.0.12-1) unstable; urgency=low
* New upstream release.
* Fixed wrong url in RELEASE-1.0. (Closes: #153840)
-- Federico Di Gregorio <fog@debian.org> Fri, 13 Sep 2002 13:16:36 +0200
psycopg (1.0.11.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Mon, 26 Aug 2002 10:41:54 +0200
psycopg (1.0.11-1) unstable; urgency=low
* New upstream release.
* The dummy python-psycopg package now depends on the new default debian
python (2.2) and on python2.2-psycopg.
* Removed support for python 1.5 (support for 2.3 has to wait for egenix
packages.)
-- Federico Di Gregorio <fog@debian.org> Fri, 23 Aug 2002 11:25:01 +0200
psycopg (1.0.10-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@initd.org> Mon, 22 Jul 2002 02:04:59 +0200
psycopg (1.0.9-1) unstable; urgency=low
* Resolved section override (main->interpreters).
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Thu, 20 Jun 2002 14:00:42 +0200
psycopg (1.0.8-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Tue, 23 Apr 2002 22:42:22 +0200
psycopg (1.0.7.1-2) unstable; urgency=low
* Moved to main.
-- Federico Di Gregorio <fog@debian.org> Fri, 19 Apr 2002 10:06:58 +0200
psycopg (1.0.7.1-1) unstable; urgency=low
* New upstream release.
* Fixed a bug in ./configure; closes: #141774.
-- Federico Di Gregorio <fog@debian.org> Mon, 8 Apr 2002 18:54:24 +0200
psycopg (1.0.7-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Fri, 29 Mar 2002 14:24:45 +0100
psycopg (1.0.6-1) unstable; urgency=low
* New upstream release.
* Builds with new libpq libraries and header layout.
-- Federico Di Gregorio <fog@debian.org> Thu, 7 Mar 2002 11:59:40 +0100
psycopg (1.0.5-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Mon, 4 Mar 2002 14:43:13 +0100
psycopg (1.0.4-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Wed, 20 Feb 2002 20:37:16 +0100
psycopg (1.0.3-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Fri, 8 Feb 2002 15:17:44 +0100
psycopg (1.0.2-1) unstable; urgency=low
* New upstream release.
* Added package for python2.2 (Closes: #132650).
-- Federico Di Gregorio <fog@debian.org> Fri, 8 Feb 2002 00:45:07 +0100
psycopg (1.0.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Sun, 20 Jan 2002 18:27:22 +0100
psycopg (1.0-4) unstable; urgency=low
* Added build depend on plain python, to really close the %£$! #121229
bug this time (Closes: #121229).
-- Federico Di Gregorio <fog@debian.org> Wed, 28 Nov 2001 10:50:06 +0100
psycopg (1.0-3) unstable; urgency=low
* Added explicit build depends on python 1.5 & 2.1 (Closes: #121229).
* Fixed bad dependency on python1.5-egenix-mxdatetime.
-- Federico Di Gregorio <fog@debian.org> Mon, 26 Nov 2001 17:18:41 +0100
psycopg (1.0-2) unstable; urgency=low
* Fixed dependencies as per python policy.
* Added default, unversioned psycopg package (python-psycopg).
* Added non-US/main and rebuilt after REJECT.
-- Federico Di Gregorio <fog@debian.org> Fri, 16 Nov 2001 01:14:54 +0100
psycopg (1.0-1) unstable; urgency=low
* New upstream release. 1.0!
* Now we build versioned packages for python 1.5 and 2.1.
-- Federico Di Gregorio <fog@debian.org> Tue, 13 Nov 2001 19:24:39 +0100
psycopg (0.99.7-1) unstable; urgency=low
* New upstream release fixing some little bugs.
* This version requires the mx DateTime packages that are not yet in
debian... waiting for them I'll distribute both psycopg and unofficial
packages on the initd psycopg page.
-- Federico Di Gregorio <fog@debian.org> Tue, 18 Sep 2001 23:28:51 +0200
psycopg (0.99.6-2) unstable; urgency=low
* Added suggested build-depends (Closes: #112112).
* Applied patch by Michael Weber to configure.in, to look for a compiler
(Closes: #112024).
-- Federico Di Gregorio <fog@debian.org> Thu, 13 Sep 2001 10:49:37 +0200
psycopg (0.99.6-1) unstable; urgency=low
* Added Build-depends line (Closes: #89798).
* Now zope-psycopgda requires python-psycopg, zope on debian still runs
with python 1.x only (Closes: #108890).
* Moved package to non-US (psycopg depends on postgresql that is in
non-US, sic).
-- Federico Di Gregorio <fog@debian.org> Mon, 3 Sep 2001 13:02:11 +0200
psycopg (0.99.5-1) unstable; urgency=low
* New upstream release with bound variables quoting (Closes: #102843).
* The build process set the correct path to DateTime module
(Closes: #102838).
* Removes .pyc files in prerm (Closes: #104382)
-- Federico Di Gregorio <fog@debian.org> Thu, 12 Jul 2001 12:56:38 +0200
psycopg (0.99.4-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Mon, 2 Jul 2001 15:33:29 +0200
psycopg (0.99.3-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Wed, 20 Jun 2001 12:55:47 +0200
psycopg (0.99.2-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Tue, 5 Jun 2001 15:37:50 +0200
psycopg (0.99.1-1) unstable; urgency=low
* New upstream release.
-- Federico Di Gregorio <fog@debian.org> Tue, 5 Jun 2001 12:46:18 +0200
psycopg (0.5.5-1) unstable; urgency=low
* New upstream release (ok, *we* are the upstream authors, but after
putting the -1 in the version i am supposed to say "new upstream
version" when the non-debian versions changes, right? ouch...)
-- Federico Di Gregorio <fog@debian.org> Fri, 1 Jun 2001 17:18:52 +0200
psycopg (0.5.4-1) unstable; urgency=low
* Another bugfixing release.
* Added debian revision to be able to release multiple versions with the
same upstream version.
-- Federico Di Gregorio <fog@debian.org> Fri, 18 May 2001 19:32:59 +0200
psycopg (0.5.3) unstable; urgency=low
* Some bugs fixed, new release.
-- Federico Di Gregorio <fog@debian.org> Fri, 4 May 2001 16:19:09 +0200
psycopg (0.5.2) unstable; urgency=low
* New bugfixing release.
-- Federico Di Gregorio <fog@debian.org> Fri, 27 Apr 2001 09:52:16 +0200
psycopg (0.5.1) unstable; urgency=low
* New bugfixing release.
-- Federico Di Gregorio <fog@debian.org> Tue, 3 Apr 2001 11:13:26 +0200
psycopg (0.5.0) unstable; urgency=low
* New release.
-- Federico Di Gregorio <fog@debian.org> Fri, 30 Mar 2001 12:54:42 +0200
psycopg (0.4.7) unstable; urgency=low
* New release.
* Lots of small bug fixes (see detailed ChangeLog.)
* Includes beginning of DBAPI-2.0 testsuite.
-- Federico Di Gregorio <fog@debian.org> Fri, 16 Mar 2001 18:29:03 +0100
psycopg (0.4.6) unstable; urgency=low
* New release.
* Fixed a little bug in debian/rules (does not create an examples
directory inside examples.)
-- Federico Di Gregorio <fog@debian.org> Wed, 14 Mar 2001 01:00:26 +0100
psycopg (0.4.5) unstable; urgency=low
* New upstream (mmm... but one of the upstream authors it is
*me*... mmm...) release.
-- Federico Di Gregorio <fog@debian.org> Mon, 12 Mar 2001 11:41:42 +0100
psycopg (0.4.4) unstable; urgency=low
* New release.
* Fixed Sections in debian/control.
-- Federico Di Gregorio <fog@debian.org> Fri, 9 Mar 2001 10:11:02 +0100
psycopg (0.4.3) unstable; urgency=low
* New release.
* Fixed typo in connectionAdd.dtml (Closes: #88817)
-- Federico Di Gregorio <fog@debian.org> Wed, 7 Mar 2001 15:54:35 +0100
psycopg (0.4.2) unstable; urgency=low
* New release (fixes bugs in ZPsycopgDA.)
-- Federico Di Gregorio <fog@debian.org> Mon, 5 Mar 2001 13:33:39 +0100
psycopg (0.4.1) unstable; urgency=low
* New release.
* we now create packages for both versions of python in debian
(1.5 and 2.0, packages python-* and python2-*)
-- Federico Di Gregorio <fog@debian.org> Fri, 2 Mar 2001 12:10:52 +0100
psycopg (0.4) unstable; urgency=low
* News release.
* Now debian/rules build the Zope Database Adapter zope-psycopgda too.
* Source name changed from python-psycopg to psycopg.
-- Federico Di Gregorio <fog@debian.org> Tue, 27 Feb 2001 15:11:04 +0100
python-psycopg (0.3) unstable; urgency=low
* New release. Tons of bugs fixed and new features, see ChangeLog for
details.
-- Federico Di Gregorio <fog@debian.org> Mon, 26 Feb 2001 21:22:23 +0100
python-psycopg (0.2) unstable; urgency=low
* New release. Fixed lots of bugs and memory leaks.
-- Federico Di Gregorio <fog@debian.org> Fri, 16 Feb 2001 11:04:17 +0100
python-psycopg (0.1) unstable; urgency=low
* Initial release.
-- Federico Di Gregorio <fog@debian.org> Mon, 12 Feb 2001 14:46:53 +0100

1
debian/compat vendored
View File

@ -1 +0,0 @@
5

64
debian/control vendored
View File

@ -1,64 +0,0 @@
Source: psycopg2
Section: python
Priority: optional
Build-Depends: debhelper (>= 5.0.37.2), python-all-dev, python-all-dbg, python-central (>= 0.5.0), python (>= 2.3.5-7), python-egenix-mx-base-dev, autoconf, libpq-dev
Build-Depends-Indep: zope-debhelper (>= 0.3.4)
Maintainer: Fabio Tranchitella <kobold@debian.org>
Standards-Version: 3.7.3
XS-Python-Version: all
Vcs-Svn: svn://svn.debian.org/python-modules/packages/psycopg2/trunk/
Vcs-Browser: http://svn.debian.org/wsvn/python-modules/packages/psycopg2/trunk/?op=log
Package: python-psycopg2
Architecture: any
Section: python
Depends: ${python:Depends}, ${shlibs:Depends}, python-egenix-mxdatetime
Provides: ${python:Provides}
XB-Python-Version: ${python:Versions}
Description: Python module for PostgreSQL
psycopg is a PostgreSQL database adapter for the Python programming language
(just like pygresql and popy.) This is version 2, a complete rewrite of the
original code to provide new-style classes for connection and cursor objects
and other sweet candies. Like the original, psycopg 2 was written with the
aim of being very small and fast, and stable as a rock.
.
psycopg is different from the other database adapter because it was designed
for heavily multi-threaded applications that create and destroy lots of
cursors and make a conspicuous number of concurrent INSERTs or UPDATEs.
psycopg 2 also provides full asycronous operations for the really brave
programmer.
.
The main advantages of psycopg2 are that it supports the full Python DBAPI-2.0
and being thread safe at level 2. It also includes some extensions to the
standard DBAPI-2.0 to allow for better thread performance.
Package: python-psycopg2-dbg
Priority: extra
Architecture: any
Section: python
Depends: python-psycopg2 (= ${binary:Version}), python-dbg, python-egenix-mxdatetime-dbg, ${shlibs:Depends}
Description: Python module for PostgreSQL (debug extension)
psycopg is a PostgreSQL database adapter for the Python programming language
(just like pygresql and popy.) This is version 2, a complete rewrite of the
original code to provide new-style classes for connection and cursor objects
and other sweet candies. Like the original, psycopg 2 was written with the
aim of being very small and fast, and stable as a rock.
.
This package contains the extensions built for the Python debug interpreter.
Package: zope-psycopgda2
Architecture: all
Section: python
Depends: ${zope:Depends}, python-psycopg2 (>= ${source:Version})
Description: Zope database adapter based on python-psycopg2
The package contains the PostgreSQL database adapter for Zope 2.7, 2.8 and
2.9 based on the psycopg2 Python module.
Package: python-psycopg2da
Architecture: all
Section: python
Depends: ${zope:Depends}, python-psycopg2 (>= ${source:Version})
XB-Python-Version: ${zope:PythonVersion}
Description: Zope database adapter based on python-psycopg2 -- zope3 version
The package contains the PostgreSQL database adapter for Zope 3 based on
the psycopg2 Python module.

112
debian/copyright vendored
View File

@ -1,112 +0,0 @@
This package was debianized by Fabio Tranchitella <kobold@debian.org> on
Sun, 16 Jul 2006 21:10:01 +0200.
psycopg2 can be downloaded from its homepage:
http://initd.org/projects/psycopg
The tarball has been re-packed to get rid of the upstream debian/ directory:
no other changes have been made to the tarball.
Copyright:
Copyright (C) 2001-2006 Federico Di Gregorio <fog@debian.org>
Copyright (C) 2001 Michele Comitini <mcm@initd.org>
For psycopg2da:
Copyright (C) 2006 Fabio Tranchitella <kobold@debian.org>
For the files doc/copy_from.py and doc/copy_to.py:
Copyright (C) 2001-2005 Federico Di Gregorio <fog@debian.org>
Copyright (C) 2002 Tom Jenkins <tjenkins@devis.com>
For the file tests/dbapi20.py:
Copyright (C) 2003 Ian Bicking <ianb@colorstudy.com>
For the file scripts/ext2html.py:
Copyright (C) 2003 Daniele Varrazzo <daniele.varrazzo@gmail.com>
License for psycopg2, ZPsycopgDA and psycopg2da:
psycopg 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.
On Debian GNU/Linux systems, the complete text of the GNU General
Public License can be found in '/usr/share/common-licenses/GPL'.
As a special exception, specific permission is granted for the GPLed
code in this distribition to be linked to OpenSSL and PostgreSQL libpq
without invoking GPL clause 2(b).
Note that the GPL was chosen to avoid proprietary adapters based on
psycopg code. Using psycopg in a proprietary product (even bundling
psycopg with the proprietary product) is fine as long as:
1. psycopg is called from Python only using only the provided API
(i.e., no linking with C code and no C modules based on it); and
2. all the other points of the GPL are respected (you offer a copy
of psycopg's source code, and so on.)
License for the files tests/dbapi20.py and scripts/ext2html.py:
These modules have been placed in the public domain.
Alternative licenses for ZPsycopgDA:
If you prefer you can use the Zope Database Adapter ZPsycopgDA (i.e.,
every file inside the ZPsycopgDA directory) user the ZPL license as
published on the Zope web site, http://www.zope.org/Resources/ZPL.
Alternative licenses for psycopg2da:
If you prefer you can use the Zope3 Database Adapter psycopg2da (i.e.,
every file inside the psycopg2da directory) user the ZPL license as
published on the Zope web site, http://www.zope.org/Resources/ZPL.
Alternative licenses for psycopg/adapter*.{j,c} and
psycopg/microprotocol*.{h.c}:
Also, the following BSD-like license applies (at your option) to the
files following the pattern psycopg/adapter*.{h,c} and
psycopg/microprotocol*.{h,c}:
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
psycopg 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.
Proprietary licenses:
A non-exclusive license is available for companies that want to include
psycopg in their proprietary products without respecting the spirit of the
GPL. The price of the license is one day of development done by the author,
at the consulting fee he applies to his usual customers at the day of the
request.
Please contact the upstream author (Federico Di Gregorio <fog@debian.org>)
for more information about this license.

1
debian/pycompat vendored
View File

@ -1 +0,0 @@
2

View File

@ -1,3 +0,0 @@
Name: psycopg2da
ZopeVersions: 3
Global: yes

93
debian/rules vendored
View File

@ -1,93 +0,0 @@
#!/usr/bin/make -f
# Sample debian/rules that uses debhelper.
# GNU copyright 1997 to 1999 by Joey Hess.
PYVERS=$(shell pyversions -r debian/control)
configure: configure-stamp
configure-stamp:
dh_testdir
rm -f configure
touch configure-stamp
build: configure build-stamp
build-stamp:
dh_testdir
for python in $(PYVERS); do \
$$python setup.py build ; \
done
for python in $(PYVERS); do \
$$python-dbg setup.py build ; \
done
touch build-stamp
clean: configure
dh_testdir
dh_testroot
rm -fr *-stamp build
for python in $(PYVERS); do \
$$python setup.py clean ; \
done
dh_clean
install-arch: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
# psycopg2
for python in $(PYVERS); do \
$$python setup.py install \
--root=$(CURDIR)/debian/python-psycopg2 --no-compile; \
done
for python in $(PYVERS); do \
$$python-dbg setup.py install \
--root=$(CURDIR)/debian/python-psycopg2-dbg --no-compile; \
done
find debian/python-*-dbg ! -type d ! -name '*.so' | xargs rm -f
find debian/python-*-dbg -depth -empty -exec rmdir {} \;
install-indep: build
# Zope package
dh_installzope -p zope-psycopgda2 ZPsycopgDA
# Zope3 package
dh_installzope -p python-psycopg2da psycopg2da
# Build architecture-independent files here.
binary-indep: build install-indep
dh_testdir
dh_testroot
dh_installdocs -i AUTHORS
dh_installchangelogs -i
dh_link -i
dh_compress -i
dh_fixperms -i
dh_pycentral -p python-psycopg2da
dh_installdeb -i
dh_gencontrol -i
dh_md5sums -i
dh_builddeb -i
# Build architecture-dependent files here.
binary-arch: build install-arch
dh_testdir
dh_testroot
dh_installdocs -a README AUTHORS doc tests
dh_installchangelogs -a ChangeLog
dh_link -a
dh_strip -ppython-psycopg2 --dbg-package=python-psycopg2-dbg
rm -rf debian/python-psycopg2-dbg/usr/share/doc/python-psycopg2-dbg
ln -s python-psycopg2 debian/python-psycopg2-dbg/usr/share/doc/python-psycopg2-dbg
dh_compress -a
dh_fixperms -a
dh_makeshlibs -a
dh_pycentral -a
dh_python -a
dh_installdeb -a
dh_shlibdeps -a
dh_gencontrol -a
dh_md5sums -a
dh_builddeb -a
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install configure

2
debian/watch vendored
View File

@ -1,2 +0,0 @@
version=3
http://www.initd.org/pub/software/psycopg/psycopg2-([0-9][0-9\.\-]*).tar.gz debian uupdate

View File

@ -1,4 +0,0 @@
Name: ZPsycopgDA
Directory: ZPsycopgDA:2
Package: zope-psycopgda2
ZopeVersions: >= 2.9

View File

@ -9,8 +9,7 @@ programming language. Its main features are the complete implementation of
the Python |DBAPI|_ specification and the thread safety (several threads can the Python |DBAPI|_ specification and the thread safety (several threads can
share the same connection). It was designed for heavily multi-threaded share the same connection). It was designed for heavily multi-threaded
applications that create and destroy lots of cursors and make a large number applications that create and destroy lots of cursors and make a large number
of concurrent :sql:`INSERT`\s or :sql:`UPDATE`\s. The Psycopg distribution of concurrent :sql:`INSERT`\s or :sql:`UPDATE`\s.
includes ZPsycopgDA, a Zope_ Database Adapter.
Psycopg 2 is mostly implemented in C as a libpq_ wrapper, resulting in being Psycopg 2 is mostly implemented in C as a libpq_ wrapper, resulting in being
both efficient and secure. It features client-side and :ref:`server-side both efficient and secure. It features client-side and :ref:`server-side
@ -33,7 +32,6 @@ Psycopg 2 is both Unicode and Python 3 friendly.
.. _Psycopg: http://initd.org/psycopg/ .. _Psycopg: http://initd.org/psycopg/
.. _PostgreSQL: http://www.postgresql.org/ .. _PostgreSQL: http://www.postgresql.org/
.. _Python: http://www.python.org/ .. _Python: http://www.python.org/
.. _Zope: http://www.zope.org/
.. _libpq: http://www.postgresql.org/docs/current/static/libpq.html .. _libpq: http://www.postgresql.org/docs/current/static/libpq.html
.. |COPY-TO-FROM| replace:: :sql:`COPY TO/COPY FROM` .. |COPY-TO-FROM| replace:: :sql:`COPY TO/COPY FROM`
.. __: http://www.postgresql.org/docs/current/static/sql-copy.html .. __: http://www.postgresql.org/docs/current/static/sql-copy.html

View File

@ -27,30 +27,6 @@ This module implements thread-safe (and not) connection pools.
import psycopg2 import psycopg2
import psycopg2.extensions as _ext import psycopg2.extensions as _ext
try:
import logging
# create logger object for psycopg2 module and sub-modules
_logger = logging.getLogger("psycopg2")
def dbg(*args):
_logger.debug("psycopg2", ' '.join([str(x) for x in args]))
try:
import App # does this make sure that we're running in Zope?
_logger.info("installed. Logging using Python logging module")
except:
_logger.debug("installed. Logging using Python logging module")
except ImportError:
from zLOG import LOG, DEBUG, INFO
def dbg(*args):
LOG('ZPsycopgDA', DEBUG, "",
' '.join([str(x) for x in args])+'\n')
LOG('ZPsycopgDA', INFO, "Installed", "Logging using Zope's zLOG\n")
except:
import sys
def dbg(*args):
sys.stderr.write(' '.join(args)+'\n')
class PoolError(psycopg2.Error): class PoolError(psycopg2.Error):
pass pass

View File

@ -1,2 +0,0 @@
psycopg2
zope.app

View File

@ -1,24 +0,0 @@
# Load the license from an external source, so we don't have to keep a
# copy of it sitting around:
<load>
LICENSE.txt http://svn.zope.org/*checkout*/Zope3/trunk/ZopePublicLicense.txt?rev=25177
</load>
# Add a few things to the distribution root.
<distribution>
README.txt
</distribution>
# Specify what is included in the component.
<collection>
# Documentation files of the package:
*.txt
# Python modules from the package:
*.py
# Configuration files of the package:
*.zcml
</collection>

View File

@ -1,9 +0,0 @@
Metadata-Version: 1.0
Name: psycopg2da
Summary: Psycopg2 Database Adapter for Zope 3
Author: Fabio Tranchitella
Author-email: kobold@debian.org
License: ZPL 2.1
Description:
This package allows Zope 3 to connect to any PostGreSQL database via
the common Zope 3 RDB connection facilities.

View File

@ -1,79 +0,0 @@
==========
psycopg2da
==========
This file outlines the basics of using Zope3 with PostgreSQL via PsycopgDA.
Installing PsycopgDA
--------------------
1. Check out the psycopg2da package into a directory in your
PYTHONPATH. INSTANCE_HOME/lib/python or Zope3/src is usually the
most convenient place:
svn co svn://svn.zope.org/repos/main/psycopg2da/trunk psycopg2da
2. Copy `psycopg2-configure.zcml` to the `package-includes` directory
of your Zope instance.
Creating Database Connections
------------------------------
It is time to add some connections. A connection in Zope 3 is
registered as a utility.
3. Open a web browser on your Zope root folder (http://localhost:8080/
if you use the default settings in zope.conf.in).
4. Click on the 'Manage Site' action on the right side of the
screen. You should see a screen which reads 'Common Site Management
Tasks'
5. Around the middle of that page, you should see a link named 'Add
Utility'. Click on it.
6. Select 'Psycopg DA' and type in a name at the bottom of the page.
7. Enter the database connection string. It looks like this:
dbi://username:password@host:port/databasename
8. Click on the 'Add' button.
9. You should be on a page which reads 'Add Database Connection
Registration'. There you can configure the permission needed to use
the database connection, the name of the registration and the
registration status. You can use any name for 'Register As' field,
as long as it doesn't clash with an existing one. Choose a
permission. Choose between 'Registered' and 'Active' for the
'Registration Status'. Only one component of a kind can be 'Active'
at a time, so be careful.
10. You should be redirected to the 'Edit' screen of the connection
utility.
11. If you want to, you can go to the Test page and execute arbitrary
SQL queries to see whether the connection is working as expected.
Using SQL Scripts
-----------------
You can create SQL Scripts in the content space. For example:
12. Go to Zope root.
13. Add an SQL script (you can use the Common Tasks box on the left,
or the Add action on the right).
14. Click on the name of your new SQL script.
15. Choose a connection name (the one you entered in step 29) from the
drop-down.
16. Enter your query and click on the 'Save Changes' button.
17. You can test the script in the -- surprise! -- Test page.

View File

@ -1 +0,0 @@
# empty file

View File

@ -1,408 +0,0 @@
# Copyright (C) 2006 Fabio Tranchitella <fabio@tranchitella.it>
#
# psycopg2da is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# In addition, as a special exception, the copyright holders give
# permission to link this program with the OpenSSL library (or with
# modified versions of OpenSSL that use the same license as OpenSSL),
# and distribute linked combinations including the two.
#
# You must obey the GNU Lesser General Public License in all respects for
# all of the code used other than OpenSSL.
#
# psycopg2da is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# Based on ZPsycopgDA.
#
# If you prefer you can use this package using the ZPL license as
# published on the Zope web site, http://www.zope.org/Resources/ZPL.
"""PostgreSQL Database Adapter for Zope 3"""
from zope.interface import implements
from zope.rdb import ZopeDatabaseAdapter, parseDSN, ZopeConnection, ZopeCursor
from zope.rdb.interfaces import DatabaseException, IZopeConnection
from zope.publisher.interfaces import Retry
from datetime import date, time, datetime, timedelta
import psycopg2
import psycopg2.extensions
import re
import sys
# OIDs from psycopg/pgtypes.h
DATE_OID = 1082
TIME_OID = 1083
TIMETZ_OID = 1266
TIMESTAMP_OID = 1114
TIMESTAMPTZ_OID = 1184
INTERVAL_OID = 1186
CHAR_OID = 18
TEXT_OID = 25
BPCHAR_OID = 1042
VARCHAR_OID = 1043
# date/time parsing functions
_dateFmt = re.compile(r"^(\d\d\d\d)-?([01]\d)-?([0-3]\d)$")
def parse_date(s):
"""Parses ISO-8601 compliant dates and returns a tuple (year, month,
day).
The following formats are accepted:
YYYY-MM-DD (extended format)
YYYYMMDD (basic format)
"""
m = _dateFmt.match(s)
if m is None:
raise ValueError, 'invalid date string: %s' % s
year, month, day = m.groups()
return int(year), int(month), int(day)
_timeFmt = re.compile(
r"^([0-2]\d)(?::?([0-5]\d)(?::?([0-5]\d)(?:[.,](\d+))?)?)?$")
def parse_time(s):
"""Parses ISO-8601 compliant times and returns a tuple (hour, minute,
second).
The following formats are accepted:
HH:MM:SS.ssss or HHMMSS.ssss
HH:MM:SS,ssss or HHMMSS,ssss
HH:MM:SS or HHMMSS
HH:MM or HHMM
HH
"""
m = _timeFmt.match(s)
if m is None:
raise ValueError, 'invalid time string: %s' % s
hr, mn, sc, msc = m.groups(0)
if msc != 0:
sc = float("%s.%s" % (sc, msc))
else:
sc = int(sc)
return int(hr), int(mn), sc
_tzFmt = re.compile(r"^([+-])([0-2]\d)(?::?([0-5]\d))?$")
def parse_tz(s):
"""Parses ISO-8601 timezones and returns the offset east of UTC in
minutes.
The following formats are accepted:
+/-HH:MM
+/-HHMM
+/-HH
Z (equivalent to +0000)
"""
if s == 'Z':
return 0
m = _tzFmt.match(s)
if m is None:
raise ValueError, 'invalid time zone: %s' % s
d, hoff, moff = m.groups(0)
if d == "-":
return - int(hoff) * 60 - int(moff)
return int(hoff) * 60 + int(moff)
_tzPos = re.compile(r"[Z+-]")
def parse_timetz(s):
"""Parses ISO-8601 compliant times that may include timezone information
and returns a tuple (hour, minute, second, tzoffset).
tzoffset is the offset east of UTC in minutes. It will be None if s does
not include time zone information.
Formats accepted are those listed in the descriptions of parse_time() and
parse_tz(). Time zone should immediatelly follow time without intervening
spaces.
"""
m = _tzPos.search(s)
if m is None:
return parse_time(s) + (None,)
pos = m.start()
return parse_time(s[:pos]) + (parse_tz(s[pos:]),)
_datetimeFmt = re.compile(r"[T ]")
def _split_datetime(s):
"""Split date and time parts of ISO-8601 compliant timestamp and
return a tuple (date, time).
' ' or 'T' used to separate date and time parts.
"""
m = _datetimeFmt.search(s)
if m is None:
raise ValueError, 'time part of datetime missing: %s' % s
pos = m.start()
return s[:pos], s[pos + 1:]
def parse_datetime(s):
"""Parses ISO-8601 compliant timestamp and returns a tuple (year, month,
day, hour, minute, second).
Formats accepted are those listed in the descriptions of parse_date() and
parse_time() with ' ' or 'T' used to separate date and time parts.
"""
dt, tm = _split_datetime(s)
return parse_date(dt) + parse_time(tm)
def parse_datetimetz(s):
"""Parses ISO-8601 compliant timestamp that may include timezone
information and returns a tuple (year, month, day, hour, minute, second,
tzoffset).
tzoffset is the offset east of UTC in minutes. It will be None if s does
not include time zone information.
Formats accepted are those listed in the descriptions of parse_date() and
parse_timetz() with ' ' or 'T' used to separate date and time parts.
"""
dt, tm = _split_datetime(s)
return parse_date(dt) + parse_timetz(tm)
def parse_interval(s):
"""Parses PostgreSQL interval notation and returns a tuple (years, months,
days, hours, minutes, seconds).
Values accepted:
interval ::= date
| time
| date time
date ::= date_comp
| date date_comp
date_comp ::= 1 'day'
| number 'days'
| 1 'month'
| 1 'mon'
| number 'months'
| number 'mons'
| 1 'year'
| number 'years'
time ::= number ':' number
| number ':' number ':' number
| number ':' number ':' number '.' fraction
"""
years = months = days = 0
hours = minutes = seconds = 0
elements = s.split()
# Tests with 7.4.6 on Ubuntu 5.4 interval output returns 'mon' and 'mons'
# and not 'month' or 'months' as expected. I've fixed this and left
# the original matches there too in case this is dependant on
# OS or PostgreSQL release.
for i in range(0, len(elements) - 1, 2):
count, unit = elements[i:i+2]
if unit == 'day' and count == '1':
days += 1
elif unit == 'days':
days += int(count)
elif unit == 'month' and count == '1':
months += 1
elif unit == 'mon' and count == '1':
months += 1
elif unit == 'months':
months += int(count)
elif unit == 'mons':
months += int(count)
elif unit == 'year' and count == '1':
years += 1
elif unit == 'years':
years += int(count)
else:
raise ValueError, 'unknown time interval %s %s' % (count, unit)
if len(elements) % 2 == 1:
hours, minutes, seconds = parse_time(elements[-1])
return (years, months, days, hours, minutes, seconds)
# Type conversions
def _conv_date(s, cursor):
if s:
return date(*parse_date(s))
def _conv_time(s, cursor):
if s:
hr, mn, sc = parse_time(s)
sc, micro = divmod(sc, 1.0)
micro = round(micro * 1000000)
return time(hr, mn, int(sc), int(micro))
def _conv_timetz(s, cursor):
if s:
from zope.datetime import tzinfo
hr, mn, sc, tz = parse_timetz(s)
sc, micro = divmod(sc, 1.0)
micro = round(micro * 1000000)
if tz: tz = tzinfo(tz)
return time(hr, mn, int(sc), int(micro), tz)
def _conv_timestamp(s, cursor):
if s:
y, m, d, hr, mn, sc = parse_datetime(s)
sc, micro = divmod(sc, 1.0)
micro = round(micro * 1000000)
return datetime(y, m, d, hr, mn, int(sc), int(micro))
def _conv_timestamptz(s, cursor):
if s:
from zope.datetime import tzinfo
y, m, d, hr, mn, sc, tz = parse_datetimetz(s)
sc, micro = divmod(sc, 1.0)
micro = round(micro * 1000000)
if tz: tz = tzinfo(tz)
return datetime(y, m, d, hr, mn, int(sc), int(micro), tz)
def _conv_interval(s, cursor):
if s:
y, m, d, hr, mn, sc = parse_interval(s)
if (y, m) != (0, 0):
# XXX: Currently there's no way to represent years and months as
# timedeltas
return s
else:
return timedelta(days=d, hours=hr, minutes=mn, seconds=sc)
def _get_string_conv(encoding):
def _conv_string(s, cursor):
if s is not None:
s = s.decode(encoding)
return s
return _conv_string
# User-defined types
DATE = psycopg2.extensions.new_type((DATE_OID,), "ZDATE", _conv_date)
TIME = psycopg2.extensions.new_type((TIME_OID,), "ZTIME", _conv_time)
TIMETZ = psycopg2.extensions.new_type((TIMETZ_OID,), "ZTIMETZ", _conv_timetz)
TIMESTAMP = psycopg2.extensions.new_type((TIMESTAMP_OID,), "ZTIMESTAMP", _conv_timestamp)
TIMESTAMPTZ = psycopg2.extensions.new_type((TIMESTAMPTZ_OID,), "ZTIMESTAMPTZ", _conv_timestamptz)
INTERVAL = psycopg2.extensions.new_type((INTERVAL_OID,), "ZINTERVAL", _conv_interval)
def registerTypes(encoding):
"""Register type conversions for psycopg"""
psycopg2.extensions.register_type(DATE)
psycopg2.extensions.register_type(TIME)
psycopg2.extensions.register_type(TIMETZ)
psycopg2.extensions.register_type(TIMESTAMP)
psycopg2.extensions.register_type(TIMESTAMPTZ)
psycopg2.extensions.register_type(INTERVAL)
STRING = psycopg2.extensions.new_type((CHAR_OID, TEXT_OID, BPCHAR_OID, VARCHAR_OID), "ZSTRING", _get_string_conv(encoding))
psycopg2.extensions.register_type(STRING)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
dsn2option_mapping = {'host': 'host',
'port': 'port',
'dbname': 'dbname',
'username': 'user',
'password': 'password'}
class Psycopg2Adapter(ZopeDatabaseAdapter):
"""A psycopg2 adapter for Zope3.
The following type conversions are performed:
DATE -> datetime.date
TIME -> datetime.time
TIMETZ -> datetime.time
TIMESTAMP -> datetime.datetime
TIMESTAMPTZ -> datetime.datetime
XXX: INTERVAL cannot be represented exactly as datetime.timedelta since
it might be something like '1 month', which is a variable number of days.
"""
def connect(self):
if not self.isConnected():
try:
self._v_connection = Psycopg2Connection(
self._connection_factory(), self
)
except psycopg2.Error, error:
raise DatabaseException, str(error)
def registerTypes(self):
registerTypes(self.getEncoding())
def _connection_factory(self):
"""Create a psycopg2 DBI connection based on the DSN"""
self.registerTypes()
conn_info = parseDSN(self.dsn)
conn_list = []
for dsnname, optname in dsn2option_mapping.iteritems():
if conn_info[dsnname]:
conn_list.append('%s=%s' % (optname, conn_info[dsnname]))
conn_str = ' '.join(conn_list)
connection = psycopg2.connect(conn_str)
connection.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
return connection
def disconnect(self):
if self.isConnected():
try:
self._v_connection.close()
except psycopg2.InterfaceError:
pass
self._v_connection = None
def _handle_psycopg_exception(error):
"""Called from a exception handler for psycopg2.Error.
If we have a serialization exception or a deadlock, we should retry the
transaction by raising a Retry exception. Otherwise, we reraise.
"""
if isinstance(error, psycopg2.extensions.TransactionRollbackError):
raise Retry(sys.exc_info())
raise
class IPsycopg2ZopeConnection(IZopeConnection):
"""A marker interface stating that this connection uses PostgreSQL."""
class Psycopg2Connection(ZopeConnection):
implements(IPsycopg2ZopeConnection)
def cursor(self):
"""See IZopeConnection"""
return Psycopg2Cursor(self.conn.cursor(), self)
def commit(self):
try:
ZopeConnection.commit(self)
except psycopg2.Error, error:
_handle_psycopg_exception(error)
class Psycopg2Cursor(ZopeCursor):
def execute(self, operation, parameters=None):
"""See IZopeCursor"""
try:
return ZopeCursor.execute(self, operation, parameters)
except psycopg2.Error, error:
_handle_psycopg_exception(error)
def executemany(operation, seq_of_parameters=None):
"""See IZopeCursor"""
raise RuntimeError, 'Oos'
try:
return ZopeCursor.execute(self, operation, seq_of_parameters)
except psycopg2.Error, error:
_handle_psycopg_exception(error)

View File

@ -1,51 +0,0 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="psycopg2da">
<class class=".adapter.Psycopg2Adapter">
<factory id="zope.da.Psycopg2DA" />
<require
permission="zope.rdb.Use"
interface="zope.rdb.interfaces.IZopeDatabaseAdapter"
/>
<require
permission="zope.ManageServices"
interface="zope.rdb.interfaces.IZopeDatabaseAdapterManagement"
/>
</class>
<class class=".adapter.Psycopg2Connection">
<require
permission="zope.rdb.Use"
interface="zope.rdb.interfaces.IZopeConnection"
/>
</class>
<class class=".adapter.Psycopg2Cursor">
<require
permission="zope.rdb.Use"
interface="zope.rdb.interfaces.IZopeCursor"
/>
</class>
<browser:addform
name="AddPsycopg2DA"
schema="zope.rdb.interfaces.IManageableZopeDatabaseAdapter"
label="Add Psycopg2 (PostGreSQL) Database Adapter"
content_factory=".adapter.Psycopg2Adapter"
arguments="dsn"
fields="dsn"
permission="zope.ManageContent"
/>
<!-- Menu entry for "add utility" menu -->
<browser:addMenuItem
class=".adapter.Psycopg2Adapter"
title="Psycopg2 DA"
description="A PostgreSQL Database Adapter using the Psycopg2 driver"
permission="zope.ManageApplication"
view="AddPsycopg2DA"
/>
</configure>

View File

@ -1 +0,0 @@
<include package="psycopg2da" />

View File

@ -1,395 +0,0 @@
# Copyright (C) 2006 Fabio Tranchitella <fabio@tranchitella.it>
#
# psycopg2da is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# In addition, as a special exception, the copyright holders give
# permission to link this program with the OpenSSL library (or with
# modified versions of OpenSSL that use the same license as OpenSSL),
# and distribute linked combinations including the two.
#
# You must obey the GNU Lesser General Public License in all respects for
# all of the code used other than OpenSSL.
#
# psycopg2da is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# If you prefer you can use this package using the ZPL license as
# published on the Zope web site, http://www.zope.org/Resources/ZPL.
"""Unit tests for Psycopg2DA."""
from unittest import TestCase, TestSuite, main, makeSuite
from datetime import tzinfo, timedelta
import psycopg2
import psycopg2.extensions
class Stub(object):
def __init__(self, **kw):
self.__dict__.update(kw)
class TZStub(tzinfo):
def __init__(self, h, m):
self.offset = h * 60 + m
def utcoffset(self, dt):
return timedelta(minutes=self.offset)
def dst(self, dt):
return 0
def tzname(self, dt):
return ''
def __repr__(self):
return 'tzinfo(%d)' % self.offset
def __reduce__(self):
return type(self), (), self.__dict__
class ConnectionStub(object):
def set_isolation_level(self, level):
pass
class Psycopg2Stub(object):
__shared_state = {} # 'Borg' design pattern
DATE = psycopg2.extensions.DATE
TIME = psycopg2.extensions.TIME
DATETIME = psycopg2.DATETIME
INTERVAL = psycopg2.extensions.INTERVAL
ISOLATION_LEVEL_SERIALIZABLE = psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE
def __init__(self):
self.__dict__ = self.__shared_state
self.types = {}
def connect(self, connection_string):
self.last_connection_string = connection_string
return ConnectionStub()
def new_type(self, values, name, converter):
return Stub(name=name, values=values)
def register_type(self, type):
for typeid in type.values:
self.types[typeid] = type
def getExtensions(self):
return self
extensions = property(getExtensions)
class TestPsycopg2TypeConversion(TestCase):
def test_conv_date(self):
from psycopg2da.adapter import _conv_date
from datetime import date
def c(s):
return _conv_date(s, None)
self.assertEquals(c(''), None)
self.assertEquals(c('2001-03-02'), date(2001, 3, 2))
def test_conv_time(self):
from psycopg2da.adapter import _conv_time
from datetime import time
def c(s):
return _conv_time(s, None)
self.assertEquals(c(''), None)
self.assertEquals(c('23:17:57'), time(23, 17, 57))
self.assertEquals(c('23:17:57.037'), time(23, 17, 57, 37000))
def test_conv_timetz(self):
from psycopg2da.adapter import _conv_timetz
from datetime import time
def c(s):
return _conv_timetz(s, None)
self.assertEquals(c(''), None)
self.assertEquals(c('12:44:01+01:00'), time(12, 44, 01, 0, TZStub(1,0)))
self.assertEquals(c('12:44:01.037-00:30'), time(12, 44, 01, 37000, TZStub(0,-30)))
def test_conv_timestamp(self):
from psycopg2da.adapter import _conv_timestamp
from datetime import datetime
def c(s):
return _conv_timestamp(s, None)
self.assertEquals(c(''), None)
self.assertEquals(c('2001-03-02 12:44:01'),
datetime(2001, 3, 2, 12, 44, 01))
self.assertEquals(c('2001-03-02 12:44:01.037'),
datetime(2001, 3, 2, 12, 44, 01, 37000))
self.assertEquals(c('2001-03-02 12:44:01.000001'),
datetime(2001, 3, 2, 12, 44, 01, 1))
def test_conv_timestamptz(self):
from psycopg2da.adapter import _conv_timestamptz
from datetime import datetime
def c(s):
return _conv_timestamptz(s, None)
self.assertEquals(c(''), None)
self.assertEquals(c('2001-03-02 12:44:01+01:00'),
datetime(2001, 3, 2, 12, 44, 01, 0, TZStub(1,0)))
self.assertEquals(c('2001-03-02 12:44:01.037-00:30'),
datetime(2001, 3, 2, 12, 44, 01, 37000, TZStub(0,-30)))
self.assertEquals(c('2001-03-02 12:44:01.000001+12:00'),
datetime(2001, 3, 2, 12, 44, 01, 1, TZStub(12,0)))
self.assertEquals(c('2001-06-25 12:14:00-07'),
datetime(2001, 6, 25, 12, 14, 00, 0, TZStub(-7,0)))
def test_conv_interval(self):
from psycopg2da.adapter import _conv_interval
from datetime import timedelta
def c(s):
return _conv_interval(s, None)
self.assertEquals(c(''), None)
self.assertEquals(c('01:00'), timedelta(hours=1))
self.assertEquals(c('00:15'), timedelta(minutes=15))
self.assertEquals(c('00:00:47'), timedelta(seconds=47))
self.assertEquals(c('00:00:00.037'), timedelta(microseconds=37000))
self.assertEquals(c('00:00:00.111111'), timedelta(microseconds=111111))
self.assertEquals(c('1 day'), timedelta(days=1))
self.assertEquals(c('2 days'), timedelta(days=2))
self.assertEquals(c('374 days'), timedelta(days=374))
self.assertEquals(c('2 days 03:20:15.123456'),
timedelta(days=2, hours=3, minutes=20,
seconds=15, microseconds=123456))
# XXX There's a problem with years and months. Currently timedelta
# cannot represent them accurately
self.assertEquals(c('1 month'), '1 month')
self.assertEquals(c('2 months'), '2 months')
self.assertEquals(c('1 mon'), '1 mon')
self.assertEquals(c('2 mons'), '2 mons')
self.assertEquals(c('1 year'), '1 year')
self.assertEquals(c('3 years'), '3 years')
# Later we might be able to use
## self.assertEquals(c('1 month'), timedelta(months=1))
## self.assertEquals(c('2 months'), timedelta(months=2))
## self.assertEquals(c('1 year'), timedelta(years=1))
## self.assertEquals(c('3 years'), timedelta(years=3))
self.assertRaises(ValueError, c, '2 day')
self.assertRaises(ValueError, c, '2days')
self.assertRaises(ValueError, c, '123')
def test_conv_string(self):
from psycopg2da.adapter import _get_string_conv
_conv_string = _get_string_conv("utf-8")
def c(s):
return _conv_string(s, None)
self.assertEquals(c(None), None)
self.assertEquals(c(''), u'')
self.assertEquals(c('c'), u'c')
self.assertEquals(c('\xc3\x82\xc2\xa2'), u'\xc2\xa2')
self.assertEquals(c('c\xc3\x82\xc2\xa2'), u'c\xc2\xa2')
class TestPsycopg2Adapter(TestCase):
def setUp(self):
import psycopg2da.adapter as adapter
self.real_psycopg2 = adapter.psycopg2
adapter.psycopg2 = self.psycopg2_stub = Psycopg2Stub()
def tearDown(self):
import psycopg2da.adapter as adapter
adapter.psycopg2 = self.real_psycopg2
def test_connection_factory(self):
from psycopg2da.adapter import Psycopg2Adapter
a = Psycopg2Adapter('dbi://username:password@hostname:port/dbname;junk=ignored')
c = a._connection_factory()
args = self.psycopg2_stub.last_connection_string.split()
args.sort()
self.assertEquals(args, ['dbname=dbname', 'host=hostname',
'password=password', 'port=port',
'user=username'])
def test_registerTypes(self):
import psycopg2da.adapter as adapter
from psycopg2da.adapter import Psycopg2Adapter
a = Psycopg2Adapter('dbi://')
a.registerTypes()
for typename in ('DATE', 'TIME', 'TIMETZ', 'TIMESTAMP',
'TIMESTAMPTZ', 'INTERVAL'):
typeid = getattr(adapter, '%s_OID' % typename)
result = self.psycopg2_stub.types.get(typeid, None)
if not result:
# comparing None with psycopg2.type object segfaults
self.fail("did not register %s (%d): got None, not Z%s"
% (typename, typeid, typename))
else:
result_name = getattr(result, 'name', 'None')
self.assertEquals(result, getattr(adapter, typename),
"did not register %s (%d): got %s, not Z%s"
% (typename, typeid, result_name, typename))
class TestISODateTime(TestCase):
# Test if date/time parsing functions accept a sensible subset of ISO-8601
# compliant date/time strings.
#
# Resources:
# http://www.cl.cam.ac.uk/~mgk25/iso-time.html
# http://www.mcs.vuw.ac.nz/technical/software/SGML/doc/iso8601/ISO8601.html
# http://www.w3.org/TR/NOTE-datetime
# http://www.ietf.org/rfc/rfc3339.txt
basic_dates = (('20020304', (2002, 03, 04)),
('20000229', (2000, 02, 29)))
extended_dates = (('2002-03-04', (2002, 03, 04)),
('2000-02-29', (2000, 02, 29)))
basic_times = (('12', (12, 0, 0)),
('1234', (12, 34, 0)),
('123417', (12, 34, 17)),
('123417.5', (12, 34, 17.5)),
('123417,5', (12, 34, 17.5)))
extended_times = (('12', (12, 0, 0)),
('12:34', (12, 34, 0)),
('12:34:17', (12, 34, 17)),
('12:34:17.5', (12, 34, 17.5)),
('12:34:17,5', (12, 34, 17.5)))
basic_tzs = (('Z', 0),
('+02', 2*60),
('+1130', 11*60+30),
('-05', -5*60),
('-0030', -30))
extended_tzs = (('Z', 0),
('+02', 2*60),
('+11:30', 11*60+30),
('-05', -5*60),
('-00:30', -30))
time_separators = (' ', 'T')
bad_dates = ('', 'foo', 'XXXXXXXX', 'XXXX-XX-XX', '2001-2-29',
'1990/13/14')
bad_times = ('', 'foo', 'XXXXXX', '12:34,5', '12:34:56,')
bad_timetzs = ('12+12 34', '15:45 +1234', '18:00-12:34:56', '18:00+123', '18:00Q')
bad_datetimes = ('', 'foo', '2002-03-0412:33')
bad_datetimetzs = ('', 'foo', '2002-03-04T12:33 +1200')
exception_type = ValueError
# We need the following funcions:
# parse_date -> (year, month, day)
# parse_time -> (hour, minute, second)
# parse_timetz -> (hour, minute, second, tzoffset)
# parse_datetime -> (year, month, day, hour, minute, second)
# parse_datetimetz -> (year, month, day, hour, minute, second, tzoffset)
# second can be a float, all other values are ints
# tzoffset is offset in minutes east of UTC
def setUp(self):
from psycopg2da.adapter import parse_date, parse_time, \
parse_timetz, parse_datetime, parse_datetimetz
self.parse_date = parse_date
self.parse_time = parse_time
self.parse_timetz = parse_timetz
self.parse_datetime = parse_datetime
self.parse_datetimetz = parse_datetimetz
def test_basic_date(self):
for s, d in self.basic_dates:
self.assertEqual(self.parse_date(s), d)
def test_extended_date(self):
for s, d in self.extended_dates:
self.assertEqual(self.parse_date(s), d)
def test_bad_date(self):
for s in self.bad_dates:
self.assertRaises(self.exception_type, self.parse_date, s)
def test_basic_time(self):
for s, t in self.basic_times:
self.assertEqual(self.parse_time(s), t)
def test_extended_time(self):
for s, t in self.extended_times:
self.assertEqual(self.parse_time(s), t)
def test_bad_time(self):
for s in self.bad_times:
self.assertRaises(self.exception_type, self.parse_time, s)
def test_basic_timetz(self):
for s, t in self.basic_times:
for tz, off in self.basic_tzs:
self.assertEqual(self.parse_timetz(s+tz), t + (off,))
def test_extended_timetz(self):
for s, t in self.extended_times:
for tz, off in self.extended_tzs:
self.assertEqual(self.parse_timetz(s+tz), t + (off,))
def test_bad_timetzs(self):
for s in self.bad_timetzs:
self.assertRaises(self.exception_type, self.parse_timetz, s)
def test_basic_datetime(self):
for ds, d in self.basic_dates:
for ts, t in self.basic_times:
for sep in self.time_separators:
self.assertEqual(self.parse_datetime(ds+sep+ts), d + t)
def test_extended_datetime(self):
for ds, d in self.extended_dates:
for ts, t in self.extended_times:
for sep in self.time_separators:
self.assertEqual(self.parse_datetime(ds+sep+ts), d + t)
def test_bad_datetimes(self):
for s in self.bad_datetimes:
self.assertRaises(self.exception_type, self.parse_datetime, s)
def test_basic_datetimetz(self):
for ds, d in self.basic_dates:
for ts, t in self.basic_times:
for tz, off in self.basic_tzs:
for sep in self.time_separators:
self.assertEqual(self.parse_datetimetz(ds+sep+ts+tz),
d + t + (off,))
def test_extended_datetimetz(self):
for ds, d in self.extended_dates:
for ts, t in self.extended_times:
for tz, off in self.extended_tzs:
for sep in self.time_separators:
self.assertEqual(self.parse_datetimetz(ds+sep+ts+tz),
d + t + (off,))
def test_bad_datetimetzs(self):
for s in self.bad_datetimetzs:
self.assertRaises(self.exception_type, self.parse_datetimetz, s)
def test_suite():
return TestSuite((
makeSuite(TestPsycopg2TypeConversion),
makeSuite(TestPsycopg2Adapter),
makeSuite(TestISODateTime),
))
if __name__=='__main__':
main(defaultTest='test_suite')

View File

@ -8,7 +8,3 @@ envlist = py26, py27
[testenv] [testenv]
commands = make check commands = make check
deps =
zope.interface
zope.rdb
zope.datetime