Issue26528
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2016-03-10 12:09 by reidfaiv, last changed 2022-04-11 14:58 by admin.
Files | ||||
---|---|---|---|---|
File name | Uploaded | Description | Edit | |
gen_err.py | reidfaiv, 2016-03-10 12:09 |
Messages (10) | |||
---|---|---|---|
msg261497 - (view) | Author: (reidfaiv) | Date: 2016-03-10 12:09 | |
Builtin open() gets NameError) in context manager __exit__ in case: * context manager yielding records * we return another generator when consuming these and * second generator raises, catches, stores and re-raises an excption Reduced down code looks like this: --------------------------------------- #!/usr/bin/env python # -*- coding: utf-8 -*- # context manager "query" (open in init, yield rows and close on exit) class _query(): def __init__(self, **kwargs): pass def __enter__(self): return [1, 2, 3] def __exit__(self, type_, value, tb): print('__exit__') # print() is also built-in, but works f = open('test.log', 'wt') # <-- NameError: name 'open' is not defined f.close() # this works fine def a(): try: raise Exception('volatile exception') except Exception as e: raise e # this does not work def b(): try: raise Exception('stored exception') except Exception as e: ee = e # <-- storing exception and then raise ee # <-- re-raising stored exception triggers the issue def event_gen(**kwargs): with _query() as cursor: for _ in cursor: yield b # <--- does not work # yield a # <--- works fine def run(**kwargs): g = event_gen(**kwargs) r = next(g) r() # This also works # for r in event_gen(**kwargs): # r() if __name__ == '__main__': run() --------------------------------------- >python.exe gen_err.py Traceback (most recent call last): File "gen_err.py", line 52, in <module> run() File "gen_err.py", line 46, in run r() File "gen_err.py", line 33, in b raise ee # <-- re-raising stored exception triggers the issue File "gen_err.py", line 30, in b raise Exception('stored exception') Exception: stored exception __exit__ Exception ignored in: <generator object event_gen at 0x000001A31F7C4048> Traceback (most recent call last): File "gen_err.py", line 39, in event_gen File "gen_err.py", line 15, in __exit__ NameError: name 'open' is not defined This happens with Python 3.4+ Works with earlier versions as expected. If exception re-raised immediately, works fine. If outermost generator is consumed in for loop, works fine. |
|||
msg261499 - (view) | Author: Martin Panter (martin.panter) * ![]() |
Date: 2016-03-10 13:15 | |
What is probably happening here is how garbage collection works at the interpreter exit. It is sort of mentioned in the warning at <https://docs.python.org/3/reference/datamodel.html#object.__del__>. The variable g is an iterator for event_gen(), and it is still “alive” when the exception is raised. The exception contains a reference to the traceback and all the local variables, including g. So the context manager in the generator cannot exit until g is deleted [or g.close() is called]. In the “volatile exception” case, I guess it is easy for the interpreter to release the exception traceback, including the g iterator that it references, straight after it has reported the traceback. But when you store an exception that you catch in a local variable, you create a reference loop (something like ee.__traceback__.tb_frame.f_locals["ee"] is ee). So it is not so easy to release the references, and by the time the garbage collector runs, I guess it has already set builtin names like “open” (global variable of the “builtins” module) to None. If you need a generator to clean up, you should probably close the generator before exiting the function rather than relying on garbage collection. Unfortunately there is no mechanism like ResourceWarning to indicate this problem with generators. |
|||
msg261500 - (view) | Author: (reidfaiv) | Date: 2016-03-10 14:17 | |
Indeed, explicitly closing generator helps, following works: def run(**kwargs): g = event_gen(**kwargs) r = next(g) g.close() r() Thanks! Do you want to keep this issue open for investigation? |
|||
msg261524 - (view) | Author: Martin Panter (martin.panter) * ![]() |
Date: 2016-03-10 21:39 | |
Actually I was a bit hasty in my response last night. NameError indicates that the “open” symbol has been deleted, not just set to None. If you add print(sorted(builtins.__dict__.keys())) inside __exit__() you can verify this is the case. This contradicts my understanding of the finalization process (symbols being deleted rather than being set to None). Maybe the documentation needs fixing? |
|||
msg261550 - (view) | Author: (reidfaiv) | Date: 2016-03-11 08:44 | |
def __exit__(self, type_, value, tb): print('__exit__') # print() is also built-in, but works print(sorted(builtins.__dict__.keys())) f = open('test.log', 'wt') # <-- NameError: name 'open' is not defined f.close() I will get following. Clearly "open" is missing: Traceback (most recent call last): File "gen_err.py", line 56, in <module> run() File "gen_err.py", line 50, in run r() File "gen_err.py", line 36, in b raise ee # <-- re-raising stored exception triggers the issue File "gen_err.py", line 33, in b raise Exception('stored exception') Exception: stored exception __exit__ ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'ord', 'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'] Exception ignored in: <generator object event_gen at 0x0000027F7C794048> Traceback (most recent call last): File "gen_err.py", line 42, in event_gen File "gen_err.py", line 18, in __exit__ NameError: name 'open' is not defined |
|||
msg297950 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * ![]() |
Date: 2017-07-08 08:00 | |
The interpreter makes a copy of the builtins module dict at startup and restores it at shutdown. open() is imported from the io module after making the copy, and therefore it is absent in restored module. |
|||
msg309462 - (view) | Author: Maciej Urbański (rooter) * | Date: 2018-01-04 09:50 | |
Reproduced in both v3.6.4 and v3.7.0a3 |
|||
msg340783 - (view) | Author: Davide Rizzo (davide.rizzo) * | Date: 2019-04-24 15:22 | |
I've just stumbled on the same thing happening on some code that attempts to use logging on __del__. Comparing dir(__builtins__) normally and on shutdown, these are missing: ['copyright', 'credits', 'exit', 'help', 'license', 'open', 'quit'] The traceback of the real error looks like this: [...] File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/__init__.py", line 1383, in info File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/__init__.py", line 1519, in _log File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/__init__.py", line 1529, in handle File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/__init__.py", line 1591, in callHandlers File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/__init__.py", line 905, in handle File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/__init__.py", line 1131, in emit File "/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/__init__.py", line 1121, in _open NameError: name 'open' is not defined |
|||
msg380026 - (view) | Author: STINNER Victor (vstinner) * ![]() |
Date: 2020-10-31 00:41 | |
Davide Rizzo: > I've just stumbled on the same thing happening on some code that attempts to use logging on __del__. > (...) > File ".../logging/__init__.py", line 1121, in _open > NameError: name 'open' is not defined This is bpo-26789 which is unrelated to this issue. |
|||
msg408504 - (view) | Author: Irit Katriel (iritkatriel) * ![]() |
Date: 2021-12-14 00:10 | |
Reproduced on 3.11. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:58:28 | admin | set | github: 70715 |
2021-12-14 00:10:12 | iritkatriel | set | nosy:
+ iritkatriel messages: + msg408504 versions: + Python 3.9, Python 3.10, Python 3.11, - Python 3.4, Python 3.5, Python 3.6, Python 3.7, Python 3.8 |
2020-10-31 00:41:29 | vstinner | set | messages: + msg380026 |
2019-04-24 15:22:24 | davide.rizzo | set | nosy:
+ davide.rizzo messages: + msg340783 versions: + Python 3.8 |
2018-01-04 09:50:03 | rooter | set | nosy:
+ rooter messages: + msg309462 versions: + Python 3.6, Python 3.7 |
2017-07-08 08:00:38 | serhiy.storchaka | set | nosy:
+ vstinner, serhiy.storchaka, pitrou messages: + msg297950 |
2016-03-11 08:44:46 | reidfaiv | set | messages: + msg261550 |
2016-03-10 21:39:06 | martin.panter | set | messages: + msg261524 |
2016-03-10 14:17:02 | reidfaiv | set | messages: + msg261500 |
2016-03-10 13:15:44 | martin.panter | set | nosy:
+ martin.panter messages: + msg261499 |
2016-03-10 12:09:57 | reidfaiv | create |