AttributeError: 'NoneType' object has no attribute 'seeds' #314
I've been able to create a small example to reproduce this issue. It seems to be a combination of using an Entity subclass and accessing member functions.
from pony import orm db = orm.Database('sqlite', ':memory:') class Location(db.Entity): name = orm.Optional(str) parent = orm.Optional('Location', reverse='children') children = orm.Set('Location', cascade_delete=True) orm.composite_index(name, parent) @classmethod def get_root(cls) -> 'Location': root = cls.get(name='', parent=None) if not root: root = cls(name='', parent=None) return root @classmethod def get_by_path(cls, path): current = cls.get_root() for i in range(len(path)): current = cls.get(name=path[i], parent=current) if not current: raise ValueError(path) return current @classmethod def from_path(cls, path): parent = cls.get_by_path(path[:-1]) entity = cls(name=path[-1], parent=parent) return entity def as_tuple(self): if not self.parent: return (None, self.name) return (self.parent.as_tuple(), self.name) # XXX Remove this subclass and the error does not happen. class Object(Location): pass @orm.db_session def list_location(path): # XXX Or don't use as_tuple() here. return (x.as_tuple() for x in Location.get_by_path(path).children) @orm.db_session def create_location(path): Location.from_path(path) db.generate_mapping(create_tables=True) with orm.db_session: Location.get_root() # XXX Or use this context manager. # with orm.db_session: print(list(list_location())) create_location(['com.example']) create_location(['com.example', 'foobar']) print(list(list_location(['com.example'])))
Unfortunately, none of the ways above to prevent the issue are an option in my use case. :( Any idea how to fix this?
The text was updated successfully, but these errors were encountered:
This is the method that the AttributeError is raised in (Line 2111):
The condition above that line explicitly checks if there's any subclasses of the
Thanks for reporting!
Yes, this is correct fix. But I want to explain what is really happened:
When you work with Pony, all operations with objects should took place inside a
The last line of your test example is
In this line you execute
@orm.db_session def list_location(path): return (x.as_tuple() for x in Location.get_by_path(path).children)
In Python, generators are evaluated lazily. That is, when the function returns a generator object, it is not fully executed yet. Specifically,
At this moment Pony allows reading previously loaded attributes of objects even after
Returning to your code: I think it is better to replace generator with list comprehension. List comprehensions are evaluated eagerly, and this way we can guarantee that all evaluation will take place inside
@orm.db_session def list_location(path): return [x.as_tuple() for x in Location.get_by_path(path).children]
But after the fix I commited, previous version of code should work too
# Major features * Hybrid methods and properties added: https://docs.ponyorm.com/entities.html#hybrid-methods-and-properties * Allow to base queries on another queries: `select(x.a for x in prev_query if x.b)` * Added support of Python 3.7 * Added support of PyPy * `group_concat()` aggregate function added * pony.flask subpackage added for integration with Flask # Other features * `distinct` option added to aggregate functions * Support of explicit casting to `float` and `bool` in queries # Improvements * Apply @cut_traceback decorator only when pony.MODE is 'INTERACTIVE' # Bugfixes * In SQLite3 `LIKE` is case sensitive now * #249: Fix incorrect mixin used for Timedelta * #251: correct dealing with qualified table names * #301: Fix aggregation over JSON Column * #306: Support of frozenset constants added * #308: Fixed an error when assigning JSON attribute value to the same attribute: obj.json_attr = obj.json_attr * #313: Fix missed retry on exception raised during db_session.__exit__ * #314: Fix AttributeError: 'NoneType' object has no attribute 'seeds' * #315: Fix attribute lifting for JSON attributes * #321: Fix KeyError on obj.delete() * #325: duplicating percentage sign in raw SQL queries without parameters * #331: Overriding __len__ in entity fails * #336: entity declaration serialization * #357: reconnect after PostgreSQL server closed the connection unexpectedly * Fix Python implementation of between() function and rename arguments: between(a, x, y) -> between(x, a, b) * Fix retry handling: in PostgreSQL and Oracle an error can be raised during commit * Fix optimistic update checks for composite foreign keys * Don't raise OptimisticCheckError if db_session is not optimistic * Handling incorrect datetime values in MySQL * Improved ImportError exception messages when MySQLdb, pymysql, psycopg2 or psycopg2cffi driver was not found * desc() function fixed to allow reverse its effect by calling desc(desc(x)) * __contains__ method should check if objects belong to the same db_session * Fix pony.MODE detection; mod_wsgi detection according to official doc * A lot of inner fixes