The context of the infinite recursion is this:

        def __getattr__(self, attribute):
            """Delegate __getattr__ to the original descriptor and/or
comparator."""

            try:
                return getattr(descriptor, attribute)
            except AttributeError:
                try:
                    return getattr(self._comparator, attribute)
<--------------- recursion here
                except AttributeError:
                    raise AttributeError('Neither %r object nor %r object
has an attribute %r' % (
                            type(descriptor).__name__,
                            type(self._comparator).__name__,
                            attribute)
                    )

I added a line to print the AttributeError that comes up.

The first time, it is 'property' object has no attribute
'_update_table_name'. So the method _update_table_name is being looked up on
the property object (or proxy, here), instead of the instance of class Foo.
This may be because of not-so-minimally invasive surgery on the part of the
debugger.

The rest of the time (in infinite recursion), the AttributeError is:
'property' object has no attribute '_comparator'.

So evaluating self._comparator involves calling
self.__getattr__('_comparator'), presumably because the self_comparator
member is missing for some reason (also quite possibly something involving
debugger hooks).

I get around this by changing self._comparator to
self.__dict__('_comparator') in both places, above. That way, the error:

    AttributeError: Neither 'property' object nor 'NoneType' object has an
attribute '_update_table_name'

is raised, which still leaves something awry under debugging (looking up the
attribute in the wrong object), but at least there is no infinite recursion.

A patch against trunk is attached, if you want to apply this change.
Possibly it is not really needed when the other problem is fixed (attribute
looked up in the wrong object when debugging), but I believe it is more
robust, and all SQLAlchemy tests pass.

Regards,

    - Gulli



On Tue, Mar 3, 2009 at 4:10 AM, Gunnlaugur Briem <gunnlau...@gmail.com>wrote:

>
> Hi,
>
> I have a table-mapped attribute that is dependent on two other
> attributes:
>
> from sqlalchemy import Table, MetaData, Column, Text, create_engine,
> Integer
> from sqlalchemy.orm import mapper, synonym
>
> class Foo(object):
>    def _get_name(self):
>        return self._name
>    def _set_name(self, name):
>        self._name = name
>        self._update_table_name()
>    name = property(_get_name, _set_name)
>
>    def _get_provider(self):
>        return self._provider
>    def _set_provider(self, provider):
>        self._provider = provider
>        self._update_table_name()
>    provider = property(_get_provider, _set_provider)
>
>    def _update_table_name(self):
>        table_name = "%s_%s" % (self.provider, self.name)
>        if len(table_name) > 50:
>            table_name = table_name[0:50]
>        self.table_name = table_name
>
> foo_table = Table('foo', MetaData(),
>                  Column('id', Integer, primary_key=True),
>                  Column('name', Text),
>                  Column('provider', Text),
>                  Column('table_name', Text)
>                  )
> mapper(Foo, foo_table, properties={
>    'name' : synonym('_name', map_column=True),
>    'provider': synonym('_provider', map_column=True),
>                                   })
>
> e = create_engine('sqlite:///:memory:')
> foo_table.metadata.create_all(bind=e)
>
> When I run this normally, nothing happens. When I run it under the
> debugger (in PyDev), I get infinite recursion, looking like this:
>
> Traceback (most recent call last):
>  File "/Applications/eclipse/plugins/
> org.python.pydev.debug_1.4.4.2636/pysrc/pydevd.py", line 883, in
> <module>
>    debugger.run(setup['file'], None, None)
>  File "/Applications/eclipse/plugins/
> org.python.pydev.debug_1.4.4.2636/pysrc/pydevd.py", line 712, in run
>    execfile(file, globals, locals) #execute the script
>  File "/Users/gthb/Documents/workspace/test/src/sqlalchemytest7.py",
> line 33, in <module>
>    'provider': synonym('_provider', map_column=True),
>  File "/path/to/SQLAlchemy/sqlalchemy/orm/__init__.py", line 752, in
> mapper
>    return Mapper(class_, local_table, *args, **params)
>  File "/path/to/SQLAlchemy/sqlalchemy/orm/mapper.py", line 198, in
> __init__
>    self._configure_properties()
>  File "/path/to/SQLAlchemy/sqlalchemy/orm/mapper.py", line 481, in
> _configure_properties
>    self._configure_property(key, prop, False)
>  File "/path/to/SQLAlchemy/sqlalchemy/orm/mapper.py", line 616, in
> _configure_property
>    prop.instrument_class(self)
>  File "/path/to/SQLAlchemy/sqlalchemy/orm/properties.py", line 302,
> in instrument_class
>    proxy_property=self.descriptor
>  File "/path/to/SQLAlchemy/sqlalchemy/orm/attributes.py", line 1590,
> in register_descriptor
>    descriptor = proxy_type(key, proxy_property, comparator,
> parententity)
>  File "/path/to/SQLAlchemy/sqlalchemy/orm/attributes.py", line 181,
> in __init__
>    self.descriptor = self.user_prop = descriptor
>  File "/Users/gthb/Documents/workspace/test/src/sqlalchemytest7.py",
> line 14, in _set_name
>    self._update_table_name()
>  File "/path/to/SQLAlchemy/sqlalchemy/orm/attributes.py", line 214,
> in __getattr__
>    return getattr(self._comparator, attribute)
>  File "/path/to/SQLAlchemy/sqlalchemy/orm/attributes.py", line 214,
> in __getattr__
>    return getattr(self._comparator, attribute)
>  ....
>
> The same can be reproduced outside of PyDev by doing:
>
> python -m pdb sqlalchemytest7.py
>
> and stepping until the above calamity strikes. (It seems it does not
> happen on cont)
>
> This is in python 2.5.2 on Mac OS X 10.5.6, with sqlalchemy 0.5.2.
>
> So, two things:
>
> 1) what am I doing wrong?
>
> 2) SQLAlchemy should handle it more gracefully. :)
>
> Regards,
>
>    - Gulli
>
> >
>

--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---

Attachment: sqlalchemy.orm.attributes-avoid-infinite-recursion-under-debugger.patch
Description: Binary data

Reply via email to