Skip to content

Commit

Permalink
Merge pull request #555 from mit-ll-responsible-ai/py312
Browse files Browse the repository at this point in the history
Add support for Python 3.12
  • Loading branch information
rsokl authored Oct 29, 2023
2 parents a8d949d + 53ba1d6 commit f827272
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tox_run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:

strategy:
matrix:
python-version: [3.8, 3.9, "3.10", 3.11]
python-version: [3.8, 3.9, "3.10", 3.11, 3.12]
fail-fast: false

steps:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<img src="https://img.shields.io/pypi/v/hydra-zen.svg" alt="PyPI" />
</a>
<a>
<img src="https://img.shields.io/badge/python-3.8%20&#8208;%203.11-blue.svg" alt="Python version support" />
<img src="https://img.shields.io/badge/python-3.8%20&#8208;%203.12-blue.svg" alt="Python version support" />
</a>
<a href="https://github.com/mit-ll-responsible-ai/hydra-zen/actions?query=workflow%3ATests+branch%3Amain">
<img src="https://github.com/mit-ll-responsible-ai/hydra-zen/workflows/Tests/badge.svg" alt="GitHub Actions" />
Expand Down
1 change: 1 addition & 0 deletions docs/source/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ For more details and examples, see :pull:`553`.
Improvements
------------
- Adds formal support for Python 3.12. See :pull:`555`
- :func:`~hydra_zen.BuildsFn` was introduced to permit customizable auto-config and type-refinement support in config-creation functions. See :pull:`553`.
- :func:`~hydra_zen.builds` and :func:`~hydra_zen.make_custom_builds_fn` now accept a `zen_exclude` field for excluding parameters from auto-population, either by name or by pattern. See :pull:`558`.
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Scientific/Engineering",
"Programming Language :: Python :: 3 :: Only",
]
Expand Down Expand Up @@ -127,14 +128,15 @@ skip = 'docs/build/*'
legacy_tox_ini = """
[tox]
isolated_build = True
envlist = py38, py39, py310, py311
envlist = py38, py39, py310, py311, py312
[gh-actions]
python =
3.8: py38
3.9: py39
3.10: py310
3.11: py311
3.12: py312
[testenv]
Expand All @@ -143,7 +145,8 @@ description = Runs test suite parallelized in the specified python enviornment a
Run `tox -e py39 -- -n 0` to run tests in a python 3.9 with
parallelization disabled.
passenv = *
deps = pytest
deps = setuptools
pytest
hypothesis
pytest-xdist
tzdata
Expand Down
3 changes: 2 additions & 1 deletion src/hydra_zen/structured_configs/_implementations.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,8 @@ def wrapper(decorated_obj: Any) -> Any:
"kw_only": kw_only,
"slots": slots,
"weakref_slot": weakref_slot,
}
},
include_module=False,
)
decorated_obj = dataclass(**dc_options)(decorated_obj) # type: ignore

Expand Down
21 changes: 18 additions & 3 deletions src/hydra_zen/structured_configs/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ def merge_settings(
_STRICT_DATACLASS_OPTION_KEYS.copy()


def parse_dataclass_options(options: Mapping[str, Any]) -> DataclassOptions:
def parse_dataclass_options(
options: Mapping[str, Any], include_module: bool = True
) -> DataclassOptions:
"""
Ensures `options` adheres to `DataclassOptions` and merges hydra-zen defaults
for missing options.
Expand Down Expand Up @@ -335,12 +337,16 @@ def parse_dataclass_options(options: Mapping[str, Any]) -> DataclassOptions:
raise ValueError(f"{name} is not a valid dataclass option.")

