Type annotations in `exec`d code do not resolve as expected
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 77.2k
- Fork
- 35.9k
- Chỉ số merge pull request
- Chỉ số pull request đang chờ
Mô tả
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
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu bằng cách chạy bản tái hiện tối thiểu trên Python 3.14 và truy vết inspect.signature qua annotationlib.get_annotations, đặc biệt là đường dẫn annotate được hiển thị trong traceback. So sánh hành vi với Python 3.13 và bài kiểm thử hiện có dựa trên exec; được xem là hoàn tất khi các chú thích được import bên trong mã được thực thi bằng exec được phân giải chính xác mà không làm hỏng các kịch bản kiểm thử đã báo cáo.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- python
- Lĩnh vực
- compilers
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Ít trao đổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 45/100