diff --git a/CHANGES.rst b/CHANGES.rst index 4186a906..e2079cb6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ 6.4 (unreleased) ================ +- Adjust for incompatible changes in Python 3.13b1. + (`#292 `) + - Build windows wheels on GHA. 6.3 (2024-04-12) diff --git a/src/zope/interface/_compat.py b/src/zope/interface/_compat.py index 2ff8d83e..dd1cedb2 100644 --- a/src/zope/interface/_compat.py +++ b/src/zope/interface/_compat.py @@ -29,7 +29,10 @@ def _normalize_name(name): return name raise TypeError("name must be a string or ASCII-only bytes") + PYPY = hasattr(sys, 'pypy_version_info') +_version = sys.version_info +IS_PY313_OR_GREATER = _version.major == 3 and _version.minor >= 13 def _c_optimizations_required(): diff --git a/src/zope/interface/interface.py b/src/zope/interface/interface.py index 733e3954..8e0d9ad2 100644 --- a/src/zope/interface/interface.py +++ b/src/zope/interface/interface.py @@ -802,6 +802,9 @@ def update_value(aname, aval): # __static_attributes__: Python 3.13a6+ # https://github.com/python/cpython/pull/115913 '__static_attributes__', + # __firstlineno__: Python 3.13b1+ + # https://github.com/python/cpython/pull/118475 + '__firstlineno__', ) and aval is not _decorator_non_return } diff --git a/src/zope/interface/tests/test_advice.py b/src/zope/interface/tests/test_advice.py index a5432011..cad36869 100644 --- a/src/zope/interface/tests/test_advice.py +++ b/src/zope/interface/tests/test_advice.py @@ -51,10 +51,22 @@ def test_w_class(self): self.assertTrue(d is advisory_testing.my_globals) def test_inside_function_call(self): + from zope.interface._compat import IS_PY313_OR_GREATER from zope.interface.advice import getFrameInfo kind, module, f_locals, f_globals = getFrameInfo(sys._getframe()) self.assertEqual(kind, "function call") - self.assertTrue(f_locals is locals()) # ??? + + if IS_PY313_OR_GREATER: + # Python 3.13b1 implements PEP 667, which changes the type of + # ``f_locals`` from a mapping to a ``FrameLocalsProxy`` object + # while the return value of ``locals()`` is still a mapping, so + # they no longer point to the same object. + # See https://peps.python.org/pep-0667 and + # https://github.com/python/cpython/pull/115153 + self.assertDictEqual(dict(f_locals), locals()) + else: + self.assertTrue(f_locals is locals()) # ??? + for d in module.__dict__, f_globals: self.assertTrue(d is globals())