sqlobject-cvs Mailing List for SQLObject (Page 155)
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
You can subscribe to this list here.
| 2003 |
Jan
|
Feb
|
Mar
(9) |
Apr
(74) |
May
(29) |
Jun
(16) |
Jul
(28) |
Aug
(10) |
Sep
(57) |
Oct
(9) |
Nov
(29) |
Dec
(12) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2004 |
Jan
(7) |
Feb
(14) |
Mar
(6) |
Apr
(3) |
May
(12) |
Jun
(34) |
Jul
(9) |
Aug
(29) |
Sep
(22) |
Oct
(2) |
Nov
(15) |
Dec
(52) |
| 2005 |
Jan
(47) |
Feb
(78) |
Mar
(14) |
Apr
(35) |
May
(33) |
Jun
(16) |
Jul
(26) |
Aug
(63) |
Sep
(40) |
Oct
(96) |
Nov
(96) |
Dec
(123) |
| 2006 |
Jan
(159) |
Feb
(144) |
Mar
(64) |
Apr
(31) |
May
(88) |
Jun
(48) |
Jul
(16) |
Aug
(64) |
Sep
(87) |
Oct
(92) |
Nov
(56) |
Dec
(76) |
| 2007 |
Jan
(94) |
Feb
(103) |
Mar
(126) |
Apr
(123) |
May
(85) |
Jun
(11) |
Jul
(130) |
Aug
(47) |
Sep
(65) |
Oct
(70) |
Nov
(12) |
Dec
(11) |
| 2008 |
Jan
(30) |
Feb
(55) |
Mar
(88) |
Apr
(20) |
May
(50) |
Jun
|
Jul
(38) |
Aug
(1) |
Sep
(9) |
Oct
(5) |
Nov
(6) |
Dec
(39) |
| 2009 |
Jan
(8) |
Feb
(16) |
Mar
(3) |
Apr
(33) |
May
(44) |
Jun
(1) |
Jul
(10) |
Aug
(33) |
Sep
(74) |
Oct
(22) |
Nov
|
Dec
(15) |
| 2010 |
Jan
(28) |
Feb
(22) |
Mar
(46) |
Apr
(29) |
May
(1) |
Jun
(1) |
Jul
(27) |
Aug
(8) |
Sep
(5) |
Oct
(33) |
Nov
(24) |
Dec
(41) |
| 2011 |
Jan
(4) |
Feb
(12) |
Mar
(35) |
Apr
(29) |
May
(19) |
Jun
(16) |
Jul
(32) |
Aug
(25) |
Sep
(5) |
Oct
(11) |
Nov
(21) |
Dec
(12) |
| 2012 |
Jan
(3) |
Feb
(4) |
Mar
(20) |
Apr
(4) |
May
(25) |
Jun
(13) |
Jul
|
Aug
|
Sep
(2) |
Oct
(25) |
Nov
(9) |
Dec
(1) |
| 2013 |
Jan
(6) |
Feb
(8) |
Mar
|
Apr
(10) |
May
(31) |
Jun
(7) |
Jul
(18) |
Aug
(33) |
Sep
(4) |
Oct
(16) |
Nov
|
Dec
(27) |
| 2014 |
Jan
(2) |
Feb
|
Mar
|
Apr
(11) |
May
(39) |
Jun
(8) |
Jul
(11) |
Aug
(4) |
Sep
|
Oct
(27) |
Nov
|
Dec
(71) |
| 2015 |
Jan
(17) |
Feb
(47) |
Mar
(33) |
Apr
|
May
|
Jun
(9) |
Jul
(7) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(8) |
| 2016 |
Jan
(4) |
Feb
(4) |
Mar
|
Apr
|
May
(12) |
Jun
(7) |
Jul
(9) |
Aug
(31) |
Sep
(8) |
Oct
(3) |
Nov
(15) |
Dec
(1) |
| 2017 |
Jan
(13) |
Feb
(7) |
Mar
(14) |
Apr
(8) |
May
(10) |
Jun
(4) |
Jul
(2) |
Aug
(1) |
Sep
|
Oct
(8) |
Nov
(4) |
Dec
(5) |
| 2018 |
Jan
(2) |
Feb
(8) |
Mar
|
Apr
(4) |
May
|
Jun
(6) |
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
(1) |
Dec
|
| 2019 |
Jan
(1) |
Feb
(16) |
Mar
(1) |
Apr
(3) |
May
(5) |
Jun
(1) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
(3) |
| 2020 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
(1) |
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
(2) |
Nov
|
Dec
(2) |
| 2021 |
Jan
|
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(1) |
Nov
(1) |
Dec
|
| 2022 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(6) |
Oct
(1) |
Nov
(1) |
Dec
(4) |
| 2023 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
(3) |
Sep
(2) |
Oct
(2) |
Nov
(4) |
Dec
|
| 2024 |
Jan
|
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
|
Sep
(1) |
Oct
|
Nov
|
Dec
(9) |
| 2025 |
Jan
|
Feb
(4) |
Mar
(2) |
Apr
|
May
|
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
(2) |
Dec
(2) |
|
From: <sub...@co...> - 2005-11-02 18:25:00
|
Author: rflosi Date: 2005-11-02 18:24:51 +0000 (Wed, 02 Nov 2005) New Revision: 1189 Modified: SQLObject/branches/0.7-bugfix/docs/News.txt SQLObject/branches/0.7-bugfix/sqlobject/joins.py SQLObject/branches/0.7-bugfix/sqlobject/main.py SQLObject/branches/0.7-bugfix/sqlobject/manager/command.py Log: merged r1136:1137 from trunk Modified: SQLObject/branches/0.7-bugfix/docs/News.txt =================================================================== --- SQLObject/branches/0.7-bugfix/docs/News.txt 2005-11-02 18:22:42 UTC (rev 1188) +++ SQLObject/branches/0.7-bugfix/docs/News.txt 2005-11-02 18:24:51 UTC (rev 1189) @@ -16,6 +16,12 @@ .. _psycopg2: http://initd.org/projects/psycopg2 +* ``RelatedJoin`` and ``SQLRelatedJoin`` objects have a + ``createRelatedTable`` keyword argument (default ``True``). If + ``False``, then the related table won't be automatically created; + instead you must manually create it (e.g., with explicit SQLObject + classes for the joins). + SQLObject 0.7.0 =============== Modified: SQLObject/branches/0.7-bugfix/sqlobject/joins.py =================================================================== --- SQLObject/branches/0.7-bugfix/sqlobject/joins.py 2005-11-02 18:22:42 UTC (rev 1188) +++ SQLObject/branches/0.7-bugfix/sqlobject/joins.py 2005-11-02 18:24:51 UTC (rev 1189) @@ -154,9 +154,12 @@ def __init__(self, otherColumn=None, - intermediateTable=None, **kw): + intermediateTable=None, + createRelatedTable=True, + **kw): self.intermediateTable = intermediateTable self.otherColumn = otherColumn + self.createRelatedTable = createRelatedTable SOMultipleJoin.__init__(self, **kw) classregistry.registry( self.soClass.sqlmeta.registry).addClassCallback( Modified: SQLObject/branches/0.7-bugfix/sqlobject/main.py =================================================================== --- SQLObject/branches/0.7-bugfix/sqlobject/main.py 2005-11-02 18:22:42 UTC (rev 1188) +++ SQLObject/branches/0.7-bugfix/sqlobject/main.py 2005-11-02 18:24:51 UTC (rev 1189) @@ -1328,6 +1328,9 @@ def createJoinTables(cls, ifNotExists=False, connection=None): conn = connection or cls._connection for join in cls._getJoinsToCreate(): + if not getattr(join, 'createRelatedTable', True): + # This join has requested not to be created + continue if (ifNotExists and conn.tableExists(join.intermediateTable)): continue @@ -1338,6 +1341,9 @@ conn = connection or cls._connection sql = [] for join in cls._getJoinsToCreate(): + if not getattr(join, 'createRelatedTable', True): + # This join has requested not to be created + continue sql.append(conn._SO_createJoinTableSQL(join)) return '\n'.join(sql) createJoinTablesSQL = classmethod(createJoinTablesSQL) Modified: SQLObject/branches/0.7-bugfix/sqlobject/manager/command.py =================================================================== --- SQLObject/branches/0.7-bugfix/sqlobject/manager/command.py 2005-11-02 18:22:42 UTC (rev 1188) +++ SQLObject/branches/0.7-bugfix/sqlobject/manager/command.py 2005-11-02 18:24:51 UTC (rev 1189) @@ -422,8 +422,7 @@ def config_from_egg(self, egg_spec, warn_no_sqlobject=True): import pkg_resources - pkg_resources.require(egg_spec) - dist = pkg_resources.working_set.find(pkg_resources.Requirement(egg_spec)) + dist = pkg_resources.get_distribution(egg_spec) if not dist.has_metadata('sqlobject.txt'): if warn_no_sqlobject: print 'No sqlobject.txt in %s egg info' % egg_spec @@ -837,6 +836,12 @@ + '_' + dbName + '.sql') if sim: continue + create, constraints = cls.createTableSQL() + if constraints: + constraints = '\n-- Constraints:\n%s\n' % ( + '\n'.join(constraints)) + else: + constraints = '' files[fn] = ''.join([ '-- Exported definition from %s\n' % time.strftime('%Y-%m-%dT%H:%M:%S'), @@ -844,8 +849,9 @@ % (cls.__module__, cls.__name__), '-- Database: %s\n' % dbName, - cls.createTableSQL().strip(), - '\n']) + create.strip(), + '\n', + constraints]) last_version_dir = self.find_last_version() if last_version_dir and not self.options.force_create: if v > 1: |
|
From: <sub...@co...> - 2005-11-02 18:22:48
|
Author: ianb
Date: 2005-11-02 18:22:42 +0000 (Wed, 02 Nov 2005)
New Revision: 1188
Modified:
SQLObject/branches/0.7-bugfix/setup.cfg
SQLObject/branches/0.7-bugfix/setup.py
Log:
Use tagged versions, and mark the next version as 0.7.1
Modified: SQLObject/branches/0.7-bugfix/setup.cfg
===================================================================
--- SQLObject/branches/0.7-bugfix/setup.cfg 2005-11-02 16:55:06 UTC (rev 1187)
+++ SQLObject/branches/0.7-bugfix/setup.cfg 2005-11-02 18:22:42 UTC (rev 1188)
@@ -1,3 +1,7 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
+
[pudge]
title = SQLObject
dest = docs/html
Modified: SQLObject/branches/0.7-bugfix/setup.py
===================================================================
--- SQLObject/branches/0.7-bugfix/setup.py 2005-11-02 16:55:06 UTC (rev 1187)
+++ SQLObject/branches/0.7-bugfix/setup.py 2005-11-02 18:22:42 UTC (rev 1188)
@@ -18,7 +18,7 @@
DistributionMetadata.download_url = None
setup(name="SQLObject",
- version="0.7.0",
+ version="0.7.1",
description="Object-Relational Manager, aka database wrapper",
long_description="""\
SQLObject is a popular *Object Relational Manager* for providing an
|
|
From: <sub...@co...> - 2005-11-02 16:55:18
|
Author: test
Date: 2005-11-02 16:55:06 +0000 (Wed, 02 Nov 2005)
New Revision: 1187
Modified:
SQLObject/trunk/docs/SQLBuilder.txt
SQLObject/trunk/docs/index.txt
Log:
Fixed a couple typos
Modified: SQLObject/trunk/docs/SQLBuilder.txt
===================================================================
--- SQLObject/trunk/docs/SQLBuilder.txt 2005-11-02 10:46:14 UTC (rev 1186)
+++ SQLObject/trunk/docs/SQLBuilder.txt 2005-11-02 16:55:06 UTC (rev 1187)
@@ -34,7 +34,7 @@
>>> person.first_name != name
person.first_name <> 'John'
>>> AND(person.first_name == 'John', person.last_name == 'Doe')
- (person.first_name = 'John AND person.last_name = 'Doe')
+ (person.first_name = 'John' AND person.last_name = 'Doe')
Most of the operators work properly: <, >, <=, >=, !=, ==, +, -, /,
\*, \*\*, %. However, ``and``, ``or``, and ``not`` **do not work**.
Modified: SQLObject/trunk/docs/index.txt
===================================================================
--- SQLObject/trunk/docs/index.txt 2005-11-02 10:46:14 UTC (rev 1186)
+++ SQLObject/trunk/docs/index.txt 2005-11-02 16:55:06 UTC (rev 1187)
@@ -19,7 +19,7 @@
* `News and updates <News.html>`_
* `Main SQLObject documentation <SQLObject.html>`_
-* `sqlbuiler documentation <SQLBuilder.html>`_
+* `sqlbuilder documentation <SQLBuilder.html>`_
* `sqlobject-admin documentation <sqlobject-admin.html>`_
* `Frequently Asked Questions <FAQ.html>`_
* `Inheritance <Inheritance.html>`_
|
|
From: SourceForge.net <no...@so...> - 2005-11-02 16:03:51
|
Bugs item #1346103, was opened at 2005-11-02 13:03 Message generated for change (Tracker Item Submitted) made by Item Submitter You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1346103&group_id=74338 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Postgres Group: None Status: Open Resolution: None Priority: 5 Submitted By: Felipe Hoffa (fhoffa) Assigned to: Nobody/Anonymous (nobody) Summary: working with schemas Initial Comment: We use heavily schemas within a database (using PostgreSQL), and SQLObject has trouble identifying the primary with a table that lives in a schema, or a table in the default schema that has the same name than a table in other schema. In pgconnection.py I patched the following code and everything worked afterwards, but now I have to specify the schema name even for the tables in the default one: primaryKeyQuery = """ SELECT pg_index.indisprimary, pg_catalog.pg_get_indexdef(pg_index.indexrelid) FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index AS pg_index WHERE c.relname = %s AND c.oid = pg_index.indrelid AND pg_index.indexrelid = c2.oid AND pg_index.indisprimary AND pg_namespace.oid=c.relnamespace AND %s=pg_namespace.nspname """ keyData = self.queryAll(keyQuery % self.sqlrepr(tableName)) keyRE = re.compile(r"\((.+)\) REFERENCES (.+)\(") keymap = {} for (condef,) in keyData: match = keyRE.search(condef) if match: field, reftable = match.groups() keymap[field] = reftable.capitalize() sqlrepr = self.sqlrepr(tableName) table = "'"+sqlrepr[sqlrepr.find('.')+1:] schema = sqlrepr[0:sqlrepr.find('.')]+"'" primaryData = self.queryAll(primaryKeyQuery % (table, schema)) The code above parses the schema and the table name from a sqlrepr as "public.user" and uses the schema for a new where clause in the select query ("%s=pg_namespace.nspname") joining with the internal table that maps oids with schema names ("pg_namespace.oid=c.relnamespace"). I don't have a lot of experience with Python yet, otherwise I would have sumbitted a patch. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1346103&group_id=74338 |
|
From: SourceForge.net <no...@so...> - 2005-11-02 15:17:27
|
Bugs item #1346066, was opened at 2005-11-02 12:17 Message generated for change (Tracker Item Submitted) made by Item Submitter You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1346066&group_id=74338 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Postgres Group: None Status: Open Resolution: None Priority: 5 Submitted By: Felipe Hoffa (fhoffa) Assigned to: Nobody/Anonymous (nobody) Summary: working with schemas Initial Comment: We use heavily schemas within a database (using PostgreSQL), and SQLObject has trouble identifying the primary with a table that lives in a schema, or a table in the default schema that has the same name than a table in other schema. In pgconnection.py I patched the following code and everything worked afterwards, but now I have to specify the schema name even for the tables in the default one: primaryKeyQuery = """ SELECT pg_index.indisprimary, pg_catalog.pg_get_indexdef(pg_index.indexrelid) FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index AS pg_index WHERE c.relname = %s AND c.oid = pg_index.indrelid AND pg_index.indexrelid = c2.oid AND pg_index.indisprimary AND pg_namespace.oid=c.relnamespace AND %s=pg_namespace.nspname """ keyData = self.queryAll(keyQuery % self.sqlrepr(tableName)) keyRE = re.compile(r"\((.+)\) REFERENCES (.+)\(") keymap = {} for (condef,) in keyData: match = keyRE.search(condef) if match: field, reftable = match.groups() keymap[field] = reftable.capitalize() sqlrepr = self.sqlrepr(tableName) table = "'"+sqlrepr[sqlrepr.find('.')+1:] schema = sqlrepr[0:sqlrepr.find('.')]+"'" primaryData = self.queryAll(primaryKeyQuery % (table, schema)) The code above parses the schema and the table name from a sqlrepr as "public.user" and uses the schema for a new where clause in the select query ("%s=pg_namespace.nspname") joining with the internal table that maps oids with schema names ("pg_namespace.oid=c.relnamespace"). I don't have a lot of experience with Python yet, otherwise I would have sumbitted a patch. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1346066&group_id=74338 |
|
From: <sub...@co...> - 2005-11-02 10:51:51
|
Author: phd
Date: 2005-11-02 10:46:14 +0000 (Wed, 02 Nov 2005)
New Revision: 1186
Added:
home/phd/SQLObject/paramstyles/sqlobject/include/pydispatch/
home/phd/SQLObject/paramstyles/sqlobject/tests/test_new_joins.py
Modified:
home/phd/SQLObject/paramstyles/sqlobject/declarative.py
home/phd/SQLObject/paramstyles/sqlobject/events.py
home/phd/SQLObject/paramstyles/sqlobject/joins.py
home/phd/SQLObject/paramstyles/sqlobject/main.py
home/phd/SQLObject/paramstyles/sqlobject/manager/command.py
Log:
Merged patches from the revisions 1181:1185 from the trunk: new joins
Modified: home/phd/SQLObject/paramstyles/sqlobject/declarative.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/declarative.py 2005-11-02 04:46:27 UTC (rev 1185)
+++ home/phd/SQLObject/paramstyles/sqlobject/declarative.py 2005-11-02 10:46:14 UTC (rev 1186)
@@ -33,6 +33,7 @@
"""
from __future__ import generators
+import events
__all__ = ('classinstancemethod', 'DeclarativeMeta', 'Declarative')
@@ -88,10 +89,16 @@
class DeclarativeMeta(type):
def __new__(meta, class_name, bases, new_attrs):
+ post_funcs = []
+ events.send(events.ClassCreateSignal,
+ bases[0], class_name, bases, new_attrs,
+ post_funcs)
cls = type.__new__(meta, class_name, bases, new_attrs)
if new_attrs.has_key('__classinit__'):
cls.__classinit__ = staticmethod(cls.__classinit__.im_func)
cls.__classinit__(cls, new_attrs)
+ for func in post_funcs:
+ func(cls)
return cls
class Declarative(object):
@@ -190,3 +197,8 @@
__repr__ = classinstancemethod(__repr__)
+def setup_attributes(cls, new_attrs):
+ for name, value in new_attrs.items():
+ if hasattr(value, '__addtoclass__'):
+ value.__addtoclass__(cls, name)
+
Modified: home/phd/SQLObject/paramstyles/sqlobject/events.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/events.py 2005-11-02 04:46:27 UTC (rev 1185)
+++ home/phd/SQLObject/paramstyles/sqlobject/events.py 2005-11-02 10:46:14 UTC (rev 1186)
@@ -1,15 +1,24 @@
-from dispatch import dispatcher
+from sqlobject.include.pydispatch import dispatcher
from weakref import ref
subclassClones = {}
def listen(receiver, soClass, signal, alsoSubclasses=True):
+ """
+ Listen for the given ``signal`` on the SQLObject subclass
+ ``soClass``, calling ``receiver()`` when ``send(soClass, signal,
+ ...)`` is called.
+
+ If ``alsoSubclasses`` is true, receiver will also be called when
+ an event is fired on any subclass.
+ """
dispatcher.connect(receiver, signal=signal, sender=soClass)
weakSOClass = ref(soClass)
weakReceiver = ref(receiver)
subclassClones.setdefault(weakSOClass, []).append((weakReceiver, signal))
+# We export this function:
send = dispatcher.send
class Signal(object):
Copied: home/phd/SQLObject/paramstyles/sqlobject/include/pydispatch (from rev 1185, SQLObject/trunk/sqlobject/include/pydispatch)
Modified: home/phd/SQLObject/paramstyles/sqlobject/joins.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/joins.py 2005-11-02 04:46:27 UTC (rev 1185)
+++ home/phd/SQLObject/paramstyles/sqlobject/joins.py 2005-11-02 10:46:14 UTC (rev 1186)
@@ -3,8 +3,10 @@
import styles
import classregistry
from col import popKey
+import events
-__all__ = ['MultipleJoin', 'SQLMultipleJoin', 'RelatedJoin', 'SQLRelatedJoin', 'SingleJoin']
+__all__ = ['MultipleJoin', 'SQLMultipleJoin', 'RelatedJoin', 'SQLRelatedJoin',
+ 'SingleJoin', 'ManyToMany', 'OneToMany']
def getID(obj):
try:
@@ -263,3 +265,209 @@
class SingleJoin(Join):
baseClass = SOSingleJoin
+
+
+
+
+class ManyToMany(object):
+
+ def __init__(self, otherClassName,
+ intermediateTable=None,
+ joinColumn=None,
+ otherColumn=None,
+ createJoinTable=True):
+ self.otherClassName = otherClassName
+ self.intermediateTable = intermediateTable
+ self.joinColumn = joinColumn
+ self.otherColumn = otherColumn
+ self.createJoinTable = createJoinTable
+
+ def __addtoclass__(self, soClass, name):
+ setattr(soClass, name,
+ SOManyToMany(soClass, name=name,
+ otherClassName=self.otherClassName,
+ intermediateTable=self.intermediateTable,
+ joinColumn=self.joinColumn,
+ otherColumn=self.otherColumn,
+ createJoinTable=self.createJoinTable))
+
+class SOManyToMany(object):
+
+ def __init__(self, soClass, name, otherClassName,
+ intermediateTable, joinColumn, otherColumn,
+ createJoinTable):
+ self.name = name
+ self.intermediateTable = intermediateTable
+ self.joinColumn = joinColumn
+ self.otherColumn = otherColumn
+ self.createJoinTable = createJoinTable
+ self.soClass = self.otherClass = None
+ classregistry.registry(
+ soClass.sqlmeta.registry).addClassCallback(
+ otherClassName, self._setOtherClass)
+ classregistry.registry(
+ soClass.sqlmeta.registry).addClassCallback(
+ soClass.__name__, self._setThisClass)
+
+ def _setThisClass(self, soClass):
+ self.soClass = soClass
+ if self.soClass and self.otherClass:
+ self._finishSet()
+
+ def _setOtherClass(self, otherClass):
+ self.otherClass = otherClass
+ if self.soClass and self.otherClass:
+ self._finishSet()
+
+ def _finishSet(self):
+ if self.intermediateTable is None:
+ names = [self.soClass.sqlmeta.table,
+ self.otherClass.sqlmeta.table]
+ names.sort()
+ self.intermediateTable = '%s_%s' % (names[0], names[1])
+ if not self.otherColumn:
+ self.otherColumn = self.soClass.sqlmeta.style.tableReference(
+ self.otherClass.sqlmeta.table)
+ if not self.joinColumn:
+ self.joinColumn = styles.getStyle(
+ self.soClass).tableReference(self.soClass.sqlmeta.table)
+ events.listen(self.event_CreateTableSignal,
+ self.soClass, events.CreateTableSignal)
+ events.listen(self.event_CreateTableSignal,
+ self.otherClass, events.CreateTableSignal)
+
+ def __get__(self, obj, type):
+ if obj is None:
+ return self
+ query = (
+ (self.otherClass.q.id ==
+ sqlbuilder.Field(self.intermediateTable, self.otherColumn))
+ & (sqlbuilder.Field(self.intermediateTable, self.joinColumn)
+ == obj.id))
+ select = self.otherClass.select(query)
+ return _ManyToManySelectWrapper(obj, self, select)
+
+ def __sqlrepr__(self, dbname):
+ return self.query.__sqlrepr__(self, dbname)
+
+ def event_CreateTableSignal(self, soClass, connection, extra_sql,
+ post_funcs):
+ if self.createJoinTable:
+ post_funcs.append(self.event_CreateTableSignalPost)
+
+ def event_CreateTableSignalPost(self, soClass, connection):
+ if connection.tableExists(self.intermediateTable):
+ return
+ connection._SO_createJoinTable(self)
+
+class _ManyToManySelectWrapper(object):
+
+ def __init__(self, forObject, join, select):
+ self.forObject = forObject
+ self.join = join
+ self.select = select
+
+ def __getattr__(self, attr):
+ # @@: This passes through private variable access too... should it?
+ # Also magic methods, like __str__
+ return getattr(self, select, attr)
+
+ def __repr__(self):
+ return '<%s for: %s>' % (self.__class__.__name__, repr(self.select))
+
+ def __str__(self):
+ return str(self.select)
+
+ def __iter__(self):
+ return iter(self.select)
+
+ def __getitem__(self, key):
+ return self.select[key]
+
+ def add(self, obj):
+ print "Add", obj, "to", self.forObject
+ obj._connection._SO_intermediateInsert(
+ self.join.intermediateTable,
+ self.join.joinColumn,
+ getID(self.forObject),
+ self.join.otherColumn,
+ getID(obj))
+
+ def remove(self, obj):
+ obj._connection._SO_intermediateDelete(
+ self.join.intermediateTable,
+ self.join.joinColumn,
+ getID(self.forObject),
+ self.join.otherColumn,
+ getID(obj))
+
+ def create(self, **kw):
+ obj = self.join.otherClass(**kw)
+ self.add(obj)
+ return obj
+
+class OneToMany(object):
+
+ def __init__(self, otherClassName, joinColumn=None):
+ self.otherClassName = otherClassName
+ self.joinColumn = joinColumn
+
+ def __addtoclass__(self, soClass, name):
+ setattr(soClass, name,
+ SOOneToMany(soClass, name=name,
+ otherClassName=self.otherClassName,
+ joinColumn=self.joinColumn))
+
+class SOOneToMany(object):
+
+ def __init__(self, soClass, name, otherClassName, joinColumn):
+ self.soClass = soClass
+ self.name = name
+ self.joinColumn = joinColumn
+ classregistry.registry(
+ soClass.sqlmeta.registry).addClassCallback(
+ otherClassName, self._setOtherClass)
+
+ def _setOtherClass(self, otherClass):
+ self.otherClass = otherClass
+ if not self.joinColumn:
+ self.joinColumn = styles.getStyle(
+ self.soClass).tableReference(self.soClass.sqlmeta.table)
+
+ def __get__(self, obj, type):
+ if obj is None:
+ return self
+ query = (
+ sqlbuilder.Field(self.otherClass.sqlmeta.table, self.joinColumn)
+ == obj.id)
+ select = self.otherClass.select(query)
+ return _OneToManySelectWrapper(obj, self, select)
+
+class _OneToManySelectWrapper(object):
+
+ def __init__(self, forObject, join, select):
+ self.forObject = forObject
+ self.join = join
+ self.select = select
+
+ def __getattr__(self, attr):
+ # @@: This passes through private variable access too... should it?
+ # Also magic methods, like __str__
+ return getattr(self, select, attr)
+
+ def __repr__(self):
+ return '<%s for: %s>' % (self.__class__.__name__, repr(self.select))
+
+ def __str__(self):
+ return str(self.select)
+
+ def __iter__(self):
+ return iter(self.select)
+
+ def __getitem__(self, key):
+ return self.select[key]
+
+ def create(self, **kw):
+ kw[self.join.joinColumn] = self.forObject.id
+ return self.join.otherClass(**kw)
+
Modified: home/phd/SQLObject/paramstyles/sqlobject/main.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/main.py 2005-11-02 04:46:27 UTC (rev 1185)
+++ home/phd/SQLObject/paramstyles/sqlobject/main.py 2005-11-02 10:46:14 UTC (rev 1186)
@@ -45,6 +45,7 @@
import index
import classregistry
import declarative
+import events
from sresults import SelectResults
from formencode import schema, compound
@@ -236,10 +237,16 @@
for attr in cls._unshared_attributes:
if not new_attrs.has_key(attr):
setattr(cls, attr, None)
+ declarative.setup_attributes(cls, new_attrs)
def __init__(self, instance):
self.instance = instance
+ def send(cls, signal, *args, **kw):
+ events.send(signal, cls.soClass, *args, **kw)
+
+ send = classmethod(send)
+
def setClass(cls, soClass):
cls.soClass = soClass
if not cls.style:
@@ -289,6 +296,9 @@
########################################
def addColumn(cls, columnDef, changeSchema=False, connection=None):
+ post_funcs = []
+ cls.send(events.AddColumnSignal, cls.soClass, connection,
+ columnDef.name, columnDef, changeSchema, post_funcs)
sqlmeta = cls
soClass = cls.soClass
del cls
@@ -396,9 +406,6 @@
setattr(soClass, setterName(name)[:-2], setter)
sqlmeta._plainForeignSetters[name[:-2]] = 1
- # We'll need to put in a real reference at
- # some point. See needSet at the top of the
- # file for more on this.
classregistry.registry(sqlmeta.registry).addClassCallback(
column.foreignKey,
lambda foreign, me, attr: setattr(me, attr, foreign),
@@ -415,6 +422,9 @@
if soClass._SO_finishedClassCreation:
makeProperties(soClass)
+ for func in post_funcs:
+ func(soClass, column)
+
addColumn = classmethod(addColumn)
def addColumnsFromDatabase(sqlmeta, connection=None):
@@ -438,6 +448,9 @@
else:
raise IndexError(
"Column with definition %r not found" % column)
+ post_funcs = []
+ cls.send(events.DeleteColumnSignal, connection, column.name, column,
+ post_funcs)
name = column.name
del sqlmeta.columns[name]
del sqlmeta.columnDefinitions[name]
@@ -465,6 +478,9 @@
if soClass._SO_finishedClassCreation:
unmakeProperties(soClass)
+ for func in post_funcs:
+ func(soClass, column)
+
delColumn = classmethod(delColumn)
########################################
@@ -666,6 +682,8 @@
def __classinit__(cls, new_attrs):
+ declarative.setup_attributes(cls, new_attrs)
+
# This is true if we're initializing the SQLObject class,
# instead of a subclass:
is_base = cls.__bases__ == (object,)
@@ -1039,6 +1057,11 @@
# in the database, and we can't insert it until all
# the parts are set. So we just keep them in a
# dictionary until later:
+ d = {name: value}
+ self.sqlmeta.send(events.RowUpdateSignal, self, d)
+ if len(d) != 1 or name not in d:
+ return self.set(**d)
+ value = d[name]
if from_python:
dbValue = from_python(value, self._SO_validatorState)
else:
@@ -1059,6 +1082,7 @@
setattr(self, instanceName(name), value)
def set(self, **kw):
+ self.sqlmeta.send(events.RowUpdateSignal, self, kw)
# set() is used to update multiple values at once,
# potentially with one SQL statement if possible.
@@ -1179,11 +1203,15 @@
if kw.has_key('_SO_fetch_no_create'):
return
+ post_funcs = []
+ self.sqlmeta.send(events.RowCreateSignal, kw, post_funcs)
+
# Pass the connection object along if we were given one.
if kw.has_key('connection'):
self._connection = kw['connection']
self.sqlmeta._perConnection = True
del kw['connection']
+
self._SO_writeLock = threading.Lock()
if kw.has_key('id'):
@@ -1193,6 +1221,8 @@
id = None
self._create(id, **kw)
+ for func in post_funcs:
+ func(self)
def _create(self, id, **kw):
@@ -1306,9 +1336,17 @@
conn = connection or cls._connection
if ifExists and not conn.tableExists(cls.sqlmeta.table):
return
+ extra_sql = []
+ post_funcs = []
+ cls.sqlmeta.send(events.DropTableSignal, cls, connection,
+ extra_sql, post_funcs)
conn.dropTable(cls.sqlmeta.table, cascade)
if dropJoinTables:
cls.dropJoinTables(ifExists=ifExists, connection=conn)
+ for sql in extra_sql:
+ connection.query(sql)
+ for func in post_funcs:
+ func(cls, conn)
dropTable = classmethod(dropTable)
def createTable(cls, ifNotExists=False, createJoinTables=True,
@@ -1317,14 +1355,21 @@
conn = connection or cls._connection
if ifNotExists and conn.tableExists(cls.sqlmeta.table):
return
+ extra_sql = []
+ post_funcs = []
+ cls.sqlmeta.send(events.CreateTableSignal, cls, connection,
+ extra_sql, post_funcs)
constraints = conn.createTable(cls)
+ extra_sql.extend(constraints)
if createJoinTables:
cls.createJoinTables(ifNotExists=ifNotExists,
connection=conn)
if createIndexes:
cls.createIndexes(ifNotExists=ifNotExists,
connection=conn)
- return constraints
+ for func in post_funcs:
+ func(cls, conn)
+ return extra_sql
createTable = classmethod(createTable)
def createTableSQL(cls, createJoinTables=True, connection=None,
@@ -1419,6 +1464,7 @@
clearTable = classmethod(clearTable)
def destroySelf(self):
+ self.sqlmeta.send(events.RowDestroySignal, self)
# Kills this object. Kills it dead!
depends = []
klass = self.__class__
Modified: home/phd/SQLObject/paramstyles/sqlobject/manager/command.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/manager/command.py 2005-11-02 04:46:27 UTC (rev 1185)
+++ home/phd/SQLObject/paramstyles/sqlobject/manager/command.py 2005-11-02 10:46:14 UTC (rev 1186)
@@ -422,8 +422,7 @@
def config_from_egg(self, egg_spec, warn_no_sqlobject=True):
import pkg_resources
- pkg_resources.require(egg_spec)
- dist = pkg_resources.working_set.find(pkg_resources.Requirement(egg_spec))
+ dist = pkg_resources.get_distribution(egg_spec)
if not dist.has_metadata('sqlobject.txt'):
if warn_no_sqlobject:
print 'No sqlobject.txt in %s egg info' % egg_spec
Copied: home/phd/SQLObject/paramstyles/sqlobject/tests/test_new_joins.py (from rev 1185, SQLObject/trunk/sqlobject/tests/test_new_joins.py)
|
|
From: SourceForge.net <no...@so...> - 2005-11-02 05:01:30
|
Bugs item #1345587, was opened at 2005-11-02 01:00 Message generated for change (Settings changed) made by elpargo You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1345587&group_id=74338 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: General Group: SQLObject from repository Status: Open Resolution: None Priority: 5 Submitted By: elpargo (elpargo) Assigned to: Nobody/Anonymous (nobody) >Summary: Documentation says SQLBuilder went it should be sqlbuilder Initial Comment: pretty much Summary, where to find it ? http://sqlobject.org/SQLObject.html#col-class-specifying-columns under default: it says SQLBuilder.func.NOW() it should be sqlbuilder.func.NOW() ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1345587&group_id=74338 |
|
From: SourceForge.net <no...@so...> - 2005-11-02 05:01:00
|
Bugs item #1345587, was opened at 2005-11-02 01:00 Message generated for change (Tracker Item Submitted) made by Item Submitter You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1345587&group_id=74338 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: General Group: SQLObject from repository Status: Open Resolution: None Priority: 5 Submitted By: elpargo (elpargo) Assigned to: Nobody/Anonymous (nobody) Summary: Documentaation says SQLBuilder went it should be sqlbuilder Initial Comment: pretty much Summary, where to find it ? http://sqlobject.org/SQLObject.html#col-class-specifying-columns under default: it says SQLBuilder.func.NOW() it should be sqlbuilder.func.NOW() ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1345587&group_id=74338 |
|
From: <sub...@co...> - 2005-11-02 04:46:33
|
Author: ianb
Date: 2005-11-02 04:46:27 +0000 (Wed, 02 Nov 2005)
New Revision: 1185
Added:
SQLObject/trunk/sqlobject/tests/test_new_joins.py
Modified:
SQLObject/trunk/sqlobject/declarative.py
SQLObject/trunk/sqlobject/joins.py
SQLObject/trunk/sqlobject/main.py
Log:
New joins! ManyToMany and OneToMany; not fully documented yet, but still more sensible and smarter. Also (in main.py) call all the events promised in events.py.
Modified: SQLObject/trunk/sqlobject/declarative.py
===================================================================
--- SQLObject/trunk/sqlobject/declarative.py 2005-11-02 04:45:26 UTC (rev 1184)
+++ SQLObject/trunk/sqlobject/declarative.py 2005-11-02 04:46:27 UTC (rev 1185)
@@ -33,6 +33,7 @@
"""
from __future__ import generators
+import events
__all__ = ('classinstancemethod', 'DeclarativeMeta', 'Declarative')
@@ -88,10 +89,16 @@
class DeclarativeMeta(type):
def __new__(meta, class_name, bases, new_attrs):
+ post_funcs = []
+ events.send(events.ClassCreateSignal,
+ bases[0], class_name, bases, new_attrs,
+ post_funcs)
cls = type.__new__(meta, class_name, bases, new_attrs)
if new_attrs.has_key('__classinit__'):
cls.__classinit__ = staticmethod(cls.__classinit__.im_func)
cls.__classinit__(cls, new_attrs)
+ for func in post_funcs:
+ func(cls)
return cls
class Declarative(object):
@@ -190,3 +197,8 @@
__repr__ = classinstancemethod(__repr__)
+def setup_attributes(cls, new_attrs):
+ for name, value in new_attrs.items():
+ if hasattr(value, '__addtoclass__'):
+ value.__addtoclass__(cls, name)
+
Modified: SQLObject/trunk/sqlobject/joins.py
===================================================================
--- SQLObject/trunk/sqlobject/joins.py 2005-11-02 04:45:26 UTC (rev 1184)
+++ SQLObject/trunk/sqlobject/joins.py 2005-11-02 04:46:27 UTC (rev 1185)
@@ -3,8 +3,10 @@
import styles
import classregistry
from col import popKey
+import events
-__all__ = ['MultipleJoin', 'SQLMultipleJoin', 'RelatedJoin', 'SQLRelatedJoin', 'SingleJoin']
+__all__ = ['MultipleJoin', 'SQLMultipleJoin', 'RelatedJoin', 'SQLRelatedJoin',
+ 'SingleJoin', 'ManyToMany', 'OneToMany']
def getID(obj):
try:
@@ -263,3 +265,209 @@
class SingleJoin(Join):
baseClass = SOSingleJoin
+
+
+
+
+class ManyToMany(object):
+
+ def __init__(self, otherClassName,
+ intermediateTable=None,
+ joinColumn=None,
+ otherColumn=None,
+ createJoinTable=True):
+ self.otherClassName = otherClassName
+ self.intermediateTable = intermediateTable
+ self.joinColumn = joinColumn
+ self.otherColumn = otherColumn
+ self.createJoinTable = createJoinTable
+
+ def __addtoclass__(self, soClass, name):
+ setattr(soClass, name,
+ SOManyToMany(soClass, name=name,
+ otherClassName=self.otherClassName,
+ intermediateTable=self.intermediateTable,
+ joinColumn=self.joinColumn,
+ otherColumn=self.otherColumn,
+ createJoinTable=self.createJoinTable))
+
+class SOManyToMany(object):
+
+ def __init__(self, soClass, name, otherClassName,
+ intermediateTable, joinColumn, otherColumn,
+ createJoinTable):
+ self.name = name
+ self.intermediateTable = intermediateTable
+ self.joinColumn = joinColumn
+ self.otherColumn = otherColumn
+ self.createJoinTable = createJoinTable
+ self.soClass = self.otherClass = None
+ classregistry.registry(
+ soClass.sqlmeta.registry).addClassCallback(
+ otherClassName, self._setOtherClass)
+ classregistry.registry(
+ soClass.sqlmeta.registry).addClassCallback(
+ soClass.__name__, self._setThisClass)
+
+ def _setThisClass(self, soClass):
+ self.soClass = soClass
+ if self.soClass and self.otherClass:
+ self._finishSet()
+
+ def _setOtherClass(self, otherClass):
+ self.otherClass = otherClass
+ if self.soClass and self.otherClass:
+ self._finishSet()
+
+ def _finishSet(self):
+ if self.intermediateTable is None:
+ names = [self.soClass.sqlmeta.table,
+ self.otherClass.sqlmeta.table]
+ names.sort()
+ self.intermediateTable = '%s_%s' % (names[0], names[1])
+ if not self.otherColumn:
+ self.otherColumn = self.soClass.sqlmeta.style.tableReference(
+ self.otherClass.sqlmeta.table)
+ if not self.joinColumn:
+ self.joinColumn = styles.getStyle(
+ self.soClass).tableReference(self.soClass.sqlmeta.table)
+ events.listen(self.event_CreateTableSignal,
+ self.soClass, events.CreateTableSignal)
+ events.listen(self.event_CreateTableSignal,
+ self.otherClass, events.CreateTableSignal)
+
+ def __get__(self, obj, type):
+ if obj is None:
+ return self
+ query = (
+ (self.otherClass.q.id ==
+ sqlbuilder.Field(self.intermediateTable, self.otherColumn))
+ & (sqlbuilder.Field(self.intermediateTable, self.joinColumn)
+ == obj.id))
+ select = self.otherClass.select(query)
+ return _ManyToManySelectWrapper(obj, self, select)
+
+ def __sqlrepr__(self, dbname):
+ return self.query.__sqlrepr__(self, dbname)
+
+ def event_CreateTableSignal(self, soClass, connection, extra_sql,
+ post_funcs):
+ if self.createJoinTable:
+ post_funcs.append(self.event_CreateTableSignalPost)
+
+ def event_CreateTableSignalPost(self, soClass, connection):
+ if connection.tableExists(self.intermediateTable):
+ return
+ connection._SO_createJoinTable(self)
+
+class _ManyToManySelectWrapper(object):
+
+ def __init__(self, forObject, join, select):
+ self.forObject = forObject
+ self.join = join
+ self.select = select
+
+ def __getattr__(self, attr):
+ # @@: This passes through private variable access too... should it?
+ # Also magic methods, like __str__
+ return getattr(self, select, attr)
+
+ def __repr__(self):
+ return '<%s for: %s>' % (self.__class__.__name__, repr(self.select))
+
+ def __str__(self):
+ return str(self.select)
+
+ def __iter__(self):
+ return iter(self.select)
+
+ def __getitem__(self, key):
+ return self.select[key]
+
+ def add(self, obj):
+ print "Add", obj, "to", self.forObject
+ obj._connection._SO_intermediateInsert(
+ self.join.intermediateTable,
+ self.join.joinColumn,
+ getID(self.forObject),
+ self.join.otherColumn,
+ getID(obj))
+
+ def remove(self, obj):
+ obj._connection._SO_intermediateDelete(
+ self.join.intermediateTable,
+ self.join.joinColumn,
+ getID(self.forObject),
+ self.join.otherColumn,
+ getID(obj))
+
+ def create(self, **kw):
+ obj = self.join.otherClass(**kw)
+ self.add(obj)
+ return obj
+
+class OneToMany(object):
+
+ def __init__(self, otherClassName, joinColumn=None):
+ self.otherClassName = otherClassName
+ self.joinColumn = joinColumn
+
+ def __addtoclass__(self, soClass, name):
+ setattr(soClass, name,
+ SOOneToMany(soClass, name=name,
+ otherClassName=self.otherClassName,
+ joinColumn=self.joinColumn))
+
+class SOOneToMany(object):
+
+ def __init__(self, soClass, name, otherClassName, joinColumn):
+ self.soClass = soClass
+ self.name = name
+ self.joinColumn = joinColumn
+ classregistry.registry(
+ soClass.sqlmeta.registry).addClassCallback(
+ otherClassName, self._setOtherClass)
+
+ def _setOtherClass(self, otherClass):
+ self.otherClass = otherClass
+ if not self.joinColumn:
+ self.joinColumn = styles.getStyle(
+ self.soClass).tableReference(self.soClass.sqlmeta.table)
+
+ def __get__(self, obj, type):
+ if obj is None:
+ return self
+ query = (
+ sqlbuilder.Field(self.otherClass.sqlmeta.table, self.joinColumn)
+ == obj.id)
+ select = self.otherClass.select(query)
+ return _OneToManySelectWrapper(obj, self, select)
+
+class _OneToManySelectWrapper(object):
+
+ def __init__(self, forObject, join, select):
+ self.forObject = forObject
+ self.join = join
+ self.select = select
+
+ def __getattr__(self, attr):
+ # @@: This passes through private variable access too... should it?
+ # Also magic methods, like __str__
+ return getattr(self, select, attr)
+
+ def __repr__(self):
+ return '<%s for: %s>' % (self.__class__.__name__, repr(self.select))
+
+ def __str__(self):
+ return str(self.select)
+
+ def __iter__(self):
+ return iter(self.select)
+
+ def __getitem__(self, key):
+ return self.select[key]
+
+ def create(self, **kw):
+ kw[self.join.joinColumn] = self.forObject.id
+ return self.join.otherClass(**kw)
+
Modified: SQLObject/trunk/sqlobject/main.py
===================================================================
--- SQLObject/trunk/sqlobject/main.py 2005-11-02 04:45:26 UTC (rev 1184)
+++ SQLObject/trunk/sqlobject/main.py 2005-11-02 04:46:27 UTC (rev 1185)
@@ -45,6 +45,7 @@
import index
import classregistry
import declarative
+import events
from sresults import SelectResults
from formencode import schema, compound
@@ -236,10 +237,16 @@
for attr in cls._unshared_attributes:
if not new_attrs.has_key(attr):
setattr(cls, attr, None)
+ declarative.setup_attributes(cls, new_attrs)
def __init__(self, instance):
self.instance = instance
+ def send(cls, signal, *args, **kw):
+ events.send(signal, cls.soClass, *args, **kw)
+
+ send = classmethod(send)
+
def setClass(cls, soClass):
cls.soClass = soClass
if not cls.style:
@@ -289,6 +296,9 @@
########################################
def addColumn(cls, columnDef, changeSchema=False, connection=None):
+ post_funcs = []
+ cls.send(events.AddColumnSignal, cls.soClass, connection,
+ columnDef.name, columnDef, changeSchema, post_funcs)
sqlmeta = cls
soClass = cls.soClass
del cls
@@ -396,9 +406,6 @@
setattr(soClass, setterName(name)[:-2], setter)
sqlmeta._plainForeignSetters[name[:-2]] = 1
- # We'll need to put in a real reference at
- # some point. See needSet at the top of the
- # file for more on this.
classregistry.registry(sqlmeta.registry).addClassCallback(
column.foreignKey,
lambda foreign, me, attr: setattr(me, attr, foreign),
@@ -415,6 +422,9 @@
if soClass._SO_finishedClassCreation:
makeProperties(soClass)
+ for func in post_funcs:
+ func(soClass, column)
+
addColumn = classmethod(addColumn)
def addColumnsFromDatabase(sqlmeta, connection=None):
@@ -438,6 +448,9 @@
else:
raise IndexError(
"Column with definition %r not found" % column)
+ post_funcs = []
+ cls.send(events.DeleteColumnSignal, connection, column.name, column,
+ post_funcs)
name = column.name
del sqlmeta.columns[name]
del sqlmeta.columnDefinitions[name]
@@ -465,6 +478,9 @@
if soClass._SO_finishedClassCreation:
unmakeProperties(soClass)
+ for func in post_funcs:
+ func(soClass, column)
+
delColumn = classmethod(delColumn)
########################################
@@ -666,6 +682,8 @@
def __classinit__(cls, new_attrs):
+ declarative.setup_attributes(cls, new_attrs)
+
# This is true if we're initializing the SQLObject class,
# instead of a subclass:
is_base = cls.__bases__ == (object,)
@@ -1039,6 +1057,11 @@
# in the database, and we can't insert it until all
# the parts are set. So we just keep them in a
# dictionary until later:
+ d = {name: value}
+ self.sqlmeta.send(events.RowUpdateSignal, self, d)
+ if len(d) != 1 or name not in d:
+ return self.set(**d)
+ value = d[name]
if from_python:
dbValue = from_python(value, self._SO_validatorState)
else:
@@ -1059,6 +1082,7 @@
setattr(self, instanceName(name), value)
def set(self, **kw):
+ self.sqlmeta.send(events.RowUpdateSignal, self, kw)
# set() is used to update multiple values at once,
# potentially with one SQL statement if possible.
@@ -1179,11 +1203,15 @@
if kw.has_key('_SO_fetch_no_create'):
return
+ post_funcs = []
+ self.sqlmeta.send(events.RowCreateSignal, kw, post_funcs)
+
# Pass the connection object along if we were given one.
if kw.has_key('connection'):
self._connection = kw['connection']
self.sqlmeta._perConnection = True
del kw['connection']
+
self._SO_writeLock = threading.Lock()
if kw.has_key('id'):
@@ -1193,6 +1221,8 @@
id = None
self._create(id, **kw)
+ for func in post_funcs:
+ func(self)
def _create(self, id, **kw):
@@ -1306,9 +1336,17 @@
conn = connection or cls._connection
if ifExists and not conn.tableExists(cls.sqlmeta.table):
return
+ extra_sql = []
+ post_funcs = []
+ cls.sqlmeta.send(events.DropTableSignal, cls, connection,
+ extra_sql, post_funcs)
conn.dropTable(cls.sqlmeta.table, cascade)
if dropJoinTables:
cls.dropJoinTables(ifExists=ifExists, connection=conn)
+ for sql in extra_sql:
+ connection.query(sql)
+ for func in post_funcs:
+ func(cls, conn)
dropTable = classmethod(dropTable)
def createTable(cls, ifNotExists=False, createJoinTables=True,
@@ -1317,14 +1355,21 @@
conn = connection or cls._connection
if ifNotExists and conn.tableExists(cls.sqlmeta.table):
return
+ extra_sql = []
+ post_funcs = []
+ cls.sqlmeta.send(events.CreateTableSignal, cls, connection,
+ extra_sql, post_funcs)
constraints = conn.createTable(cls)
+ extra_sql.extend(constraints)
if createJoinTables:
cls.createJoinTables(ifNotExists=ifNotExists,
connection=conn)
if createIndexes:
cls.createIndexes(ifNotExists=ifNotExists,
connection=conn)
- return constraints
+ for func in post_funcs:
+ func(cls, conn)
+ return extra_sql
createTable = classmethod(createTable)
def createTableSQL(cls, createJoinTables=True, connection=None,
@@ -1419,6 +1464,7 @@
clearTable = classmethod(clearTable)
def destroySelf(self):
+ self.sqlmeta.send(events.RowDestroySignal, self)
# Kills this object. Kills it dead!
depends = []
klass = self.__class__
Copied: SQLObject/trunk/sqlobject/tests/test_new_joins.py (from rev 1174, SQLObject/trunk/sqlobject/tests/test_joins.py)
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_joins.py 2005-10-28 16:29:54 UTC (rev 1174)
+++ SQLObject/trunk/sqlobject/tests/test_new_joins.py 2005-11-02 04:46:27 UTC (rev 1185)
@@ -0,0 +1,153 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Joins
+########################################
+
+class PersonJoinerNew(SQLObject):
+
+ name = StringCol(length=40, alternateID=True)
+ addressJoiners = ManyToMany('AddressJoinerNew')
+
+class AddressJoinerNew(SQLObject):
+
+ zip = StringCol(length=5, alternateID=True)
+ personJoiners = ManyToMany('PersonJoinerNew')
+
+class ImplicitJoiningSONew(SQLObject):
+ foo = ManyToMany('Bar')
+
+class ExplicitJoiningSONew(SQLObject):
+ foo = OneToMany('Bar')
+
+class TestJoin:
+
+ def setup_method(self, meth):
+ setupClass(PersonJoinerNew)
+ setupClass(AddressJoinerNew)
+ for n in ['bob', 'tim', 'jane', 'joe', 'fred', 'barb']:
+ PersonJoinerNew(name=n)
+ for z in ['11111', '22222', '33333', '44444']:
+ AddressJoinerNew(zip=z)
+
+ def test_join(self):
+ b = PersonJoinerNew.byName('bob')
+ assert list(b.addressJoiners) == []
+ z = AddressJoinerNew.byZip('11111')
+ b.addressJoiners.add(z)
+ self.assertZipsEqual(b.addressJoiners, ['11111'])
+ print str(z.personJoiners), repr(z.personJoiners)
+ self.assertNamesEqual(z.personJoiners, ['bob'])
+ z2 = AddressJoinerNew.byZip('22222')
+ b.addressJoiners.add(z2)
+ print str(b.addressJoiners)
+ self.assertZipsEqual(b.addressJoiners, ['11111', '22222'])
+ self.assertNamesEqual(z2.personJoiners, ['bob'])
+ b.addressJoiners.remove(z)
+ self.assertZipsEqual(b.addressJoiners, ['22222'])
+ self.assertNamesEqual(z.personJoiners, [])
+
+ def assertZipsEqual(self, zips, dest):
+ assert [a.zip for a in zips] == dest
+
+ def assertNamesEqual(self, people, dest):
+ assert [p.name for p in people] == dest
+
+ def test_joinAttributeWithUnderscores(self):
+ # Make sure that the implicit setting of joinMethodName works
+ assert hasattr(ImplicitJoiningSONew, 'foo')
+ assert not hasattr(ImplicitJoiningSONew, 'bars')
+
+ # And make sure explicit setting also works
+ assert hasattr(ExplicitJoiningSONew, 'foo')
+ assert not hasattr(ExplicitJoiningSONew, 'bars')
+
+
+class PersonJoinerNew2(SQLObject):
+
+ name = StringCol('name', length=40, alternateID=True)
+ addressJoiner2s = OneToMany('AddressJoinerNew2')
+
+class AddressJoinerNew2(SQLObject):
+
+ class sqlmeta:
+ defaultOrder = ['-zip', 'plus4']
+
+ zip = StringCol(length=5)
+ plus4 = StringCol(length=4, default=None)
+ personJoinerNew2 = ForeignKey('PersonJoinerNew2')
+
+class TestJoin2:
+
+ def setup_method(self, meth):
+ setupClass([PersonJoinerNew2, AddressJoinerNew2])
+ p1 = PersonJoinerNew2(name='bob')
+ p2 = PersonJoinerNew2(name='sally')
+ for z in ['11111', '22222', '33333']:
+ a = AddressJoinerNew2(zip=z, personJoinerNew2=p1)
+ #p1.addAddressJoinerNew2(a)
+ AddressJoinerNew2(zip='00000', personJoinerNew2=p2)
+
+ def test_basic(self):
+ bob = PersonJoinerNew2.byName('bob')
+ sally = PersonJoinerNew2.byName('sally')
+ print bob.addressJoiner2s
+ print bob
+ assert len(list(bob.addressJoiner2s)) == 3
+ assert len(list(sally.addressJoiner2s)) == 1
+ bob.addressJoiner2s[0].destroySelf()
+ assert len(list(bob.addressJoiner2s)) == 2
+ z = bob.addressJoiner2s[0]
+ z.zip = 'xxxxx'
+ id = z.id
+ del z
+ z = AddressJoinerNew2.get(id)
+ assert z.zip == 'xxxxx'
+
+ def test_defaultOrder(self):
+ p1 = PersonJoinerNew2.byName('bob')
+ assert ([i.zip for i in p1.addressJoiner2s]
+ == ['33333', '22222', '11111'])
+
+
+_personJoiner3_getters = []
+_personJoiner3_setters = []
+
+class PersonJoinerNew3(SQLObject):
+
+ name = StringCol('name', length=40, alternateID=True)
+ addressJoinerNew3s = OneToMany('AddressJoinerNew3')
+
+class AddressJoinerNew3(SQLObject):
+
+ zip = StringCol(length=5)
+ personJoinerNew3 = ForeignKey('PersonJoinerNew3')
+
+ def _get_personJoinerNew3(self):
+ value = self._SO_get_personJoinerNew3()
+ _personJoiner3_getters.append((self, value))
+ return value
+
+ def _set_personJoinerNew3(self, value):
+ self._SO_set_personJoinerNew3(value)
+ _personJoiner3_setters.append((self, value))
+
+class TestJoin3:
+
+ def setup_method(self, meth):
+ setupClass([PersonJoinerNew3, AddressJoinerNew3])
+ p1 = PersonJoinerNew3(name='bob')
+ p2 = PersonJoinerNew3(name='sally')
+ for z in ['11111', '22222', '33333']:
+ a = AddressJoinerNew3(zip=z, personJoinerNew3=p1)
+ AddressJoinerNew3(zip='00000', personJoinerNew3=p2)
+
+ def test_accessors(self):
+ assert len(list(_personJoiner3_getters)) == 0
+ assert len(list(_personJoiner3_setters)) == 4
+ bob = PersonJoinerNew3.byName('bob')
+ for addressJoiner3 in bob.addressJoinerNew3s:
+ addressJoiner3.personJoinerNew3
+ assert len(list(_personJoiner3_getters)) == 3
+ assert len(list(_personJoiner3_setters)) == 4
|
|
From: <sub...@co...> - 2005-11-02 04:45:34
|
Author: ianb
Date: 2005-11-02 04:45:26 +0000 (Wed, 02 Nov 2005)
New Revision: 1184
Added:
SQLObject/trunk/sqlobject/include/
SQLObject/trunk/sqlobject/include/__init__.py
SQLObject/trunk/sqlobject/include/pydispatch/
SQLObject/trunk/sqlobject/include/pydispatch/README.txt
SQLObject/trunk/sqlobject/include/pydispatch/__init__.py
SQLObject/trunk/sqlobject/include/pydispatch/dispatcher.py
SQLObject/trunk/sqlobject/include/pydispatch/errors.py
SQLObject/trunk/sqlobject/include/pydispatch/robust.py
SQLObject/trunk/sqlobject/include/pydispatch/robustapply.py
SQLObject/trunk/sqlobject/include/pydispatch/saferef.py
Modified:
SQLObject/trunk/sqlobject/events.py
Log:
Instead of using the normal PyDispatcher package for events, I'm including PyDispatcher in the package directly.
Modified: SQLObject/trunk/sqlobject/events.py
===================================================================
--- SQLObject/trunk/sqlobject/events.py 2005-11-01 22:27:27 UTC (rev 1183)
+++ SQLObject/trunk/sqlobject/events.py 2005-11-02 04:45:26 UTC (rev 1184)
@@ -1,15 +1,24 @@
-from dispatch import dispatcher
+from sqlobject.include.pydispatch import dispatcher
from weakref import ref
subclassClones = {}
def listen(receiver, soClass, signal, alsoSubclasses=True):
+ """
+ Listen for the given ``signal`` on the SQLObject subclass
+ ``soClass``, calling ``receiver()`` when ``send(soClass, signal,
+ ...)`` is called.
+
+ If ``alsoSubclasses`` is true, receiver will also be called when
+ an event is fired on any subclass.
+ """
dispatcher.connect(receiver, signal=signal, sender=soClass)
weakSOClass = ref(soClass)
weakReceiver = ref(receiver)
subclassClones.setdefault(weakSOClass, []).append((weakReceiver, signal))
+# We export this function:
send = dispatcher.send
class Signal(object):
Added: SQLObject/trunk/sqlobject/include/__init__.py
===================================================================
--- SQLObject/trunk/sqlobject/include/__init__.py 2005-11-01 22:27:27 UTC (rev 1183)
+++ SQLObject/trunk/sqlobject/include/__init__.py 2005-11-02 04:45:26 UTC (rev 1184)
@@ -0,0 +1 @@
+#
Added: SQLObject/trunk/sqlobject/include/pydispatch/README.txt
===================================================================
--- SQLObject/trunk/sqlobject/include/pydispatch/README.txt 2005-11-01 22:27:27 UTC (rev 1183)
+++ SQLObject/trunk/sqlobject/include/pydispatch/README.txt 2005-11-02 04:45:26 UTC (rev 1184)
@@ -0,0 +1,9 @@
+This is from PyDispatcher <http://pydispatcher.sf.net>
+
+It was moved here because installation of PyDispatcher conflicts with
+RuleDispatch (they both use the dispatch top-level module), and I
+thought it would be easier to just put it here. Also, PyDispatcher is
+small and stable and doesn't need updating often.
+
+If the name conflict is resolved in the future, this package can go
+away.
Added: SQLObject/trunk/sqlobject/include/pydispatch/__init__.py
===================================================================
--- SQLObject/trunk/sqlobject/include/pydispatch/__init__.py 2005-11-01 22:27:27 UTC (rev 1183)
+++ SQLObject/trunk/sqlobject/include/pydispatch/__init__.py 2005-11-02 04:45:26 UTC (rev 1184)
@@ -0,0 +1,6 @@
+"""Multi-consumer multi-producer dispatching mechanism
+"""
+__version__ = "1.0.0"
+__author__ = "Patrick K. O'Brien"
+__license__ = "BSD-style, see license.txt for details"
+
Added: SQLObject/trunk/sqlobject/include/pydispatch/dispatcher.py
===================================================================
--- SQLObject/trunk/sqlobject/include/pydispatch/dispatcher.py 2005-11-01 22:27:27 UTC (rev 1183)
+++ SQLObject/trunk/sqlobject/include/pydispatch/dispatcher.py 2005-11-02 04:45:26 UTC (rev 1184)
@@ -0,0 +1,497 @@
+"""Multiple-producer-multiple-consumer signal-dispatching
+
+dispatcher is the core of the PyDispatcher system,
+providing the primary API and the core logic for the
+system.
+
+Module attributes of note:
+
+ Any -- Singleton used to signal either "Any Sender" or
+ "Any Signal". See documentation of the _Any class.
+ Anonymous -- Singleton used to signal "Anonymous Sender"
+ See documentation of the _Anonymous class.
+
+Internal attributes:
+ WEAKREF_TYPES -- tuple of types/classes which represent
+ weak references to receivers, and thus must be de-
+ referenced on retrieval to retrieve the callable
+ object
+ connections -- { senderkey (id) : { signal : [receivers...]}}
+ senders -- { senderkey (id) : weakref(sender) }
+ used for cleaning up sender references on sender
+ deletion
+ sendersBack -- { receiverkey (id) : [senderkey (id)...] }
+ used for cleaning up receiver references on receiver
+ deletion, (considerably speeds up the cleanup process
+ vs. the original code.)
+"""
+from __future__ import generators
+import types, weakref
+import saferef, robustapply, errors
+
+__author__ = "Patrick K. O'Brien <po...@or...>"
+__cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $"
+__version__ = "$Revision: 1.9 $"[11:-2]
+
+try:
+ True
+except NameError:
+ True = 1==1
+ False = 1==0
+
+class _Parameter:
+ """Used to represent default parameter values."""
+ def __repr__(self):
+ return self.__class__.__name__
+
+class _Any(_Parameter):
+ """Singleton used to signal either "Any Sender" or "Any Signal"
+
+ The Any object can be used with connect, disconnect,
+ send, or sendExact to signal that the parameter given
+ Any should react to all senders/signals, not just
+ a particular sender/signal.
+ """
+Any = _Any()
+
+class _Anonymous(_Parameter):
+ """Singleton used to signal "Anonymous Sender"
+
+ The Anonymous object is used to signal that the sender
+ of a message is not specified (as distinct from being
+ "any sender"). Registering callbacks for Anonymous
+ will only receive messages sent without senders. Sending
+ with anonymous will only send messages to those receivers
+ registered for Any or Anonymous.
+
+ Note:
+ The default sender for connect is Any, while the
+ default sender for send is Anonymous. This has
+ the effect that if you do not specify any senders
+ in either function then all messages are routed
+ as though there was a single sender (Anonymous)
+ being used everywhere.
+ """
+Anonymous = _Anonymous()
+
+WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
+
+connections = {}
+senders = {}
+sendersBack = {}
+
+
+def connect(receiver, signal=Any, sender=Any, weak=True):
+ """Connect receiver to sender for signal
+
+ receiver -- a callable Python object which is to receive
+ messages/signals/events. Receivers must be hashable
+ objects.
+
+ if weak is True, then receiver must be weak-referencable
+ (more precisely saferef.safeRef() must be able to create
+ a reference to the receiver).
+
+ Receivers are fairly flexible in their specification,
+ as the machinery in the robustApply module takes care
+ of most of the details regarding figuring out appropriate
+ subsets of the sent arguments to apply to a given
+ receiver.
+
+ Note:
+ if receiver is itself a weak reference (a callable),
+ it will be de-referenced by the system's machinery,
+ so *generally* weak references are not suitable as
+ receivers, though some use might be found for the
+ facility whereby a higher-level library passes in
+ pre-weakrefed receiver references.
+
+ signal -- the signal to which the receiver should respond
+
+ if Any, receiver will receive any signal from the
+ indicated sender (which might also be Any, but is not
+ necessarily Any).
+
+ Otherwise must be a hashable Python object other than
+ None (DispatcherError raised on None).
+
+ sender -- the sender to which the receiver should respond
+
+ if Any, receiver will receive the indicated signals
+ from any sender.
+
+ if Anonymous, receiver will only receive indicated
+ signals from send/sendExact which do not specify a
+ sender, or specify Anonymous explicitly as the sender.
+
+ Otherwise can be any python object.
+
+ weak -- whether to use weak references to the receiver
+ By default, the module will attempt to use weak
+ references to the receiver objects. If this parameter
+ is false, then strong references will be used.
+
+ returns None, may raise DispatcherTypeError
+ """
+ if signal is None:
+ raise errors.DispatcherTypeError(
+ 'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
+ )
+ if weak:
+ receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
+ senderkey = id(sender)
+ if connections.has_key(senderkey):
+ signals = connections[senderkey]
+ else:
+ connections[senderkey] = signals = {}
+ # Keep track of senders for cleanup.
+ # Is Anonymous something we want to clean up?
+ if sender not in (None, Anonymous, Any):
+ def remove(object, senderkey=senderkey):
+ _removeSender(senderkey=senderkey)
+ # Skip objects that can not be weakly referenced, which means
+ # they won't be automatically cleaned up, but that's too bad.
+ try:
+ weakSender = weakref.ref(sender, remove)
+ senders[senderkey] = weakSender
+ except:
+ pass
+
+ receiverID = id(receiver)
+ # get current set, remove any current references to
+ # this receiver in the set, including back-references
+ if signals.has_key(signal):
+ receivers = signals[signal]
+ _removeOldBackRefs(senderkey, signal, receiver, receivers)
+ else:
+ receivers = signals[signal] = []
+ try:
+ current = sendersBack.get( receiverID )
+ if current is None:
+ sendersBack[ receiverID ] = current = []
+ if senderkey not in current:
+ current.append(senderkey)
+ except:
+ pass
+
+ receivers.append(receiver)
+
+
+
+def disconnect(receiver, signal=Any, sender=Any, weak=True):
+ """Disconnect receiver from sender for signal
+
+ receiver -- the registered receiver to disconnect
+ signal -- the registered signal to disconnect
+ sender -- the registered sender to disconnect
+ weak -- the weakref state to disconnect
+
+ disconnect reverses the process of connect,
+ the semantics for the individual elements are
+ logically equivalent to a tuple of
+ (receiver, signal, sender, weak) used as a key
+ to be deleted from the internal routing tables.
+ (The actual process is slightly more complex
+ but the semantics are basically the same).
+
+ Note:
+ Using disconnect is not required to cleanup
+ routing when an object is deleted, the framework
+ will remove routes for deleted objects
+ automatically. It's only necessary to disconnect
+ if you want to stop routing to a live object.
+
+ returns None, may raise DispatcherTypeError or
+ DispatcherKeyError
+ """
+ if signal is None:
+ raise errors.DispatcherTypeError(
+ 'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
+ )
+ if weak: receiver = saferef.safeRef(receiver)
+ senderkey = id(sender)
+ try:
+ signals = connections[senderkey]
+ receivers = signals[signal]
+ except KeyError:
+ raise errors.DispatcherKeyError(
+ """No receivers found for signal %r from sender %r""" %(
+ signal,
+ sender
+ )
+ )
+ try:
+ # also removes from receivers
+ _removeOldBackRefs(senderkey, signal, receiver, receivers)
+ except ValueError:
+ raise errors.DispatcherKeyError(
+ """No connection to receiver %s for signal %s from sender %s""" %(
+ receiver,
+ signal,
+ sender
+ )
+ )
+ _cleanupConnections(senderkey, signal)
+
+def getReceivers( sender = Any, signal = Any ):
+ """Get list of receivers from global tables
+
+ This utility function allows you to retrieve the
+ raw list of receivers from the connections table
+ for the given sender and signal pair.
+
+ Note:
+ there is no guarantee that this is the actual list
+ stored in the connections table, so the value
+ should be treated as a simple iterable/truth value
+ rather than, for instance a list to which you
+ might append new records.
+
+ Normally you would use liveReceivers( getReceivers( ...))
+ to retrieve the actual receiver objects as an iterable
+ object.
+ """
+ try:
+ return connections[id(sender)][signal]
+ except KeyError:
+ return []
+
+def liveReceivers(receivers):
+ """Filter sequence of receivers to get resolved, live receivers
+
+ This is a generator which will iterate over
+ the passed sequence, checking for weak references
+ and resolving them, then returning all live
+ receivers.
+ """
+ for receiver in receivers:
+ if isinstance( receiver, WEAKREF_TYPES):
+ # Dereference the weak reference.
+ receiver = receiver()
+ if receiver is not None:
+ yield receiver
+ else:
+ yield receiver
+
+
+
+def getAllReceivers( sender = Any, signal = Any ):
+ """Get list of all receivers from global tables
+
+ This gets all receivers which should receive
+ the given signal from sender, each receiver should
+ be produced only once by the resulting generator
+ """
+ receivers = {}
+ for set in (
+ # Get receivers that receive *this* signal from *this* sender.
+ getReceivers( sender, signal ),
+ # Add receivers that receive *any* signal from *this* sender.
+ getReceivers( sender, Any ),
+ # Add receivers that receive *this* signal from *any* sender.
+ getReceivers( Any, signal ),
+ # Add receivers that receive *any* signal from *any* sender.
+ getReceivers( Any, Any ),
+ ):
+ for receiver in set:
+ if receiver: # filter out dead instance-method weakrefs
+ try:
+ if not receivers.has_key( receiver ):
+ receivers[receiver] = 1
+ yield receiver
+ except TypeError:
+ # dead weakrefs raise TypeError on hash...
+ pass
+
+def send(signal=Any, sender=Anonymous, *arguments, **named):
+ """Send signal from sender to all connected receivers.
+
+ signal -- (hashable) signal value, see connect for details
+
+ sender -- the sender of the signal
+
+ if Any, only receivers registered for Any will receive
+ the message.
+
+ if Anonymous, only receivers registered to receive
+ messages from Anonymous or Any will receive the message
+
+ Otherwise can be any python object (normally one
+ registered with a connect if you actually want
+ something to occur).
+
+ arguments -- positional arguments which will be passed to
+ *all* receivers. Note that this may raise TypeErrors
+ if the receivers do not allow the particular arguments.
+ Note also that arguments are applied before named
+ arguments, so they should be used with care.
+
+ named -- named arguments which will be filtered according
+ to the parameters of the receivers to only provide those
+ acceptable to the receiver.
+
+ Return a list of tuple pairs [(receiver, response), ... ]
+
+ if any receiver raises an error, the error propagates back
+ through send, terminating the dispatch loop, so it is quite
+ possible to not have all receivers called if a raises an
+ error.
+ """
+ # Call each receiver with whatever arguments it can accept.
+ # Return a list of tuple pairs [(receiver, response), ... ].
+ responses = []
+ for receiver in liveReceivers(getAllReceivers(sender, signal)):
+ response = robustapply.robustApply(
+ receiver,
+ signal=signal,
+ sender=sender,
+ *arguments,
+ **named
+ )
+ responses.append((receiver, response))
+ return responses
+def sendExact( signal=Any, sender=Anonymous, *arguments, **named ):
+ """Send signal only to those receivers registered for exact message
+
+ sendExact allows for avoiding Any/Anonymous registered
+ handlers, sending only to those receivers explicitly
+ registered for a particular signal on a particular
+ sender.
+ """
+ responses = []
+ for receiver in liveReceivers(getReceivers(sender, signal)):
+ response = robustapply.robustApply(
+ receiver,
+ signal=signal,
+ sender=sender,
+ *arguments,
+ **named
+ )
+ responses.append((receiver, response))
+ return responses
+
+
+def _removeReceiver(receiver):
+ """Remove receiver from connections."""
+ if not sendersBack:
+ # During module cleanup the mapping will be replaced with None
+ return False
+ backKey = id(receiver)
+ for senderkey in sendersBack.get(backKey,()):
+ try:
+ signals = connections[senderkey].keys()
+ except KeyError,err:
+ pass
+ else:
+ for signal in signals:
+ try:
+ receivers = connections[senderkey][signal]
+ except KeyError:
+ pass
+ else:
+ try:
+ receivers.remove( receiver )
+ except Exception, err:
+ pass
+ _cleanupConnections(senderkey, signal)
+ try:
+ del sendersBack[ backKey ]
+ except KeyError:
+ pass
+
+def _cleanupConnections(senderkey, signal):
+ """Delete any empty signals for senderkey. Delete senderkey if empty."""
+ try:
+ receivers = connections[senderkey][signal]
+ except:
+ pass
+ else:
+ if not receivers:
+ # No more connected receivers. Therefore, remove the signal.
+ try:
+ signals = connections[senderkey]
+ except KeyError:
+ pass
+ else:
+ del signals[signal]
+ if not signals:
+ # No more signal connections. Therefore, remove the sender.
+ _removeSender(senderkey)
+
+def _removeSender(senderkey):
+ """Remove senderkey from connections."""
+ _removeBackrefs(senderkey)
+ try:
+ del connections[senderkey]
+ except KeyError:
+ pass
+ # Senderkey will only be in senders dictionary if sender
+ # could be weakly referenced.
+ try:
+ del senders[senderkey]
+ except:
+ pass
+
+
+def _removeBackrefs( senderkey):
+ """Remove all back-references to this senderkey"""
+ try:
+ signals = connections[senderkey]
+ except KeyError:
+ signals = None
+ else:
+ items = signals.items()
+ def allReceivers( ):
+ for signal,set in items:
+ for item in set:
+ yield item
+ for receiver in allReceivers():
+ _killBackref( receiver, senderkey )
+
+def _removeOldBackRefs(senderkey, signal, receiver, receivers):
+ """Kill old sendersBack references from receiver
+
+ This guards against multiple registration of the same
+ receiver for a given signal and sender leaking memory
+ as old back reference records build up.
+
+ Also removes old receiver instance from receivers
+ """
+ try:
+ index = receivers.index(receiver)
+ # need to scan back references here and remove senderkey
+ except ValueError:
+ return False
+ else:
+ oldReceiver = receivers[index]
+ del receivers[index]
+ found = 0
+ signals = connections.get(signal)
+ if signals is not None:
+ for sig,recs in connections.get(signal,{}).iteritems():
+ if sig != signal:
+ for rec in recs:
+ if rec is oldReceiver:
+ found = 1
+ break
+ if not found:
+ _killBackref( oldReceiver, senderkey )
+ return True
+ return False
+
+
+def _killBackref( receiver, senderkey ):
+ """Do the actual removal of back reference from receiver to senderkey"""
+ receiverkey = id(receiver)
+ set = sendersBack.get( receiverkey, () )
+ while senderkey in set:
+ try:
+ set.remove( senderkey )
+ except:
+ break
+ if not set:
+ try:
+ del sendersBack[ receiverkey ]
+ except KeyError:
+ pass
+ return True
Added: SQLObject/trunk/sqlobject/include/pydispatch/errors.py
===================================================================
--- SQLObject/trunk/sqlobject/include/pydispatch/errors.py 2005-11-01 22:27:27 UTC (rev 1183)
+++ SQLObject/trunk/sqlobject/include/pydispatch/errors.py 2005-11-02 04:45:26 UTC (rev 1184)
@@ -0,0 +1,10 @@
+"""Error types for dispatcher mechanism
+"""
+
+class DispatcherError(Exception):
+ """Base class for all Dispatcher errors"""
+class DispatcherKeyError(KeyError, DispatcherError):
+ """Error raised when unknown (sender,signal) set specified"""
+class DispatcherTypeError(TypeError, DispatcherError):
+ """Error raised when inappropriate signal-type specified (None)"""
+
Added: SQLObject/trunk/sqlobject/include/pydispatch/robust.py
===================================================================
--- SQLObject/trunk/sqlobject/include/pydispatch/robust.py 2005-11-01 22:27:27 UTC (rev 1183)
+++ SQLObject/trunk/sqlobject/include/pydispatch/robust.py 2005-11-02 04:45:26 UTC (rev 1184)
@@ -0,0 +1,57 @@
+"""Module implementing error-catching version of send (sendRobust)"""
+from dispatcher import Any, Anonymous, liveReceivers, getAllReceivers
+from robustapply import robustApply
+
+def sendRobust(
+ signal=Any,
+ sender=Anonymous,
+ *arguments, **named
+):
+ """Send signal from sender to all connected receivers catching errors
+
+ signal -- (hashable) signal value, see connect for details
+
+ sender -- the sender of the signal
+
+ if Any, only receivers registered for Any will receive
+ the message.
+
+ if Anonymous, only receivers registered to receive
+ messages from Anonymous or Any will receive the message
+
+ Otherwise can be any python object (normally one
+ registered with a connect if you actually want
+ something to occur).
+
+ arguments -- positional arguments which will be passed to
+ *all* receivers. Note that this may raise TypeErrors
+ if the receivers do not allow the particular arguments.
+ Note also that arguments are applied before named
+ arguments, so they should be used with care.
+
+ named -- named arguments which will be filtered according
+ to the parameters of the receivers to only provide those
+ acceptable to the receiver.
+
+ Return a list of tuple pairs [(receiver, response), ... ]
+
+ if any receiver raises an error (specifically any subclass of Exception),
+ the error instance is returned as the result for that receiver.
+ """
+ # Call each receiver with whatever arguments it can accept.
+ # Return a list of tuple pairs [(receiver, response), ... ].
+ responses = []
+ for receiver in liveReceivers(getAllReceivers(sender, signal)):
+ try:
+ response = robustApply(
+ receiver,
+ signal=signal,
+ sender=sender,
+ *arguments,
+ **named
+ )
+ except Exception, err:
+ responses.append((receiver, err))
+ else:
+ responses.append((receiver, response))
+ return responses
Added: SQLObject/trunk/sqlobject/include/pydispatch/robustapply.py
===================================================================
--- SQLObject/trunk/sqlobject/include/pydispatch/robustapply.py 2005-11-01 22:27:27 UTC (rev 1183)
+++ SQLObject/trunk/sqlobject/include/pydispatch/robustapply.py 2005-11-02 04:45:26 UTC (rev 1184)
@@ -0,0 +1,49 @@
+"""Robust apply mechanism
+
+Provides a function "call", which can sort out
+what arguments a given callable object can take,
+and subset the given arguments to match only
+those which are acceptable.
+"""
+
+def function( receiver ):
+ """Get function-like callable object for given receiver
+
+ returns (function_or_method, codeObject, fromMethod)
+
+ If fromMethod is true, then the callable already
+ has its first argument bound
+ """
+ if hasattr(receiver, '__call__'):
+ # receiver is a class instance; assume it is callable.
+ # Reassign receiver to the actual method that will be called.
+ if hasattr( receiver.__call__, 'im_func') or hasattr( receiver.__call__, 'im_code'):
+ receiver = receiver.__call__
+ if hasattr( receiver, 'im_func' ):
+ # an instance-method...
+ return receiver, receiver.im_func.func_code, 1
+ elif not hasattr( receiver, 'func_code'):
+ raise ValueError('unknown reciever type %s %s'%(receiver, type(receiver)))
+ return receiver, receiver.func_code, 0
+
+def robustApply(receiver, *arguments, **named):
+ """Call receiver with arguments and an appropriate subset of named
+ """
+ receiver, codeObject, startIndex = function( receiver )
+ acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount]
+ for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]:
+ if named.has_key( name ):
+ raise TypeError(
+ """Argument %r specified both positionally and as a keyword for calling %r"""% (
+ name, receiver,
+ )
+ )
+ if not (codeObject.co_flags & 8):
+ # fc does not have a **kwds type parameter, therefore
+ # remove unacceptable arguments.
+ for arg in named.keys():
+ if arg not in acceptable:
+ del named[arg]
+ return receiver(*arguments, **named)
+
+
\ No newline at end of file
Added: SQLObject/trunk/sqlobject/include/pydispatch/saferef.py
===================================================================
--- SQLObject/trunk/sqlobject/include/pydispatch/saferef.py 2005-11-01 22:27:27 UTC (rev 1183)
+++ SQLObject/trunk/sqlobject/include/pydispatch/saferef.py 2005-11-02 04:45:26 UTC (rev 1184)
@@ -0,0 +1,165 @@
+"""Refactored "safe reference" from dispatcher.py"""
+import weakref, traceback
+
+def safeRef(target, onDelete = None):
+ """Return a *safe* weak reference to a callable target
+
+ target -- the object to be weakly referenced, if it's a
+ bound method reference, will create a BoundMethodWeakref,
+ otherwise creates a simple weakref.
+ onDelete -- if provided, will have a hard reference stored
+ to the callable to be called after the safe reference
+ goes out of scope with the reference object, (either a
+ weakref or a BoundMethodWeakref) as argument.
+ """
+ if hasattr(target, 'im_self'):
+ if target.im_self is not None:
+ # Turn a bound method into a BoundMethodWeakref instance.
+ # Keep track of these instances for lookup by disconnect().
+ assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,)
+ reference = BoundMethodWeakref(
+ target=target,
+ onDelete=onDelete
+ )
+ return reference
+ if callable(onDelete):
+ return weakref.ref(target, onDelete)
+ else:
+ return weakref.ref( target )
+
+class BoundMethodWeakref(object):
+ """'Safe' and reusable weak references to instance methods
+
+ BoundMethodWeakref objects provide a mechanism for
+ referencing a bound method without requiring that the
+ method object itself (which is normally a transient
+ object) is kept alive. Instead, the BoundMethodWeakref
+ object keeps weak references to both the object and the
+ function which together define the instance method.
+
+ Attributes:
+ key -- the identity key for the reference, calculated
+ by the class's calculateKey method applied to the
+ target instance method
+ deletionMethods -- sequence of callable objects taking
+ single argument, a reference to this object which
+ will be called when *either* the target object or
+ target function is garbage collected (i.e. when
+ this object becomes invalid). These are specified
+ as the onDelete parameters of safeRef calls.
+ weakSelf -- weak reference to the target object
+ weakFunc -- weak reference to the target function
+
+ Class Attributes:
+ _allInstances -- class attribute pointing to all live
+ BoundMethodWeakref objects indexed by the class's
+ calculateKey(target) method applied to the target
+ objects. This weak value dictionary is used to
+ short-circuit creation so that multiple references
+ to the same (object, function) pair produce the
+ same BoundMethodWeakref instance.
+
+ """
+ _allInstances = weakref.WeakValueDictionary()
+ def __new__( cls, target, onDelete=None, *arguments,**named ):
+ """Create new instance or return current instance
+
+ Basically this method of construction allows us to
+ short-circuit creation of references to already-
+ referenced instance methods. The key corresponding
+ to the target is calculated, and if there is already
+ an existing reference, that is returned, with its
+ deletionMethods attribute updated. Otherwise the
+ new instance is created and registered in the table
+ of already-referenced methods.
+ """
+ key = cls.calculateKey(target)
+ current =cls._allInstances.get(key)
+ if current is not None:
+ current.deletionMethods.append( onDelete)
+ return current
+ else:
+ base = super( BoundMethodWeakref, cls).__new__( cls )
+ cls._allInstances[key] = base
+ base.__init__( target, onDelete, *arguments,**named)
+ return base
+ def __init__(self, target, onDelete=None):
+ """Return a weak-reference-like instance for a bound method
+
+ target -- the instance-method target for the weak
+ reference, must have im_self and im_func attributes
+ and be reconstructable via:
+ target.im_func.__get__( target.im_self )
+ which is true of built-in instance methods.
+ onDelete -- optional callback which will be called
+ when this weak reference ceases to be valid
+ (i.e. either the object or the function is garbage
+ collected). Should take a single argument,
+ which will be passed a pointer to this object.
+ """
+ def remove(weak, self=self):
+ """Set self.isDead to true when method or instance is destroyed"""
+ methods = self.deletionMethods[:]
+ del self.deletionMethods[:]
+ try:
+ del self.__class__._allInstances[ self.key ]
+ except KeyError:
+ pass
+ for function in methods:
+ try:
+ if callable( function ):
+ function( self )
+ except Exception, e:
+ try:
+ traceback.print_exc()
+ except AttributeError, err:
+ print '''Exception during saferef %s cleanup function %s: %s'''%(
+ self, function, e
+ )
+ self.deletionMethods = [onDelete]
+ self.key = self.calculateKey( target )
+ self.weakSelf = weakref.ref(target.im_self, remove)
+ self.weakFunc = weakref.ref(target.im_func, remove)
+ self.selfName = str(target.im_self)
+ self.funcName = str(target.im_func.__name__)
+ def calculateKey( cls, target ):
+ """Calculate the reference key for this reference
+
+ Currently this is a two-tuple of the id()'s of the
+ target object and the target function respectively.
+ """
+ return (id(target.im_self),id(target.im_func))
+ calculateKey = classmethod( calculateKey )
+ def __str__(self):
+ """Give a friendly representation of the object"""
+ return """%s( %s.%s )"""%(
+ self.__class__.__name__,
+ self.selfName,
+ self.funcName,
+ )
+ __repr__ = __str__
+ def __nonzero__( self ):
+ """Whether we are still a valid reference"""
+ return self() is not None
+ def __cmp__( self, other ):
+ """Compare with another reference"""
+ if not isinstance (other,self.__class__):
+ return cmp( self.__class__, type(other) )
+ return cmp( self.key, other.key)
+ def __call__(self):
+ """Return a strong reference to the bound method
+
+ If the target cannot be retrieved, then will
+ return None, otherwise returns a bound instance
+ method for our object and function.
+
+ Note:
+ You may call this method any number of times,
+ as it does not invalidate the reference.
+ """
+ target = self.weakSelf()
+ if target is not None:
+ function = self.weakFunc()
+ if function is not None:
+ return function.__get__(target)
+ return None
|
|
From: <sub...@co...> - 2005-11-01 22:27:57
|
Author: test
Date: 2005-11-01 22:27:27 +0000 (Tue, 01 Nov 2005)
New Revision: 1183
Modified:
SQLObject/trunk/sqlobject/manager/command.py
Log:
Fix egg loading a little
Modified: SQLObject/trunk/sqlobject/manager/command.py
===================================================================
--- SQLObject/trunk/sqlobject/manager/command.py 2005-11-01 22:15:07 UTC (rev 1182)
+++ SQLObject/trunk/sqlobject/manager/command.py 2005-11-01 22:27:27 UTC (rev 1183)
@@ -422,8 +422,7 @@
def config_from_egg(self, egg_spec, warn_no_sqlobject=True):
import pkg_resources
- pkg_resources.require(egg_spec)
- dist = pkg_resources.working_set.find(pkg_resources.Requirement(egg_spec))
+ dist = pkg_resources.get_distribution(egg_spec)
if not dist.has_metadata('sqlobject.txt'):
if warn_no_sqlobject:
print 'No sqlobject.txt in %s egg info' % egg_spec
|
|
From: <sub...@co...> - 2005-11-01 11:18:06
|
Author: phd Date: 2005-11-01 11:14:21 +0000 (Tue, 01 Nov 2005) New Revision: 1181 Modified: home/phd/SQLObject/paramstyles/docs/News.txt home/phd/SQLObject/paramstyles/sqlobject/conftest.py home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py home/phd/SQLObject/paramstyles/sqlobject/events.py Log: Merged patches from the revisions 1175:1180 from the trunk Modified: home/phd/SQLObject/paramstyles/docs/News.txt =================================================================== --- home/phd/SQLObject/paramstyles/docs/News.txt 2005-10-31 23:50:28 UTC (rev 1180) +++ home/phd/SQLObject/paramstyles/docs/News.txt 2005-11-01 11:14:21 UTC (rev 1181) @@ -27,13 +27,22 @@ instead you must manually create it (e.g., with explicit SQLObject classes for the joins). -* Added ``createSQL`` as an option for sqlmeta. Here you can add related SQL - you want executed by sqlobject-admin create after table creation. createSQL - expects a string, list, or dictionary. If using a dictionary the key should - be a dbName value (ex. 'postgres') and the value should be a string or list. - Examples in sqlobject/tests/test_sqlobject_admin.py or at +* Added ``createSQL`` as an option for sqlmeta. Here you can add + related SQL you want executed by sqlobject-admin create after table + creation. createSQL expects a string, list, or dictionary. If using + a dictionary the key should be a dbName value (ex. 'postgres') and + the value should be a string or list. Examples in + sqlobject/tests/test_sqlobject_admin.py or at <http://sqlobject.org/sqlobject-admin.html#the-create-command> +* Added method ``sqlhub.doInTransaction(callable, *args, **kwargs)``, + to be used like:: + + sqlhub.doInTransaction(process_request, os.environ) + + This will run ``process_request(os.environ)``. The return + value will be preserved. + SQLObject 0.7.1 =============== Modified: home/phd/SQLObject/paramstyles/sqlobject/conftest.py =================================================================== --- home/phd/SQLObject/paramstyles/sqlobject/conftest.py 2005-10-31 23:50:28 UTC (rev 1180) +++ home/phd/SQLObject/paramstyles/sqlobject/conftest.py 2005-11-01 11:14:21 UTC (rev 1181) @@ -10,6 +10,8 @@ import py import os import sqlobject +import pkg_resources +pkg_resources.require('SQLObject') connectionShortcuts = { 'mysql': 'mysql://test@localhost/test', Modified: home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py =================================================================== --- home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py 2005-10-31 23:50:28 UTC (rev 1180) +++ home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py 2005-11-01 11:14:21 UTC (rev 1181) @@ -916,6 +916,34 @@ "No connection has been defined for this thread " "or process") + def doInTransaction(self, func, *args, **kw): + """ + This routine can be used to run a function in a transaction, + rolling the transaction back if any exception is raised from + that function, and committing otherwise. Use like:: + + sqlhub.doInTransaction(process_request, os.environ) + + This will run ``process_request(os.environ)``. The return + value will be preserved. + """ + # @@: In Python 2.5, something usable with with: should also + # be added. + old_conn = self.getConnection() + conn = old_conn.transaction() + self.threadConnection = conn + try: + try: + value = func(*args, **kw) + except: + conn.rollback() + raise + else: + conn.commit() + return value + finally: + self.threadConnection = old_conn + def _set_threadConnection(self, value): self.threadingLocal.connection = value Modified: home/phd/SQLObject/paramstyles/sqlobject/events.py =================================================================== --- home/phd/SQLObject/paramstyles/sqlobject/events.py 2005-10-31 23:50:28 UTC (rev 1180) +++ home/phd/SQLObject/paramstyles/sqlobject/events.py 2005-11-01 11:14:21 UTC (rev 1181) @@ -1,4 +1,18 @@ -class Event(object): +from dispatch import dispatcher +from weakref import ref + + +subclassClones = {} + +def listen(receiver, soClass, signal, alsoSubclasses=True): + dispatcher.connect(receiver, signal=signal, sender=soClass) + weakSOClass = ref(soClass) + weakReceiver = ref(receiver) + subclassClones.setdefault(weakSOClass, []).append((weakReceiver, signal)) + +send = dispatcher.send + +class Signal(object): """ Base event for all SQLObject events. @@ -6,9 +20,9 @@ instance. """ -class ClassCreateEvent(Event): +class ClassCreateSignal(Signal): """ - Event raised before class creation. The sender is the superclass + Signal raised before class creation. The sender is the superclass (in case of multiple superclasses, the first superclass). The arguments are ``(new_class_name, bases, new_attrs, post_funcs)``. ``new_attrs`` is a dictionary and may be modified (but @@ -21,30 +35,41 @@ ``(new_class)``. """ +def _makeSubclassConnections(new_class_name, bases, new_attrs, post_funcs): + post_funcs.append(_makeSubclassConnectionsPost) + +def _makeSubclassConnectionsPost(new_class): + for cls in new_class.__bases__: + for weakReceiver, signal in subclassClones.get(cls, []): + receiver = weakReceiver() + if not receiver: + continue + dispatcher.connect(receiver, signal=signal, sender=new_class) + +dispatcher.connect(_makeSubclassConnections, signal=ClassCreateSignal) + # @@: Should there be a class reload event? This would allow modules # to be reloaded, possibly. Or it could even be folded into -# ClassCreateEvent, since anything that listens to that needs to pay +# ClassCreateSignal, since anything that listens to that needs to pay # attention to reloads (or else it is probably buggy). -class InstanceCreateEvent(Event): +class RowCreateSignal(Signal): """ Called before an instance is created, with the class as the - sender. Called with the arguments ``(varargs, kwargs, - post_funcs)``. Both ``kwargs`` and ``varargs`` may be usefully - modified (``varargs`` is converted from a tuple to a list before - calling). ``post_funcs`` is a list of callbacks, intended to have + sender. Called with the arguments ``(kwargs, post_funcs)``. + There may be a ``connection`` argument. ``kwargs``may be usefully + modified. ``post_funcs`` is a list of callbacks, intended to have functions appended to it, and are called with the arguments ``(new_instance)``. Note: this is not called when an instance is created from an - existing database row. (@@: Show the name be RowCreateEvent, or - RowInsertEvent?) + existing database row. """ # @@: An event for getting a row? But for each row, when doing a # select? For .sync, .syncUpdate, .expire? -class InstanceDestroyEvent(Event): +class RowDestroySignal(Signal): """ Called before an instance is deleted. Sender is the instance's class. Arguments are ``(instance)``. You cannot cancel the delete, @@ -52,14 +77,13 @@ delete, but also cause an uncaught exception if not expected). Note: this is not called when an instance is destroyed through - garbage collection (@@: Should this be called RowDestroyEvent or - RowDeleteEvent?) + garbage collection. @@: Should this allow ``instance`` to be a primary key, so that a row can be deleted without first fetching it? """ -class InstanceUpdateEvent(Event): +class RowUpdateSignal(Signal): """ Called when an instance is updated through a call to ``.set()``. The arguments are ``(instance, kwargs)``. ``kwargs`` can be @@ -68,45 +92,57 @@ @@: Should this also catch single-column updates? Should this only be run before updates go to the server? Should this be - called RowUpdateEvent? + called RowUpdateSignal? """ -class AddColumnEvent(Event): +class AddColumnSignal(Signal): """ Called when a column is added to a class, with arguments ``(cls, - column_name, column_definition)``. This is called *after* the - column has been added, and is called for each column after class - creation. + connection, column_name, column_definition, changeSchema, + post_funcs)``. This is called *after* the column has been added, + and is called for each column after class creation. + + post_funcs are called with ``(cls, so_column_obj)`` """ -class DeleteColumnEvent(Event): +class DeleteColumnSignal(Signal): """ Called when a column is removed from a class, with the arguments - ``(cls, column_name, column_definition)``. Like - ``AddColumnEvent`` this is called after the action has been + ``(cls, connection, column_name, so_column_obj, post_funcs)``. + Like ``AddColumnSignal`` this is called after the action has been performed, and is called for subclassing (when a column is implicitly removed by setting it to ``None``). + + post_funcs are called with ``(cls, so_column_obj)`` """ -# @@: Events for indexes and joins? These are mostly event consumers, +# @@: Signals for indexes and joins? These are mostly event consumers, # though. -class CreateTableEvent(Event): +class CreateTableSignal(Signal): """ Called when a table is created. If ``ifNotExists==True`` and the table exists, this event is not called. - Called with ``(cls, connection, post_funcs)``. ``post_funcs`` + Called with ``(cls, connection, extra_sql, post_funcs)``. + ``extra_sql`` is a list (which can be appended to) of extra SQL + statements to be run after the table is created. ``post_funcs`` functions are called with ``(cls, connection)`` after the table - has been created. + has been created. Those functions are *not* called simply when + constructing the SQL. """ -class DropTableEvent(Event): +class DropTableSignal(Signal): """ Called when a table is dropped. If ``ifExists==True`` and the table doesn't exist, this event is not called. - Called with ``(cls, connection, post_funcs)``. ``post_funcs`` - functions are called with ``(cls, connection)`` after the table - has been dropped. + Called with ``(cls, connection, extra_sql, post_funcs)``. + ``post_funcs`` functions are called with ``(cls, connection)`` + after the table has been dropped. """ + +__all__ = ['listen', 'send'] +for name, value in globals().items(): + if isinstance(value, type) and issubclass(value, Signal): + __all__.append('value') |
|
From: SourceForge.net <no...@so...> - 2005-10-31 22:34:51
|
Bugs item #1305062, was opened at 2005-09-26 13:07 Message generated for change (Comment added) made by adustman You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1305062&group_id=74338 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: General Group: SQLObject release (specify) Status: Open Resolution: None Priority: 5 Submitted By: Andy Dustman (adustman) Assigned to: Nobody/Anonymous (nobody) Summary: 0.7a: RelatedJoin() glue table has no constraints Initial Comment: Consider the example from the online 0.6.1 documentation, with the User and Role objects, where each object does a RelatedJoin() to the other. This results in a glue table that looks like this: CREATE TABLE user_role ( user_id INT NOT NULL, role_id INT NOT NULL ) The schema lacks any FOREIGN KEY constraints, and a PRIMARY KEY. It should probalby look more like this: CREATE TABLE user_role ( user_id INT NOT NULL, CONSTRAINT user_id_exists FOREIGN KEY (user_id) REFERENCES user (id), role_id INT NOT NULL, CONSTRAINT role_id_exists FOREIGN KEY (role_id) REFERENCES role (id), PRIMARY KEY (user_id, role_id) ) ON DELETE CASCADE may be needed as well. Note that I am using patch #1299109 to add FOREIGN KEY support for MySQL. However, looking at join.py, it does not seem to do anything with foreign keys, so I do not think this is a dependency on MySQL; I think this behavior should be reproducable on other databases. The intermediate table schema is created in dbconnection.DBAPI._SO_createJoinTableSQL(), and it calls self.joinSQLType(join) to get the SQL for the column. In all the implementations (except Sybase which is slightly different), it returns 'INT NOT NULL' The table creation order looks like it could be a problem, as the glue/intermediate table seems to be created before the second of the other two tables, which would produce referential integrity problems (i.e.reference to a non-existent table). ---------------------------------------------------------------------- >Comment By: Andy Dustman (adustman) Date: 2005-10-31 17:34 Message: Logged In: YES user_id=71372 BTW, the table creation order comment is really more of a problem for Turbogears. I've done a bit of testing by altering an existing glue table to use ON DELETE CASCADE and it seems to work as expected/desired. Without ON DELETE CASCADE, you have to delete the glue entry tables manually, (typically with obj.removeXXX(other_id)); otherwise your glue table has a bunch of dead entries, though if you have the FOREIGN KEY entry without ON CASCADE DELETE, then you can't remove the parent objects without first deleting the glue records. In other words, having FOREIGN KEY ... REFERENCES ... ON DELETE CASCADE keeps your glue table clean when you remove either object of the RelatedJoin(). ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1305062&group_id=74338 |
|
From: Ian B. <ia...@co...> - 2005-10-31 22:34:43
|
BTW, notifications of patches and bug changes will be coming to this list now. SourceForge.net wrote: > Bugs item #1344338, was opened at 2005-10-31 17:28 > Message generated for change (Tracker Item Submitted) made by Item Submitter > You can respond by visiting: > https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1344338&group_id=74338 > > Please note that this message will contain a full copy of the comment thread, > including the initial issue submission, for this request, > not just the latest update. > Category: MySQL > Group: SQLObject release (specify) > Status: Open > Resolution: None > Priority: 5 > Submitted By: Andy Dustman (adustman) > Assigned to: Nobody/Anonymous (nobody) > Summary: 0.7.0: autoCommit from URI wrong type > > Initial Comment: > In dbconnection.py, when the URI is parsed, there is no > special treatment or type conversion of the autoCommit > parameter, so it is always stored as a string. In the > MySQL case (mysql.mysqlconnection), this causes the > call to conn.autocommit() to always, since that API > function requires an integer (booleans work too). > > A workaround for this is to change > conn.autocommit(self.autoCommit) to > conn.autocommit(int(self.autoCommit)), though only > works if an integer is used as a parameter; it fails if > True or False is used. > > Note that sybase support has this sequence in > SybaseConnection.__init__(): > > autoCommit = kw.get('autoCommit') > if autoCommit: > autoCommmit = int(autoCommit) > else: > autoCommit = None > kw['autoCommit'] = autoCommit > DBAPI.__init__(self, **kw) > > A similar block of code would also work for MySQLdb. > > ---------------------------------------------------------------------- > > You can respond by visiting: > https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1344338&group_id=74338 > > > ------------------------------------------------------- > This SF.Net email is sponsored by the JBoss Inc. > Get Certified Today * Register for a JBoss Training Course > Free Certification Exam for All Training Attendees Through End of 2005 > Visit http://www.jboss.com/services/certification for more information > _______________________________________________ > sqlobject-cvs mailing list > sql...@li... > https://lists.sourceforge.net/lists/listinfo/sqlobject-cvs -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
|
From: SourceForge.net <no...@so...> - 2005-10-31 22:28:56
|
Bugs item #1344338, was opened at 2005-10-31 17:28 Message generated for change (Tracker Item Submitted) made by Item Submitter You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1344338&group_id=74338 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: MySQL Group: SQLObject release (specify) Status: Open Resolution: None Priority: 5 Submitted By: Andy Dustman (adustman) Assigned to: Nobody/Anonymous (nobody) Summary: 0.7.0: autoCommit from URI wrong type Initial Comment: In dbconnection.py, when the URI is parsed, there is no special treatment or type conversion of the autoCommit parameter, so it is always stored as a string. In the MySQL case (mysql.mysqlconnection), this causes the call to conn.autocommit() to always, since that API function requires an integer (booleans work too). A workaround for this is to change conn.autocommit(self.autoCommit) to conn.autocommit(int(self.autoCommit)), though only works if an integer is used as a parameter; it fails if True or False is used. Note that sybase support has this sequence in SybaseConnection.__init__(): autoCommit = kw.get('autoCommit') if autoCommit: autoCommmit = int(autoCommit) else: autoCommit = None kw['autoCommit'] = autoCommit DBAPI.__init__(self, **kw) A similar block of code would also work for MySQLdb. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=540672&aid=1344338&group_id=74338 |
|
From: <sub...@co...> - 2005-10-31 17:46:46
|
Author: ianb
Date: 2005-10-31 17:46:41 +0000 (Mon, 31 Oct 2005)
New Revision: 1179
Modified:
SQLObject/trunk/sqlobject/conftest.py
Log:
Make sure SQLObject gets require()d before testing, so that any dependencies will also be loaded.
Modified: SQLObject/trunk/sqlobject/conftest.py
===================================================================
--- SQLObject/trunk/sqlobject/conftest.py 2005-10-31 17:45:56 UTC (rev 1178)
+++ SQLObject/trunk/sqlobject/conftest.py 2005-10-31 17:46:41 UTC (rev 1179)
@@ -10,6 +10,8 @@
import py
import os
import sqlobject
+import pkg_resources
+pkg_resources.require('SQLObject')
connectionShortcuts = {
'mysql': 'mysql://test@localhost/test',
|
|
From: <sub...@co...> - 2005-10-31 17:46:13
|
Author: ianb
Date: 2005-10-31 17:45:56 +0000 (Mon, 31 Oct 2005)
New Revision: 1178
Modified:
SQLObject/trunk/sqlobject/events.py
Log:
Some changed names for the event signals, and a few signature changes. Also some routines using PyDispatcher for handling subclassing (and listening to classes and their subclasses); actual use of these events will come about in a later commit
Modified: SQLObject/trunk/sqlobject/events.py
===================================================================
--- SQLObject/trunk/sqlobject/events.py 2005-10-31 17:15:43 UTC (rev 1177)
+++ SQLObject/trunk/sqlobject/events.py 2005-10-31 17:45:56 UTC (rev 1178)
@@ -1,4 +1,18 @@
-class Event(object):
+from dispatch import dispatcher
+from weakref import ref
+
+
+subclassClones = {}
+
+def listen(receiver, soClass, signal, alsoSubclasses=True):
+ dispatcher.connect(receiver, signal=signal, sender=soClass)
+ weakSOClass = ref(soClass)
+ weakReceiver = ref(receiver)
+ subclassClones.setdefault(weakSOClass, []).append((weakReceiver, signal))
+
+send = dispatcher.send
+
+class Signal(object):
"""
Base event for all SQLObject events.
@@ -6,9 +20,9 @@
instance.
"""
-class ClassCreateEvent(Event):
+class ClassCreateSignal(Signal):
"""
- Event raised before class creation. The sender is the superclass
+ Signal raised before class creation. The sender is the superclass
(in case of multiple superclasses, the first superclass). The
arguments are ``(new_class_name, bases, new_attrs, post_funcs)``.
``new_attrs`` is a dictionary and may be modified (but
@@ -21,30 +35,41 @@
``(new_class)``.
"""
+def _makeSubclassConnections(new_class_name, bases, new_attrs, post_funcs):
+ post_funcs.append(_makeSubclassConnectionsPost)
+
+def _makeSubclassConnectionsPost(new_class):
+ for cls in new_class.__bases__:
+ for weakReceiver, signal in subclassClones.get(cls, []):
+ receiver = weakReceiver()
+ if not receiver:
+ continue
+ dispatcher.connect(receiver, signal=signal, sender=new_class)
+
+dispatcher.connect(_makeSubclassConnections, signal=ClassCreateSignal)
+
# @@: Should there be a class reload event? This would allow modules
# to be reloaded, possibly. Or it could even be folded into
-# ClassCreateEvent, since anything that listens to that needs to pay
+# ClassCreateSignal, since anything that listens to that needs to pay
# attention to reloads (or else it is probably buggy).
-class InstanceCreateEvent(Event):
+class RowCreateSignal(Signal):
"""
Called before an instance is created, with the class as the
- sender. Called with the arguments ``(varargs, kwargs,
- post_funcs)``. Both ``kwargs`` and ``varargs`` may be usefully
- modified (``varargs`` is converted from a tuple to a list before
- calling). ``post_funcs`` is a list of callbacks, intended to have
+ sender. Called with the arguments ``(kwargs, post_funcs)``.
+ There may be a ``connection`` argument. ``kwargs``may be usefully
+ modified. ``post_funcs`` is a list of callbacks, intended to have
functions appended to it, and are called with the arguments
``(new_instance)``.
Note: this is not called when an instance is created from an
- existing database row. (@@: Show the name be RowCreateEvent, or
- RowInsertEvent?)
+ existing database row.
"""
# @@: An event for getting a row? But for each row, when doing a
# select? For .sync, .syncUpdate, .expire?
-class InstanceDestroyEvent(Event):
+class RowDestroySignal(Signal):
"""
Called before an instance is deleted. Sender is the instance's
class. Arguments are ``(instance)``. You cannot cancel the delete,
@@ -52,14 +77,13 @@
delete, but also cause an uncaught exception if not expected).
Note: this is not called when an instance is destroyed through
- garbage collection (@@: Should this be called RowDestroyEvent or
- RowDeleteEvent?)
+ garbage collection.
@@: Should this allow ``instance`` to be a primary key, so that a
row can be deleted without first fetching it?
"""
-class InstanceUpdateEvent(Event):
+class RowUpdateSignal(Signal):
"""
Called when an instance is updated through a call to ``.set()``.
The arguments are ``(instance, kwargs)``. ``kwargs`` can be
@@ -68,45 +92,57 @@
@@: Should this also catch single-column updates? Should this
only be run before updates go to the server? Should this be
- called RowUpdateEvent?
+ called RowUpdateSignal?
"""
-class AddColumnEvent(Event):
+class AddColumnSignal(Signal):
"""
Called when a column is added to a class, with arguments ``(cls,
- column_name, column_definition)``. This is called *after* the
- column has been added, and is called for each column after class
- creation.
+ connection, column_name, column_definition, changeSchema,
+ post_funcs)``. This is called *after* the column has been added,
+ and is called for each column after class creation.
+
+ post_funcs are called with ``(cls, so_column_obj)``
"""
-class DeleteColumnEvent(Event):
+class DeleteColumnSignal(Signal):
"""
Called when a column is removed from a class, with the arguments
- ``(cls, column_name, column_definition)``. Like
- ``AddColumnEvent`` this is called after the action has been
+ ``(cls, connection, column_name, so_column_obj, post_funcs)``.
+ Like ``AddColumnSignal`` this is called after the action has been
performed, and is called for subclassing (when a column is
implicitly removed by setting it to ``None``).
+
+ post_funcs are called with ``(cls, so_column_obj)``
"""
-# @@: Events for indexes and joins? These are mostly event consumers,
+# @@: Signals for indexes and joins? These are mostly event consumers,
# though.
-class CreateTableEvent(Event):
+class CreateTableSignal(Signal):
"""
Called when a table is created. If ``ifNotExists==True`` and the
table exists, this event is not called.
- Called with ``(cls, connection, post_funcs)``. ``post_funcs``
+ Called with ``(cls, connection, extra_sql, post_funcs)``.
+ ``extra_sql`` is a list (which can be appended to) of extra SQL
+ statements to be run after the table is created. ``post_funcs``
functions are called with ``(cls, connection)`` after the table
- has been created.
+ has been created. Those functions are *not* called simply when
+ constructing the SQL.
"""
-class DropTableEvent(Event):
+class DropTableSignal(Signal):
"""
Called when a table is dropped. If ``ifExists==True`` and the
table doesn't exist, this event is not called.
- Called with ``(cls, connection, post_funcs)``. ``post_funcs``
- functions are called with ``(cls, connection)`` after the table
- has been dropped.
+ Called with ``(cls, connection, extra_sql, post_funcs)``.
+ ``post_funcs`` functions are called with ``(cls, connection)``
+ after the table has been dropped.
"""
+
+__all__ = ['listen', 'send']
+for name, value in globals().items():
+ if isinstance(value, type) and issubclass(value, Signal):
+ __all__.append('value')
|
|
From: <sub...@co...> - 2005-10-31 17:15:58
|
Author: test Date: 2005-10-31 17:15:43 +0000 (Mon, 31 Oct 2005) New Revision: 1177 Modified: SQLObject/trunk/docs/News.txt Log: Added news on new doInTransaction method Modified: SQLObject/trunk/docs/News.txt =================================================================== --- SQLObject/trunk/docs/News.txt 2005-10-31 16:54:16 UTC (rev 1176) +++ SQLObject/trunk/docs/News.txt 2005-10-31 17:15:43 UTC (rev 1177) @@ -27,13 +27,22 @@ instead you must manually create it (e.g., with explicit SQLObject classes for the joins). -* Added ``createSQL`` as an option for sqlmeta. Here you can add related SQL - you want executed by sqlobject-admin create after table creation. createSQL - expects a string, list, or dictionary. If using a dictionary the key should - be a dbName value (ex. 'postgres') and the value should be a string or list. - Examples in sqlobject/tests/test_sqlobject_admin.py or at +* Added ``createSQL`` as an option for sqlmeta. Here you can add + related SQL you want executed by sqlobject-admin create after table + creation. createSQL expects a string, list, or dictionary. If using + a dictionary the key should be a dbName value (ex. 'postgres') and + the value should be a string or list. Examples in + sqlobject/tests/test_sqlobject_admin.py or at <http://sqlobject.org/sqlobject-admin.html#the-create-command> +* Added method ``sqlhub.doInTransaction(callable, *args, **kwargs)``, + to be used like:: + + sqlhub.doInTransaction(process_request, os.environ) + + This will run ``process_request(os.environ)``. The return + value will be preserved. + SQLObject 0.7.1 =============== |
|
From: <sub...@co...> - 2005-10-31 16:54:29
|
Author: test
Date: 2005-10-31 16:54:16 +0000 (Mon, 31 Oct 2005)
New Revision: 1176
Modified:
SQLObject/trunk/sqlobject/dbconnection.py
Log:
Added method to ConnectionHub objects to run code in a transaction
Modified: SQLObject/trunk/sqlobject/dbconnection.py
===================================================================
--- SQLObject/trunk/sqlobject/dbconnection.py 2005-10-31 16:01:38 UTC (rev 1175)
+++ SQLObject/trunk/sqlobject/dbconnection.py 2005-10-31 16:54:16 UTC (rev 1176)
@@ -902,6 +902,34 @@
"No connection has been defined for this thread "
"or process")
+ def doInTransaction(self, func, *args, **kw):
+ """
+ This routine can be used to run a function in a transaction,
+ rolling the transaction back if any exception is raised from
+ that function, and committing otherwise. Use like::
+
+ sqlhub.doInTransaction(process_request, os.environ)
+
+ This will run ``process_request(os.environ)``. The return
+ value will be preserved.
+ """
+ # @@: In Python 2.5, something usable with with: should also
+ # be added.
+ old_conn = self.getConnection()
+ conn = old_conn.transaction()
+ self.threadConnection = conn
+ try:
+ try:
+ value = func(*args, **kw)
+ except:
+ conn.rollback()
+ raise
+ else:
+ conn.commit()
+ return value
+ finally:
+ self.threadConnection = old_conn
+
def _set_threadConnection(self, value):
self.threadingLocal.connection = value
|
|
From: <sub...@co...> - 2005-10-31 16:02:02
|
Author: phd
Date: 2005-10-31 16:01:38 +0000 (Mon, 31 Oct 2005)
New Revision: 1175
Modified:
home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py
home/phd/SQLObject/paramstyles/sqlobject/firebird/firebirdconnection.py
home/phd/SQLObject/paramstyles/sqlobject/manager/command.py
Log:
Merged patches from the revisions 1171:1174 from the trunk
Modified: home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py 2005-10-28 16:29:54 UTC (rev 1174)
+++ home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py 2005-10-31 16:01:38 UTC (rev 1175)
@@ -547,11 +547,29 @@
if constraint]
return refConstraintDefs
+ def createSQL(self, soClass):
+ tableCreateSQLs = getattr(soClass.sqlmeta, 'createSQL', None)
+ if tableCreateSQLs:
+ assert isinstance(tableCreateSQLs,(str,list,dict,tuple)), (
+ '%s.sqlmeta.createSQL must be a str, list, dict or tuple.' %
+ (soClass.__name__))
+ if isinstance(tableCreateSQLs, dict):
+ tableCreateSQLs = tableCreateSQLs.get(soClass._connection.dbName, [])
+ if isinstance(tableCreateSQLs, str):
+ tableCreateSQLs = [tableCreateSQLs]
+ if isinstance(tableCreateSQLs, tuple):
+ tableCreateSQLs = list(tableCreateSQLs)
+ assert isinstance(tableCreateSQLs,list), (
+ 'Unable to create a list from %s.sqlmeta.createSQL' %
+ (soClass.__name__))
+ return tableCreateSQLs or []
+
def createTableSQL(self, soClass):
constraints = self.createReferenceConstraints(soClass)
+ extraSQL = self.createSQL(soClass)
createSql = ('CREATE TABLE %s (\n%s\n)' %
(soClass.sqlmeta.table, self.createColumns(soClass)))
- return createSql, constraints
+ return createSql, constraints + extraSQL
def createColumns(self, soClass):
columnDefs = [self.createIDColumn(soClass)] \
Modified: home/phd/SQLObject/paramstyles/sqlobject/firebird/firebirdconnection.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/firebird/firebirdconnection.py 2005-10-28 16:29:54 UTC (rev 1174)
+++ home/phd/SQLObject/paramstyles/sqlobject/firebird/firebirdconnection.py 2005-10-31 16:01:38 UTC (rev 1175)
@@ -1,7 +1,8 @@
+import re
+import os
from sqlobject.dbconnection import DBAPI
+from sqlobject import col
kinterbasdb = None
-import re
-import os
class FirebirdConnection(DBAPI):
Modified: home/phd/SQLObject/paramstyles/sqlobject/manager/command.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/manager/command.py 2005-10-28 16:29:54 UTC (rev 1174)
+++ home/phd/SQLObject/paramstyles/sqlobject/manager/command.py 2005-10-31 16:01:38 UTC (rev 1175)
@@ -188,7 +188,7 @@
# This abstract base class
return
register(cls)
-
+
def __init__(self, invoked_as, command_name, args, runner):
self.invoked_as = invoked_as
self.command_name = command_name
@@ -394,7 +394,7 @@
continue
classes = self.classes_from_module(module)
all.extend(classes)
-
+
os.path.walk(package_dir, find_classes_in_file, None)
return all
@@ -419,7 +419,7 @@
dir = conf['history_dir']
dir = dir.replace('$base', dist.location)
self.options.output_dir = dir
-
+
def config_from_egg(self, egg_spec, warn_no_sqlobject=True):
import pkg_resources
pkg_resources.require(egg_spec)
@@ -515,8 +515,8 @@
for constraint in constraints:
if constraint:
print constraint.strip() + ';\n'
-
+
class CommandList(Command):
name = 'list'
@@ -577,7 +577,6 @@
tableConstraints = soClass.createTable()
if tableConstraints:
constraints[soClass._connection].append(tableConstraints)
-
else:
print 'Cancelled'
else:
@@ -593,8 +592,8 @@
if v >= 1:
print '%i tables created (%i already exist)' % (
created, existing)
-
+
class CommandDrop(Command):
name = 'drop'
@@ -795,7 +794,7 @@
'database is currently at. Use the upgrade command to '
'sync databases with code.'
% SQLObjectVersionTable.sqlmeta.table)
-
+
parser = standard_parser()
parser.add_option('--output-dir',
help="Base directory for recorded definitions",
@@ -838,7 +837,7 @@
if self.options.force_db_version:
self.command_force_db_version()
return
-
+
v = self.options.verbose
sim = self.options.simulate
classes = self.classes()
@@ -981,7 +980,7 @@
def strip_comments(self, sql):
lines = [l for l in sql.splitlines()
if not l.strip().startswith('--')]
- return '\n'.join(lines)
+ return '\n'.join(lines)
def base_dir(self):
base = self.options.output_dir
@@ -996,7 +995,7 @@
def find_output_dir(self):
today = time.strftime('%Y-%m-%d', time.localtime())
if self.options.version_name:
- dir = os.path.join(self.base_dir(), today + '-' +
+ dir = os.path.join(self.base_dir(), today + '-' +
self.options.version_name)
if os.path.exists(dir):
print ("Error, directory already exists: %s"
@@ -1012,7 +1011,7 @@
extra = 'a'
else:
extra = chr(ord(extra)+1)
-
+
def find_last_version(self):
names = []
for fn in os.listdir(self.base_dir()):
@@ -1105,8 +1104,8 @@
raise
self.update_db(next_version, conn)
print 'Done.'
-
+
def current_version(self):
conn = self.connection()
if not conn.tableExists(SQLObjectVersionTable.sqlmeta.table):
@@ -1166,8 +1165,8 @@
print 'No upgraders found in %s' % current_dir
return None, None
upgraders.sort()
- return upgraders[-1]
-
+ return upgraders[-1]
+
def update_sys_path(paths, verbose):
if isinstance(paths, (str, unicode)):
paths = [paths]
|
|
From: <sub...@co...> - 2005-10-28 16:30:04
|
Author: rflosi
Date: 2005-10-28 16:29:54 +0000 (Fri, 28 Oct 2005)
New Revision: 1174
Modified:
SQLObject/trunk/sqlobject/dbconnection.py
Log:
fixed createSQL to return an empty list if tableCreateSQLs is None
Modified: SQLObject/trunk/sqlobject/dbconnection.py
===================================================================
--- SQLObject/trunk/sqlobject/dbconnection.py 2005-10-28 16:22:17 UTC (rev 1173)
+++ SQLObject/trunk/sqlobject/dbconnection.py 2005-10-28 16:29:54 UTC (rev 1174)
@@ -548,7 +548,7 @@
assert isinstance(tableCreateSQLs,list), (
'Unable to create a list from %s.sqlmeta.createSQL' %
(soClass.__name__))
- return tableCreateSQLs
+ return tableCreateSQLs or []
def createTableSQL(self, soClass):
constraints = self.createReferenceConstraints(soClass)
|
|
From: <sub...@co...> - 2005-10-28 16:22:30
|
Author: ianb
Date: 2005-10-28 16:22:17 +0000 (Fri, 28 Oct 2005)
New Revision: 1173
Modified:
SQLObject/trunk/sqlobject/dbconnection.py
SQLObject/trunk/sqlobject/manager/command.py
Log:
moved createSQL code from manager/command.py:CommandCreate to extend the constraints returned in dbconnection.py:createTableSQL
Modified: SQLObject/trunk/sqlobject/dbconnection.py
===================================================================
--- SQLObject/trunk/sqlobject/dbconnection.py 2005-10-28 16:03:16 UTC (rev 1172)
+++ SQLObject/trunk/sqlobject/dbconnection.py 2005-10-28 16:22:17 UTC (rev 1173)
@@ -533,11 +533,29 @@
if constraint]
return refConstraintDefs
+ def createSQL(self, soClass):
+ tableCreateSQLs = getattr(soClass.sqlmeta, 'createSQL', None)
+ if tableCreateSQLs:
+ assert isinstance(tableCreateSQLs,(str,list,dict,tuple)), (
+ '%s.sqlmeta.createSQL must be a str, list, dict or tuple.' %
+ (soClass.__name__))
+ if isinstance(tableCreateSQLs, dict):
+ tableCreateSQLs = tableCreateSQLs.get(soClass._connection.dbName, [])
+ if isinstance(tableCreateSQLs, str):
+ tableCreateSQLs = [tableCreateSQLs]
+ if isinstance(tableCreateSQLs, tuple):
+ tableCreateSQLs = list(tableCreateSQLs)
+ assert isinstance(tableCreateSQLs,list), (
+ 'Unable to create a list from %s.sqlmeta.createSQL' %
+ (soClass.__name__))
+ return tableCreateSQLs
+
def createTableSQL(self, soClass):
constraints = self.createReferenceConstraints(soClass)
+ extraSQL = self.createSQL(soClass)
createSql = ('CREATE TABLE %s (\n%s\n)' %
(soClass.sqlmeta.table, self.createColumns(soClass)))
- return createSql, constraints
+ return createSql, constraints + extraSQL
def createColumns(self, soClass):
columnDefs = [self.createIDColumn(soClass)] \
Modified: SQLObject/trunk/sqlobject/manager/command.py
===================================================================
--- SQLObject/trunk/sqlobject/manager/command.py 2005-10-28 16:03:16 UTC (rev 1172)
+++ SQLObject/trunk/sqlobject/manager/command.py 2005-10-28 16:22:17 UTC (rev 1173)
@@ -550,7 +550,6 @@
existing = 0
dbs_created = []
constraints = {}
- createSQLs = {}
for soClass in self.classes(require_some=True):
if (self.options.create_db
and soClass._connection not in dbs_created):
@@ -561,8 +560,6 @@
dbs_created.append(soClass._connection)
if soClass._connection not in constraints.keys():
constraints[soClass._connection] = []
- if soClass._connection not in createSQLs.keys():
- createSQLs[soClass._connection] = []
exists = soClass._connection.tableExists(soClass.sqlmeta.table)
if v >= 1:
if exists:
@@ -580,16 +577,6 @@
tableConstraints = soClass.createTable()
if tableConstraints:
constraints[soClass._connection].append(tableConstraints)
- tableCreateSQLs = getattr(soClass.sqlmeta, 'createSQL', None)
- if tableCreateSQLs:
- assert isinstance(tableCreateSQLs,(str,list,dict)), (
- '%s.sqlmeta.createSQL must be a str, list or dict.' %
- (soClass.__name__))
- if isinstance(tableCreateSQLs, dict):
- tableCreateSQLs = tableCreateSQLs.get(soClass._connection.dbName, [])
- if isinstance(tableCreateSQLs, str):
- tableCreateSQLs = [tableCreateSQLs]
- createSQLs[soClass._connection].append(tableCreateSQLs)
else:
print 'Cancelled'
else:
@@ -597,26 +584,11 @@
tableConstraints = soClass.createTable()
if tableConstraints:
constraints[soClass._connection].append(tableConstraints)
- tableCreateSQLs = getattr(soClass.sqlmeta, 'createSQL', None)
- if tableCreateSQLs:
- assert isinstance(tableCreateSQLs,(str,list,dict)), (
- '%s.sqlmeta.createSQL must be a str, list or dict.' %
- (soClass.__name__))
- if isinstance(tableCreateSQLs, dict):
- tableCreateSQLs = tableCreateSQLs.get(soClass._connection.dbName, [])
- if isinstance(tableCreateSQLs, str):
- tableCreateSQLs = [tableCreateSQLs]
- createSQLs[soClass._connection].append(tableCreateSQLs)
for connection in constraints.keys():
for constraintList in constraints[connection]:
for constraint in constraintList:
if constraint:
connection.query(constraint)
- for connection in createSQLs.keys():
- for createSQLsList in createSQLs[connection]:
- for createSQL in createSQLsList:
- if createSQL:
- connection.query(createSQL)
if v >= 1:
print '%i tables created (%i already exist)' % (
created, existing)
|
|
From: <sub...@co...> - 2005-10-28 16:03:37
|
Author: test Date: 2005-10-28 16:03:16 +0000 (Fri, 28 Oct 2005) New Revision: 1172 Modified: SQLObject/trunk/sqlobject/firebird/firebirdconnection.py Log: Missing import from Garrett Smith Modified: SQLObject/trunk/sqlobject/firebird/firebirdconnection.py =================================================================== --- SQLObject/trunk/sqlobject/firebird/firebirdconnection.py 2005-10-28 15:39:22 UTC (rev 1171) +++ SQLObject/trunk/sqlobject/firebird/firebirdconnection.py 2005-10-28 16:03:16 UTC (rev 1172) @@ -1,7 +1,8 @@ +import re +import os from sqlobject.dbconnection import DBAPI +from sqlobject import col kinterbasdb = None -import re -import os class FirebirdConnection(DBAPI): |
|
From: <sub...@co...> - 2005-10-28 15:39:28
|
Author: phd Date: 2005-10-28 15:39:22 +0000 (Fri, 28 Oct 2005) New Revision: 1171 Modified: home/phd/SQLObject/paramstyles/docs/News.txt Log: Updated News. Modified: home/phd/SQLObject/paramstyles/docs/News.txt =================================================================== --- home/phd/SQLObject/paramstyles/docs/News.txt 2005-10-28 15:39:11 UTC (rev 1170) +++ home/phd/SQLObject/paramstyles/docs/News.txt 2005-10-28 15:39:22 UTC (rev 1171) @@ -21,8 +21,6 @@ Constraints (in the DBMSes that support constraints) are added after the tables have been created. -* Added ``TimeCol``. - * ``RelatedJoin`` and ``SQLRelatedJoin`` objects have a ``createRelatedTable`` keyword argument (default ``True``). If ``False``, then the related table won't be automatically created; @@ -36,6 +34,15 @@ Examples in sqlobject/tests/test_sqlobject_admin.py or at <http://sqlobject.org/sqlobject-admin.html#the-create-command> +SQLObject 0.7.1 +=============== + +* Added ``TimeCol``. + +* Added support for psycopg2_ + +.. _psycopg2: http://initd.org/projects/psycopg2 + SQLObject 0.7.0 =============== |
|
From: <sub...@co...> - 2005-10-28 15:39:19
|
Author: phd Date: 2005-10-28 15:39:11 +0000 (Fri, 28 Oct 2005) New Revision: 1170 Modified: SQLObject/branches/0.7-bugfix/docs/News.txt Log: Updated News. Modified: SQLObject/branches/0.7-bugfix/docs/News.txt =================================================================== --- SQLObject/branches/0.7-bugfix/docs/News.txt 2005-10-28 15:38:54 UTC (rev 1169) +++ SQLObject/branches/0.7-bugfix/docs/News.txt 2005-10-28 15:39:11 UTC (rev 1170) @@ -12,6 +12,10 @@ * Added ``TimeCol``. +* Added support for psycopg2_ + +.. _psycopg2: http://initd.org/projects/psycopg2 + SQLObject 0.7.0 =============== |