diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e1188eb5..55d86a6d 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -54,4 +54,20 @@ jobs: - name: Run pyright basic on src run: pyright --lib tests/annotations/ src/ - name: (pyright) verify type completeness - run: pyright --ignoreexternal --verifytypes hydra_zen \ No newline at end of file + run: pyright --ignoreexternal --verifytypes hydra_zen + + run-mypy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.8 + uses: actions/setup-python@v3 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install mypy + pip install -e . + - name: Run mypy on test script + run: mypy --warn-unused-ignores tests/annotations/mypy_checks.py diff --git a/.github/workflows/tox_run.yml b/.github/workflows/tox_run.yml index c857e936..ea4671c4 100644 --- a/.github/workflows/tox_run.yml +++ b/.github/workflows/tox_run.yml @@ -182,4 +182,20 @@ jobs: - name: Run pyright basic on src run: pyright --lib tests/annotations/ src/ - name: (pyright) verify type completeness - run: pyright --ignoreexternal --verifytypes hydra_zen \ No newline at end of file + run: pyright --ignoreexternal --verifytypes hydra_zen + + run-mypy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.8 + uses: actions/setup-python@v3 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install mypy + pip install -e . + - name: Run mypy on test script + run: mypy --warn-unused-ignores tests/annotations/mypy_checks.py diff --git a/docs/source/changes.rst b/docs/source/changes.rst index b27fd1db..84904542 100644 --- a/docs/source/changes.rst +++ b/docs/source/changes.rst @@ -135,13 +135,13 @@ If you depended on the previous default behavior, you can recreate it by using t Improvements ------------ - Adds auto-config support for `dataclasses.dataclass` (as highlighted above). (See :pull:`301`) +- We now verify that basic use cases of our config-creation and instantiation functions type-check correctly via mypy. Previously, we had only assured type-checking behavior via pyright - Adds support for using `builds(, populate_full_signature=True)` where `` is a dataclass type that has a field with a default factory. (See :pull:`299`) - Adds auto-config support for `pydantic.Field`, improving hydra-zen's ability to automatically construct configs that describe pydantic models and dataclasses. (See :pull:`303`) - :func:`~hydra_zen.builds` no longer has restrictions on inheritance patterns involving `PartialBuilds`-type configs. (See :pull:`290`) - Two new utility functions were added to the public API: :func:`~hydra_zen.is_partial_builds` and :func:`~hydra_zen.uses_zen_processing` - The :ref:`automatic type refinement ` performed by :func:`~hydra_zen.builds` now has enhanced support for ``typing.Annotated``, ``typing.NewType``, and ``typing.TypeVarTuple``. (See :pull:`283`) - **Support for New Hydra/OmegaConf Features** - OmegaConf ``v2.2.1`` added native support for :py:class:`pathlib.Path`. hydra-zen :ref:`already provides support for these `, but will now defer to OmegaConf's native support when possible. (See :pull:`276`) diff --git a/src/hydra_zen/structured_configs/_implementations.py b/src/hydra_zen/structured_configs/_implementations.py index f6f1f5e5..863347b5 100644 --- a/src/hydra_zen/structured_configs/_implementations.py +++ b/src/hydra_zen/structured_configs/_implementations.py @@ -34,7 +34,7 @@ ) from omegaconf import DictConfig, ListConfig -from typing_extensions import Final, Literal, ParamSpec, TypeAlias +from typing_extensions import Final, Literal, ParamSpec, TypeAlias, dataclass_transform from hydra_zen._compatibility import ( HYDRA_SUPPORTED_PRIMITIVES, @@ -176,24 +176,7 @@ def mutable_value(x: _T, *, zen_convert: Optional[ZenConvert] = None) -> _T: return field(default_factory=lambda: cast(x)) -# Alternate form, from PEP proposal: -# https://github.com/microsoft/pyright/blob/master/specs/dataclass_transforms.md -# -# This enables static checkers to work with third-party decorators that create -# dataclass-like objects -def __dataclass_transform__( - *, - eq_default: bool = True, - order_default: bool = False, - kw_only_default: bool = False, - field_descriptors: Tuple[Union[type, Callable[..., Any]], ...] = (()), -) -> Callable[[_T], _T]: - # If used within a stub file, the following implementation can be - # replaced with "...". - return lambda a: a - - -@__dataclass_transform__() +@dataclass_transform() def hydrated_dataclass( target: Callable[..., Any], *pos_args: SupportedPrimitive, diff --git a/tests/annotations/mypy_checks.py b/tests/annotations/mypy_checks.py new file mode 100644 index 00000000..5be2f1ff --- /dev/null +++ b/tests/annotations/mypy_checks.py @@ -0,0 +1,68 @@ +# Copyright (c) 2022 Massachusetts Institute of Technology +# SPDX-License-Identifier: MIT +from typing import Type + +from typing_extensions import assert_type + +from hydra_zen import builds, instantiate, make_custom_builds_fn + + +def check_builds() -> None: + class A: + ... + + def f(x: int) -> str: + ... + + assert_type(instantiate(builds(A)), A) + assert_type(instantiate(builds(A, populate_full_signature=True)), A) + assert_type(instantiate(builds(A, zen_partial=True))(), A) + + assert_type(instantiate(builds(f)), str) + assert_type(instantiate(builds(f, populate_full_signature=True)), str) + assert_type(instantiate(builds(f, zen_partial=True))(), str) + + builds(f, populate_full_signature=True)(y=2) # type: ignore [call-arg] + + +def check_make_custom_builds() -> None: + class A: + ... + + def f(x: int) -> str: + ... + + builds_ = make_custom_builds_fn() + partial_builds = make_custom_builds_fn(zen_partial=True) + full_builds = make_custom_builds_fn(populate_full_signature=True) + + assert_type(instantiate(builds_(A)), A) + assert_type(instantiate(full_builds(A)), A) + assert_type(instantiate(partial_builds(A))(), A) + + assert_type(instantiate(builds_(f)), str) + assert_type(instantiate(full_builds(f)), str) + assert_type(instantiate(partial_builds(f))(), str) + + full_builds(f)(y=2) # type: ignore [call-arg] + + +# def check_just() -> None: +# from hydra_zen import just + +# class A: +# ... + +# # fails due to: https://github.com/python/mypy/issues/13623 +# assert_type(instantiate(just(A)), Type[A]) + + +## hydrated_dataclass not yet supported: https://github.com/python/mypy/issues/12840 +# def check_hydrated_dataclass() -> None: +# from hydra_zen import hydrated_dataclass + +# @hydrated_dataclass(dict) +# class A: +# x: int + +# assert_type(A(1).x, int)