summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2022-04-13 16:42:29 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2022-04-13 16:42:29 +0000
commit623aae51f609002ee98b9c41208ecabcb94e9b15 (patch)
treef1e35ec9e034d33228f2b86d4e1ca631b55f531f /lib/sqlalchemy
parent0bfb620009f668e97ad3c2c25a564ca36428b9ae (diff)
parent428ea01f00a9cc7f85e435018565eb6da7af1b77 (diff)
downloadsqlalchemy-623aae51f609002ee98b9c41208ecabcb94e9b15.tar.gz
Merge "read from cls.__dict__ so init_subclass works" into main
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/decl_api.py7
-rw-r--r--lib/sqlalchemy/orm/decl_base.py16
-rw-r--r--lib/sqlalchemy/orm/instrumentation.py3
3 files changed, 20 insertions, 6 deletions
diff --git a/lib/sqlalchemy/orm/decl_api.py b/lib/sqlalchemy/orm/decl_api.py
index 4a699f63b..70507015b 100644
--- a/lib/sqlalchemy/orm/decl_api.py
+++ b/lib/sqlalchemy/orm/decl_api.py
@@ -109,6 +109,10 @@ class DeclarativeMeta(
def __init__(
cls, classname: Any, bases: Any, dict_: Any, **kw: Any
) -> None:
+ # use cls.__dict__, which can be modified by an
+ # __init_subclass__() method (#7900)
+ dict_ = cls.__dict__
+
# early-consume registry from the initial declarative base,
# assign privately to not conflict with subclass attributes named
# "registry"
@@ -293,7 +297,8 @@ class declared_attr(interfaces._MappedAttribute[_T]):
# here, we are inside of the declarative scan. use the registry
# that is tracking the values of these attributes.
- declarative_scan = manager.declarative_scan
+ declarative_scan = manager.declarative_scan()
+ assert declarative_scan is not None
reg = declarative_scan.declared_attr_reg
if self in reg:
diff --git a/lib/sqlalchemy/orm/decl_base.py b/lib/sqlalchemy/orm/decl_base.py
index 3fb8af80c..804d05ce1 100644
--- a/lib/sqlalchemy/orm/decl_base.py
+++ b/lib/sqlalchemy/orm/decl_base.py
@@ -161,7 +161,13 @@ def _check_declared_props_nocascade(obj, name, cls):
class _MapperConfig:
- __slots__ = ("cls", "classname", "properties", "declared_attr_reg")
+ __slots__ = (
+ "cls",
+ "classname",
+ "properties",
+ "declared_attr_reg",
+ "__weakref__",
+ )
@classmethod
def setup_mapping(cls, registry, cls_, dict_, table, mapper_kw):
@@ -311,13 +317,15 @@ class _ClassScanMapperConfig(_MapperConfig):
mapper_kw,
):
+ # grab class dict before the instrumentation manager has been added.
+ # reduces cycles
+ self.clsdict_view = (
+ util.immutabledict(dict_) if dict_ else util.EMPTY_DICT
+ )
super(_ClassScanMapperConfig, self).__init__(registry, cls_, mapper_kw)
self.registry = registry
self.persist_selectable = None
- self.clsdict_view = (
- util.immutabledict(dict_) if dict_ else util.EMPTY_DICT
- )
self.collected_attributes = {}
self.collected_annotations: Dict[str, Tuple[Any, bool]] = {}
self.declared_columns = util.OrderedSet()
diff --git a/lib/sqlalchemy/orm/instrumentation.py b/lib/sqlalchemy/orm/instrumentation.py
index a5dc305d2..0d4b630da 100644
--- a/lib/sqlalchemy/orm/instrumentation.py
+++ b/lib/sqlalchemy/orm/instrumentation.py
@@ -39,6 +39,7 @@ from typing import Optional
from typing import Set
from typing import TYPE_CHECKING
from typing import TypeVar
+import weakref
from . import base
from . import collections
@@ -167,7 +168,7 @@ class ClassManager(
if registry:
registry._add_manager(self)
if declarative_scan:
- self.declarative_scan = declarative_scan
+ self.declarative_scan = weakref.ref(declarative_scan)
if expired_attribute_loader:
self.expired_attribute_loader = expired_attribute_loader