mirror of https://github.com/python/cpython
gh-125614: annotationlib: Fix bug where not all Stringifiers are converted (#125635)
This commit is contained in:
parent
8f2c0f7a03
commit
d3be6f945a
|
@ -45,6 +45,7 @@ _SLOTS = (
|
||||||
"__globals__",
|
"__globals__",
|
||||||
"__owner__",
|
"__owner__",
|
||||||
"__cell__",
|
"__cell__",
|
||||||
|
"__stringifier_dict__",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -268,7 +269,16 @@ class _Stringifier:
|
||||||
# instance of the other in place.
|
# instance of the other in place.
|
||||||
__slots__ = _SLOTS
|
__slots__ = _SLOTS
|
||||||
|
|
||||||
def __init__(self, node, globals=None, owner=None, is_class=False, cell=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
node,
|
||||||
|
globals=None,
|
||||||
|
owner=None,
|
||||||
|
is_class=False,
|
||||||
|
cell=None,
|
||||||
|
*,
|
||||||
|
stringifier_dict,
|
||||||
|
):
|
||||||
# Either an AST node or a simple str (for the common case where a ForwardRef
|
# Either an AST node or a simple str (for the common case where a ForwardRef
|
||||||
# represent a single name).
|
# represent a single name).
|
||||||
assert isinstance(node, (ast.AST, str))
|
assert isinstance(node, (ast.AST, str))
|
||||||
|
@ -283,6 +293,7 @@ class _Stringifier:
|
||||||
self.__globals__ = globals
|
self.__globals__ = globals
|
||||||
self.__cell__ = cell
|
self.__cell__ = cell
|
||||||
self.__owner__ = owner
|
self.__owner__ = owner
|
||||||
|
self.__stringifier_dict__ = stringifier_dict
|
||||||
|
|
||||||
def __convert_to_ast(self, other):
|
def __convert_to_ast(self, other):
|
||||||
if isinstance(other, _Stringifier):
|
if isinstance(other, _Stringifier):
|
||||||
|
@ -317,9 +328,15 @@ class _Stringifier:
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def __make_new(self, node):
|
def __make_new(self, node):
|
||||||
return _Stringifier(
|
stringifier = _Stringifier(
|
||||||
node, self.__globals__, self.__owner__, self.__forward_is_class__
|
node,
|
||||||
|
self.__globals__,
|
||||||
|
self.__owner__,
|
||||||
|
self.__forward_is_class__,
|
||||||
|
stringifier_dict=self.__stringifier_dict__,
|
||||||
)
|
)
|
||||||
|
self.__stringifier_dict__.stringifiers.append(stringifier)
|
||||||
|
return stringifier
|
||||||
|
|
||||||
# Must implement this since we set __eq__. We hash by identity so that
|
# Must implement this since we set __eq__. We hash by identity so that
|
||||||
# stringifiers in dict keys are kept separate.
|
# stringifiers in dict keys are kept separate.
|
||||||
|
@ -462,6 +479,7 @@ class _StringifierDict(dict):
|
||||||
globals=self.globals,
|
globals=self.globals,
|
||||||
owner=self.owner,
|
owner=self.owner,
|
||||||
is_class=self.is_class,
|
is_class=self.is_class,
|
||||||
|
stringifier_dict=self,
|
||||||
)
|
)
|
||||||
self.stringifiers.append(fwdref)
|
self.stringifiers.append(fwdref)
|
||||||
return fwdref
|
return fwdref
|
||||||
|
@ -516,7 +534,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
|
||||||
name = freevars[i]
|
name = freevars[i]
|
||||||
else:
|
else:
|
||||||
name = "__cell__"
|
name = "__cell__"
|
||||||
fwdref = _Stringifier(name)
|
fwdref = _Stringifier(name, stringifier_dict=globals)
|
||||||
new_closure.append(types.CellType(fwdref))
|
new_closure.append(types.CellType(fwdref))
|
||||||
closure = tuple(new_closure)
|
closure = tuple(new_closure)
|
||||||
else:
|
else:
|
||||||
|
@ -573,6 +591,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
|
||||||
owner=owner,
|
owner=owner,
|
||||||
globals=annotate.__globals__,
|
globals=annotate.__globals__,
|
||||||
is_class=is_class,
|
is_class=is_class,
|
||||||
|
stringifier_dict=globals,
|
||||||
)
|
)
|
||||||
globals.stringifiers.append(fwdref)
|
globals.stringifiers.append(fwdref)
|
||||||
new_closure.append(types.CellType(fwdref))
|
new_closure.append(types.CellType(fwdref))
|
||||||
|
@ -591,6 +610,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
|
||||||
result = func(Format.VALUE)
|
result = func(Format.VALUE)
|
||||||
for obj in globals.stringifiers:
|
for obj in globals.stringifiers:
|
||||||
obj.__class__ = ForwardRef
|
obj.__class__ = ForwardRef
|
||||||
|
obj.__stringifier_dict__ = None # not needed for ForwardRef
|
||||||
if isinstance(obj.__ast_node__, str):
|
if isinstance(obj.__ast_node__, str):
|
||||||
obj.__arg__ = obj.__ast_node__
|
obj.__arg__ = obj.__ast_node__
|
||||||
obj.__ast_node__ = None
|
obj.__ast_node__ = None
|
||||||
|
|
|
@ -80,6 +80,42 @@ class TestForwardRefFormat(unittest.TestCase):
|
||||||
fwdref.evaluate()
|
fwdref.evaluate()
|
||||||
self.assertEqual(fwdref.evaluate(globals={"doesntexist": 1}), 1)
|
self.assertEqual(fwdref.evaluate(globals={"doesntexist": 1}), 1)
|
||||||
|
|
||||||
|
def test_nonexistent_attribute(self):
|
||||||
|
def f(
|
||||||
|
x: some.module,
|
||||||
|
y: some[module],
|
||||||
|
z: some(module),
|
||||||
|
alpha: some | obj,
|
||||||
|
beta: +some,
|
||||||
|
gamma: some < obj,
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
anno = annotationlib.get_annotations(f, format=Format.FORWARDREF)
|
||||||
|
x_anno = anno["x"]
|
||||||
|
self.assertIsInstance(x_anno, ForwardRef)
|
||||||
|
self.assertEqual(x_anno, ForwardRef("some.module"))
|
||||||
|
|
||||||
|
y_anno = anno["y"]
|
||||||
|
self.assertIsInstance(y_anno, ForwardRef)
|
||||||
|
self.assertEqual(y_anno, ForwardRef("some[module]"))
|
||||||
|
|
||||||
|
z_anno = anno["z"]
|
||||||
|
self.assertIsInstance(z_anno, ForwardRef)
|
||||||
|
self.assertEqual(z_anno, ForwardRef("some(module)"))
|
||||||
|
|
||||||
|
alpha_anno = anno["alpha"]
|
||||||
|
self.assertIsInstance(alpha_anno, ForwardRef)
|
||||||
|
self.assertEqual(alpha_anno, ForwardRef("some | obj"))
|
||||||
|
|
||||||
|
beta_anno = anno["beta"]
|
||||||
|
self.assertIsInstance(beta_anno, ForwardRef)
|
||||||
|
self.assertEqual(beta_anno, ForwardRef("+some"))
|
||||||
|
|
||||||
|
gamma_anno = anno["gamma"]
|
||||||
|
self.assertIsInstance(gamma_anno, ForwardRef)
|
||||||
|
self.assertEqual(gamma_anno, ForwardRef("some < obj"))
|
||||||
|
|
||||||
|
|
||||||
class TestSourceFormat(unittest.TestCase):
|
class TestSourceFormat(unittest.TestCase):
|
||||||
def test_closure(self):
|
def test_closure(self):
|
||||||
|
@ -91,6 +127,16 @@ class TestSourceFormat(unittest.TestCase):
|
||||||
anno = annotationlib.get_annotations(inner, format=Format.STRING)
|
anno = annotationlib.get_annotations(inner, format=Format.STRING)
|
||||||
self.assertEqual(anno, {"arg": "x"})
|
self.assertEqual(anno, {"arg": "x"})
|
||||||
|
|
||||||
|
def test_closure_undefined(self):
|
||||||
|
if False:
|
||||||
|
x = 0
|
||||||
|
|
||||||
|
def inner(arg: x):
|
||||||
|
pass
|
||||||
|
|
||||||
|
anno = annotationlib.get_annotations(inner, format=Format.STRING)
|
||||||
|
self.assertEqual(anno, {"arg": "x"})
|
||||||
|
|
||||||
def test_function(self):
|
def test_function(self):
|
||||||
def f(x: int, y: doesntexist):
|
def f(x: int, y: doesntexist):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
In the :data:`~annotationlib.Format.FORWARDREF` format of
|
||||||
|
:mod:`annotationlib`, fix bug where nested expressions were not returned as
|
||||||
|
:class:`annotationlib.ForwardRef` format.
|
Loading…
Reference in New Issue