- edited description
LW KeyedTuples have instance dicts (ineffective __slots__)
import sqlalchemy
import sqlalchemy.orm
engine = sqlalchemy.create_engine('sqlite://')
session = sqlalchemy.orm.Session(engine)
result = session.query(sqlalchemy.func.now())[0]
result.__dict__ # should raise AttributeError
print('\n'.join('%s\t%s' % (getattr(cls, '__slots__', None), cls)
for cls in result.__class__.mro()))
# () <class 'sqlalchemy.util._collections.result'>
# () <class 'sqlalchemy.util._collections._LW'>
# None <class 'sqlalchemy.util._collections.AbstractKeyedTuple'>
# None <type 'tuple'>
# None <type 'object'>
Note that __slots__
only works if every class along the mro spares instance __dict__
s.
__slots__ = ()
for sqlalchemy.util._collections.AbstractKeyedTuple
shoud fix this.
Comments (10)
-
reporter -
repo owner - changed status to resolved
- The "lightweight named tuple" used when a :class:
.Query
returns rows failed to implement__slots__
correctly such that it still had a__dict__
. This is resolved, but in the extremely unlikely case someone was assigning values to the returned tuples, that will no longer work. fixes#3420
→ <<cset 64c1f2e56888>>
-
repo owner thanks for catching that. countdown to someone was assigning to the tuple in 5..4..
-
repo owner .. 3... 2.. 1.. GO !!! https://groups.google.com/forum/#!topic/sqlalchemy/syWt_EZqCbk
this user is already getting an error even without the change here because they want to set one of the actual named attributes.
-
reporter Uh, you really know your users damn well!
I think Python even raises with new attributes (with and without the change). I was curious if the change affects the benchmarks from the docs (maybe for some that would count as argument for/against
__slots__
). -
repo owner __slots__
doesn't have a huge impact on speed, it is very useful for keeping memory under control though. The amount of memory that was being wasted throughout huge parts of SQLA on dictionaries that were hardly needed is why I put a lot of__slots__
in 1.0. -
Awww mannnn. You caught me :)
I assigned some helper lambdas to the returning rows:
class MyQuery(Query): def __iter__(self): return (self.decorate(r) for r in Query.__iter__(q) ) handy_lambda_1 = lambda r: stuff handy_lambda_2 = lambda r: more_stuff def decorate(self, row): row.handy_lambda_1 = handy_lambda_1 row.handy_lambda_2 = handy_lambda_2 return row MySession = scoped_session( sessionmaker( extension=ZopeTransactionExtension(), query_cls=MyQuery ) )
Any chance we'll get row_cls as an option to sessionmaker? I found these helper lambdas super useful in certain scenarios.
-
repo owner you can use Column Bundles and assign any kind of ORM-level processing to column-loads that you'd want.
-
repo owner for interception of objects as they are loaded you can use the load event.
-
Whoa! Column bundles are great. Thanks!
- Log in to comment