From: Arie Bovenberg Date: Tue, 1 Feb 2022 20:08:19 +0000 (-0500) Subject: Fix overlapping slots, base classes without slots X-Git-Tag: rel_2_0_0b1~501^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0409c88c297efa324218340b265a8d107d57deee;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Fix overlapping slots, base classes without slots Some `__slots__` were not in order. Fixes #7527 ### Description I'm fixing two types of slots mistakes: - [x] remove overlapping slots (i.e. slots already defined on a base class) - [x] fix broken inheritance (i.e. slots class inheriting from a non-slots class) - [x] slots added to base class `TransactionalContext`. It seemed to use two attributes, which I've added as slots. - [x] empty slots removed from `ORMOption`. Its base class explicitly makes use of `__dict__` so empty slots don't add anything. - [x] empty slots added to `PostLoader`. It doesn't appear to use any slots not already defined on its base classes. - [x] empty slots added to `IterateMappersMixin`. It doesn't appear to use any slots not already defined on its subclasses. - [x] empty slots added to `ImmutableContainer`. It doesn't use any fields. - [x] empty slots added to `OperatorType`. It's a protocol. - [x] empty slots added to `InternalTraversal`, `_HasTraversalDispatch`. They don't seem to use attributes on their own. ### Checklist This pull request is: - [x] A short code fix - please include the issue number, and create an issue if none exists, which must include a complete example of the issue. one line code fixes without an issue and demonstration will not be accepted. - Please include: `Fixes: #` in the commit message - please include tests. one line code fixes without tests will not be accepted. **Have a nice day!** Closes: #7589 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/7589 Pull-request-sha: 70a9c4d46916b7c6907eb1d3ad4f7033ec964191 Change-Id: I6c6e3e69c3c34d0f3bdda7f0684849834fdd1863 --- diff --git a/lib/sqlalchemy/cyextension/immutabledict.pyx b/lib/sqlalchemy/cyextension/immutabledict.pyx index d07c81bd49..861e7574da 100644 --- a/lib/sqlalchemy/cyextension/immutabledict.pyx +++ b/lib/sqlalchemy/cyextension/immutabledict.pyx @@ -6,6 +6,9 @@ def _immutable_fn(obj): class ImmutableContainer: + + __slots__ = () + def _immutable(self, *a,**kw): _immutable_fn(self) diff --git a/lib/sqlalchemy/dialects/mysql/aiomysql.py b/lib/sqlalchemy/dialects/mysql/aiomysql.py index 975467c24f..df716346ef 100644 --- a/lib/sqlalchemy/dialects/mysql/aiomysql.py +++ b/lib/sqlalchemy/dialects/mysql/aiomysql.py @@ -179,7 +179,7 @@ class AsyncAdapt_aiomysql_ss_cursor(AsyncAdapt_aiomysql_cursor): class AsyncAdapt_aiomysql_connection(AdaptedConnection): await_ = staticmethod(await_only) - __slots__ = ("dbapi", "_connection", "_execute_mutex") + __slots__ = ("dbapi", "_execute_mutex") def __init__(self, dbapi, connection): self.dbapi = dbapi diff --git a/lib/sqlalchemy/dialects/sqlite/aiosqlite.py b/lib/sqlalchemy/dialects/sqlite/aiosqlite.py index c73afc9d1c..e88ab1a0fa 100644 --- a/lib/sqlalchemy/dialects/sqlite/aiosqlite.py +++ b/lib/sqlalchemy/dialects/sqlite/aiosqlite.py @@ -165,7 +165,7 @@ class AsyncAdapt_aiosqlite_ss_cursor(AsyncAdapt_aiosqlite_cursor): class AsyncAdapt_aiosqlite_connection(AdaptedConnection): await_ = staticmethod(await_only) - __slots__ = ("dbapi", "_connection") + __slots__ = ("dbapi",) def __init__(self, dbapi, connection): self.dbapi = dbapi diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index b67a212c83..4045eae907 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -2257,7 +2257,7 @@ class TwoPhaseTransaction(RootTransaction): """ - __slots__ = ("connection", "is_active", "xid", "_is_prepared") + __slots__ = ("xid", "_is_prepared") def __init__(self, connection, xid): self._is_prepared = False diff --git a/lib/sqlalchemy/engine/row.py b/lib/sqlalchemy/engine/row.py index 16215ccc47..75c56450e2 100644 --- a/lib/sqlalchemy/engine/row.py +++ b/lib/sqlalchemy/engine/row.py @@ -194,10 +194,7 @@ class ROMappingView( collections_abc.ValuesView, collections_abc.ItemsView, ): - __slots__ = ( - "_mapping", - "_items", - ) + __slots__ = ("_items",) def __init__(self, mapping, items): self._mapping = mapping diff --git a/lib/sqlalchemy/engine/util.py b/lib/sqlalchemy/engine/util.py index 7e41339bbb..f74cd3f847 100644 --- a/lib/sqlalchemy/engine/util.py +++ b/lib/sqlalchemy/engine/util.py @@ -43,7 +43,7 @@ class TransactionalContext: """ - _trans_subject = None + __slots__ = ("_outer_trans_ctx", "_trans_subject", "__weakref__") def _transaction_is_active(self): raise NotImplementedError() @@ -95,7 +95,7 @@ class TransactionalContext: return self def __exit__(self, type_, value, traceback): - subject = self._trans_subject + subject = getattr(self, "_trans_subject", None) # simplistically we could assume that # "subject._trans_context_manager is self". However, any calling diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index c4aac5a385..b035dbef2f 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -64,7 +64,6 @@ class ColumnProperty(StrategizedProperty[_T]): "descriptor", "active_history", "expire_on_flush", - "info", "doc", "strategy_key", "_creation_order", diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index beaf649b78..07e71d4c0b 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -1165,6 +1165,8 @@ class LoadLazyAttribute: class PostLoader(AbstractRelationshipLoader): """A relationship loader that emits a second SELECT statement.""" + __slots__ = () + def _check_recursive_postload(self, context, path, join_depth=None): effective_path = ( context.compile_state.current_path or orm_util.PathRegistry.root diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index e6cf3ad6b5..b478f427cc 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -456,6 +456,9 @@ class UOWTransaction: class IterateMappersMixin: + + __slots__ = () + def _mappers(self, uow): if self.fromparent: return iter( diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py index 8ae8f8f65f..f4fe7afab2 100644 --- a/lib/sqlalchemy/sql/base.py +++ b/lib/sqlalchemy/sql/base.py @@ -639,6 +639,8 @@ class _MetaOptions(type): class Options(metaclass=_MetaOptions): """A cacheable option dictionary with defaults.""" + __slots__ = () + def __init_subclass__(cls) -> None: dict_ = cls.__dict__ cls._cache_attrs = tuple( diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py index cf61f26376..255e77b7f9 100644 --- a/lib/sqlalchemy/sql/operators.py +++ b/lib/sqlalchemy/sql/operators.py @@ -57,6 +57,8 @@ _T = TypeVar("_T", bound=Any) class OperatorType(Protocol): """describe an op() function.""" + __slots__ = () + __name__: str def __call__( diff --git a/lib/sqlalchemy/sql/visitors.py b/lib/sqlalchemy/sql/visitors.py index 78384782b8..640c07d618 100644 --- a/lib/sqlalchemy/sql/visitors.py +++ b/lib/sqlalchemy/sql/visitors.py @@ -184,6 +184,8 @@ class _HasTraversalDispatch: """ + __slots__ = () + def __init_subclass__(cls) -> None: cls._generate_traversal_dispatch() super().__init_subclass__() @@ -299,6 +301,8 @@ class InternalTraversal(_HasTraversalDispatch): """ + __slots__ = () + dp_has_cache_key = symbol("HC") """Visit a :class:`.HasCacheKey` object.""" @@ -504,6 +508,8 @@ class ExtendedInternalTraversal(InternalTraversal): """ + __slots__ = () + dp_ignore = symbol("IG") """Specify an object that should be ignored entirely.