mirror of
https://github.com/python/cpython.git
synced 2025-11-25 04:34:37 +00:00
[3.14] gh-137969: Fix double evaluation of ForwardRefs which rely on globals (GH-140974) (#141527)
Some checks failed
Tests / (push) Blocked by required conditions
Tests / Sanitizers (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Windows MSI (push) Blocked by required conditions
Tests / Check if the ABI has changed (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Android (aarch64) (push) Blocked by required conditions
Tests / Android (x86_64) (push) Blocked by required conditions
Tests / iOS (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
JIT / Interpreter (Debug) (push) Has been cancelled
Tail calling interpreter / aarch64-apple-darwin/clang (push) Has been cancelled
Tail calling interpreter / aarch64-unknown-linux-gnu/gcc (push) Has been cancelled
Tail calling interpreter / x86_64-pc-windows-msvc/msvc (push) Has been cancelled
Tail calling interpreter / x86_64-apple-darwin/clang (push) Has been cancelled
Tail calling interpreter / free-threading (push) Has been cancelled
Tail calling interpreter / x86_64-unknown-linux-gnu/gcc (push) Has been cancelled
JIT / aarch64-pc-windows-msvc/msvc (Release) (push) Has been cancelled
JIT / aarch64-pc-windows-msvc/msvc (Debug) (push) Has been cancelled
JIT / i686-pc-windows-msvc/msvc (Release) (push) Has been cancelled
JIT / i686-pc-windows-msvc/msvc (Debug) (push) Has been cancelled
JIT / aarch64-apple-darwin/clang (Release) (push) Has been cancelled
JIT / aarch64-unknown-linux-gnu/gcc (Release) (push) Has been cancelled
JIT / aarch64-apple-darwin/clang (Debug) (push) Has been cancelled
JIT / aarch64-unknown-linux-gnu/gcc (Debug) (push) Has been cancelled
JIT / x86_64-pc-windows-msvc/msvc (Release) (push) Has been cancelled
JIT / x86_64-pc-windows-msvc/msvc (Debug) (push) Has been cancelled
JIT / x86_64-apple-darwin/clang (Release) (push) Has been cancelled
JIT / x86_64-unknown-linux-gnu/gcc (Release) (push) Has been cancelled
JIT / x86_64-apple-darwin/clang (Debug) (push) Has been cancelled
JIT / x86_64-unknown-linux-gnu/gcc (Debug) (push) Has been cancelled
JIT / JIT with tail calling interpreter (push) Has been cancelled
Some checks failed
Tests / (push) Blocked by required conditions
Tests / Sanitizers (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Windows MSI (push) Blocked by required conditions
Tests / Check if the ABI has changed (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Android (aarch64) (push) Blocked by required conditions
Tests / Android (x86_64) (push) Blocked by required conditions
Tests / iOS (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
JIT / Interpreter (Debug) (push) Has been cancelled
Tail calling interpreter / aarch64-apple-darwin/clang (push) Has been cancelled
Tail calling interpreter / aarch64-unknown-linux-gnu/gcc (push) Has been cancelled
Tail calling interpreter / x86_64-pc-windows-msvc/msvc (push) Has been cancelled
Tail calling interpreter / x86_64-apple-darwin/clang (push) Has been cancelled
Tail calling interpreter / free-threading (push) Has been cancelled
Tail calling interpreter / x86_64-unknown-linux-gnu/gcc (push) Has been cancelled
JIT / aarch64-pc-windows-msvc/msvc (Release) (push) Has been cancelled
JIT / aarch64-pc-windows-msvc/msvc (Debug) (push) Has been cancelled
JIT / i686-pc-windows-msvc/msvc (Release) (push) Has been cancelled
JIT / i686-pc-windows-msvc/msvc (Debug) (push) Has been cancelled
JIT / aarch64-apple-darwin/clang (Release) (push) Has been cancelled
JIT / aarch64-unknown-linux-gnu/gcc (Release) (push) Has been cancelled
JIT / aarch64-apple-darwin/clang (Debug) (push) Has been cancelled
JIT / aarch64-unknown-linux-gnu/gcc (Debug) (push) Has been cancelled
JIT / x86_64-pc-windows-msvc/msvc (Release) (push) Has been cancelled
JIT / x86_64-pc-windows-msvc/msvc (Debug) (push) Has been cancelled
JIT / x86_64-apple-darwin/clang (Release) (push) Has been cancelled
JIT / x86_64-unknown-linux-gnu/gcc (Release) (push) Has been cancelled
JIT / x86_64-apple-darwin/clang (Debug) (push) Has been cancelled
JIT / x86_64-unknown-linux-gnu/gcc (Debug) (push) Has been cancelled
JIT / JIT with tail calling interpreter (push) Has been cancelled
gh-137969: Fix double evaluation of `ForwardRef`s which rely on globals (GH-140974)
(cherry picked from commit 209eaff68c)
Co-authored-by: dr-carlos <77367421+dr-carlos@users.noreply.github.com>
This commit is contained in:
parent
af20b880fc
commit
dd28db664b
3 changed files with 72 additions and 15 deletions
|
|
@ -150,33 +150,42 @@ class ForwardRef:
|
|||
if globals is None:
|
||||
globals = {}
|
||||
|
||||
if type_params is None and owner is not None:
|
||||
type_params = getattr(owner, "__type_params__", None)
|
||||
|
||||
if locals is None:
|
||||
locals = {}
|
||||
if isinstance(owner, type):
|
||||
locals.update(vars(owner))
|
||||
elif (
|
||||
type_params is not None
|
||||
or isinstance(self.__cell__, dict)
|
||||
or self.__extra_names__
|
||||
):
|
||||
# Create a new locals dict if necessary,
|
||||
# to avoid mutating the argument.
|
||||
locals = dict(locals)
|
||||
|
||||
if type_params is None and owner is not None:
|
||||
# "Inject" type parameters into the local namespace
|
||||
# (unless they are shadowed by assignments *in* the local namespace),
|
||||
# as a way of emulating annotation scopes when calling `eval()`
|
||||
type_params = getattr(owner, "__type_params__", None)
|
||||
|
||||
# Type parameters exist in their own scope, which is logically
|
||||
# between the locals and the globals. We simulate this by adding
|
||||
# them to the globals. Similar reasoning applies to nonlocals stored in cells.
|
||||
if type_params is not None or isinstance(self.__cell__, dict):
|
||||
globals = dict(globals)
|
||||
if type_params is not None:
|
||||
for param in type_params:
|
||||
globals[param.__name__] = param
|
||||
locals.setdefault(param.__name__, param)
|
||||
|
||||
# Similar logic can be used for nonlocals, which should not
|
||||
# override locals.
|
||||
if isinstance(self.__cell__, dict):
|
||||
for cell_name, cell_value in self.__cell__.items():
|
||||
for cell_name, cell in self.__cell__.items():
|
||||
try:
|
||||
globals[cell_name] = cell_value.cell_contents
|
||||
cell_value = cell.cell_contents
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
locals.setdefault(cell_name, cell_value)
|
||||
|
||||
if self.__extra_names__:
|
||||
locals = {**locals, **self.__extra_names__}
|
||||
locals.update(self.__extra_names__)
|
||||
|
||||
arg = self.__forward_arg__
|
||||
if arg.isidentifier() and not keyword.iskeyword(arg):
|
||||
|
|
|
|||
|
|
@ -2149,6 +2149,51 @@ class TestForwardRefClass(unittest.TestCase):
|
|||
with self.assertRaises(SyntaxError):
|
||||
fr.evaluate()
|
||||
|
||||
def test_re_evaluate_generics(self):
|
||||
global global_alias
|
||||
|
||||
# If we've already run this test before,
|
||||
# ensure the variable is still undefined
|
||||
if "global_alias" in globals():
|
||||
del global_alias
|
||||
|
||||
class C:
|
||||
x: global_alias[int]
|
||||
|
||||
# Evaluate the ForwardRef once
|
||||
evaluated = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate(
|
||||
format=Format.FORWARDREF
|
||||
)
|
||||
|
||||
# Now define the global and ensure that the ForwardRef evaluates
|
||||
global_alias = list
|
||||
self.assertEqual(evaluated.evaluate(), list[int])
|
||||
|
||||
def test_fwdref_evaluate_argument_mutation(self):
|
||||
class C[T]:
|
||||
nonlocal alias
|
||||
x: alias[T]
|
||||
|
||||
# Mutable arguments
|
||||
globals_ = globals()
|
||||
globals_copy = globals_.copy()
|
||||
locals_ = locals()
|
||||
locals_copy = locals_.copy()
|
||||
|
||||
# Evaluate the ForwardRef, ensuring we use __cell__ and type params
|
||||
get_annotations(C, format=Format.FORWARDREF)["x"].evaluate(
|
||||
globals=globals_,
|
||||
locals=locals_,
|
||||
type_params=C.__type_params__,
|
||||
format=Format.FORWARDREF,
|
||||
)
|
||||
|
||||
# Check if the passed in mutable arguments equal the originals
|
||||
self.assertEqual(globals_, globals_copy)
|
||||
self.assertEqual(locals_, locals_copy)
|
||||
|
||||
alias = list
|
||||
|
||||
def test_fwdref_final_class(self):
|
||||
with self.assertRaises(TypeError):
|
||||
class C(ForwardRef):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
Fix :meth:`annotationlib.ForwardRef.evaluate` returning
|
||||
:class:`~annotationlib.ForwardRef` objects which don't update with new
|
||||
globals.
|
||||
Loading…
Add table
Add a link
Reference in a new issue