Type annotations in `exec`d code do not resolve as expected
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 77.2k
- Fork
- 35.9k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
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
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia eseguendo la riproduzione minima su Python 3.14 e traccia inspect.signature attraverso annotationlib.get_annotations, in particolare il percorso annotate mostrato nel traceback. Confronta il comportamento con Python 3.13 e con il test esistente basato su exec; il lavoro è concluso quando le annotazioni importate all'interno di codice eseguito tramite exec vengono risolte correttamente senza compromettere gli scenari di test riportati.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- python
- Ambito
- compilers
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Tranquilla
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 45/100