-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adjust for incompatible changes in Python 3.13b1 #294
Conversation
# https://github.com/python/cpython/pull/115153 | ||
self.assertDictEqual(dict(f_locals), locals()) | ||
else: | ||
self.assertTrue(f_locals is locals()) # ??? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do tests all pass if we just change to use the self.assertDictEqual
assertion everywhere? Seems cleaner to me, anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I am seeing is that the test fails for Python<3.13 when running it normally. However, when I insert a pdb.set_trace()
and step through it the test succeeds. Not sure what that means, a time.sleep(.5)
doesn't have that effect, so it's not timing I guess:
Traceback (most recent call last):
File "/Users/jens/src/zope/zope.interface/src/zope/interface/tests/test_advice.py", line 68, in test_inside_function_call
self.assertDictEqual(dict(f_locals), locals())
AssertionError: {'sel[170 chars]0220>} != {'sel[170 chars]0220>, 'kind': 'function call', 'module': <mod[8946 chars]s'>}}
{'IS_PY313_OR_GREATER': False,
+ 'f_globals': {'FrameInfoTest': <class 'zope.interface.tests.test_advice.FrameInfoTest'>,
+ 'Test_determineMetaclass': <class 'zope.interface.tests.test_advice.Test_determineMetaclass'>,
+ 'Test_isClassAdvisor': <class 'zope.interface.tests.test_advice.Test_isClassAdvisor'>,
+ 'Test_minimalBases': <class 'zope.interface.tests.test_advice.Test_minimalBases'>,
+ '_FUNKY_EXEC': 'import sys\n'
+ 'kind, module, f_locals, f_globals = '
+ 'getFrameInfo(sys._getframe())\n',
+ '__builtins__': {'ArithmeticError': <class 'ArithmeticError'>,
+ 'AssertionError': <class 'AssertionError'>,
+ 'AttributeError': <class 'AttributeError'>,
+ 'BaseException': <class 'BaseException'>,
+ 'BaseExceptionGroup': <class 'BaseExceptionGroup'>,
+ 'BlockingIOError': <class 'BlockingIOError'>,
+ 'BrokenPipeError': <class 'BrokenPipeError'>,
+ 'BufferError': <class 'BufferError'>,
+ 'BytesWarning': <class 'BytesWarning'>,
+ 'ChildProcessError': <class 'ChildProcessError'>,
+ 'ConnectionAbortedError': <class 'ConnectionAbortedError'>,
+ 'ConnectionError': <class 'ConnectionError'>,
+ 'ConnectionRefusedError': <class 'ConnectionRefusedError'>,
+ 'ConnectionResetError': <class 'ConnectionResetError'>,
+ 'DeprecationWarning': <class 'DeprecationWarning'>,
+ 'EOFError': <class 'EOFError'>,
+ 'Ellipsis': Ellipsis,
+ 'EncodingWarning': <class 'EncodingWarning'>,
+ 'EnvironmentError': <class 'OSError'>,
+ 'Exception': <class 'Exception'>,
+ 'ExceptionGroup': <class 'ExceptionGroup'>,
+ 'False': False,
+ 'FileExistsError': <class 'FileExistsError'>,
+ 'FileNotFoundError': <class 'FileNotFoundError'>,
+ 'FloatingPointError': <class 'FloatingPointError'>,
+ 'FutureWarning': <class 'FutureWarning'>,
+ 'GeneratorExit': <class 'GeneratorExit'>,
+ 'IOError': <class 'OSError'>,
+ 'ImportError': <class 'ImportError'>,
+ 'ImportWarning': <class 'ImportWarning'>,
+ 'IndentationError': <class 'IndentationError'>,
+ 'IndexError': <class 'IndexError'>,
+ 'InterruptedError': <class 'InterruptedError'>,
+ 'IsADirectoryError': <class 'IsADirectoryError'>,
+ 'KeyError': <class 'KeyError'>,
+ 'KeyboardInterrupt': <class 'KeyboardInterrupt'>,
+ 'LookupError': <class 'LookupError'>,
+ 'MemoryError': <class 'MemoryError'>,
+ 'ModuleNotFoundError': <class 'ModuleNotFoundError'>,
+ 'NameError': <class 'NameError'>,
+ 'None': None,
+ 'NotADirectoryError': <class 'NotADirectoryError'>,
+ 'NotImplemented': NotImplemented,
+ 'NotImplementedError': <class 'NotImplementedError'>,
+ 'OSError': <class 'OSError'>,
+ 'OverflowError': <class 'OverflowError'>,
+ 'PendingDeprecationWarning': <class 'PendingDeprecationWarning'>,
+ 'PermissionError': <class 'PermissionError'>,
+ 'ProcessLookupError': <class 'ProcessLookupError'>,
+ 'RecursionError': <class 'RecursionError'>,
+ 'ReferenceError': <class 'ReferenceError'>,
+ 'ResourceWarning': <class 'ResourceWarning'>,
+ 'RuntimeError': <class 'RuntimeError'>,
+ 'RuntimeWarning': <class 'RuntimeWarning'>,
+ 'StopAsyncIteration': <class 'StopAsyncIteration'>,
+ 'StopIteration': <class 'StopIteration'>,
+ 'SyntaxError': <class 'SyntaxError'>,
+ 'SyntaxWarning': <class 'SyntaxWarning'>,
+ 'SystemError': <class 'SystemError'>,
+ 'SystemExit': <class 'SystemExit'>,
+ 'TabError': <class 'TabError'>,
+ 'TimeoutError': <class 'TimeoutError'>,
+ 'True': True,
+ 'TypeError': <class 'TypeError'>,
+ 'UnboundLocalError': <class 'UnboundLocalError'>,
+ 'UnicodeDecodeError': <class 'UnicodeDecodeError'>,
+ 'UnicodeEncodeError': <class 'UnicodeEncodeError'>,
+ 'UnicodeError': <class 'UnicodeError'>,
+ 'UnicodeTranslateError': <class 'UnicodeTranslateError'>,
+ 'UnicodeWarning': <class 'UnicodeWarning'>,
+ 'UserWarning': <class 'UserWarning'>,
+ 'ValueError': <class 'ValueError'>,
+ 'Warning': <class 'Warning'>,
+ 'ZeroDivisionError': <class 'ZeroDivisionError'>,
+ '__build_class__': <built-in function __build_class__>,
+ '__debug__': True,
+ '__doc__': 'Built-in functions, types, '
+ 'exceptions, and other objects.\n'
+ '\n'
+ 'This module provides direct access '
+ "to all 'built-in'\n"
+ 'identifiers of Python; for '
+ 'example, builtins.len is\n'
+ 'the full name for the built-in '
+ 'function len().\n'
+ '\n'
+ 'This module is not normally '
+ 'accessed explicitly by most\n'
+ 'applications, but can be useful in '
+ 'modules that provide\n'
+ 'objects with the same name as a '
+ 'built-in value, but in\n'
+ 'which the built-in of that name is '
+ 'also needed.',
+ '__import__': <built-in function __import__>,
+ '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
+ '__name__': 'builtins',
+ '__package__': '',
+ '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>, origin='built-in'),
+ 'abs': <built-in function abs>,
+ 'aiter': <built-in function aiter>,
+ 'all': <built-in function all>,
+ 'anext': <built-in function anext>,
+ 'any': <built-in function any>,
+ 'ascii': <built-in function ascii>,
+ 'bin': <built-in function bin>,
+ 'bool': <class 'bool'>,
+ 'breakpoint': <built-in function breakpoint>,
+ 'bytearray': <class 'bytearray'>,
+ 'bytes': <class 'bytes'>,
+ 'callable': <built-in function callable>,
+ 'chr': <built-in function chr>,
+ 'classmethod': <class 'classmethod'>,
+ 'compile': <built-in function compile>,
+ 'complex': <class 'complex'>,
+ 'copyright': Copyright (c) 2001-2023 Python Software Foundation.
+ All Rights Reserved.
+
+ Copyright (c) 2000 BeOpen.com.
+ All Rights Reserved.
+
+ Copyright (c) 1995-2001 Corporation for National Research Initiatives.
+ All Rights Reserved.
+
+ Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
+ All Rights Reserved.,
+ 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
+ for supporting Python development. See www.python.org for more information.,
+ 'delattr': <built-in function delattr>,
+ 'dict': <class 'dict'>,
+ 'dir': <built-in function dir>,
+ 'divmod': <built-in function divmod>,
+ 'enumerate': <class 'enumerate'>,
+ 'eval': <built-in function eval>,
+ 'exec': <built-in function exec>,
+ 'exit': Use exit() or Ctrl-D (i.e. EOF) to exit,
+ 'filter': <class 'filter'>,
+ 'float': <class 'float'>,
+ 'format': <built-in function format>,
+ 'frozenset': <class 'frozenset'>,
+ 'getattr': <built-in function getattr>,
+ 'globals': <built-in function globals>,
+ 'hasattr': <built-in function hasattr>,
+ 'hash': <built-in function hash>,
+ 'help': Type help() for interactive help, or help(object) for help about object.,
+ 'hex': <built-in function hex>,
+ 'id': <built-in function id>,
+ 'input': <built-in function input>,
+ 'int': <class 'int'>,
+ 'isinstance': <built-in function isinstance>,
+ 'issubclass': <built-in function issubclass>,
+ 'iter': <built-in function iter>,
+ 'len': <built-in function len>,
+ 'license': Type license() to see the full license text,
+ 'list': <class 'list'>,
+ 'locals': <built-in function locals>,
+ 'map': <class 'map'>,
+ 'max': <built-in function max>,
+ 'memoryview': <class 'memoryview'>,
+ 'min': <built-in function min>,
+ 'next': <built-in function next>,
+ 'object': <class 'object'>,
+ 'oct': <built-in function oct>,
+ 'open': <built-in function open>,
+ 'ord': <built-in function ord>,
+ 'pow': <built-in function pow>,
+ 'print': <built-in function print>,
+ 'property': <class 'property'>,
+ 'quit': Use quit() or Ctrl-D (i.e. EOF) to exit,
+ 'range': <class 'range'>,
+ 'repr': <built-in function repr>,
+ 'reversed': <class 'reversed'>,
+ 'round': <built-in function round>,
+ 'set': <class 'set'>,
+ 'setattr': <built-in function setattr>,
+ 'slice': <class 'slice'>,
+ 'sorted': <built-in function sorted>,
+ 'staticmethod': <class 'staticmethod'>,
+ 'str': <class 'str'>,
+ 'sum': <built-in function sum>,
+ 'super': <class 'super'>,
+ 'tuple': <class 'tuple'>,
+ 'type': <class 'type'>,
+ 'vars': <built-in function vars>,
+ 'zip': <class 'zip'>},
+ '__cached__': '/Users/jens/src/zope/zope.interface/src/zope/interface/tests/__pycache__/test_advice.cpython-312.pyc',
+ '__doc__': 'Tests for advice\n'
+ '\n'
+ 'This module was adapted from '
+ "'protocols.tests.advice', part of the Python\n"
+ 'Enterprise Application Kit (PEAK). Please notify '
+ 'the PEAK authors\n'
+ '([email protected] and [email protected]) if '
+ 'bugs are found or\n'
+ 'Zope-specific changes are required, so that the '
+ 'PEAK version of this module\n'
+ 'can be kept in sync.\n'
+ '\n'
+ 'PEAK is a Python application framework that '
+ 'interoperates with (but does\n'
+ 'not require) Zope 3 and Twisted. It provides tools '
+ 'for manipulating UML\n'
+ 'models, object-relational persistence, '
+ 'aspect-oriented programming, and more.\n'
+ 'Visit the PEAK home page at '
+ 'http://peak.telecommunity.com for more '
+ 'information.\n',
+ '__file__': '/Users/jens/src/zope/zope.interface/src/zope/interface/tests/test_advice.py',
+ '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x105d917f0>,
+ '__name__': 'zope.interface.tests.test_advice',
+ '__package__': 'zope.interface.tests',
+ '__spec__': ModuleSpec(name='zope.interface.tests.test_advice', loader=<_frozen_importlib_external.SourceFileLoader object at 0x105d917f0>, origin='/Users/jens/src/zope/zope.interface/src/zope/interface/tests/test_advice.py'),
+ 'sys': <module 'sys' (built-in)>,
+ 'unittest': <module 'unittest' from '/Users/jens/src/python/Python-3.12.1/lib/python3.12/unittest/__init__.py'>},
+ 'f_locals': <Recursion on dict with id=4393161536>,
'getFrameInfo': <function getFrameInfo at 0x106080220>,
+ 'kind': 'function call',
+ 'module': <module 'zope.interface.tests.test_advice' from '/Users/jens/src/zope/zope.interface/src/zope/interface/tests/test_advice.py'>,
'self': <zope.interface.tests.test_advice.FrameInfoTest testMethod=test_inside_function_call>}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like an issue about how the f_locals
returned from the getFrameInfo
call at the top and the mapping from the later call to locals()
do or do not get updated with the local variables that get added during the test method run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently, the test should check getFrameInfo
.
Like Tres, I think we can avoid the version dependency:
For the getFrameInfo
check, it would be enough to determine that the returned f_locals
war hightly likely derived from the current locals. I see different options:
- compare against the result of
sys._getframe
(this could useis
) - compare with
DictEqual
- put a marker variable into the local frame and check this marker variable
Fails under Python 3.13:
Fails under Python 3.13 because
You mean like the following, which works under Python 3.13 and Python <3.13?
|
Jens Vagelpohl wrote at 2024-5-14 01:38 -0700:
> Like Tres, I think we can avoid the version dependency: For the `getFrameInfo` check, it would be enough to determine that the returned `f_locals` war hightly likely derived from the current locals. I see different options:
>
> * compare against the result of `sys._getframe` (this could use `is`)
Fails under Python 3.13: `self.assertTrue(f_locals is sys._getframe().f_locals)`
Could you try:
frame = sys._getframe()
.... = getFrameInfo(frame)
...
|
Jens Vagelpohl wrote at 2024-5-14 01:38 -0700:
> * put a marker variable into the local frame and check this marker variable
You mean like the following, which works under Python 3.13 and Python <3.13?
```
# Set a variable to test against
test_local_variable = 'foobar'
self.assertIn('test_local_variable', locals())
self.assertIn('test_local_variable', f_locals)
```
Yes, just a bit simplified:
```
self.assertEqual(f_locals["test_local_variable"], "foobar")
```
|
This passes under Python 3.13 but fails with a The following works on all supported Python versions:
|
The `frame.f_locals` is now a write-through proxy object of type `FrameLocalsProxy`; see PEP 667. This fix is based on zopefoundation/zope.interface#294 and specifically on zopefoundation/zope.interface#294 (comment). Fixes Pylons#91.
The `frame.f_locals` is now a write-through proxy object of type `FrameLocalsProxy`; see PEP 667. This fix is based on zopefoundation/zope.interface#294 and specifically on zopefoundation/zope.interface#294 (comment). Fixes Pylons#91.
I just proposed this in Pylons/venusian#92 for |
* Accommodate FrameLocalsProxy introduction in Python 3.13 The `frame.f_locals` is now a write-through proxy object of type `FrameLocalsProxy`; see PEP 667. This fix is based on zopefoundation/zope.interface#294 and specifically on zopefoundation/zope.interface#294 (comment). Fixes #91. * add 3.13 to checked ci * use macos-12 to test macos (later versions dont seem to build python correctly) --------- Co-authored-by: Benjamin A. Beasley <[email protected]>
Fixes #292
In case you are wondering, the question marks next to the original comparison
f_locals is locals()
have existed since at least 2007 when the code was moved to GitHub. I am guessing the original author thought this identity test is questionable.