On Sunday 22 March 2009 21:17:15 Christiaan Putter wrote:
> Hi Svilen,
>
> Setting up tests is a very good idea, I'll start on some unit
> testing immediately.  Definitely the best way to insure behaviour
> remains constant as you're working on the implementation, was just
> to lazy to do so up till now.
i'm sure if u do the tests first trying to cover as many use cases as 
u can, u may even better understand what u really need... TDD of 
sorts as first feedback to requirements-analisys.

> Speaking of laziness, I've noticed that setting lazy=False on a
> relation does not effect refresh operations on the relation, it
> only loads on reconstruction.  Having cascade='all' doesn't change
> this either.  Is their some setting that I'm missing?
dunno.

> Another little issue I'm trying to get fixed out is extending a
> class's attributes at run time, let's call it Parent.  The need for
> this is allowing users to add custom fields (in a seperate table as
> the parent) at run time to Parent.  My approach is letting the user
> set up a new class (ChildX), which inherits  Base (not parent), 
> with the attributes and methods that compute said attributes during
> run time.  Then I compile that using exec, and and do some magic in
> its metaclass.  Basically I add a relation to Parent with backref
> to ChildX and uselist=False on both sides.  Enthought then allows
> me to add new traits to the Parent class that delegate their value
> the ChildX instance through the backref on Parent.  This is
> basically just mapping the Parent class over several tables, with
> the ability to extend the class on the fly.

> It's mostly working.  One problem though is that on reconstructing
> a Parent instance (or creating a new one) I have to have a
> mechanism that checks if all the Child classes have instances
> related to this record of Parent.  Not a gig deal though.
>
> Is their a better way to do this?  I'm doing some really ugly
> hacking to the Parent's mapper at run time, seeing as Mapper
> doesn't have a delete_property method.
>
> Should I try using normal inheritance instead? Will I be able to
> access ChildX.x from ChildY for instance?  And what about changing
> ChildX's definition and running exec on it again?

i don't really get the reason of why u do all this. 
wanna extend existing class runtime or want to add new class?
IMO just subclassing on-the-fly would be easier. 


svil

> Perhaps someone has done something similar before and can give me
> some pointers.
>
> Thanks for all the advice up till now, it's been really useful.
>
> Have a great day,
> Christian
>
> 2009/3/22  <a...@svilendobrev.com>:
> > good on yer!
> > one thing that i sorta failed is making a clean set of tests for
> > this. if u aren't too far in the usage, save some major future
> > headaches and do yourself a favour, make such test set. All the
> > things u've tried has to be (simple) testcases - it will be the
> > _spec_ of how the thing works / what u want from it. Anyone else
> > on the same path could also use it then ;-)
> >
> > i hope that once u do that u'll get an idea of where exactly u
> > have failed and fix it.
> >
> > as of expiration, it also did me some trouble. i have not stepped
> > on that session-gone one but i can see how it happens. The
> > (expected) life-times of SA-touched things aren't that obvious,
> > and aren't documented. e.g. Objects living longer than session or
> > in an extreme case, the mappers, might be troublesome.
> >
> > what i can suggest is, put debug statements and run with
> > echo=True and watch what goes on behind and how SA-activities
> > intertwist with traits' /your ones.
> >
> > ciao
> > svilen
> >
> > On Sunday 22 March 2009 04:00:27 Christiaan Putter wrote:
> >> Hi svilen,
> >>
> >> Thanks for your advice, going through your code helped a lot in
> >> understanding how SA works.
> >>
> >> I've gotten traited classes to behave like classes generated by
> >> SA's declarative extension.  After a lot of stepping through
> >> code I realised the main problem was that SA removes values from
> >> an instance __dict__ once they have been expired.  Traits
> >> smartly (or not so smartly) picks this up and on the next access
> >> to those attributes and returns their default values.  Whereas
> >> SA would normally refresh the values from SQL.  This caused some
> >> serious headaches, and really strange behaviour.
> >>
> >> My solution was to the overwrite the class's __getattribute__
> >> with:
> >>
> >>     def __getattribute__(self, key):
> >>         try:
> >>             dict = super(HasTraitsORM,
> >> self).__getattribute__('__dict__')
> >>
> >>             if ('_sa_state' in dict):
> >>                 state = dict['_sa_state']
> >>                 if key in state.manager.keys():
> >>                     return state.get_impl(key).get(state)
> >>
> >>         except Exception, e:
> >>             print "Exception in SA getattr for %s: %s" % (key,
> >> e) pass
> >>
> >>         return super(HasTraitsORM, self).__getattribute__(key)
> >>
> >>
> >> Which tries to first get an attribute from the instance's state
> >> (which will issue SQL if needed), and if that fails pass the
> >> request along to its super class where Traits will do it's magic
> >> if needed.
> >>
> >> This seems to work in the tests if been trying.  Most of the
> >> work is done by SA declarative, and I've just added a thin layer
> >> on top to get it to behave well with Traited classes and map
> >> traits to SA columns.
> >>
> >> One issue I'm still having though is that after commits all
> >> attributes get expired.  If you close the session the instance
> >> was attached to the data you just commited can't be accessed any
> >> more. This is also the behaviour of plain declarative classes.
> >>  This is rather unintuitive I believe since once you've comitted
> >> an instance and closed the session you can't use the data you
> >> just set any more. In my case this means records being displayed
> >> in a gui get corrupted.
> >>
> >> Though what's strange is that if you access the attributes after
> >> the commit and before you close the session, and only then close
> >> the session, the values remain in the instance's __dict__ and
> >> the class behaves normally again.  Is this intended for some
> >> reason? Is there a workaround?  Personally I don't think quantum
> >> physics should apply to the bits of my programs and thus merely
> >> observing them should not change their behaviour. From going
> >> through the source I found a 'dont_expire_missing' attribute on
> >> attribute implementations, though this does unfortunately not do
> >> what I'd like.
> >>
> >> So what I'm doing now is keeping a session open on the gui's
> >> thread. Which causes some problems when trying to modify said
> >> instances from other threads and commiting the new values.
> >>
> >> How's this usually done is SA?  Right now I've put a lock on the
> >> gui thread's session and allow other threads to issue a commit
> >> on that main thread if need be.  I can't imagine this is the
> >> best way to do it...
> >>
> >> Hope you're all having a great weekend,
> >>
> >> Christian
> >>
> >> 2009/2/8  <a...@svilendobrev.com>:
> >> > afaiknow these traits are like my own static_types, i.e.
> >> > descriptors holding metadata and applying it to attribute
> >> > access. i have been combining SA with static_type for more
> >> > than 2 years now, since SA 3.0.
> >> >
> >> > The approach i did in the beginning was to replace the
> >> > object's __dict__ by something smart that is
> >> > static_type-aware. When InstrumentionManager framework came
> >> > into place, i did not find it any different, as SA still uses
> >> > the __dict__ for data access. The difference is that now my
> >> >  __dict__ replacement is created once and not at every
> >> > attr.access.
> >> >
> >> > i did suggest one patch about replaceing the SA's
> >> > obj.__dict__.whatever usage with an explicit set of methods
> >> > (so one knows what access to mime), and that was making SA
> >> > 1-2% faster, but it wasn't accepted.
> >> >
> >> > basicaly now there's a thin layer on top of SA, then SA
> >> > itself, then a thick layer underneath managing the data (the
> >> > fake __dict__).
> >> >
> >> > declarative+traits... u'll end up where i was. dbcook.sf.net
> >> > is doing that - since beginning. and it's switchable on/off.
> >> > It all works well and stable, in project with 250-300 classes,
> >> > although about 15% slower than without it (-:)
> >> >
> >> > The sa2static code:
> >> > svn co
> >> > http://dbcook.svn.sourceforge.net/svnroot/dbcook/trunk/dbcook/
> >> >usa ge/static_type/
> >> >
> >> > The static_type itself:
> >> > svn co
> >> > https://dbcook.svn.sourceforge.net/svnroot/dbcook/static_type
> >> >
> >> > whole dbcook:
> >> > svn co https://dbcook.svn.sourceforge.net/svnroot/dbcook/trunk
> >> >
> >> > i have quite some experience fighting this field, ask if u
> >> > want.
> >> >
> >> > ciao
> >> > svilen
> >> > www.svilendobrev.com
> >> >
> >> > On Sunday 08 February 2009 01:51:20 cputter wrote:
> >> >> Hi guys and girls,
> >> >>
> >> >> I've recently discovered the joys of using sqlalchemy and
> >> >> would love to using it together with Traits.  A few months
> >> >> back there was an attempt to integrate sqlalchemy into
> >> >> traits, though it wasn't really comprehensive in exploiting
> >> >> all of sqlalchemy's potential.
> >> >>
> >> >> So I'm trying to work on that and combine ext.Declarative
> >> >> with traits. The basic idea is to use the DeclarativeMeta
> >> >> type to generate Columns from Traits and pass those on for
> >> >> the Declarative extension to do its magic.  This would allow
> >> >> mixing of sqlalchemy attributes and trait attributes in a
> >> >> single class so that we could still make use of all the
> >> >> relational setup sqlalchemy does in any case.
> >> >>
> >> >> Reading through several threads and looking at Elixir's SA
> >> >> integration helped me a bit though I couldn't find any
> >> >> documentation on how to implement the InstrumentationManager
> >> >> interface.  I'm assuming this would be essential for letting
> >> >> Traits and SQLAlchemy play well together.
> >> >>
> >> >> There's still a lot of work to do, and I'm not really sure
> >> >> what needs to be done for everything to work properly.  Would
> >> >> really appreciate it if someone could help me out.
> >> >>
> >> >>
> >> >> Here's an example of how it's working at the moment, I'll add
> >> >> the actual implementation at the end.
> >> >>
> >> >> #####################################
> >> >> class User(HasTraitsORM):
> >> >>     __tablename__ = 'users'
> >> >>
> >> >>     id = Column('id', Integer, primary_key=True)
> >> >>     name = Str(sqldb=True)
> >> >>
> >> >>     def _name_changed(self, old, new):
> >> >>         print 'Changed name from %s to %s.' % (old, new)
> >> >>
> >> >>     def __repr__(self):
> >> >>         return '<User(%s, %s)>' % (self.id, self.name)
> >> >>
> >> >> people = ['John', 'Charls','Steve','Smith','Jane']
> >> >>
> >> >> for per in people:
> >> >>     obj = User(name=per)
> >> >>     sess = sqlservice.Session()
> >> >>     sess.add(obj)
> >> >>     sess.commit()
> >> >>     sess.close()
> >> >>     print obj
> >> >>
> >> >> session = sqlservice.Session()
> >> >> print '\nQuery all users\n'
> >> >> for user in session.query(User).order_by(User.name).all():
> >> >>     print user
> >> >>
> >> >> session.close()
> >> >>
> >> >>
> >> >> ############################
> >> >>
> >> >> Which spits out:
> >> >> ###############################
> >> >> Changed name from  to John.
> >> >> <User(users.id, )>
> >> >> Changed name from  to Charls.
> >> >> <User(users.id, )>
> >> >> Changed name from  to Steve.
> >> >> <User(users.id, Steve)>
> >> >> Changed name from  to Smith.
> >> >> <User(users.id, Smith)>
> >> >> Changed name from  to Jane.
> >> >> <User(users.id, Jane)>
> >> >>
> >> >> Query all users
> >> >>
> >> >> <User(2, Charls)>
> >> >> <User(1, John)>
> >> >>
> >> >> ##############################
> >> >>
> >> >> Which is really strange behaviour.  There's obviously
> >> >> something wrong in my implementation of HasTraitsORM but why
> >> >> the different results within the same loop???  Why add only
> >> >> two instances?
> >> >>
> >> >> Totally baffles me.
> >> >>
> >> >> Here's the rest of my code, hope somehow can help me out.
> >> >>  It's very messy, I've been hacking at it like crazy with no
> >> >> success
> >> >>
> >> >> :-)
> >> >>
> >> >> Hope you're all having a great weekend.
> >> >> -Chris
> >> >>
> >> >>
> >> >> ##############################
> >> >>
> >> >> # Standard library imports.
> >> >> import logging
> >> >>
> >> >> # Enthought library imports
> >> >> from enthought.preferences.api import Preferences
> >> >> from enthought.traits.api import \
> >> >>     HasTraits, MetaHasTraits, Int, Str, Bool, Float, Any,\
> >> >>     String, Enum, Python, \
> >> >>     on_trait_change, TraitListObject
> >> >>
> >> >> # Package imports
> >> >> import sqlalchemy
> >> >> from sqlalchemy import Column, Integer
> >> >> from sqlalchemy.schema import MetaData
> >> >> from sqlalchemy.orm.interfaces import MapperProperty,
> >> >> InstrumentationManager
> >> >> from sqlalchemy.orm.attributes import get_attribute,
> >> >> set_attribute, is_instrumented
> >> >> from sqlalchemy.orm.collections import InstrumentedList,
> >> >> collection_adapter
> >> >> from sqlalchemy.ext.declarative import _as_declarative
> >> >>
> >> >>
> >> >> # Setup a logger for this module.
> >> >> logger = logging.getLogger(__name__)
> >> >>
> >> >>
> >> >> TRAIT_MAPPING = {
> >> >>          Int : 'sqlalchemy.Integer',
> >> >>          Str : 'sqlalchemy.Text',
> >> >>          Enum : 'sqlalchemy.Text',
> >> >>          String : 'sqlalchemy.Text',
> >> >>          Float : 'sqlalchemy.Float',
> >> >>          Bool : 'sqlalchemy.Boolean',
> >> >>          }
> >> >>
> >> >>
> >> >> class HasTraitsORMState(InstrumentationManager):
> >> >>     def __init__(self, cls):
> >> >>         self.states = {}
> >> >>
> >> >>     def instrument_attribute(self, class_, key, attr):
> >> >>         pass
> >> >>
> >> >>     def install_descriptor(self, class_, key, attr):
> >> >>         pass
> >> >>
> >> >>     def uninstall_descriptor(self, class_, key, attr):
> >> >>         pass
> >> >>
> >> >>     def instrument_collection_class(self, class_, key,
> >> >> collection_class):
> >> >>         return ObjectCollection
> >> >>
> >> >>     def get_instance_dict(self, class_, instance):
> >> >>         return instance.__dict__
> >> >>
> >> >>     def initialize_instance_dict(self, class_, instance):
> >> >>         instance.reset_traits()
> >> >>
> >> >>     def initialize_collection(self, key, state, factory):
> >> >>         data = factory()
> >> >>         return ObjectCollectionAdapter(key, state, data),
> >> >> data
> >> >>
> >> >>     def install_state(self, class_, instance, state):
> >> >>         self.states[id(instance)] = state
> >> >>
> >> >>     def state_getter(self, class_):
> >> >>         def find(instance):
> >> >>             return self.states[id(instance)]
> >> >>         return find
> >> >>
> >> >>
> >> >> class ObjectCollectionAdapter(object):
> >> >>     """
> >> >>         An adapter for SQLAlchemy for TraitsListObject which
> >> >> is the collection
> >> >>         we use for storing instances of classes within
> >> >> attributes of other
> >> >>         classes.
> >> >>
> >> >>         TODO:  Think of a way to get this to behave like a
> >> >> normal traited
> >> >>         attribute of objects.
> >> >>         At the moment collection attributes are set through
> >> >> the mapper by SA
> >> >>         when using relations or backref.
> >> >>
> >> >>     """
> >> >>     def __init__(self, key, state, collection):
> >> >>         self.key = key
> >> >>         self.state = state
> >> >>         self.collection = collection
> >> >>         setattr(collection, '_sa_adapter', self)
> >> >>
> >> >>     def unlink(self, data):
> >> >>         setattr(data, '_sa_adapter', None)
> >> >>
> >> >>     def adapt_like_to_iterable(self, obj):
> >> >>         return iter(obj)
> >> >>
> >> >>     def append_with_event(self, item, initiator=None):
> >> >>         self.collection.append(item, emit=initiator)
> >> >>
> >> >>     def append_without_event(self, item):
> >> >>         self.collection.append(item, emit=False)
> >> >>
> >> >>     def remove_with_event(self, item, initiator=None):
> >> >>         self.collection.remove(item, emit=initiator)
> >> >>
> >> >>     def remove_without_event(self, item):
> >> >>         self.collection.remove(item, emit=False)
> >> >>
> >> >>     def clear_with_event(self, initiator=None):
> >> >>         for item in list(self):
> >> >>             self.remove_with_event(item, initiator)
> >> >>
> >> >>     def clear_without_event(self):
> >> >>         for item in list(self):
> >> >>             self.remove_without_event(item)
> >> >>
> >> >>     def __iter__(self):
> >> >>         return iter(self.collection)
> >> >>
> >> >>     def fire_append_event(self, item, initiator=None):
> >> >>         if initiator is not False and item is not None:
> >> >>             self.state.get_impl(self.key).fire_append_event
> >> >> (self.state, item,
> >> >>
> >> >> initiator)
> >> >>
> >> >>     def fire_remove_event(self, item, initiator=None):
> >> >>         if initiator is not False and item is not None:
> >> >>             self.state.get_impl(self.key).fire_remove_event
> >> >> (self.state, item,
> >> >>
> >> >> initiator)
> >> >>
> >> >>     def fire_pre_remove_event(self, initiator=None):
> >> >>         self.state.get_impl(self.key).fire_pre_remove_event
> >> >> (self.state,
> >> >>
> >> >> initiator)
> >> >>
> >> >> class ObjectCollection(TraitListObject):
> >> >>     __emulates__ = list
> >> >>
> >> >>     def __init__( self, trait, object, name, value ):
> >> >>         pass
> >> >>
> >> >>     def __init__(self):
> >> >>         self.members = list()
> >> >>     def append(self, object, emit=None):
> >> >>         self.members.append(object)
> >> >>         #collection_adapter(self).fire_append_event(object,
> >> >> emit) def remove(self, object, emit=None):
> >> >>        
> >> >> #collection_adapter(self).fire_pre_remove_event(object)
> >> >> self.members.remove(object)
> >> >>         #collection_adapter(self).fire_remove_event(object,
> >> >> emit) def __getitem__(self, index):
> >> >>         return self.members[index]
> >> >>     def __iter__(self):
> >> >>         return iter(self.members)
> >> >>     def __len__(self):
> >> >>         return len(self.members)
> >> >>     def __repr__(self):
> >> >>         if len(self.members) == 0:
> >> >>             return '[empty collection]'
> >> >>         s = '['
> >> >>         for item in self.members[0:max(5,
> >> >> len(self.members))]: s += '%s,' % item
> >> >>         s += '...]'
> >> >>         return s
> >> >>
> >> >>
> >> >> class DeclarativeMetaTraits(MetaHasTraits):
> >> >>     pass
> >> >>
> >> >> #    def __new__(cls, classname, bases, dict_):
> >> >> #        return MetaHasTraits.__new__(cls, classname, bases,
> >> >> dict_)
> >> >>
> >> >>     def __init__(cls, classname, bases, dict_):
> >> >>
> >> >>         if '_decl_class_registry' in cls.__dict__:
> >> >>             super(DeclarativeMetaTraits, cls).__init__(cls,
> >> >> classname, bases, dict_)
> >> >>             return
> >> >>
> >> >>         if '__tablename__' not in dict_:
> >> >>             setattr(cls, '__tablename__', cls.__name__)
> >> >>
> >> >>         # create sql columns from flagged traits
> >> >>         if '__class_traits__' in cls.__dict__:
> >> >>             traits = cls.__dict__['__class_traits__']
> >> >>             for key, trait in traits.iteritems():
> >> >>                 if getattr( trait, 'sqldb' ):
> >> >>                     args = getattr( trait, 'col_args' ) or ()
> >> >>                     kwargs = getattr( trait, 'col_kwargs' )
> >> >> or {} if 'name' not in kwargs:
> >> >>                         kwargs['name'] = key
> >> >>                     if 'type_' not in kwargs:
> >> >>                         kwargs['type_'] =
> >> >> eval(TRAIT_MAPPING[type (trait.trait_type)])
> >> >>
> >> >>                     if getattr(trait, 'sqlpk'):
> >> >>                         kwargs['primary_key'] = True
> >> >>
> >> >>                     c = Column(*args, **kwargs)
> >> >>                     dict_[key] = c
> >> >>                     setattr(cls, key, c)
> >> >>
> >> >>         # Pass this along to SA's declarative mapper stuff
> >> >>         _as_declarative(cls, classname, dict_)
> >> >>
> >> >>         super(DeclarativeMetaTraits, cls).__init__(cls,
> >> >> classname, bases, dict_)
> >> >>
> >> >>
> >> >> class HasTraitsORM(HasTraits):
> >> >>     __metaclass__ = DeclarativeMetaTraits
> >> >>
> >> >>     # some SA hacking
> >> >>     __sa_instrumentation_manager__ = HasTraitsORMState
> >> >>
> >> >>     _decl_class_registry = dict()
> >> >>     metadata = MetaData()
> >> >>
> >> >>     # Any implicit traits added by SQLAlchemy are transient
> >> >> and should not be
> >> >>     # copied through .clone_traits(), copy.copy(), or
> >> >> pickling. _ = Python(transient=True)
> >> >>
> >> >>
> >> >>     # For some reason we need to override these, our
> >> >> superclass screws things up
> >> >>
> >> >>     def __getattr__(self, key):
> >> >>         if is_instrumented(self, key):
> >> >>             return get_attribute(self, key)
> >> >>         else:
> >> >>             try:
> >> >>                 return getattr(self, key)
> >> >>             except:
> >> >>                 pass
> >> >>
> >> >>     def __setattr__(self, key, value):
> >> >>         if is_instrumented(self, key):
> >> >>             set_attribute(self, key, value)
> >> >>         else:
> >> >>             setattr(self, key, value)
> >> >>
> >> >>
> >> >> #################################################
>
> 


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to