diff --git a/backend/src/hatchling/version/source/code.py b/backend/src/hatchling/version/source/code.py index dc708aee7..4e611b078 100644 --- a/backend/src/hatchling/version/source/code.py +++ b/backend/src/hatchling/version/source/code.py @@ -47,6 +47,10 @@ def get_version_data(self) -> dict: spec = spec_from_file_location(os.path.splitext(path)[0], path) module = module_from_spec(spec) # type: ignore[arg-type] + # This fixes using PEP 563 (__future__ annotations) with dataclasses. + # https://github.com/pypa/hatch/issues/1863 + sys.modules[os.path.splitext(path)[0]] = module + old_search_paths = list(sys.path) try: sys.path[:] = [*absolute_search_paths, *old_search_paths] diff --git a/tests/backend/version/source/test_code.py b/tests/backend/version/source/test_code.py index 1e1cf639e..18c67d1d3 100644 --- a/tests/backend/version/source/test_code.py +++ b/tests/backend/version/source/test_code.py @@ -125,3 +125,65 @@ def foo(version_info): with temp_dir.as_cwd(): assert source.get_version_data()['version'] == '1.0.0.1.dev0' + + +def test_pep563_with_dataclasses_1(temp_dir, helpers): + """ + Test postponed evaluation of annotations (using __future__) with dataclasses. + + References: + - https://github.com/pypa/hatch/issues/1863 + """ + source = CodeSource(str(temp_dir), {'path': 'a/b.py'}) + + file_path = temp_dir / 'a' / 'b.py' + file_path.ensure_parent_dir_exists() + file_path.write_text( + helpers.dedent( + """ + from __future__ import annotations + + from dataclasses import dataclass + + @dataclass + class VersionConfig: + test_dir: str | None = None + verbose: bool = False + + __version__ = "0.1.1" + """ + ) + ) + + with temp_dir.as_cwd(): + assert source.get_version_data()['version'] == '0.1.1' + + +def test_pep563_with_dataclasses_2(temp_dir, helpers): + """ + Test postponed evaluation of annotations (using "type" string) with dataclasses. + + References: + - https://github.com/pypa/hatch/issues/1863 + """ + source = CodeSource(str(temp_dir), {'path': 'a/b.py'}) + + file_path = temp_dir / 'a' / 'b.py' + file_path.ensure_parent_dir_exists() + file_path.write_text( + helpers.dedent( + """ + from dataclasses import dataclass + + @dataclass + class VersionConfig: + test_dir: "str | None" = None + verbose: bool = False + + __version__ = "0.1.1" + """ + ) + ) + + with temp_dir.as_cwd(): + assert source.get_version_data()['version'] == '0.1.1'