summaryrefslogtreecommitdiff
path: root/test/orm/declarative/test_concurrency.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2020-08-31 11:46:55 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2020-09-10 17:53:53 -0400
commit450f5c0d6519a439f4025c3892fe4cf3ee2d892c (patch)
tree1f3f2467306304a5e9ccb25f10bfdf9989327ae2 /test/orm/declarative/test_concurrency.py
parent96bb6dc56d1da2b4fa30afd08ac4dfa665752913 (diff)
downloadsqlalchemy-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.py90
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]