Synonym for Association Proxy results in infinite recursion

Issue #4067 resolved
Martynas Puronas created an issue

Hi everyone,

I ran into an issue where accessing the prop attribute of a synonym for an association proxy makes SQLAlchemy go into an infinite recursion resulting in the program crashing. I'm using SQLAlchemy 1.1.13, Python 3.5.2, SQLite 3.8.11 on Windows 8.1 machine.

Let me know if you need any more details.

Thanks!

Code that reproduces the issue:

from sqlalchemy import (
    create_engine, Column, Integer, ForeignKey
)
from sqlalchemy.orm import sessionmaker, relationship, synonym
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy


engine = create_engine('sqlite://')
Session = sessionmaker(bind=engine)

session = Session()

Base = declarative_base()


class Parent(Base):
    __tablename__ = 'Parent'

    id = Column('Id', Integer, primary_key=True)


class Child(Base):
    __tablename__ = 'Child'

    id = Column('Id', Integer, primary_key=True)
    parent_id = Column('ParentId', Integer, ForeignKey('Parent.Id'), nullable=False)

    parent = relationship(Parent)

    id_of_parent = association_proxy('parent', 'id')
    also_id_of_parent = synonym('id_of_parent')


Base.metadata.create_all(session.bind)

print('also_id_of_parent:', Child.also_id_of_parent.prop)

Stack trace of the error (I'm not sure if I should add it as an attachment to the ticket or not)

#!
C:\Users\root\virtualenv\random\Scripts\python.exe C:/Users/root/python/random-scripts/synonym_association_proxy/reproduce.py
Traceback (most recent call last):
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\attributes.py", line 303, in __getattr__
    return getattr(descriptor, attribute)
AttributeError: 'property' object has no attribute 'prop'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\attributes.py", line 303, in __getattr__
    return getattr(descriptor, attribute)
AttributeError: 'property' object has no attribute 'comparator'

rm\attributes.py", line 306, in __getattr__
    return getattr(self.comparator, attribute)
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\attributes.py", line 306, in __getattr__
    return getattr(self.comparator, attribute)
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\attributes.py", line 306, in __getattr__

...

  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\attributes.py", line 306, in __getattr__
    return getattr(self.comparator, attribute)
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\attributes.py", line 306, in __getattr__
    return getattr(self.comparator, attribute)
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\util\langhelpers.py", line 764, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\attributes.py", line 276, in comparator
    self._comparator = self._comparator()
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\descriptor_props.py", line 71, in <lambda>
    lambda: self._comparator_factory(mapper),
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\descriptor_props.py", line 580, in _comparator_factory
    prop = self._proxied_property
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\util\langhelpers.py", line 764, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "C:\Users\root\virtualenv\random\lib\site-packages\sqlalchemy\orm\descriptor_props.py", line 577, in _proxied_property
    return getattr(self.parent.class_, self.name).property
RecursionError: maximum recursion depth exceeded

Process finished with exit code 1

Comments (5)

  1. Mike Bayer repo owner

    So you don't normally want to put an entire recursion overflow stack trace on the issue, for those you should trim out the redundant parts, but there we are.

    This is a bug but synonym() does not support assocaition proxies in the first place, so the way I'd fix this for the time being would be for synonym to detect that it's being used against something that is not a "mapped" property, e.g. column property or relationship(), and just raise an error.

  2. Mike Bayer repo owner
    • changed component to orm
    • edited description
    • changed milestone to 1.1.x
    • marked as major

    this is one of those AttributeError endless things in proxied_property() and it will take some effort to extricate from inside the loop, even in py36 it gets caught inside of Proxy.getattr()

  3. Mike Bayer repo owner

    Check for non-mapped property in synonym

    An :class:.InvalidRequestError is raised when a :func:.synonym is used against an attribute that is not against a :class:.MapperProperty, such as an association proxy. Previously, a recursion overflow would occur trying to locate non-existent attributes.

    Change-Id: If2ce38c429a69951df4c94b71b74edbd59d775e3 Fixes: #4067

    → <<cset 130f31ca79c7>>

  4. Mike Bayer repo owner

    Check for non-mapped property in synonym

    An :class:.InvalidRequestError is raised when a :func:.synonym is used against an attribute that is not against a :class:.MapperProperty, such as an association proxy. Previously, a recursion overflow would occur trying to locate non-existent attributes.

    Change-Id: If2ce38c429a69951df4c94b71b74edbd59d775e3 Fixes: #4067 (cherry picked from commit 130f31ca79c7b40b2cb8aa1a4af7049408074d12)

    → <<cset 8ec1369eb3dc>>

  5. Log in to comment