I forgot to mention, we’re not using clear_mappers(). Thanks, Will
> On 21 Feb 2016, at 19:19, Will Angenent <w.angen...@gmail.com> wrote: > > Hi Mike, > > Thanks for your quick response yet again! Here’s the stack trace. > > tests/integration/test_database.py:14: in test_database_is_up_to_date > create_test_db(session) > __init__.py:111: in create_test_db > pd_utils.do_import(dtype='locations', ifile=yaml_file) > ../utils/provider_data/__init__.py:54: in do_import > inserted, updated = getattr(self, 'import_%s' % item)(ifile) > ../utils/provider_data/__init__.py:22: in import_locations > return import_locations(self.session, ifile) > ../utils/provider_data/locations.py:190: in import_locations > Location).filter_by( > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py:1260: > in query > return self._query_cls(entities, self, **kwargs) > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py:110: > in __init__ > self._set_entities(entities) > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py:120: > in _set_entities > self._set_entity_selectables(self._entities) > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py:150: > in _set_entity_selectables > ent.setup_entity(*d[entity]) > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py:3250: > in setup_entity > self._with_polymorphic = ext_info.with_polymorphic_mappers > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py:747: > in __get__ > obj.__dict__[self.__name__] = result = self.fget(obj) > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py:1893: > in _with_polymorphic_mappers > configure_mappers() > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py:2756: > in configure_mappers > mapper._post_configure_properties() > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py:1710: > in _post_configure_properties > prop.init() > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/interfaces.py:183: > in init > self.do_init() > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py:1613: > in do_init > self._setup_join_conditions() > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py:1688: > in _setup_join_conditions > can_be_synced_fn=self._columns_are_mapped > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py:1956: > in __init__ > self._determine_joins() > ../../devenv/local/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py:2060: > in _determine_joins > "specify a 'primaryjoin' expression." % self.prop) > E NoForeignKeysError: Could not determine join condition between > parent/child tables on relationship Resource.tags - there are no foreign keys > linking these tables. Ensure that referencing columns are associated with a > ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression. > >> The sys.modules activity is not really the primary cause, it's that alembic >> makes use of a module object in a temporary way. > Absolutely agree. What I did to diagnose this was to replace > secondary=lambda: tag_to_resource with secondary=non_lambda_tag_to_resource, > using this: > > def non_lambda_tag_to_resource(): > import sys > sys.stderr.write('*** tag_to_resource=%s\n' % tag_to_resource) > # > sys.stderr.write('*** name=%s\n' % __name__) > # > return tag_to_resource > > What I found is that in the bad case, both tag_to_resource and __name__ were > None. > > Thanks, > Will > >> On 21 Feb 2016, at 19:12, Mike Bayer <clas...@zzzcomputing.com >> <mailto:clas...@zzzcomputing.com>> wrote: >> >> >> >> Hi there - >> >> Can you post a stack trace, and also is your test suite making use of >> clear_mappers() ? >> >> The sys.modules activity is not really the primary cause, it's that alembic >> makes use of a module object in a temporary way. >> >> On Feb 21, 2016, at 1:48 PM, Will Angenent <w.angen...@gmail.com >> <mailto:w.angen...@gmail.com>> wrote: >> >>> Hi, >>> >>> We had this interesting issue recently, and I've been trying to figure out >>> if we deserve this, if this is simply unavoidable, or whether it can be >>> considered a bug. We're using python 2.7.6, sqlalchemy 1.0.12 and alembic >>> 0.8.4. >>> >>> Summary: >>> >>> This statement in alembic.util.pyfiles.load_python_file(): >>> del sys.modules[module_id] >>> randomly causes the reference count of the module object to become zero; >>> triggering cleanup of the object. This effectively causes all variables in >>> the migration file to become None, leading to an sqlalchemy mapper problem >>> initializing a mapper configuration for a many-to-many relationship in a >>> model defined in the migration file. >>> >>> Are we being stupid to be using the ORM in alembic migrations? If not, is >>> it worth for me to spend more time on this? Is there any way to get this to >>> behave non-randomly? More details are below. >>> >>> Thanks, >>> Will >>> >>> Long version... >>> >>> What happened is that someone in my team added an alembic migration. He >>> used the sqlalchemy ORM and used a declarative_base with a couple of model >>> files to get the job done. The migration was fine and everyone was happy. >>> Then, about a week later, I added an import statement in a totally >>> unrelated area of code, and suddenly running alembic upgrade starting >>> failing with a ORM mapper error. I didn't spend much time on it, but >>> refactored a couple of things and the problem vanished. >>> >>> Then a couple of days later, our tests started failing with the same error. >>> We had a closer look and found the failure to be random. The inclusion of >>> the import statment seemed to trigger the random behavior. It wasn't just >>> the import statement though, other changes, such as removing a property in >>> an ORM class could make the problem appear or go away. What we were doing >>> in this particualr failure mode, is running py.test which would, in order: >>> >>> - import this random 3rd party module >>> - use the alembic API to upgrade to ensure a postgres database is up to date >>> - later on, in an unrelated test, do a query, triggering the initialization >>> of the mappings and crashing >>> >>> At first, I thought it might be a problem with sqlalchemy. Spurred on by >>> this comment in mapper.py: >>> >>> # initialize properties on all mappers >>> # note that _mapper_registry is unordered, which >>> # may randomly conceal/reveal issues related to >>> # the order of mapper compilation >>> >>> I added a couple of sorted() statements throughout the code, but it made no >>> difference. Finally, I found that the problem was a lambda function in a >>> relationship with a secondary. Something like e.g. >>> >>> tag_to_resource = Table( >>> 'tag_to_resource', Base.metadata, >>> Column('tag_id', ForeignKey('tags.id', ondelete='CASCADE'), >>> primary_key=True, index=True), >>> Column('resource_id', ForeignKey('resources.id', ondelete='CASCADE'), >>> primary_key=True, index=True) >>> ) >>> >>> class Resource(Base): >>> __tablename__ = 'resources' >>> id = Column(UUIDType(binary=True), primary_key=True, default=uuid.uuid4) >>> >>> tags = relationship("Tag", secondary=lambda: tag_to_resource, >>> backref='resources') >>> >>> The lambda function called in _process_dependent_arguments() was returning >>> None instead of tag_to_resource. Resulting in a: >>> >>> sqlalchemy.exc.NoForeignKeysError: Could not determine join condition >>> between parent/child tables on relationship Resource.tags - there are no >>> foreign keys linking these tables. Ensure that referencing columns are >>> associated with a ForeignKey or ForeignKeyConstraint, or specify a >>> 'primaryjoin' expression. >>> >>> Looking deeper I found that __name__ was also None. This kind of thing >>> happens when sys.modules is messed with. I looked at the alembic code and >>> found this in load_python_file(): >>> >>> del sys.modules[module_id] >>> >>> If I remove that statement, the problem goes away. >>> >>> Could it be that the reference count of the module object is becoming zero >>> randomly, causing python to delete the data, as explained in this post? >>> http://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name >>> >>> <http://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name> >>> >>> I've narrowed the problem down to a python test script, but it still >>> imports a load of other stuff. I can trigger the good + bad case by just >>> removing an import statement. I've been trying to get this down to a simple >>> script in an attempt to prove what's going on, but the problem tends to >>> come and go while I'm deleting code; making it difficult to narrow down. >>> For example, I was convinced one day that the problem vanished by upgrading >>> to sql alchemy 1.0.12, but the very next day the same code started failing >>> again! >>> >>> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "sqlalchemy" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to sqlalchemy+unsubscr...@googlegroups.com >>> <mailto:sqlalchemy+unsubscr...@googlegroups.com>. >>> To post to this group, send email to sqlalchemy@googlegroups.com >>> <mailto:sqlalchemy@googlegroups.com>. >>> Visit this group at https://groups.google.com/group/sqlalchemy >>> <https://groups.google.com/group/sqlalchemy>. >>> For more options, visit https://groups.google.com/d/optout >>> <https://groups.google.com/d/optout>. >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "sqlalchemy" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to sqlalchemy+unsubscr...@googlegroups.com >> <mailto:sqlalchemy+unsubscr...@googlegroups.com>. >> To post to this group, send email to sqlalchemy@googlegroups.com >> <mailto:sqlalchemy@googlegroups.com>. >> Visit this group at https://groups.google.com/group/sqlalchemy >> <https://groups.google.com/group/sqlalchemy>. >> For more options, visit https://groups.google.com/d/optout >> <https://groups.google.com/d/optout>. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.