Skip to content

Commit

Permalink
gh-119180: Set the name of the param to __annotate__ to "format" (#12…
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra authored Dec 30, 2024
1 parent 2bd5a7a commit 3480124
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Lib/test/test_pydoc/test_pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class A(builtins.object)
class B(builtins.object)
| Methods defined here:
|
| __annotate__(...)
| __annotate__(format, /)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
Expand Down Expand Up @@ -180,7 +180,7 @@ class A(builtins.object)
class B(builtins.object)
Methods defined here:
__annotate__(...)
__annotate__(format, /)
----------------------------------------------------------------------
Data descriptors defined here:
__dict__
Expand Down
49 changes: 49 additions & 0 deletions Lib/test/test_type_annotations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import annotationlib
import inspect
import textwrap
import types
import unittest
Expand Down Expand Up @@ -380,6 +381,11 @@ class X:
annotate(None)
self.assertEqual(annotate(annotationlib.Format.VALUE), {"x": int})

sig = inspect.signature(annotate)
self.assertEqual(sig, inspect.Signature([
inspect.Parameter("format", inspect.Parameter.POSITIONAL_ONLY)
]))

def test_comprehension_in_annotation(self):
# This crashed in an earlier version of the code
ns = run_code("x: [y for y in range(10)]")
Expand All @@ -400,6 +406,7 @@ def f(x: int) -> int: pass

def test_name_clash_with_format(self):
# this test would fail if __annotate__'s parameter was called "format"
# during symbol table construction
code = """
class format: pass
Expand All @@ -408,3 +415,45 @@ def f(x: format): pass
ns = run_code(code)
f = ns["f"]
self.assertEqual(f.__annotations__, {"x": ns["format"]})

code = """
class Outer:
class format: pass
def meth(self, x: format): ...
"""
ns = run_code(code)
self.assertEqual(ns["Outer"].meth.__annotations__, {"x": ns["Outer"].format})

code = """
def f(format):
def inner(x: format): pass
return inner
res = f("closure var")
"""
ns = run_code(code)
self.assertEqual(ns["res"].__annotations__, {"x": "closure var"})

code = """
def f(x: format):
pass
"""
ns = run_code(code)
# picks up the format() builtin
self.assertEqual(ns["f"].__annotations__, {"x": format})

code = """
def outer():
def f(x: format):
pass
if False:
class format: pass
return f
f = outer()
"""
ns = run_code(code)
with self.assertRaisesRegex(
NameError,
"cannot access free variable 'format' where it is not associated with a value in enclosing scope",
):
ns["f"].__annotations__
27 changes: 27 additions & 0 deletions Python/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,33 @@ codegen_leave_annotations_scope(compiler *c, location loc,
ADDOP_I(c, loc, BUILD_MAP, annotations_len);
ADDOP_IN_SCOPE(c, loc, RETURN_VALUE);
PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1);

// We want the parameter to __annotate__ to be named "format" in the
// signature shown by inspect.signature(), but we need to use a
// different name (.format) in the symtable; if the name
// "format" appears in the annotations, it doesn't get clobbered
// by this name. This code is essentially:
// co->co_localsplusnames = ("format", *co->co_localsplusnames[1:])
const Py_ssize_t size = PyObject_Size(co->co_localsplusnames);
if (size == -1) {
return ERROR;
}
PyObject *new_names = PyTuple_New(size);
if (new_names == NULL) {
return ERROR;
}
PyTuple_SET_ITEM(new_names, 0, Py_NewRef(&_Py_ID(format)));
for (int i = 1; i < size; i++) {
PyObject *item = PyTuple_GetItem(co->co_localsplusnames, i);
if (item == NULL) {
Py_DECREF(new_names);
return ERROR;
}
Py_INCREF(item);
PyTuple_SET_ITEM(new_names, i, item);
}
Py_SETREF(co->co_localsplusnames, new_names);

_PyCompile_ExitScope(c);
if (co == NULL) {
return ERROR;
Expand Down

0 comments on commit 3480124

Please sign in to comment.