if name == "module":
if not isinstance(val, str) or not all(
v.isidentifier() and not iskeyword(v) for v in val.split(".")
if val is not None and (
not isinstance(val, str)
or not all(
v.isidentifier() and not iskeyword(v) for v in val.split(".")
)
):
raise ValueError(
f"dataclass option `{name}` must be a valid module name, got {val}"
)

elif name == "cls_name":
if val is not None and (not isinstance(val, str) or not val.isidentifier()):
raise ValueError(
Expand All @@ -367,6 +373,15 @@ def parse_dataclass_options(options: Mapping[str, Any]) -> DataclassOptions:
f"(type: {type(val)})"
)
merged[name] = val
if (
include_module
and "module" not in merged
and "module" in _STRICT_DATACLASS_OPTION_KEYS
): # pragma: no cover
# For Python 3.12+ we want the default module to
# remain "types" rather than being inferred as some
# internal hydra-zen module.
merged["module"] = "types"
return merged


Expand Down
23 changes: 16 additions & 7 deletions src/hydra_zen/typing/_implementations.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,19 +393,25 @@ class _Py311Dataclass(_Py310Dataclass, total=False):
weakref_slot: bool


class _Py312Dataclass(_Py311Dataclass, total=False):
module: Optional[str]


if sys.version_info < (3, 10):
_StrictDataclassOptions = _AllPyDataclassOptions
elif sys.version_info < (3, 11):
_StrictDataclassOptions = _Py310Dataclass
else: # pragma: no cover
elif sys.version_info < (3, 12): # pragma: no cover
_StrictDataclassOptions = _Py311Dataclass
else: # pragma: no cover
_StrictDataclassOptions = _Py312Dataclass


class StrictDataclassOptions(_StrictDataclassOptions):
cls_name: Required[str]


class DataclassOptions(_Py311Dataclass, total=False):
class DataclassOptions(_Py312Dataclass, total=False):
"""Specifies dataclass-creation options via `builds`, `just` et al.
Note that, unlike :func:`dataclasses.make_dataclass`, the default value for
Expand Down Expand Up @@ -492,6 +498,9 @@ class DataclassOptions(_Py311Dataclass, total=False):
“__weakref__”, which is required to make an instance weakref-able. It is an
error to specify `weakref_slot=True` without also specifying `slots=True`.
module : str | None
If module is defined, the __module__ attribute of the dataclass is set to that value. By default, it is set to the module name of the caller.
References
----------
.. [1] https://docs.python.org/3/library/dataclasses.html
Expand Down Expand Up @@ -563,7 +572,7 @@ class DataclassOptions(_Py311Dataclass, total=False):
None
"""

module: str # zen-only
module: Optional[str]


def _permitted_keys(typed_dict: Any) -> FrozenSet[str]:
Expand All @@ -572,7 +581,7 @@ def _permitted_keys(typed_dict: Any) -> FrozenSet[str]:

DEFAULT_DATACLASS_OPTIONS = DataclassOptions(unsafe_hash=True)
PERMITTED_DATACLASS_OPTIONS = _permitted_keys(DataclassOptions)
UNSUPPORTED_DATACLASS_OPTIONS = _permitted_keys(_Py311Dataclass) - _permitted_keys(
StrictDataclassOptions
)
del _AllPyDataclassOptions, _Py310Dataclass, _Py311Dataclass
UNSUPPORTED_DATACLASS_OPTIONS = (
_permitted_keys(_Py312Dataclass) - {"module"}
) - _permitted_keys(StrictDataclassOptions)
del _AllPyDataclassOptions, _Py310Dataclass, _Py311Dataclass, _Py312Dataclass
41 changes: 41 additions & 0 deletions tests/test_dataclass_semantics.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,44 @@ class Unhash:
Conf = builds(dict, y=unhash, zen_convert={"dataclass": False})
assert not hasattr(Conf, "y")
assert Conf().y is unhash


@pytest.mark.parametrize(
"Conf",
[
make_config(zen_dataclass={"module": None}),
builds(dict, zen_dataclass={"module": None}),
make_custom_builds_fn(zen_dataclass={"module": None})(dict),
],
)
def test_module_is_None(Conf):
if sys.version_info < (3, 12):
assert Conf.__module__ == "types"
else:
assert Conf.__module__.startswith("hydra_zen")


@pytest.mark.parametrize(
"Conf",
[
make_config(),
builds(
dict,
),
make_custom_builds_fn()(dict),
],
)
def test_module_is_not_specified(Conf):
assert Conf.__module__ == "types"


@pytest.mark.parametrize(
"Conf",
[
make_config(zen_dataclass={"module": "aaa"}),
builds(dict, zen_dataclass={"module": "aaa"}),
make_custom_builds_fn(zen_dataclass={"module": "aaa"})(dict),
],
)
def test_modules_is_specified(Conf):
assert Conf.__module__ == "aaa"

0 comments on commit f827272

Please sign in to comment.