sqlobject-cvs Mailing List for SQLObject (Page 167)
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
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <sub...@co...> - 2005-04-05 13:05:57
|
Author: phd Date: 2005-04-05 13:05:51 +0000 (Tue, 05 Apr 2005) New Revision: 713 Modified: trunk/SQLObject/sqlobject/col.py trunk/SQLObject/sqlobject/mysql/mysqlconnection.py Log: BINARY columns in MySQL are too binary - they ignore locale order and are sorted in their binary order. Because of that I have changed the entire concept of case_sensitive attribute: it is renamed to char_binary, the default is None, and it is still only meaninful for MySQL. Modified: trunk/SQLObject/sqlobject/col.py =================================================================== --- trunk/SQLObject/sqlobject/col.py 2005-04-04 20:50:38 UTC (rev 712) +++ trunk/SQLObject/sqlobject/col.py 2005-04-05 13:05:51 UTC (rev 713) @@ -352,7 +352,7 @@ def __init__(self, **kw): self.length = popKey(kw, 'length') self.varchar = popKey(kw, 'varchar', 'auto') - self.case_sensitive = popKey(kw, 'case_sensitive', True) + self.char_binary = popKey(kw, 'char_binary', None) # A hack for MySQL if not self.length: assert self.varchar == 'auto' or not self.varchar, \ "Without a length strings are treated as TEXT, not varchar" @@ -379,12 +379,12 @@ return 'CHAR(%i)' % self.length def _check_case_sensitive(self, db): - if not self.case_sensitive: - raise ValueError, "%s does not support case-insensitive columns" % db + if self.char_binary: + raise ValueError, "%s does not support binary character columns" % db def _mysqlType(self): type = self._sqlType() - if self.case_sensitive: + if self.char_binary: type += " BINARY" return type Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py =================================================================== --- trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-04-04 20:50:38 UTC (rev 712) +++ trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-04-05 13:05:51 UTC (rev 713) @@ -141,18 +141,18 @@ return col.IntCol, {} elif t.startswith('varchar'): if t.endswith('binary'): - return col.StringCol, {'length': int(t[8:-8])} + return col.StringCol, {'length': int(t[8:-8]), + 'char_binary': True} else: - return col.StringCol, {'length': int(t[8:-1]), - 'case_sensitive': False} + return col.StringCol, {'length': int(t[8:-1])} elif t.startswith('char'): if t.endswith('binary'): return col.StringCol, {'length': int(t[5:-8]), - 'varchar': False} + 'varchar': False, + 'char_binary': True} else: return col.StringCol, {'length': int(t[5:-1]), - 'varchar': False, - 'case_sensitive': False} + 'varchar': False} elif t.startswith('datetime'): return col.DateTimeCol, {} elif t.startswith('bool'): |
From: <sub...@co...> - 2005-04-04 20:50:43
|
Author: phd Date: 2005-04-04 20:50:38 +0000 (Mon, 04 Apr 2005) New Revision: 712 Modified: trunk/SQLObject/sqlobject/col.py trunk/SQLObject/sqlobject/mysql/mysqlconnection.py Log: StringCol now has a new attribute: case_sensitive. Default value for is True. Currently it is meaningful to set it to False only for MySQL: it strips CHAR/VARCHAR columns off of the BINARY attribute. Modified: trunk/SQLObject/sqlobject/col.py =================================================================== --- trunk/SQLObject/sqlobject/col.py 2005-04-04 19:08:30 UTC (rev 711) +++ trunk/SQLObject/sqlobject/col.py 2005-04-04 20:50:38 UTC (rev 712) @@ -352,6 +352,7 @@ def __init__(self, **kw): self.length = popKey(kw, 'length') self.varchar = popKey(kw, 'varchar', 'auto') + self.case_sensitive = popKey(kw, 'case_sensitive', True) if not self.length: assert self.varchar == 'auto' or not self.varchar, \ "Without a length strings are treated as TEXT, not varchar" @@ -377,19 +378,40 @@ else: return 'CHAR(%i)' % self.length + def _check_case_sensitive(self, db): + if not self.case_sensitive: + raise ValueError, "%s does not support case-insensitive columns" % db + + def _mysqlType(self): + type = self._sqlType() + if self.case_sensitive: + type += " BINARY" + return type + + def _postgresType(self): + self._check_case_sensitive("PostgreSQL") + return super(SOStringCol, self)._postgresType() + + def _sqliteType(self): + self._check_case_sensitive("SQLite") + return super(SOStringCol, self)._sqliteType() + def _sybaseType(self): + self._check_case_sensitive("SYBASE") type = self._sqlType() if not self.notNone and not self.alternateID: type += ' NULL' return type def _firebirdType(self): + self._check_case_sensitive("FireBird") if not self.length: return 'BLOB SUB_TYPE TEXT' else: return self._sqlType() def _maxdbType(self): + self._check_case_sensitive("SAP DB/MaxDB") if not self.length: return 'LONG ASCII' else: Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py =================================================================== --- trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-04-04 19:08:30 UTC (rev 711) +++ trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-04-04 20:50:38 UTC (rev 712) @@ -140,10 +140,19 @@ if t.startswith('int'): return col.IntCol, {} elif t.startswith('varchar'): - return col.StringCol, {'length': int(t[8:-1])} + if t.endswith('binary'): + return col.StringCol, {'length': int(t[8:-8])} + else: + return col.StringCol, {'length': int(t[8:-1]), + 'case_sensitive': False} elif t.startswith('char'): - return col.StringCol, {'length': int(t[5:-1]), - 'varchar': False} + if t.endswith('binary'): + return col.StringCol, {'length': int(t[5:-8]), + 'varchar': False} + else: + return col.StringCol, {'length': int(t[5:-1]), + 'varchar': False, + 'case_sensitive': False} elif t.startswith('datetime'): return col.DateTimeCol, {} elif t.startswith('bool'): |
From: <sub...@co...> - 2005-04-04 19:08:35
|
Author: phd Date: 2005-04-04 19:08:30 +0000 (Mon, 04 Apr 2005) New Revision: 711 Modified: trunk/SQLObject/sqlobject/cache.py Log: Fixed a misspelling. Modified: trunk/SQLObject/sqlobject/cache.py =================================================================== --- trunk/SQLObject/sqlobject/cache.py 2005-04-04 16:58:07 UTC (rev 710) +++ trunk/SQLObject/sqlobject/cache.py 2005-04-04 19:08:30 UTC (rev 711) @@ -186,7 +186,7 @@ # This offset tries to balance out which objects we # expire, so no object will just hang out in the cache # forever. - self.cullOffset = (self.culldOffset + 1) % self.cullFraction + self.cullOffset = (self.cullOffset + 1) % self.cullFraction finally: self.lock.release() |
From: <sub...@co...> - 2005-04-04 05:56:32
|
Author: ianb Date: 2005-04-04 05:56:29 +0000 (Mon, 04 Apr 2005) New Revision: 708 Modified: trunk/SQLObject/sqlobject/col.py Log: Reverting commits I didn't mean to make Modified: trunk/SQLObject/sqlobject/col.py =================================================================== --- trunk/SQLObject/sqlobject/col.py 2005-04-04 05:54:10 UTC (rev 707) +++ trunk/SQLObject/sqlobject/col.py 2005-04-04 05:56:29 UTC (rev 708) @@ -980,218 +980,3 @@ all.append(key) __all__.extend(all) del all - - - -# import boundattributes -# import declarative - -# class BoundCol(object): - -# __metaclass__ = declarative.DeclarativeMeta - -# sqlTypeRegistry = {} - -# def __classinit__(cls, new_attrs): -# cls.sqlTypeRegistry = cls.sqlTypeRegistry.copy() - -# def registerSQLType(cls, dbname, type_func): -# if not isinstance(dbname, (list, tuple)): -# dbname = [dbname] -# for name in dbname: -# cls.sqlTypeRegistry[name] = type_func -# registerSQLType = classmethod(registerSQLType) - -# def __init__(self, added_class, attr_name, -# dbName=None, -# default=NoDefault, -# alternateID=False, -# alternateMethodName=None, -# constraints=None, -# notNull=NoDefault, -# notNon=NoDefault, -# sqlType=None, -# validator=None, -# immutable=False, -# cascade=None, -# lazy=False, -# noCache=False, -# forceDBName=False): -# if not forceDBName: -# assert sqlbuilder.sqlIdentifier(name), ( -# "Name must be SQL-safe (letters, numbers, underscores): " -# "%s (or use forceDBName=True)" % repr(name)) -# assert attr_name != 'id', ( -# "The column name 'id' is reserved for SQLObject use (and is " -# "implicitly created).") -# assert name, "You must provide a name for all columns" -# self.immutable = immutable -# assert cascade in (None, True, False), ( -# "The cascade attribute must be one of None, True, or False " -# "(you gave: %r" % cascade) -# self.cascade = cascade - -# if not isinstance(constraints, (list, tuple)): -# constraints = [constraints] -# self.constraints = self.autoConstraints() + constraints - -# self.notNone = False -# if notNull is not NoDefault: -# self.notNone = notNull -# assert (notNone is NoDefault or -# (not notNone) == (not notNull)), ( -# "The notNull and notNone arguments are aliases, and must " -# "not conflict. You gave notNull=%r, notNone=%r" -# % (notNull, notNone)) -# elif notNone is not NoDefault: -# self.notNone = notNone -# if self.notNone: -# self.constraints = [consts.notNull] + self.constraints - -# self.name = attr_name -# self.soClass = added_class -# self._default = default -# self.customSQLType = sqlType - -# # if they don't give us a specific database name for -# # the column, we separate the mixedCase into mixed_case -# # and assume that. -# if dbName is None: -# self.dbName = added_class.sqlmeta.style.pythonAttrToDBColumn(self.name) -# else: -# self.dbName = dbName - -# # alternateID means that this is a unique column that -# # can be used to identify rows -# self.alternateID = alternateID -# if self.alternateID and alternateMethodName is None: -# self.alternateMethodName = 'by' + self.name[0].capitalize() + self.name[1:] -# else: -# self.alternateMethodName = alternateMethodName - -# if unique is NoDefault: -# self.unique = alternateID -# else: -# self.unique = unique -# self.validator = validator -# self.noCache = noCache -# self.lazy = lazy - -# self.soClass.addColumn(self.name, self) - -# def _set_validator(self, value): -# self._validator = value -# if self._validator: -# self.toPython = self._validator.toPython -# self.fromPython = self._validator.fromPython -# else: -# self.toPython = None -# self.fromPython = None - -# def _get_validator(self): -# return self._validator - -# validator = property(_get_validator, _set_validator) - -# def autoConstraints(self): -# return [] - -# def _get_default(self): -# # A default can be a callback or a plain value, -# # here we resolve the callback -# if self._default is NoDefault: -# return NoDefault -# elif hasattr(self._default, '__sqlrepr__'): -# return self._default -# elif callable(self._default): -# return self._default() -# else: -# return self._default -# default = property(_get_default, None, None) - -# def __repr__(self): -# r = '<%s %s' % (self.__class__.__name__, self.name) -# if self.default is not NoDefault: -# r += ' default=%s' % repr(self.default) -# if self.foreignKey: -# r += ' connected to %s' % self.foreignKey -# if self.alternateID: -# r += ' alternate ID' -# if self.notNone: -# r += ' not null' -# return r + '>' - -# def createSQL(self, dbname): -# if self.customSQLType is not None: -# return self.customSQLType -# try: -# func = self.sqlTypeRegistry[dbname] -# except KeyError: -# assert 0, ( -# "The database %s does not support the column type %s " -# "(no SQL type generator found; only %s found)" -# % (dbname, self.__class__.__name__, -# ', '.join(self.sqlTypeRegistry.keys()))) -# else: -# return func(self, dbname) - -# def __get__(self, obj, type=None): -# if obj is None: -# # class attribute, return the descriptor itself -# return self -# if obj.sqlmeta.obsolete: -# raise SQLObjectNotFound, ( -# "This object has been deleted from the database: %r" -# % obj) -# if obj.sqlmeta.cacheColumns: -# columns = obj.sqlmeta._columnCache -# if columns is None: -# obj.sqlmeta.loadValues() -# try: -# return columns[name] -# except KeyError: -# return obj.sqlmeta.loadColumn(self) -# else: -# return obj.sqlmeta.loadColumn(self) - -# def __set__(self, obj, value): -# if self.immutable: -# raise AttributeError("The column %s.%s is immutable" % -# (obj.__class__.__name__, -# self.name)) -# obj.sqlmeta.setColumn(self, value) - -# def __delete__(self, obj): -# raise AttributeError("I can't be deleted from %r" % obj) - -# class Col(boundattributes.BoundFactory): -# factory_class = BoundCol - -# def addToClass(self, cls, addToThisClass, name): -# me = self or cls -# me.__addtoclass__(addToThisClass, name) -# addToClass = declarative.classinstancemethod(addToClass) - -# class BoundStringCol(BoundCol): - -# def __init__(self, *args, **kw): -# self.length = popKey(kw, 'length') -# self.varchar = popKey(kw, 'varchar', 'auto') -# if not self.length: -# assert self.varchar == 'auto' or not self.varchar, ( -# "Without a length strings are treated as TEXT, not " -# "VARCHAR") -# self.varchar = False -# elif self.varchar == 'auto': -# self.varchar = True -# BoundCol.__init__(self, *args, **kw) - -# def autoConstraints(self): -# constraints = [consts.isString] -# if self.length is not None: -# constraints += [consts.MaxLength(self.length)] -# return constraints - - -# class StringCol(Col): -# factory_class = BoundStringCol |
From: <sub...@co...> - 2005-04-04 05:54:22
|
Author: ianb Date: 2005-04-04 05:54:10 +0000 (Mon, 04 Apr 2005) New Revision: 707 Modified: trunk/SQLObject/sqlobject/boundattributes.py trunk/SQLObject/sqlobject/col.py trunk/SQLObject/sqlobject/main.py trunk/SQLObject/sqlobject/mysql/__init__.py Log: Added a default connection, sqlhub; made _connection.style setting in __classinit__ catch the AttributeError that hubs can throw Modified: trunk/SQLObject/sqlobject/boundattributes.py =================================================================== --- trunk/SQLObject/sqlobject/boundattributes.py 2005-04-04 02:21:00 UTC (rev 706) +++ trunk/SQLObject/sqlobject/boundattributes.py 2005-04-04 05:54:10 UTC (rev 707) @@ -100,8 +100,8 @@ factory_class = None - def make_object(cls, *args, **kw): - return cls.factory_class(*args, **kw) + def make_object(cls, added_class, attr_name, *args, **kw): + return cls.factory_class(added_class, attr_name, *args, **kw) def bind_attributes(cls, new_attrs): for name, value in new_attrs.items(): Modified: trunk/SQLObject/sqlobject/col.py =================================================================== --- trunk/SQLObject/sqlobject/col.py 2005-04-04 02:21:00 UTC (rev 706) +++ trunk/SQLObject/sqlobject/col.py 2005-04-04 05:54:10 UTC (rev 707) @@ -980,3 +980,218 @@ all.append(key) __all__.extend(all) del all + + + +# import boundattributes +# import declarative + +# class BoundCol(object): + +# __metaclass__ = declarative.DeclarativeMeta + +# sqlTypeRegistry = {} + +# def __classinit__(cls, new_attrs): +# cls.sqlTypeRegistry = cls.sqlTypeRegistry.copy() + +# def registerSQLType(cls, dbname, type_func): +# if not isinstance(dbname, (list, tuple)): +# dbname = [dbname] +# for name in dbname: +# cls.sqlTypeRegistry[name] = type_func +# registerSQLType = classmethod(registerSQLType) + +# def __init__(self, added_class, attr_name, +# dbName=None, +# default=NoDefault, +# alternateID=False, +# alternateMethodName=None, +# constraints=None, +# notNull=NoDefault, +# notNon=NoDefault, +# sqlType=None, +# validator=None, +# immutable=False, +# cascade=None, +# lazy=False, +# noCache=False, +# forceDBName=False): +# if not forceDBName: +# assert sqlbuilder.sqlIdentifier(name), ( +# "Name must be SQL-safe (letters, numbers, underscores): " +# "%s (or use forceDBName=True)" % repr(name)) +# assert attr_name != 'id', ( +# "The column name 'id' is reserved for SQLObject use (and is " +# "implicitly created).") +# assert name, "You must provide a name for all columns" +# self.immutable = immutable +# assert cascade in (None, True, False), ( +# "The cascade attribute must be one of None, True, or False " +# "(you gave: %r" % cascade) +# self.cascade = cascade + +# if not isinstance(constraints, (list, tuple)): +# constraints = [constraints] +# self.constraints = self.autoConstraints() + constraints + +# self.notNone = False +# if notNull is not NoDefault: +# self.notNone = notNull +# assert (notNone is NoDefault or +# (not notNone) == (not notNull)), ( +# "The notNull and notNone arguments are aliases, and must " +# "not conflict. You gave notNull=%r, notNone=%r" +# % (notNull, notNone)) +# elif notNone is not NoDefault: +# self.notNone = notNone +# if self.notNone: +# self.constraints = [consts.notNull] + self.constraints + +# self.name = attr_name +# self.soClass = added_class +# self._default = default +# self.customSQLType = sqlType + +# # if they don't give us a specific database name for +# # the column, we separate the mixedCase into mixed_case +# # and assume that. +# if dbName is None: +# self.dbName = added_class.sqlmeta.style.pythonAttrToDBColumn(self.name) +# else: +# self.dbName = dbName + +# # alternateID means that this is a unique column that +# # can be used to identify rows +# self.alternateID = alternateID +# if self.alternateID and alternateMethodName is None: +# self.alternateMethodName = 'by' + self.name[0].capitalize() + self.name[1:] +# else: +# self.alternateMethodName = alternateMethodName + +# if unique is NoDefault: +# self.unique = alternateID +# else: +# self.unique = unique +# self.validator = validator +# self.noCache = noCache +# self.lazy = lazy + +# self.soClass.addColumn(self.name, self) + +# def _set_validator(self, value): +# self._validator = value +# if self._validator: +# self.toPython = self._validator.toPython +# self.fromPython = self._validator.fromPython +# else: +# self.toPython = None +# self.fromPython = None + +# def _get_validator(self): +# return self._validator + +# validator = property(_get_validator, _set_validator) + +# def autoConstraints(self): +# return [] + +# def _get_default(self): +# # A default can be a callback or a plain value, +# # here we resolve the callback +# if self._default is NoDefault: +# return NoDefault +# elif hasattr(self._default, '__sqlrepr__'): +# return self._default +# elif callable(self._default): +# return self._default() +# else: +# return self._default +# default = property(_get_default, None, None) + +# def __repr__(self): +# r = '<%s %s' % (self.__class__.__name__, self.name) +# if self.default is not NoDefault: +# r += ' default=%s' % repr(self.default) +# if self.foreignKey: +# r += ' connected to %s' % self.foreignKey +# if self.alternateID: +# r += ' alternate ID' +# if self.notNone: +# r += ' not null' +# return r + '>' + +# def createSQL(self, dbname): +# if self.customSQLType is not None: +# return self.customSQLType +# try: +# func = self.sqlTypeRegistry[dbname] +# except KeyError: +# assert 0, ( +# "The database %s does not support the column type %s " +# "(no SQL type generator found; only %s found)" +# % (dbname, self.__class__.__name__, +# ', '.join(self.sqlTypeRegistry.keys()))) +# else: +# return func(self, dbname) + +# def __get__(self, obj, type=None): +# if obj is None: +# # class attribute, return the descriptor itself +# return self +# if obj.sqlmeta.obsolete: +# raise SQLObjectNotFound, ( +# "This object has been deleted from the database: %r" +# % obj) +# if obj.sqlmeta.cacheColumns: +# columns = obj.sqlmeta._columnCache +# if columns is None: +# obj.sqlmeta.loadValues() +# try: +# return columns[name] +# except KeyError: +# return obj.sqlmeta.loadColumn(self) +# else: +# return obj.sqlmeta.loadColumn(self) + +# def __set__(self, obj, value): +# if self.immutable: +# raise AttributeError("The column %s.%s is immutable" % +# (obj.__class__.__name__, +# self.name)) +# obj.sqlmeta.setColumn(self, value) + +# def __delete__(self, obj): +# raise AttributeError("I can't be deleted from %r" % obj) + +# class Col(boundattributes.BoundFactory): +# factory_class = BoundCol + +# def addToClass(self, cls, addToThisClass, name): +# me = self or cls +# me.__addtoclass__(addToThisClass, name) +# addToClass = declarative.classinstancemethod(addToClass) + +# class BoundStringCol(BoundCol): + +# def __init__(self, *args, **kw): +# self.length = popKey(kw, 'length') +# self.varchar = popKey(kw, 'varchar', 'auto') +# if not self.length: +# assert self.varchar == 'auto' or not self.varchar, ( +# "Without a length strings are treated as TEXT, not " +# "VARCHAR") +# self.varchar = False +# elif self.varchar == 'auto': +# self.varchar = True +# BoundCol.__init__(self, *args, **kw) + +# def autoConstraints(self): +# constraints = [consts.isString] +# if self.length is not None: +# constraints += [consts.MaxLength(self.length)] +# return constraints + + +# class StringCol(Col): +# factory_class = BoundStringCol Modified: trunk/SQLObject/sqlobject/main.py =================================================================== --- trunk/SQLObject/sqlobject/main.py 2005-04-04 02:21:00 UTC (rev 706) +++ trunk/SQLObject/sqlobject/main.py 2005-04-04 05:54:10 UTC (rev 707) @@ -187,10 +187,12 @@ def setClass(cls, soClass): cls.soClass = soClass if not cls.style: - if cls.soClass._connection and cls.soClass._connection.style: - cls.style = cls.soClass._connection.style - else: - cls.style = styles.defaultStyle + cls.style = styles.defaultStyle + try: + if cls.soClass._connection and cls.soClass._connection.style: + cls.style = cls.soClass._connection.style + except AttributeError: + pass if cls.table is None: cls.table = cls.style.pythonClassToDBTable(cls.soClass.__name__) if cls.idName is None: @@ -221,6 +223,8 @@ setClass = classmethod(setClass) +sqlhub = dbconnection.ConnectionHub() + class _sqlmeta_attr(object): def __init__(self, name, deprecation_level): @@ -267,7 +271,7 @@ # will be true. It's false by default: _SO_perConnection = False - _connection = None + _connection = sqlhub _columns = [] @@ -1397,4 +1401,4 @@ __all__ = ['NoDefault', 'SQLObject', 'sqlmeta', 'getID', 'getObject', - 'SQLObjectNotFound'] + 'SQLObjectNotFound', 'sqlhub'] Modified: trunk/SQLObject/sqlobject/mysql/__init__.py =================================================================== --- trunk/SQLObject/sqlobject/mysql/__init__.py 2005-04-04 02:21:00 UTC (rev 706) +++ trunk/SQLObject/sqlobject/mysql/__init__.py 2005-04-04 05:54:10 UTC (rev 707) @@ -1,4 +1,5 @@ from sqlobject.dbconnection import registerConnection +#import mysqltypes def builder(): import mysqlconnection |
From: <sub...@co...> - 2005-04-04 02:21:05
|
Author: ianb Date: 2005-04-04 02:21:00 +0000 (Mon, 04 Apr 2005) New Revision: 706 Modified: trunk/SQLObject/sqlobject/manager/command.py Log: Added support for WSGIKit configuration files, including 'database' key and 'sys_path' key Modified: trunk/SQLObject/sqlobject/manager/command.py =================================================================== --- trunk/SQLObject/sqlobject/manager/command.py 2005-04-04 02:20:15 UTC (rev 705) +++ trunk/SQLObject/sqlobject/manager/command.py 2005-04-04 02:21:00 UTC (rev 706) @@ -3,6 +3,10 @@ import fnmatch import os import sys +try: + from wsgikit import pyconfig +except ImportError: + pyconfig = None import sqlobject from sqlobject.util import moduleloader @@ -65,6 +69,11 @@ help="The database connection URI", metavar='URI', dest='connection_uri') + if pyconfig: + parser.add_option('-f', '--config-file', + help="The WSGIKit config file that contains the database URI (in the database key)", + metavar="FILE", + dest="config_file") parser.add_option('-m', '--module', help="Module in which to find SQLObject classes", action='append', @@ -138,6 +147,9 @@ if not getattr(self.options, var_name, None): self.runner.invalid( 'You must provide the option %s' % option_name) + conf = self.config() + if conf and conf.get('sys_path'): + update_sys_path(conf['sys_path'], self.options.verbose) self.command() def classes(self, require_connection=True): @@ -193,11 +205,27 @@ return all def connection(self): - if getattr(self.options, 'connection_uri', None): + config = self.config() + if config is not None: + assert config.get('database'), ( + "No database variable found in config file %s" + % self.options.config_file) + return sqlobject.connectionForURI(config['database']) + elif getattr(self.options, 'connection_uri', None): return sqlobject.connectionForURI(self.options.connection_uri) else: return None + def config(self): + if getattr(self.options, 'config_file', None): + assert pyconfig, ( + "The --config-file option should not be available without wsgikit.pyconfig installed") + config = pyconfig.Config() + config.load(self.options.config_file) + return config + else: + return None + def classes_from_package(self, package_name): raise NotImplementedError @@ -417,6 +445,15 @@ print '%s (Aliases: %s)' % ( ' '*max_len, ', '.join(command.aliases)) +def update_sys_path(paths, verbose): + if isinstance(paths, (str, unicode)): + paths = [paths] + for path in paths: + path = os.path.abspath(path) + if path not in sys.path: + if verbose: + print 'Adding %s to path' % path + sys.path.append(path) if __name__ == '__main__': the_runner.run(sys.argv) |
From: <sub...@co...> - 2005-04-04 02:20:20
|
Author: ianb Date: 2005-04-04 02:20:15 +0000 (Mon, 04 Apr 2005) New Revision: 705 Modified: trunk/SQLObject/scripts/sqlobject-admin Log: Fixed sys.path fixup Modified: trunk/SQLObject/scripts/sqlobject-admin =================================================================== --- trunk/SQLObject/scripts/sqlobject-admin 2005-04-02 00:14:25 UTC (rev 704) +++ trunk/SQLObject/scripts/sqlobject-admin 2005-04-04 02:20:15 UTC (rev 705) @@ -11,8 +11,8 @@ updir = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(here))), 'sqlobject') - if sys.path.exists(updir): - sys.path.insert(0, updir) + if os.path.exists(updir): + sys.path.insert(0, os.path.dirname(updir)) else: print 'I cannot find the sqlobject module' print 'If SQLObject is installed, you may need to set $PYTHONPATH' |
From: <sub...@co...> - 2005-04-02 00:14:33
|
Author: ianb Date: 2005-04-02 00:14:25 +0000 (Sat, 02 Apr 2005) New Revision: 704 Modified: trunk/SQLObject/sqlobject/manager/command.py Log: Added status command Modified: trunk/SQLObject/sqlobject/manager/command.py =================================================================== --- trunk/SQLObject/sqlobject/manager/command.py 2005-04-01 23:54:02 UTC (rev 703) +++ trunk/SQLObject/sqlobject/manager/command.py 2005-04-02 00:14:25 UTC (rev 704) @@ -116,11 +116,12 @@ self.runner = runner def run(self): - self.parser.usage = "%%prog [options]\n%s" % ( - self.description or self.summary) + self.parser.usage = "%%prog [options]\n%s" % self.summary self.parser.prog = '%s %s' % ( os.path.basename(self.invoked_as), self.command_name) + if self.description: + self.parser.description = description self.options, self.args = self.parser.parse_args(self.raw_args) if (getattr(self.options, 'simulate', False) and not self.options.verbose): @@ -192,7 +193,7 @@ return all def connection(self): - if self.options.connection_uri: + if getattr(self.options, 'connection_uri', None): return sqlobject.connectionForURI(self.options.connection_uri) else: return None @@ -324,6 +325,70 @@ print '%i tables dropped (%i didn\'t exist)' % ( dropped, not_existing) +class CommandStatus(Command): + + name = 'status' + summary = 'Show status of classes vs. database' + + parser = standard_parser(simulate=False) + + def print_class(self, soClass): + if self.printed: + return + self.printed = True + print 'Checking %s...' % soClass.__name__ + + def command(self): + good = 0 + bad = 0 + missing_tables = 0 + columnsFromSchema_warning = False + for soClass in self.classes(): + conn = soClass._connection + self.printed = False + if self.options.verbose: + self.print_class(soClass) + if not conn.tableExists(soClass.sqlmeta.table): + self.print_class(soClass) + print ' Does not exist in database' + missing_tables += 1 + continue + try: + columns = conn.columnsFromSchema(soClass.sqlmeta.table, + soClass) + except AttributeError: + if not columnsFromSchema_warning: + print 'Database does not support reading columns' + columnsFromSchema_warning = True + good += 1 + continue + existing = {} + for col in columns: + col = col.withClass(soClass) + existing[col.dbName] = col + missing = {} + for col in soClass.sqlmeta._columns: + if existing.has_key(col.dbName): + del existing[col.dbName] + else: + missing[col.dbName] = col + if existing: + self.print_class(soClass) + for col in existing.values(): + print ' Database has extra column: %s' % col.dbName + if missing: + self.print_class(soClass) + for col in missing.values(): + print ' Database missing column: %s' % col.dbName + if existing or missing: + bad += 1 + else: + good += 1 + if self.options.verbose: + print '%i in sync; %i out of sync; %i not in database' % ( + good, bad, missing_tables) + + class CommandHelp(Command): name = 'help' |
From: <sub...@co...> - 2005-04-01 23:54:05
|
Author: ianb Date: 2005-04-01 23:54:02 +0000 (Fri, 01 Apr 2005) New Revision: 703 Modified: trunk/SQLObject/sqlobject/manager/command.py Log: Improve error and help messages slightly Modified: trunk/SQLObject/sqlobject/manager/command.py =================================================================== --- trunk/SQLObject/sqlobject/manager/command.py 2005-04-01 23:36:18 UTC (rev 702) +++ trunk/SQLObject/sqlobject/manager/command.py 2005-04-01 23:54:02 UTC (rev 703) @@ -25,7 +25,8 @@ break else: # no command found - self.invalid('No COMMAND given') + self.invalid('No COMMAND given (try "%s help")' + % os.path.basename(invoked_as)) real_command = self.command_aliases.get(command, command) if real_command not in self.commands.keys(): self.invalid('COMMAND %s unknown' % command) @@ -100,6 +101,7 @@ max_args_error = 'You must provide no more than %(max_args)s arguments' aliases = () required_args = [] + description = None def __classinit__(cls, new_args): if cls.__bases__ == (object,): @@ -114,6 +116,11 @@ self.runner = runner def run(self): + self.parser.usage = "%%prog [options]\n%s" % ( + self.description or self.summary) + self.parser.prog = '%s %s' % ( + os.path.basename(self.invoked_as), + self.command_name) self.options, self.args = self.parser.parse_args(self.raw_args) if (getattr(self.options, 'simulate', False) and not self.options.verbose): @@ -331,8 +338,9 @@ the_runner.run([self.invoked_as, self.args[0], '-h']) else: print 'Available commands:' - print ' (use "%s help COMMAND" or "%s COMMAND -h" for more)' % ( + print ' (use "%s help COMMAND" or "%s COMMAND -h" ' % ( self.prog_name, self.prog_name) + print ' for more information)' items = the_runner.commands.items() items.sort() max_len = max([len(cn) for cn, c in items]) |
From: <sub...@co...> - 2005-04-01 23:36:41
|
Author: ianb Date: 2005-04-01 23:36:18 +0000 (Fri, 01 Apr 2005) New Revision: 702 Added: trunk/SQLObject/scripts/ trunk/SQLObject/scripts/sqlobject-admin trunk/SQLObject/sqlobject/manager/ trunk/SQLObject/sqlobject/manager/__init__.py trunk/SQLObject/sqlobject/manager/command.py trunk/SQLObject/sqlobject/util/moduleloader.py Modified: trunk/SQLObject/setup.py Log: First go at a command-line client Added: trunk/SQLObject/scripts/sqlobject-admin =================================================================== --- trunk/SQLObject/scripts/sqlobject-admin 2005-04-01 22:41:16 UTC (rev 701) +++ trunk/SQLObject/scripts/sqlobject-admin 2005-04-01 23:36:18 UTC (rev 702) @@ -0,0 +1,23 @@ +#!/usr/bin/env python +import sys +import os +try: + import sqlobject +except ImportError: + try: + here = __file__ + except NameError: + here = sys.argv[0] + updir = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(here))), + 'sqlobject') + if sys.path.exists(updir): + sys.path.insert(0, updir) + else: + print 'I cannot find the sqlobject module' + print 'If SQLObject is installed, you may need to set $PYTHONPATH' + sys.exit(3) + +from sqlobject.manager import command +command.the_runner.run(sys.argv) + Property changes on: trunk/SQLObject/scripts/sqlobject-admin ___________________________________________________________________ Name: svn:executable + * Modified: trunk/SQLObject/setup.py =================================================================== --- trunk/SQLObject/setup.py 2005-04-01 22:41:16 UTC (rev 701) +++ trunk/SQLObject/setup.py 2005-04-01 23:36:18 UTC (rev 702) @@ -3,7 +3,7 @@ warnings.filterwarnings("ignore", "Unknown distribution option") subpackages = ['firebird', 'include', 'inheritance', 'mysql', 'postgres', - 'sqlite', 'sybase', 'maxdb'] + 'sqlite', 'sybase', 'maxdb', 'util', 'manager'] import sys # patch distutils if it can't cope with the "classifiers" keyword @@ -34,6 +34,7 @@ url="http://sqlobject.org", license="LGPL", packages=["sqlobject"] + ['sqlobject.%s' % package for package in subpackages], + scripts=["scripts/sqlobject-admin"], download_url="http://prdownloads.sourceforge.net/sqlobject/SQLObject-0.6.1.tar.gz?download") # Send announce to: Added: trunk/SQLObject/sqlobject/manager/__init__.py =================================================================== --- trunk/SQLObject/sqlobject/manager/__init__.py 2005-04-01 22:41:16 UTC (rev 701) +++ trunk/SQLObject/sqlobject/manager/__init__.py 2005-04-01 23:36:18 UTC (rev 702) @@ -0,0 +1 @@ +# Added: trunk/SQLObject/sqlobject/manager/command.py =================================================================== --- trunk/SQLObject/sqlobject/manager/command.py 2005-04-01 22:41:16 UTC (rev 701) +++ trunk/SQLObject/sqlobject/manager/command.py 2005-04-01 23:36:18 UTC (rev 702) @@ -0,0 +1,349 @@ +#!/usr/bin/env python +import optparse +import fnmatch +import os +import sys + +import sqlobject +from sqlobject.util import moduleloader +from sqlobject.declarative import DeclarativeMeta + +class CommandRunner(object): + + def __init__(self): + self.commands = {} + self.command_aliases = {} + + def run(self, argv): + invoked_as = argv[0] + args = argv[1:] + for i in range(len(args)): + if not args[i].startswith('-'): + # this must be a command + command = args[i].lower() + del args[i] + break + else: + # no command found + self.invalid('No COMMAND given') + real_command = self.command_aliases.get(command, command) + if real_command not in self.commands.keys(): + self.invalid('COMMAND %s unknown' % command) + runner = self.commands[real_command]( + invoked_as, command, args, self) + runner.run() + + def register(self, command): + name = command.name + self.commands[name] = command + for alias in command.aliases: + self.command_aliases[alias] = name + + def invalid(self, msg, code=2): + print msg + sys.exit(code) + +the_runner = CommandRunner() +register = the_runner.register + +def standard_parser(connection=True, simulate=True, + interactive=False): + parser = optparse.OptionParser() + parser.add_option('-v', '--verbose', + help='Be verbose (multiple times for more verbosity)', + action='count', + dest='verbose', + default=0) + if simulate: + parser.add_option('-n', '--simulate', + help="Don't actually do anything (implies -v)", + action='store_true', + dest='simulate') + if connection: + parser.add_option('-c', '--connection', + help="The database connection URI", + metavar='URI', + dest='connection_uri') + parser.add_option('-m', '--module', + help="Module in which to find SQLObject classes", + action='append', + metavar='MODULE', + dest='modules', + default=[]) + parser.add_option('-p', '--package', + help="Package to search for SQLObject classes", + action="append", + metavar="PACKAGE", + dest="packages", + default=[]) + parser.add_option('--class', + help="Select only named classes (wildcards allowed)", + action="append", + metavar="NAME", + dest="class_matchers", + default=[]) + if interactive: + parser.add_option('-i', '--interactive', + help="Ask before doing anything (use twice to be more careful)", + action="count", + dest="interactive", + default=0) + return parser + +class Command(object): + + __metaclass__ = DeclarativeMeta + + min_args = 0 + min_args_error = 'You must provide at least %(min_args)s arguments' + max_args = 0 + max_args_error = 'You must provide no more than %(max_args)s arguments' + aliases = () + required_args = [] + + def __classinit__(cls, new_args): + if cls.__bases__ == (object,): + # 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 + self.raw_args = args + self.runner = runner + + def run(self): + self.options, self.args = self.parser.parse_args(self.raw_args) + if (getattr(self.options, 'simulate', False) + and not self.options.verbose): + self.options.verbose = 1 + if self.min_args is not None and len(self.args) < self.min_args: + self.runner.invalid( + self.min_args_error % {'min_args': self.min_args, + 'actual_args': len(self.args)}) + if self.max_args is not None and len(self.args) > self.max_args: + self.runner.invalid( + self.max_args_error % {'max_args': self.max_args, + 'actual_args': len(self.args)}) + for var_name, option_name in self.required_args: + if not getattr(self.options, var_name, None): + self.runner.invalid( + 'You must provide the option %s' % option_name) + self.command() + + def classes(self, require_connection=True): + all = [] + for module_name in self.options.modules: + all.extend(self.classes_from_module( + moduleloader.load_module(module_name))) + for package_name in self.options.packages: + all.extend(self.classes_from_package(package_name)) + if self.options.class_matchers: + filtered = [] + for soClass in all: + name = soClass.__name__ + for matcher in self.options.class_matchers: + if fnmatch.fnmatch(name, matcher): + filtered.append(soClass) + break + all = filtered + conn = self.connection() + if conn: + for soClass in all: + soClass._connection = conn + else: + missing = [] + for soClass in all: + try: + if not soClass._connection: + missing.append(soClass) + except AttributeError: + missing.append(soClass) + if missing and require_connection: + self.runner.invalid( + 'These classes do not have connections set:\n * %s\n' + 'You must indicate --connection=URI' + % '\n * '.join([soClass.__name__ + for soClass in missing])) + return all + + def classes_from_module(self, module): + all = [] + if hasattr(module, 'soClasses'): + for name_or_class in module.soClasses: + if isinstance(name_or_class, str): + name_or_class = getattr(module, name_or_class) + all.append(name_or_class) + else: + for name in dir(module): + value = getattr(module, name) + if (isinstance(value, type) + and issubclass(value, sqlobject.SQLObject) + and value.__module__ == module.__name__): + all.append(value) + return all + + def connection(self): + if self.options.connection_uri: + return sqlobject.connectionForURI(self.options.connection_uri) + else: + return None + + def classes_from_package(self, package_name): + raise NotImplementedError + + def command(self): + raise NotImplementedError + + def _get_prog_name(self): + return os.path.basename(self.invoked_as) + prog_name = property(_get_prog_name) + + def ask(self, prompt, safe=False, default=True): + if self.options.interactive >= 2: + default = safe + if default: + prompt += ' [Y/n]? ' + else: + prompt += ' [y/N]? ' + while 1: + response = raw_input(prompt).strip() + if not response.strip(): + return default + if response and response[0].lower() in ('y', 'n'): + return response[0].lower() == 'y' + print 'Y or N please' + +class CommandSQL(Command): + + name = 'sql' + summary = 'Show SQL CREATE statements' + + parser = standard_parser(simulate=False) + + def command(self): + classes = self.classes() + for cls in classes: + if self.options.verbose >= 1: + print '-- %s from %s' % ( + cls.__name__, cls.__module__) + print cls.createTableSQL().strip() + ';\n' + +class CommandList(Command): + + name = 'list' + summary = 'Show all SQLObject classes found' + + parser = standard_parser(simulate=False, connection=False) + + def command(self): + if self.options.verbose >= 1: + print 'Classes found:' + classes = self.classes(require_connection=False) + for soClass in classes: + print '%s.%s' % (soClass.__module__, soClass.__name__) + if self.options.verbose >= 1: + print ' Table: %s' % soClass.sqlmeta.table + +class CommandCreate(Command): + + name = 'create' + summary = 'Create tables' + + parser = standard_parser(interactive=True) + + def command(self): + v = self.options.verbose + created = 0 + existing = 0 + for soClass in self.classes(): + exists = soClass._connection.tableExists(soClass.sqlmeta.table) + if v >= 1: + if exists: + existing += 1 + print '%s already exists.' % soClass.__name__ + else: + print 'Creating %s' % soClass.__name__ + if v >= 2: + print soClass.createTableSQL() + if (not self.options.simulate + and not exists): + if self.options.interactive: + if self.ask('Create %s' % soClass.__name__): + created += 1 + soClass.createTable() + else: + print 'Cancelled' + else: + created += 1 + soClass.createTable() + if v >= 1: + print '%i tables created (%i already exist)' % ( + created, existing) + + +class CommandDrop(Command): + + name = 'drop' + summary = 'Drop tables' + + parser = standard_parser(interactive=True) + + def command(self): + v = self.options.verbose + dropped = 0 + not_existing = 0 + for soClass in self.classes(): + exists = soClass._connection.tableExists(soClass.sqlmeta.table) + if v >= 1: + if exists: + print 'Dropping %s' % soClass.__name__ + else: + not_existing += 1 + print '%s does not exist.' % soClass.__name__ + if (not self.options.simulate + and exists): + if self.options.interactive: + if self.ask('Drop %s' % soClass.__name__): + dropped += 1 + soClass.dropTable() + else: + print 'Cancelled' + else: + dropped += 1 + soClass.dropTable() + if v >= 1: + print '%i tables dropped (%i didn\'t exist)' % ( + dropped, not_existing) + +class CommandHelp(Command): + + name = 'help' + summary = 'Show help' + + parser = optparse.OptionParser() + + max_args = 1 + + def command(self): + if self.args: + the_runner.run([self.invoked_as, self.args[0], '-h']) + else: + print 'Available commands:' + print ' (use "%s help COMMAND" or "%s COMMAND -h" for more)' % ( + self.prog_name, self.prog_name) + items = the_runner.commands.items() + items.sort() + max_len = max([len(cn) for cn, c in items]) + for command_name, command in items: + print '%s:%s %s' % (command_name, + ' '*(max_len-len(command_name)), + command.summary) + if command.aliases: + print '%s (Aliases: %s)' % ( + ' '*max_len, ', '.join(command.aliases)) + + +if __name__ == '__main__': + the_runner.run(sys.argv) Property changes on: trunk/SQLObject/sqlobject/manager/command.py ___________________________________________________________________ Name: svn:executable + * Added: trunk/SQLObject/sqlobject/util/moduleloader.py =================================================================== --- trunk/SQLObject/sqlobject/util/moduleloader.py 2005-04-01 22:41:16 UTC (rev 701) +++ trunk/SQLObject/sqlobject/util/moduleloader.py 2005-04-01 23:36:18 UTC (rev 702) @@ -0,0 +1,42 @@ +import sys +import imp + +def load_module(module_name): + mod = __import__(module_name) + components = module_name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + +def load_module_from_name(filename, module_name): + if sys.modules.has_key(module_name): + return sys.modules[module_name] + init_filename = os.path.join(os.path.dirname(filename), '__init__.py') + if not os.path.exists(init_filename): + try: + f = open(init_filename, 'w') + except (OSError, IOError), e: + raise IOError( + 'Cannot write __init__.py file into directory %s (%s)\n' + % (os.path.dirname(filename), e)) + f.write('#\n') + f.close() + fp = None + if sys.modules.has_key(module_name): + return sys.modules[module_name] + if '.' in module_name: + parent_name = '.'.join(module_name.split('.')[:-1]) + base_name = module_name.split('.')[-1] + parent = load_module_from_name(os.path.dirname(filename), + parent_name) + else: + base_name = module_name + fp = None + try: + fp, pathname, stuff = imp.find_module( + base_name, [os.path.dirname(filename)]) + module = imp.load_module(module_name, fp, pathname, stuff) + finally: + if fp is not None: + fp.close() + return module |
From: <sub...@co...> - 2005-04-01 22:41:19
|
Author: ianb Date: 2005-04-01 22:41:16 +0000 (Fri, 01 Apr 2005) New Revision: 701 Removed: trunk/SQLObject/sqlobject/threadinglocal.py Log: and deleting... Deleted: trunk/SQLObject/sqlobject/threadinglocal.py =================================================================== --- trunk/SQLObject/sqlobject/threadinglocal.py 2005-04-01 22:40:51 UTC (rev 700) +++ trunk/SQLObject/sqlobject/threadinglocal.py 2005-04-01 22:41:16 UTC (rev 701) @@ -1,35 +0,0 @@ -try: - import threading -except ImportError: - # No threads, so "thread local" means process-global - class local(object): - pass -else: - try: - local = threading.local - except AttributeError: - # Added in 2.4, but now we'll have to define it ourselves - import thread - class local(object): - - def __init__(self): - self.__dict__['__objs'] = {} - - def __getattr__(self, attr, g=thread.get_ident): - try: - return self.__dict__['__objs'][g()][attr] - except KeyError: - raise AttributeError( - "No variable %s defined for the thread %s" - % (attr, g())) - - def __setattr__(self, attr, value, g=thread.get_ident): - self.__dict__['__objs'].setdefault(g(), {})[attr] = value - - def __delattr__(self, attr, g=thread.get_ident): - try: - del self.__dict__['__objs'][g()][attr] - except KeyError: - raise AttributeError( - "No variable %s defined for thread %s" - % (attr, g())) |
From: <sub...@co...> - 2005-04-01 22:40:58
|
Author: ianb Date: 2005-04-01 22:40:51 +0000 (Fri, 01 Apr 2005) New Revision: 700 Added: trunk/SQLObject/sqlobject/util/__init__.py trunk/SQLObject/sqlobject/util/threadinglocal.py Modified: trunk/SQLObject/sqlobject/dbconnection.py Log: Moved threadinglocal into the util package Modified: trunk/SQLObject/sqlobject/dbconnection.py =================================================================== --- trunk/SQLObject/sqlobject/dbconnection.py 2005-04-01 17:45:03 UTC (rev 699) +++ trunk/SQLObject/sqlobject/dbconnection.py 2005-04-01 22:40:51 UTC (rev 700) @@ -3,7 +3,7 @@ True, False = 1==1, 0==1 import threading -from threadinglocal import local as threading_local +from util.threadinglocal import local as threading_local import re import warnings import atexit Added: trunk/SQLObject/sqlobject/util/__init__.py =================================================================== --- trunk/SQLObject/sqlobject/util/__init__.py 2005-04-01 17:45:03 UTC (rev 699) +++ trunk/SQLObject/sqlobject/util/__init__.py 2005-04-01 22:40:51 UTC (rev 700) @@ -0,0 +1 @@ +# Copied: trunk/SQLObject/sqlobject/util/threadinglocal.py (from rev 699, trunk/SQLObject/sqlobject/threadinglocal.py) |
From: <sub...@co...> - 2005-04-01 17:45:07
|
Author: ianb Date: 2005-04-01 17:45:03 +0000 (Fri, 01 Apr 2005) New Revision: 699 Added: trunk/SQLObject/sqlobject/threadinglocal.py Modified: trunk/SQLObject/sqlobject/dbconnection.py trunk/SQLObject/sqlobject/tests/dbtest.py Log: Added ConnectionHub, an alternate way to set up connections on a process or thread basis. I'm not sure how to test it yet, except by providing a different implementation of setupClass which uses it (now commented out) Modified: trunk/SQLObject/sqlobject/dbconnection.py =================================================================== --- trunk/SQLObject/sqlobject/dbconnection.py 2005-04-01 17:37:04 UTC (rev 698) +++ trunk/SQLObject/sqlobject/dbconnection.py 2005-04-01 17:45:03 UTC (rev 699) @@ -3,6 +3,7 @@ True, False = 1==1, 0==1 import threading +from threadinglocal import local as threading_local import re import warnings import atexit @@ -255,7 +256,8 @@ elif self.autoCommit: if self.debug: self.printDebug(conn, 'auto', 'COMMIT') - conn.commit() + if not getattr(conn, 'autocommit', False): + conn.commit() else: if self.debug: self.printDebug(conn, 'auto', 'ROLLBACK') @@ -727,6 +729,60 @@ return self.rollback() +class ConnectionHub(object): + + """ + This object serves as a hub for connections, so that you can pass + in a ConnectionHub to a SQLObject subclass as though it was a + connection, but actually bind a real database connection later. + You can also bind connections on a per-thread basis. + + You must hang onto the original ConnectionHub instance, as you + cannot retrieve it again from the class or instance. + + To use the hub, do something like:: + + hub = ConnectionHub() + class MyClass(SQLObject): + _connection = hub + + hub.threadConnection = connectionFromURI('...') + + """ + + def __init__(self): + self.threadingLocal = threading_local() + + def __get__(self, obj, type=None): + return self.getConnection() + + def __set__(self, obj, value): + obj.__dict__['_connection'] = value + + def getConnection(self): + try: + return self.threadingLocal.connection + except AttributeError: + try: + return self.processConnection + except AttributeError: + raise AttributeError( + "No connection has been defined for this thread " + "or process") + + def _set_threadConnection(self, value): + self.threadingLocal.connection = value + + def _get_threadConnection(self): + return self.threadingLocal.connection + + def _del_threadConnection(self): + del self.threadingLocal.connection + + threadConnection = property(_get_threadConnection, + _set_threadConnection, + _del_threadConnection) + class ConnectionURIOpener(object): def __init__(self): Modified: trunk/SQLObject/sqlobject/tests/dbtest.py =================================================================== --- trunk/SQLObject/sqlobject/tests/dbtest.py 2005-04-01 17:37:04 UTC (rev 698) +++ trunk/SQLObject/sqlobject/tests/dbtest.py 2005-04-01 17:45:03 UTC (rev 699) @@ -63,10 +63,19 @@ B, then do setupClass([B, A]) and B won't be destroyed or cleared until after A is destroyed or cleared. """ + global hub if not isinstance(soClasses, (list, tuple)): soClasses = [soClasses] connection = getConnection() for soClass in soClasses: + ## This would be an alternate way to register connections... + #try: + # hub + #except NameError: + # hub = sqlobject.dbconnection.ConnectionHub() + #soClass._connection = hub + #hub.threadConnection = connection + #hub.processConnection = connection soClass._connection = connection installOrClear(soClasses) return soClasses Added: trunk/SQLObject/sqlobject/threadinglocal.py =================================================================== --- trunk/SQLObject/sqlobject/threadinglocal.py 2005-04-01 17:37:04 UTC (rev 698) +++ trunk/SQLObject/sqlobject/threadinglocal.py 2005-04-01 17:45:03 UTC (rev 699) @@ -0,0 +1,35 @@ +try: + import threading +except ImportError: + # No threads, so "thread local" means process-global + class local(object): + pass +else: + try: + local = threading.local + except AttributeError: + # Added in 2.4, but now we'll have to define it ourselves + import thread + class local(object): + + def __init__(self): + self.__dict__['__objs'] = {} + + def __getattr__(self, attr, g=thread.get_ident): + try: + return self.__dict__['__objs'][g()][attr] + except KeyError: + raise AttributeError( + "No variable %s defined for the thread %s" + % (attr, g())) + + def __setattr__(self, attr, value, g=thread.get_ident): + self.__dict__['__objs'].setdefault(g(), {})[attr] = value + + def __delattr__(self, attr, g=thread.get_ident): + try: + del self.__dict__['__objs'][g()][attr] + except KeyError: + raise AttributeError( + "No variable %s defined for thread %s" + % (attr, g())) |
From: <sub...@co...> - 2005-04-01 17:37:26
|
Author: ianb Date: 2005-04-01 17:37:04 +0000 (Fri, 01 Apr 2005) New Revision: 698 Modified: trunk/SQLObject/sqlobject/tests/test_conngetter.py Log: Updated for changed private attribute name Modified: trunk/SQLObject/sqlobject/tests/test_conngetter.py =================================================================== --- trunk/SQLObject/sqlobject/tests/test_conngetter.py 2005-04-01 17:36:23 UTC (rev 697) +++ trunk/SQLObject/sqlobject/tests/test_conngetter.py 2005-04-01 17:37:04 UTC (rev 698) @@ -22,7 +22,7 @@ TestSimple.dropTable(connection=conn, ifExists=True) TestSimple.createTable(connection=conn, ifNotExists=True) TestJoined.createTable(connection=conn, ifNotExists=True) - assert conn.TestSimple.__sqlobject_class__ is TestSimple + assert conn.TestSimple._soClass is TestSimple obj = conn.TestSimple(name='test') assert (TestSimple.get(obj.id, connection=conn) is obj) assert obj._connection is conn |
From: <sub...@co...> - 2005-04-01 17:36:44
|
Author: ianb Date: 2005-04-01 17:36:23 +0000 (Fri, 01 Apr 2005) New Revision: 697 Modified: trunk/SQLObject/sqlobject/col.py Log: Two bugs: the class wasn't being tracked by the columns (!), and when creating foreign key constraints we weren't looking up the other class respecting its registry Modified: trunk/SQLObject/sqlobject/col.py =================================================================== --- trunk/SQLObject/sqlobject/col.py 2005-04-01 17:34:57 UTC (rev 696) +++ trunk/SQLObject/sqlobject/col.py 2005-04-01 17:36:23 UTC (rev 697) @@ -140,7 +140,7 @@ self.constraints = [consts.notNull] + self.constraints self.name = name - self.soClass = None + self.soClass = soClass self._default = default self.customSQLType = sqlType @@ -622,7 +622,8 @@ def postgresCreateSQL(self): sql = SOKeyCol.postgresCreateSQL(self) - other = findClass(self.foreignKey) + print [self, self.soClass] + other = findClass(self.foreignKey, self.soClass.sqlmeta.registry) tName = other.sqlmeta.table idName = other.sqlmeta.idName if self.cascade is not None: |
From: <sub...@co...> - 2005-04-01 17:35:27
|
Author: ianb Date: 2005-04-01 17:34:57 +0000 (Fri, 01 Apr 2005) New Revision: 696 Modified: trunk/SQLObject/sqlobject/classregistry.py Log: Give better error message when a class is not found Modified: trunk/SQLObject/sqlobject/classregistry.py =================================================================== --- trunk/SQLObject/sqlobject/classregistry.py 2005-03-30 15:28:00 UTC (rev 695) +++ trunk/SQLObject/sqlobject/classregistry.py 2005-04-01 17:34:57 UTC (rev 696) @@ -98,7 +98,15 @@ callback(cls, *args, **kw) def getClass(self, className): - return self.classes[className] + try: + return self.classes[className] + except KeyError: + all = self.classes.keys() + all.sort() + raise KeyError( + "No class %s found in the registry %s (these classes " + "exist: %s)" + % (className, self.name or '[default]', ', '.join(all))) def allClasses(self): return self.classes.values() |
From: <sub...@co...> - 2005-03-30 15:28:05
|
Author: phd Date: 2005-03-30 15:28:00 +0000 (Wed, 30 Mar 2005) New Revision: 695 Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py Log: Added "unix_socket", "named_pipe", "init_command", "read_default_file", "read_default_group", "connect_time", "compress", "named_pipe", "use_unicode", "client_flag", "local_infile" parameters. Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py =================================================================== --- trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-03-30 15:23:45 UTC (rev 694) +++ trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-03-30 15:28:00 UTC (rev 695) @@ -18,6 +18,15 @@ self.db = db self.user = user self.password = passwd + self.kw = {} + for key in ("unix_socket", "named_pipe", "init_command", + "read_default_file", "read_default_group"): + if key in kw: + self.kw[key] = col.popKey(kw, key) + for key in ("connect_time", "compress", "named_pipe", "use_unicode", + "client_flag", "local_infile"): + if key in kw: + self.kw[key] = int(col.popKey(kw, key)) DBAPI.__init__(self, **kw) def connectionFromURI(cls, uri): @@ -29,7 +38,7 @@ def makeConnection(self): try: conn = self.module.connect(host=self.host, port=self.port, - db=self.db, user=self.user, passwd=self.password) + db=self.db, user=self.user, passwd=self.password, **self.kw) except self.module.OperationalError, e: raise self.module.OperationalError( "%s; used connection string: host=%s, port=%s, db=%s, user=%s, pwd=%s" % ( |
From: <sub...@co...> - 2005-03-30 15:23:55
|
Author: phd Date: 2005-03-30 15:23:45 +0000 (Wed, 30 Mar 2005) New Revision: 694 Modified: trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py Log: Applied patch 1172964, and added "mode" and "encoding" parameters. Modified: trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py =================================================================== --- trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py 2005-03-30 11:54:36 UTC (rev 693) +++ trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py 2005-03-30 15:23:45 UTC (rev 694) @@ -1,4 +1,5 @@ from sqlobject.dbconnection import DBAPI +from sqlobject.col import popKey sqlite = None class SQLiteConnection(DBAPI): @@ -16,9 +17,17 @@ if not autoCommit and not kw.has_key('pool'): # Pooling doesn't work with transactions... kw['pool'] = 0 - # use only one connection for sqlite - supports multiple + # connection options + opts = {'autocommit': autoCommit} + if 'encoding' in kw: + opts['encoding'] = popKey(kw, 'encoding') + if 'mode' in kw: + opts['mode'] = int(popKey(kw, 'mode'), 0) + if 'timeout' in kw: + opts['timeout'] = float(popKey(kw, 'timeout')) + # use only one connection for sqlite - supports multiple) # cursors per connection - self._conn = sqlite.connect(self.filename, autocommit=autoCommit) + self._conn = sqlite.connect(self.filename, **opts) DBAPI.__init__(self, **kw) def connectionFromURI(cls, uri): |
From: <sub...@co...> - 2005-03-30 11:54:41
|
Author: phd Date: 2005-03-30 11:54:36 +0000 (Wed, 30 Mar 2005) New Revision: 693 Modified: trunk/SQLObject/examples/people.py trunk/SQLObject/examples/setup.py trunk/SQLObject/sqlobject/sresults.py Log: Applied patch 1040531: implemented SELECT COUNT(DISTINCT id). Modified: trunk/SQLObject/examples/people.py =================================================================== --- trunk/SQLObject/examples/people.py 2005-03-29 16:48:12 UTC (rev 692) +++ trunk/SQLObject/examples/people.py 2005-03-30 11:54:36 UTC (rev 693) @@ -125,6 +125,18 @@ >>> print [p.id for p in peeps3] """ +test4 = """ +>>> bob = Person(firstName="Bob", lastName="Smith", username="bs") +>>> phone1 = PhoneNumber(person=bob, phoneNumber='123-555-3210', phoneType='home') +>>> phone2 = PhoneNumber(person=bob, phoneNumber='333-555-8888', phoneType='work') +>>> alice = Person(firstName="Alice", lastName="Nowak", username="alicen") +>>> print "Number of people:", Person.select().count() +3 +>>> print "Number of phones:", PhoneNumber.select().count() +3 +>>> print "Number of people with phones:", Person.select(Person.q.id == PhoneNumber.q.personID, distinct=True).count() +2 +""" ############################################################ ## Run tests: @@ -137,6 +149,8 @@ print line exec line[4:] +reset() runTest(test1) runTest(test2) runTest(test3) +runTest(test4) Modified: trunk/SQLObject/examples/setup.py =================================================================== --- trunk/SQLObject/examples/setup.py 2005-03-29 16:48:12 UTC (rev 692) +++ trunk/SQLObject/examples/setup.py 2005-03-30 11:54:36 UTC (rev 693) @@ -5,7 +5,7 @@ main = sys.modules['__main__'] if '-v' in sys.argv: - conn.debug = 1 + sqlobject.connectionForURI(conn).debug = True def reset(): classes = [] Modified: trunk/SQLObject/sqlobject/sresults.py =================================================================== --- trunk/SQLObject/sqlobject/sresults.py 2005-03-29 16:48:12 UTC (rev 692) +++ trunk/SQLObject/sqlobject/sresults.py 2005-03-30 11:54:36 UTC (rev 693) @@ -140,9 +140,19 @@ def count(self): """ Counting elements of current select results """ - assert not self.ops.get('distinct'), "It is not currently supported to count distinct objects" - - count = self.accumulate('COUNT(*)') + assert not (self.ops.get('distinct') and (self.ops.get('start') + or self.ops.get('end'))), \ + "distinct-counting of sliced objects is not supported" + if self.ops.get('distinct'): + # Column must be specified, so we are using unique ID column. + # COUNT(DISTINCT column) is supported by MySQL and PostgreSQL, + # but not by SQLite. Perhaps more portable would be subquery: + # SELECT COUNT(*) FROM (SELECT DISTINCT id FROM table) + count = self.accumulate('COUNT(DISTINCT %s.%s)' % ( + self.sourceClass.sqlmeta.table, + self.sourceClass.sqlmeta.idName)) + else: + count = self.accumulate('COUNT(*)') if self.ops.get('start'): count -= self.ops['start'] if self.ops.get('end'): |
From: <sub...@co...> - 2005-03-29 16:48:15
|
Author: phd Date: 2005-03-29 16:48:12 +0000 (Tue, 29 Mar 2005) New Revision: 692 Modified: trunk/SQLObject/sqlobject/tests/dbtest.py Log: Applied fix 1171905. Modified: trunk/SQLObject/sqlobject/tests/dbtest.py =================================================================== --- trunk/SQLObject/sqlobject/tests/dbtest.py 2005-03-29 16:30:20 UTC (rev 691) +++ trunk/SQLObject/sqlobject/tests/dbtest.py 2005-03-29 16:48:12 UTC (rev 692) @@ -2,18 +2,25 @@ The framework for making database tests. """ +import sys import os import re import sqlobject from py.test import raises +if sys.platform[:3] == "win": + def getcwd(): + return os.getcwd().replace(':', '|') +else: + getcwd = os.getcwd + connectionShortcuts = { 'mysql': 'mysql://test@localhost/test', 'dbm': 'dbm:///data', 'postgres': 'postgres:///test', 'postgresql': 'postgres:///test', 'pygresql': 'pygresql://localhost/test', - 'sqlite': 'sqlite:///%s/data/sqlite.data' % os.getcwd(), + 'sqlite': 'sqlite:///%s/data/sqlite.data' % getcwd(), 'sybase': 'sybase://test:test123@sybase/test?autoCommit=0', 'firebird': 'firebird://sysdba:masterkey@localhost/var/lib/firebird/data/test.gdb', } @@ -64,7 +71,7 @@ installOrClear(soClasses) return soClasses -installedDBFilename = os.path.join(os.getcwd(), 'dbs_data.tmp') +installedDBFilename = os.path.join(getcwd(), 'dbs_data.tmp') installedDBTracker = sqlobject.connectionForURI( 'sqlite:///' + installedDBFilename) |
From: <sub...@co...> - 2005-03-29 16:30:26
|
Author: phd Date: 2005-03-29 16:30:20 +0000 (Tue, 29 Mar 2005) New Revision: 691 Modified: trunk/SQLObject/sqlobject/__init__.py Log: Applied patch 1162265. Modified: trunk/SQLObject/sqlobject/__init__.py =================================================================== --- trunk/SQLObject/sqlobject/__init__.py 2005-03-29 15:52:06 UTC (rev 690) +++ trunk/SQLObject/sqlobject/__init__.py 2005-03-29 16:30:20 UTC (rev 691) @@ -1,6 +1,6 @@ from main import * from col import * -from sqlbuilder import AND, OR, NOT, IN, LIKE, CONTAINSSTRING, const, func +from sqlbuilder import AND, OR, NOT, IN, LIKE, DESC, CONTAINSSTRING, const, func from styles import * from joins import * from index import * |
From: <sub...@co...> - 2005-03-29 15:52:14
|
Author: phd Date: 2005-03-29 15:52:06 +0000 (Tue, 29 Mar 2005) New Revision: 690 Modified: trunk/SQLObject/sqlobject/col.py Log: Applied patch 1157423. Modified: trunk/SQLObject/sqlobject/col.py =================================================================== --- trunk/SQLObject/sqlobject/col.py 2005-03-27 19:37:29 UTC (rev 689) +++ trunk/SQLObject/sqlobject/col.py 2005-03-29 15:52:06 UTC (rev 690) @@ -482,13 +482,17 @@ class BoolValidator(validators.Validator): def fromPython(self, value, state): - if value: + if value is None: + return None + elif value: return sqlbuilder.TRUE else: return sqlbuilder.FALSE def toPython(self, value, state): - if not value: + if value is None: + return None + elif not value: return sqlbuilder.FALSE else: return sqlbuilder.TRUE |
From: <sub...@co...> - 2005-03-27 19:37:40
|
Author: ianb Date: 2005-03-27 19:37:29 +0000 (Sun, 27 Mar 2005) New Revision: 689 Added: trunk/SQLObject/sqlobject/tests/test_conngetter.py Modified: trunk/SQLObject/sqlobject/classregistry.py trunk/SQLObject/sqlobject/dbconnection.py trunk/SQLObject/sqlobject/tests/dbtest.py Log: * Allow keyword arguments to connectionForURI (and pass through from dbtest.getConnection) * Add callback to registry that is called for all classes * Wrappers added to dbconnection instances that create a psuedo-class that is bound to a connection Modified: trunk/SQLObject/sqlobject/classregistry.py =================================================================== --- trunk/SQLObject/sqlobject/classregistry.py 2005-03-25 17:39:04 UTC (rev 688) +++ trunk/SQLObject/sqlobject/classregistry.py 2005-03-27 19:37:29 UTC (rev 689) @@ -46,6 +46,7 @@ self.name = name self.classes = {} self.callbacks = {} + self.genericCallbacks = [] def addClassCallback(self, className, callback, *args, **kw): """ @@ -59,6 +60,15 @@ else: self.callbacks.setdefault(className, []).append((callback, args, kw)) + def addCallback(self, callback, *args, **kw): + """ + This callback is called for all classes, not just specific + ones (like addClassCallback). + """ + self.genericCallbacks.append((callback, args, kw)) + for cls in self.classes.values(): + callback(cls, *args, **kw) + def addClass(self, cls): """ Everytime a class is created, we add it to the registry, so @@ -84,6 +94,8 @@ for callback, args, kw in self.callbacks[cls.__name__]: callback(cls, *args, **kw) del self.callbacks[cls.__name__] + for callback, args, kw in self.genericCallbacks: + callback(cls, *args, **kw) def getClass(self, className): return self.classes[className] Modified: trunk/SQLObject/sqlobject/dbconnection.py =================================================================== --- trunk/SQLObject/sqlobject/dbconnection.py 2005-03-25 17:39:04 UTC (rev 688) +++ trunk/SQLObject/sqlobject/dbconnection.py 2005-03-27 19:37:29 UTC (rev 689) @@ -8,14 +8,16 @@ import atexit import os import new +import types +import urllib +import weakref +import inspect import sqlbuilder from cache import CacheSet import col from joins import sorter from converters import sqlrepr -import urllib -import weakref -from classregistry import findClass +import classregistry warnings.filterwarnings("ignore", "DB-API extension cursor.lastrowid used") @@ -30,7 +32,7 @@ def __init__(self, name=None, debug=False, debugOutput=False, cache=True, style=None, autoCommit=True, - debugThreading=False): + debugThreading=False, registry=None): self.name = name self.debug = debug self.debugOutput = debugOutput @@ -41,6 +43,9 @@ self._connectionNumbers = {} self._connectionCount = 1 self.autoCommit = autoCommit + self.registry = registry or None + classregistry.registry(self.registry).addCallback( + self.soClassAdded) registerConnectionInstance(self) atexit.register(_closeConnection, weakref.ref(self)) @@ -119,6 +124,75 @@ return user, password, host, port, path, args _parseURI = staticmethod(_parseURI) + def soClassAdded(self, soClass): + """ + This is called for each new class; we use this opportunity + to create an instance method that is bound to the class + and this connection. + """ + name = soClass.__name__ + assert not hasattr(self, name), ( + "Connection %r already has an attribute with the name " + "%r (and you just created the conflicting class %r)" + % (self, name, soClass)) + setattr(self, name, ConnWrapper(soClass, self)) + +class ConnWrapper(object): + + """ + This represents a SQLObject class that is bound to a specific + connection (instances have a connection instance variable, but + classes are global, so this is binds the connection variable + lazily when a class method is accessed) + """ + # @@: methods that take connection arguments should be explicitly + # marked up instead of the implicit use of a connection argument + # and inspect.getargspec() + + def __init__(self, soClass, connection): + self._soClass = soClass + self._connection = connection + + def __call__(self, *args, **kw): + kw['connection'] = self._connection + return self._soClass(*args, **kw) + + def __getattr__(self, attr): + meth = getattr(self._soClass, attr) + if not isinstance(meth, types.MethodType): + # We don't need to wrap non-methods + return meth + try: + takes_conn = meth.takes_connection + except AttributeError: + args, varargs, varkw, defaults = inspect.getargspec(meth) + assert not varkw and not varargs, ( + "I cannot tell whether I must wrap this method, " + "because it takes **kw: %r" + % meth) + takes_conn = 'connection' in args + meth.im_func.takes_connection = takes_conn + if not takes_conn: + return meth + return ConnMethodWrapper(meth, self._connection) + +class ConnMethodWrapper(object): + + def __init__(self, method, connection): + self._method = method + self._connection = connection + + def __getattr__(self, attr): + return getattr(self._method, attr) + + def __call__(self, *args, **kw): + kw['connection'] = self._connection + return self._method(*args, **kw) + + def __repr__(self): + return '<Wrapped %r with connection %r>' % ( + self._method, self._connection) + class DBAPI(DBConnection): """ @@ -627,7 +701,10 @@ try: func = attr.im_func except AttributeError: - return attr + if isinstance(attr, ConnWrapper): + return ConnWrapper(attr._soClass, self) + else: + return attr else: meth = new.instancemethod(func, self, self.__class__) return meth @@ -674,13 +751,18 @@ assert inst.name.find(':') == -1, "You cannot include ':' in your class names (%r)" % cls.name self.instanceNames[inst.name] = inst - def connectionForURI(self, uri): + def connectionForURI(self, uri, **args): + if args: + if '?' not in uri: + uri += '?' + uri += urllib.urlencode(args) if self.cachedURIs.has_key(uri): return self.cachedURIs[uri] if uri.find(':') != -1: scheme, rest = uri.split(':', 1) - assert self.schemeBuilders.has_key(scheme), \ - "No SQLObject driver exists for %s" % scheme + assert self.schemeBuilders.has_key(scheme), ( + "No SQLObject driver exists for %s (only %s)" + % (scheme, ', '.join(self.schemeBuilders.keys()))) conn = self.schemeBuilders[scheme]().connectionFromURI(uri) else: # We just have a name, not a URI Modified: trunk/SQLObject/sqlobject/tests/dbtest.py =================================================================== --- trunk/SQLObject/sqlobject/tests/dbtest.py 2005-03-25 17:39:04 UTC (rev 688) +++ trunk/SQLObject/sqlobject/tests/dbtest.py 2005-03-27 19:37:29 UTC (rev 689) @@ -69,12 +69,12 @@ installedDBTracker = sqlobject.connectionForURI( 'sqlite:///' + installedDBFilename) -def getConnection(): +def getConnection(**kw): name = os.environ.get('TESTDB') assert name, 'You must set $TESTDB to do database operations' if connectionShortcuts.has_key(name): name = connectionShortcuts[name] - return sqlobject.connectionForURI(name) + return sqlobject.connectionForURI(name, **kw) connection = getConnection() Added: trunk/SQLObject/sqlobject/tests/test_conngetter.py =================================================================== --- trunk/SQLObject/sqlobject/tests/test_conngetter.py 2005-03-25 17:39:04 UTC (rev 688) +++ trunk/SQLObject/sqlobject/tests/test_conngetter.py 2005-03-27 19:37:29 UTC (rev 689) @@ -0,0 +1,37 @@ +from sqlobject import * +from sqlobject.tests.dbtest import * + +class TestSimple(SQLObject): + + class sqlmeta: + registry = 'conngetter' + + name = StringCol(alternateID=True) + +class TestJoined(SQLObject): + + class sqlmeta: + registry = 'conngetter' + + this_name = StringCol(alternateID=True) + simple = ForeignKey('TestSimple') + +def test_autogetter(): + conn = getConnection(registry='conngetter') + TestJoined.dropTable(connection=conn, ifExists=True) + TestSimple.dropTable(connection=conn, ifExists=True) + TestSimple.createTable(connection=conn, ifNotExists=True) + TestJoined.createTable(connection=conn, ifNotExists=True) + assert conn.TestSimple.__sqlobject_class__ is TestSimple + obj = conn.TestSimple(name='test') + assert (TestSimple.get(obj.id, connection=conn) is obj) + assert obj._connection is conn + obj2 = TestSimple(name='test2', connection=conn) + assert (conn.TestSimple.byName('test2') is obj2) + joined = conn.TestJoined(this_name='join_test', simple=obj) + assert joined.simple is obj + assert joined.simple._connection is conn + assert joined._connection is conn + for item in conn.TestSimple.select(): + assert item._connection is conn + |
From: <sub...@co...> - 2005-03-03 12:15:32
|
Author: phd Date: 2005-03-03 12:15:10 +0000 (Thu, 03 Mar 2005) New Revision: 663 Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py Log: Added _setAutoCommit(). Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py =================================================================== --- trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-03-03 12:12:15 UTC (rev 662) +++ trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-03-03 12:15:10 UTC (rev 663) @@ -41,6 +41,10 @@ return conn + def _setAutoCommit(self, conn, auto): + if hasattr(conn, 'autocommit'): + conn.autocommit(auto) + def _executeRetry(self, conn, cursor, query): while 1: try: |
From: <sub...@co...> - 2005-03-03 12:12:25
|
Author: phd Date: 2005-03-03 12:12:15 +0000 (Thu, 03 Mar 2005) New Revision: 662 Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py Log: Fixed the bug https://sourceforge.net/tracker/index.php?func=detail&aid=1150183&group_id=74338&atid=540672 Modified makeConnection and set autocommit if it's supported. Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py =================================================================== --- trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-03-01 23:09:06 UTC (rev 661) +++ trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2005-03-03 12:12:15 UTC (rev 662) @@ -27,9 +27,20 @@ connectionFromURI = classmethod(connectionFromURI) def makeConnection(self): - return MySQLdb.connect(host=self.host, port=self.port, db=self.db, - user=self.user, passwd=self.password) + try: + conn = self.module.connect(host=self.host, port=self.port, + db=self.db, user=self.user, passwd=self.password) + except self.module.OperationalError, e: + raise self.module.OperationalError( + "%s; used connection string: host=%s, port=%s, db=%s, user=%s, pwd=%s" % ( + e, self.host, self.port, self.db, self.user, self.password) + ) + if hasattr(conn, 'autocommit'): + conn.autocommit(self.autoCommit) + + return conn + def _executeRetry(self, conn, cursor, query): while 1: try: |