diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-11-09 18:41:54 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-11-10 12:19:02 -0500 |
| commit | e3a8d198917f4246365e09fa975d55c64082cd2e (patch) | |
| tree | 19c47960c4ab212a59528e0f8dedf6d3a77b557f /lib/sqlalchemy | |
| parent | ebb54e80a5a52d0cce4cba1abc21c707a42c2c73 (diff) | |
| download | sqlalchemy-e3a8d198917f4246365e09fa975d55c64082cd2e.tar.gz | |
work around Python 3.11 IntEnum issue; update FastIntFlag
in [1], Python 3.11 seems to have changed the behavior of
IntEnum. We didn't notice this because we have our own
workaround class already, but typing did. Ensure we remain
compatible with IntFlag.
This change also modifies FastIntFlag to no longer use
global symbols; this is unnecessary as we assign FastIntFlag
members explicitly. Use of ``symbol()`` should probably
be phased out.
[1] https://github.com/python/cpython/issues/99304
Fixes: #8783
Change-Id: I8ae2e871ff1467ae5ca1f63e66b5dae45d4a6c93
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/dialects/postgresql/psycopg2.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/base.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/collections.py | 15 | ||||
| -rw-r--r-- | lib/sqlalchemy/util/langhelpers.py | 25 |
4 files changed, 26 insertions, 18 deletions
diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py index 4a8df5b5e..cb5cab178 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py @@ -583,7 +583,7 @@ class ExecutemanyMode(FastIntFlag): ( EXECUTEMANY_VALUES, EXECUTEMANY_VALUES_PLUS_BATCH, -) = tuple(ExecutemanyMode) +) = ExecutemanyMode.__members__.values() class PGDialect_psycopg2(_PGDialect_common_psycopg): diff --git a/lib/sqlalchemy/orm/base.py b/lib/sqlalchemy/orm/base.py index e4a69a352..b46c78799 100644 --- a/lib/sqlalchemy/orm/base.py +++ b/lib/sqlalchemy/orm/base.py @@ -188,7 +188,7 @@ class PassiveFlag(FastIntFlag): PASSIVE_NO_FETCH, PASSIVE_NO_FETCH_RELATED, PASSIVE_ONLY_PERSISTENT, -) = tuple(PassiveFlag) +) = PassiveFlag.__members__.values() DEFAULT_MANAGER_ATTR = "_sa_class_manager" DEFAULT_STATE_ATTR = "_sa_instance_state" diff --git a/lib/sqlalchemy/orm/collections.py b/lib/sqlalchemy/orm/collections.py index e3051e268..0c1ccbf10 100644 --- a/lib/sqlalchemy/orm/collections.py +++ b/lib/sqlalchemy/orm/collections.py @@ -128,6 +128,7 @@ import weakref from .base import NO_KEY from .. import exc as sa_exc from .. import util +from ..sql.base import NO_ARG from ..util.compat import inspect_getfullargspec from ..util.typing import Protocol @@ -1222,8 +1223,6 @@ def _dict_decorators() -> Dict[str, Callable[[_FN], _FN]]: fn._sa_instrumented = True fn.__doc__ = getattr(dict, fn.__name__).__doc__ - Unspecified = util.symbol("Unspecified") - def __setitem__(fn): def __setitem__(self, key, value, _sa_initiator=None): if key in self: @@ -1253,10 +1252,10 @@ def _dict_decorators() -> Dict[str, Callable[[_FN], _FN]]: return clear def pop(fn): - def pop(self, key, default=Unspecified): + def pop(self, key, default=NO_ARG): __before_pop(self) _to_del = key in self - if default is Unspecified: + if default is NO_ARG: item = fn(self, key) else: item = fn(self, key, default) @@ -1293,8 +1292,8 @@ def _dict_decorators() -> Dict[str, Callable[[_FN], _FN]]: return setdefault def update(fn): - def update(self, __other=Unspecified, **kw): - if __other is not Unspecified: + def update(self, __other=NO_ARG, **kw): + if __other is not NO_ARG: if hasattr(__other, "keys"): for key in list(__other): if key not in self or self[key] is not __other[key]: @@ -1318,7 +1317,6 @@ def _dict_decorators() -> Dict[str, Callable[[_FN], _FN]]: l = locals().copy() l.pop("_tidy") - l.pop("Unspecified") return l @@ -1346,8 +1344,6 @@ def _set_decorators() -> Dict[str, Callable[[_FN], _FN]]: fn._sa_instrumented = True fn.__doc__ = getattr(set, fn.__name__).__doc__ - Unspecified = util.symbol("Unspecified") - def add(fn): def add(self, value, _sa_initiator=None): if value not in self: @@ -1500,7 +1496,6 @@ def _set_decorators() -> Dict[str, Callable[[_FN], _FN]]: l = locals().copy() l.pop("_tidy") - l.pop("Unspecified") return l diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index d8d39f56c..051a8c89e 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -1603,11 +1603,6 @@ class symbol(int): Repeated calls of symbol('name') will all return the same instance. - In SQLAlchemy 2.0, symbol() is used for the implementation of - ``_FastIntFlag``, but otherwise should be mostly replaced by - ``enum.Enum`` and variants. - - """ name: str @@ -1632,7 +1627,17 @@ class symbol(int): if doc: sym.__doc__ = doc + # NOTE: we should ultimately get rid of this global thing, + # however, currently it is to support pickling. The best + # change would be when we are on py3.11 at a minimum, we + # switch to stdlib enum.IntFlag. cls.symbols[name] = sym + else: + if canonical and canonical != sym: + raise TypeError( + f"Can't replace canonical symbol for {name} " + f"with new int value {canonical}" + ) return sym def __reduce__(self): @@ -1665,8 +1670,16 @@ class _IntFlagMeta(type): setattr(cls, k, sym) items.append(sym) + cls.__members__ = _collections.immutabledict( + {sym.name: sym for sym in items} + ) + def __iter__(self) -> Iterator[symbol]: - return iter(self._items) + raise NotImplementedError( + "iter not implemented to ensure compatibility with " + "Python 3.11 IntFlag. Please use __members__. See " + "https://github.com/python/cpython/issues/99304" + ) class _FastIntFlag(metaclass=_IntFlagMeta): |
