mirror of
https://github.com/python/cpython.git
synced 2025-10-17 20:28:43 +00:00
gh-119180: Set the name of the param to __annotate__ to "format" (#124730)
This commit is contained in:
parent
2bd5a7ab0f
commit
3480124321
3 changed files with 78 additions and 2 deletions
|
@ -79,7 +79,7 @@ CLASSES
|
||||||
class B(builtins.object)
|
class B(builtins.object)
|
||||||
| Methods defined here:
|
| Methods defined here:
|
||||||
|
|
|
|
||||||
| __annotate__(...)
|
| __annotate__(format, /)
|
||||||
|
|
|
|
||||||
| ----------------------------------------------------------------------
|
| ----------------------------------------------------------------------
|
||||||
| Data descriptors defined here:
|
| Data descriptors defined here:
|
||||||
|
@ -180,7 +180,7 @@ class A(builtins.object)
|
||||||
|
|
||||||
class B(builtins.object)
|
class B(builtins.object)
|
||||||
Methods defined here:
|
Methods defined here:
|
||||||
__annotate__(...)
|
__annotate__(format, /)
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Data descriptors defined here:
|
Data descriptors defined here:
|
||||||
__dict__
|
__dict__
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import annotationlib
|
import annotationlib
|
||||||
|
import inspect
|
||||||
import textwrap
|
import textwrap
|
||||||
import types
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
|
@ -380,6 +381,11 @@ class DeferredEvaluationTests(unittest.TestCase):
|
||||||
annotate(None)
|
annotate(None)
|
||||||
self.assertEqual(annotate(annotationlib.Format.VALUE), {"x": int})
|
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):
|
def test_comprehension_in_annotation(self):
|
||||||
# This crashed in an earlier version of the code
|
# This crashed in an earlier version of the code
|
||||||
ns = run_code("x: [y for y in range(10)]")
|
ns = run_code("x: [y for y in range(10)]")
|
||||||
|
@ -400,6 +406,7 @@ class DeferredEvaluationTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_name_clash_with_format(self):
|
def test_name_clash_with_format(self):
|
||||||
# this test would fail if __annotate__'s parameter was called "format"
|
# this test would fail if __annotate__'s parameter was called "format"
|
||||||
|
# during symbol table construction
|
||||||
code = """
|
code = """
|
||||||
class format: pass
|
class format: pass
|
||||||
|
|
||||||
|
@ -408,3 +415,45 @@ class DeferredEvaluationTests(unittest.TestCase):
|
||||||
ns = run_code(code)
|
ns = run_code(code)
|
||||||
f = ns["f"]
|
f = ns["f"]
|
||||||
self.assertEqual(f.__annotations__, {"x": ns["format"]})
|
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__
|
||||||
|
|
|
@ -701,6 +701,33 @@ codegen_leave_annotations_scope(compiler *c, location loc,
|
||||||
ADDOP_I(c, loc, BUILD_MAP, annotations_len);
|
ADDOP_I(c, loc, BUILD_MAP, annotations_len);
|
||||||
ADDOP_IN_SCOPE(c, loc, RETURN_VALUE);
|
ADDOP_IN_SCOPE(c, loc, RETURN_VALUE);
|
||||||
PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1);
|
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);
|
_PyCompile_ExitScope(c);
|
||||||
if (co == NULL) {
|
if (co == NULL) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue