From b7dc9313e591951dc456f9a2487ff968651e1f74 Mon Sep 17 00:00:00 2001 From: brentyi Date: Thu, 16 Jan 2025 11:48:10 -0800 Subject: [PATCH 1/5] Add test from #235 --- tests/test_new_style_annotations_min_py39.py | 26 ++++++++++++++++++- ...ew_style_annotations_min_py39_generated.py | 26 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/tests/test_new_style_annotations_min_py39.py b/tests/test_new_style_annotations_min_py39.py index 9b8908a3..a61ce91f 100644 --- a/tests/test_new_style_annotations_min_py39.py +++ b/tests/test_new_style_annotations_min_py39.py @@ -1,7 +1,8 @@ import dataclasses -from typing import Any, Literal, Optional, Union +from typing import Any, Literal, Optional, Type, Union import pytest +from helptext_utils import get_helptext_with_checks import tyro @@ -67,3 +68,26 @@ def main( def test_tuple_direct() -> None: assert tyro.cli(tuple[int, ...], args="1 2".split(" ")) == (1, 2) # type: ignore assert tyro.cli(tuple[int, int], args="1 2".split(" ")) == (1, 2) # type: ignore + + +def test_type_with_init_false() -> None: + """https://github.com/brentyi/tyro/issues/235""" + from torch.optim.lr_scheduler import LinearLR, LRScheduler + + @dataclasses.dataclass(frozen=True) + class LinearLRConfig: + _target: type[LRScheduler] = dataclasses.field( + init=False, default_factory=lambda: LinearLR + ) + _target2: Type[LRScheduler] = dataclasses.field( + init=False, default_factory=lambda: LinearLR + ) + start_factor: float = 1.0 / 3 + end_factor: float = 1.0 + total_iters: Optional[int] = None + + def main(config: LinearLRConfig) -> LinearLRConfig: + return config + + assert tyro.cli(main, args=[]) == LinearLRConfig() + assert "_target" not in get_helptext_with_checks(LinearLRConfig) diff --git a/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py b/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py index 0fe423b5..f17e9371 100644 --- a/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py +++ b/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py @@ -1,7 +1,8 @@ import dataclasses -from typing import Any, Literal, Optional +from typing import Any, Literal, Optional, Type import pytest +from helptext_utils import get_helptext_with_checks import tyro @@ -67,3 +68,26 @@ def main( def test_tuple_direct() -> None: assert tyro.cli(tuple[int, ...], args="1 2".split(" ")) == (1, 2) # type: ignore assert tyro.cli(tuple[int, int], args="1 2".split(" ")) == (1, 2) # type: ignore + + +def test_type_with_init_false() -> None: + """https://github.com/brentyi/tyro/issues/235""" + from torch.optim.lr_scheduler import LinearLR, LRScheduler + + @dataclasses.dataclass(frozen=True) + class LinearLRConfig: + _target: type[LRScheduler] = dataclasses.field( + init=False, default_factory=lambda: LinearLR + ) + _target2: Type[LRScheduler] = dataclasses.field( + init=False, default_factory=lambda: LinearLR + ) + start_factor: float = 1.0 / 3 + end_factor: float = 1.0 + total_iters: Optional[int] = None + + def main(config: LinearLRConfig) -> LinearLRConfig: + return config + + assert tyro.cli(main, args=[]) == LinearLRConfig() + assert "_target" not in get_helptext_with_checks(LinearLRConfig) From 8d687d1c6ef32a2d916dcfd59e9f551eda793726 Mon Sep 17 00:00:00 2001 From: brentyi Date: Thu, 16 Jan 2025 11:54:53 -0800 Subject: [PATCH 2/5] torch import try/except --- tests/test_new_style_annotations_min_py39.py | 49 +++++++++++-------- ...ew_style_annotations_min_py39_generated.py | 2 +- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/tests/test_new_style_annotations_min_py39.py b/tests/test_new_style_annotations_min_py39.py index a61ce91f..999774af 100644 --- a/tests/test_new_style_annotations_min_py39.py +++ b/tests/test_new_style_annotations_min_py39.py @@ -2,10 +2,10 @@ from typing import Any, Literal, Optional, Type, Union import pytest -from helptext_utils import get_helptext_with_checks - import tyro +from helptext_utils import get_helptext_with_checks + def test_list() -> None: def main(x: list[bool]) -> Any: @@ -70,24 +70,31 @@ def test_tuple_direct() -> None: assert tyro.cli(tuple[int, int], args="1 2".split(" ")) == (1, 2) # type: ignore -def test_type_with_init_false() -> None: - """https://github.com/brentyi/tyro/issues/235""" +try: from torch.optim.lr_scheduler import LinearLR, LRScheduler - @dataclasses.dataclass(frozen=True) - class LinearLRConfig: - _target: type[LRScheduler] = dataclasses.field( - init=False, default_factory=lambda: LinearLR - ) - _target2: Type[LRScheduler] = dataclasses.field( - init=False, default_factory=lambda: LinearLR - ) - start_factor: float = 1.0 / 3 - end_factor: float = 1.0 - total_iters: Optional[int] = None - - def main(config: LinearLRConfig) -> LinearLRConfig: - return config - - assert tyro.cli(main, args=[]) == LinearLRConfig() - assert "_target" not in get_helptext_with_checks(LinearLRConfig) + def test_type_with_init_false() -> None: + """https://github.com/brentyi/tyro/issues/235""" + + @dataclasses.dataclass(frozen=True) + class LinearLRConfig: + _target: type[LRScheduler] = dataclasses.field( + init=False, default_factory=lambda: LinearLR + ) + _target2: Type[LRScheduler] = dataclasses.field( + init=False, default_factory=lambda: LinearLR + ) + start_factor: float = 1.0 / 3 + end_factor: float = 1.0 + total_iters: Optional[int] = None + + def main(config: LinearLRConfig) -> LinearLRConfig: + return config + + assert tyro.cli(main, args=[]) == LinearLRConfig() + assert "_target" not in get_helptext_with_checks(LinearLRConfig) +except ImportError: + # We can't install PyTorch in Python 3.13. + import sys + + assert sys.version_info >= (3, 13) diff --git a/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py b/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py index f17e9371..0b51242e 100644 --- a/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py +++ b/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py @@ -3,6 +3,7 @@ import pytest from helptext_utils import get_helptext_with_checks +from torch.optim.lr_scheduler import LinearLR, LRScheduler import tyro @@ -72,7 +73,6 @@ def test_tuple_direct() -> None: def test_type_with_init_false() -> None: """https://github.com/brentyi/tyro/issues/235""" - from torch.optim.lr_scheduler import LinearLR, LRScheduler @dataclasses.dataclass(frozen=True) class LinearLRConfig: From 20e8ed623b31ab7b8a1b585197d865d68d85c774 Mon Sep 17 00:00:00 2001 From: brentyi Date: Thu, 16 Jan 2025 11:59:25 -0800 Subject: [PATCH 3/5] test gen, ruff --- tests/test_new_style_annotations_min_py39.py | 4 +- ...ew_style_annotations_min_py39_generated.py | 43 +++++++++++-------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/tests/test_new_style_annotations_min_py39.py b/tests/test_new_style_annotations_min_py39.py index 999774af..80ccfabc 100644 --- a/tests/test_new_style_annotations_min_py39.py +++ b/tests/test_new_style_annotations_min_py39.py @@ -2,10 +2,10 @@ from typing import Any, Literal, Optional, Type, Union import pytest -import tyro - from helptext_utils import get_helptext_with_checks +import tyro + def test_list() -> None: def main(x: list[bool]) -> Any: diff --git a/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py b/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py index 0b51242e..6747eb8f 100644 --- a/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py +++ b/tests/test_py311_generated/test_new_style_annotations_min_py39_generated.py @@ -3,7 +3,6 @@ import pytest from helptext_utils import get_helptext_with_checks -from torch.optim.lr_scheduler import LinearLR, LRScheduler import tyro @@ -71,23 +70,31 @@ def test_tuple_direct() -> None: assert tyro.cli(tuple[int, int], args="1 2".split(" ")) == (1, 2) # type: ignore -def test_type_with_init_false() -> None: - """https://github.com/brentyi/tyro/issues/235""" +try: + from torch.optim.lr_scheduler import LinearLR, LRScheduler - @dataclasses.dataclass(frozen=True) - class LinearLRConfig: - _target: type[LRScheduler] = dataclasses.field( - init=False, default_factory=lambda: LinearLR - ) - _target2: Type[LRScheduler] = dataclasses.field( - init=False, default_factory=lambda: LinearLR - ) - start_factor: float = 1.0 / 3 - end_factor: float = 1.0 - total_iters: Optional[int] = None + def test_type_with_init_false() -> None: + """https://github.com/brentyi/tyro/issues/235""" - def main(config: LinearLRConfig) -> LinearLRConfig: - return config + @dataclasses.dataclass(frozen=True) + class LinearLRConfig: + _target: type[LRScheduler] = dataclasses.field( + init=False, default_factory=lambda: LinearLR + ) + _target2: Type[LRScheduler] = dataclasses.field( + init=False, default_factory=lambda: LinearLR + ) + start_factor: float = 1.0 / 3 + end_factor: float = 1.0 + total_iters: Optional[int] = None - assert tyro.cli(main, args=[]) == LinearLRConfig() - assert "_target" not in get_helptext_with_checks(LinearLRConfig) + def main(config: LinearLRConfig) -> LinearLRConfig: + return config + + assert tyro.cli(main, args=[]) == LinearLRConfig() + assert "_target" not in get_helptext_with_checks(LinearLRConfig) +except ImportError: + # We can't install PyTorch in Python 3.13. + import sys + + assert sys.version_info >= (3, 13) From 03dfd750ae3c83766b033c35ce6e404736199fd1 Mon Sep 17 00:00:00 2001 From: brentyi Date: Thu, 16 Jan 2025 13:31:40 -0800 Subject: [PATCH 4/5] Add 3.12.0 to pytest yml --- .github/workflows/pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 1ee6ff09..e9767170 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.12.0", "3.13"] steps: - uses: actions/checkout@v2 From a7b131a21bc6707c9e2e2059f114e2465b648fdb Mon Sep 17 00:00:00 2001 From: brentyi Date: Thu, 16 Jan 2025 13:36:09 -0800 Subject: [PATCH 5/5] Workaround for `type[T]` bug in Python 3.12.0 --- src/tyro/_resolver.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/tyro/_resolver.py b/src/tyro/_resolver.py index 6a3c3f14..08098189 100644 --- a/src/tyro/_resolver.py +++ b/src/tyro/_resolver.py @@ -381,12 +381,14 @@ def concretize_type_params( typ = resolve_newtype_and_aliases(typ) type_from_typevar = {} GenericAlias = getattr(types, "GenericAlias", None) - while ( - GenericAlias is not None - and isinstance(typ, GenericAlias) - and len(getattr(typ, "__type_params__", ())) > 0 - ): - for k, v in zip(typ.__type_params__, get_args(typ)): # type: ignore + while GenericAlias is not None and isinstance(typ, GenericAlias): + type_params = getattr(typ, "__type_params__", ()) + # The __len__ check is for a bug in Python 3.12.0: + # https://github.com/brentyi/tyro/issues/235 + if not hasattr(type_params, "__len__") or len(type_params) == 0: + break + + for k, v in zip(type_params, get_args(typ)): type_from_typevar[k] = TypeParamResolver.concretize_type_params( v, seen=seen )