diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-08-31 11:46:55 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-09-10 17:53:53 -0400 |
commit | 450f5c0d6519a439f4025c3892fe4cf3ee2d892c (patch) | |
tree | 1f3f2467306304a5e9ccb25f10bfdf9989327ae2 /test/orm/declarative/test_concurrency.py | |
parent | 96bb6dc56d1da2b4fa30afd08ac4dfa665752913 (diff) | |
download | sqlalchemy-450f5c0d6519a439f4025c3892fe4cf3ee2d892c.tar.gz |
Build out new declarative systems; deprecate mapper()
The ORM Declarative system is now unified into the ORM itself, with new
import spaces under ``sqlalchemy.orm`` and new kinds of mappings. Support
for decorator-based mappings without using a base class, support for
classical style-mapper() calls that have access to the declarative class
registry for relationships, and full integration of Declarative with 3rd
party class attribute systems like ``dataclasses`` and ``attrs`` is now
supported.
Fixes: #5508
Change-Id: I130b2b6edff6450bfe8a3e6baa099ff04b5471ff
Diffstat (limited to 'test/orm/declarative/test_concurrency.py')
-rw-r--r-- | test/orm/declarative/test_concurrency.py | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/test/orm/declarative/test_concurrency.py b/test/orm/declarative/test_concurrency.py new file mode 100644 index 000000000..d731c6afa --- /dev/null +++ b/test/orm/declarative/test_concurrency.py @@ -0,0 +1,90 @@ +import random +import threading +import time + +from sqlalchemy import Column +from sqlalchemy import exc +from sqlalchemy import ForeignKey +from sqlalchemy import Integer +from sqlalchemy import String +from sqlalchemy.orm import clear_mappers +from sqlalchemy.orm import declarative_base +from sqlalchemy.orm import declared_attr +from sqlalchemy.orm import exc as orm_exc +from sqlalchemy.orm import relationship +from sqlalchemy.orm import Session +from sqlalchemy.testing import fixtures + + +class ConcurrentUseDeclMappingTest(fixtures.TestBase): + def teardown(self): + clear_mappers() + + @classmethod + def make_a(cls, Base): + class A(Base): + __tablename__ = "a" + + id = Column(Integer, primary_key=True) + data = Column(String) + bs = relationship("B") + + # need a strong ref so that the class is not gc'ed + cls.A = A + + @classmethod + def query_a(cls, Base, result): + s = Session() + time.sleep(random.random() / 100) + A = cls.A + try: + s.query(A).join(A.bs) + except orm_exc.UnmappedClassError as oe: + # this is the failure mode, where B is being handled by + # declarative and is in the registry but not mapped yet. + result[0] = oe + except exc.InvalidRequestError: + # if make_b() starts too slowly, we can reach here, because + # B isn't in the registry yet. We can't guard against this + # case in the library because a class can refer to a name that + # doesn't exist and that has to raise. + result[0] = True + else: + # no conflict + result[0] = True + + @classmethod + def make_b(cls, Base): + class B(Base): + __tablename__ = "b" + id = Column(Integer, primary_key=True) + + @declared_attr + def data(cls): + time.sleep(0.001) + return Column(String) + + a_id = Column(ForeignKey("a.id")) + + cls.B = B + + def test_concurrent_create(self): + for i in range(50): + Base = declarative_base() + clear_mappers() + + self.make_a(Base) + result = [False] + threads = [ + threading.Thread(target=self.make_b, args=(Base,)), + threading.Thread(target=self.query_a, args=(Base, result)), + ] + + for t in threads: + t.start() + + for t in threads: + t.join() + + if isinstance(result[0], orm_exc.UnmappedClassError): + raise result[0] |