-
-
Notifications
You must be signed in to change notification settings - Fork 34.3k
Open
Labels
3.14bugs and security fixesbugs and security fixes3.15new features, bugs and security fixesnew features, bugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-typingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Bug report
Bug description:
We've counted on the ability to exec code to test that various type annotations lead to the correct success/error scenarios
@pytest.mark.parametrize(
"annotation, value, raises, match",
[
pytest.param("Annotated[int, at.Gt(1)]", "0", True, "0 is not greater than 1", id="Gt fail"),
pytest.param("Annotated[int, at.Gt(1)]", "2", False, "", id="Gt ok"),
pytest.param("Annotated[int, at.Lt(1)]", "2", True, "2 is not less than 1", id="Lt fail"),
pytest.param("Annotated[int, at.Lt(1)]", "0", False, "", id="Lt ok"),
pytest.param("Annotated[int, at.Ge(1)]", "0", True, "0 is not greater than or equal to 1", id="Ge fail"),
pytest.param("Annotated[int, at.Ge(1)]", "1", False, "", id="Ge ok"),
pytest.param("Annotated[int, at.Le(1)]", "2", True, "2 is not less than or equal to 1", id="Le fail"),
pytest.param("Annotated[int, at.Le(1)]", "1", False, "", id="Le ok"),
pytest.param("Annotated[int, at.MultipleOf(2)]", "3", True, "3 is not a multiple of 2", id="MultipleOf fail"),
pytest.param("Annotated[int, at.MultipleOf(2)]", "4", False, "", id="MultipleOf ok"),
pytest.param("Annotated[str, at.MinLen(2)]", "'a'", True, "a is not longer than 2", id="MinLen fail"),
pytest.param("Annotated[str, at.MinLen(2)]", "'abc'", False, "", id="MinLen ok"),
pytest.param("Annotated[str, at.MaxLen(2)]", "'abc'", True, "abc is not shorter than 2", id="MaxLen fail"),
pytest.param("Annotated[str, at.MaxLen(2)]", "'a'", False, "", id="MaxLen ok"),
pytest.param(
"Annotated[int, at.Predicate(lambda x: False)]", "0", True, "0 does not satisfy", id="Predicate fail"
),
pytest.param("Annotated[int, at.Predicate(lambda x: True)]", "0", False, "", id="Predicate ok"),
],
)
async def test_test_with_annotated_type(annotation: str, value: str, raises: bool, match: str):
loc: dict = {}
exec(
dedent(
f"""
from typing import Annotated
import annotated_types as at
@Test("test")
async def test(test: TestInstance, foo: {annotation}) -> None:
pass
test_instance = test.use_options(foo={value})
"""
),
globals(),
loc,
)
test = loc["test_instance"]
ctx: AbstractContextManager
if raises:
ctx = pytest.raises(ValueError, match=match)
else:
ctx = nullcontext()
with ctx:
await parameterize(test, Environment(TestConfig("test")))However, this now fails in python 3.14+
A "minimal" reproduction:
import inspect
from textwrap import dedent
from typing import TypeAlias, Callable, Coroutine, Self, get_type_hints
TestFunc: TypeAlias = Callable[..., Coroutine[None, None, None]]
class Test:
def __call__(self, func: TestFunc) -> Self:
print(inspect.signature(func).parameters)
print(get_type_hints(func))
def main():
exec(
dedent(
f"""
from typing import Annotated
import annotated_types as at
@Test()
async def test(foo: Annotated[int, at.Gt(1)]) -> None:
pass
print("After function call")
"""
),
globals(),
{},
)
if __name__ == "__main__":
main()Despite the types Annotated and at being imported in the exec'd code, the values do not get resolved when trying to get annotations.
Traceback (most recent call last):
File "/home/vfazio/development/xtf/314annotations.py", line 33, in <module>
main()
~~~~^^
File "/home/vfazio/development/xtf/314annotations.py", line 14, in main
exec(
~~~~^
dedent(
^^^^^^^
...<12 lines>...
{},
^^^
)
^
File "<string>", line 5, in <module>
File "/home/vfazio/development/xtf/314annotations.py", line 10, in __call__
print(inspect.signature(func).parameters)
~~~~~~~~~~~~~~~~~^^^^^^
File "/home/vfazio/.pyenv/versions/3.14.3/lib/python3.14/inspect.py", line 3322, in signature
return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
globals=globals, locals=locals, eval_str=eval_str,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
annotation_format=annotation_format)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/vfazio/.pyenv/versions/3.14.3/lib/python3.14/inspect.py", line 3037, in from_callable
return _signature_from_callable(obj, sigcls=cls,
follow_wrapper_chains=follow_wrapped,
globals=globals, locals=locals, eval_str=eval_str,
annotation_format=annotation_format)
File "/home/vfazio/.pyenv/versions/3.14.3/lib/python3.14/inspect.py", line 2512, in _signature_from_callable
return _signature_from_function(sigcls, obj,
skip_bound_arg=skip_bound_arg,
globals=globals, locals=locals, eval_str=eval_str,
annotation_format=annotation_format)
File "/home/vfazio/.pyenv/versions/3.14.3/lib/python3.14/inspect.py", line 2335, in _signature_from_function
annotations = get_annotations(func, globals=globals, locals=locals, eval_str=eval_str,
format=annotation_format)
File "/home/vfazio/.pyenv/versions/3.14.3/lib/python3.14/annotationlib.py", line 966, in get_annotations
ann = _get_dunder_annotations(obj)
File "/home/vfazio/.pyenv/versions/3.14.3/lib/python3.14/annotationlib.py", line 1146, in _get_dunder_annotations
ann = getattr(obj, "__annotations__", None)
File "<string>", line 6, in __annotate__
NameError: name 'Annotated' is not defined
This seems to imply that code generators/exec can no longer rely on imports in code to satisfy annotations? I don't know if this is due to changes in exec or PEP 649/749 but it is inconvenient.
CPython versions tested on:
3.14, 3.13, 3.12, 3.11
Operating systems tested on:
Linux
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
3.14bugs and security fixesbugs and security fixes3.15new features, bugs and security fixesnew features, bugs and security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-typingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error