This is an automated email from the ASF dual-hosted git repository. ebenizzy pushed a commit to branch remove-3.9-support in repository https://gitbox.apache.org/repos/asf/hamilton.git
commit 9d7292bf7b90d471502246c8e54df529640e54d8 Author: Elijah ben Izzy <[email protected]> AuthorDate: Sun Oct 19 10:22:47 2025 -0700 Drops support for 3.9, adds support for 3.13 3.9 is EOL in october. This also fixes some iffy numpy tests that relied on unspecified numpy behavior with type-annotations for shapes --- .github/workflows/hamilton-lsp.yml | 2 +- .github/workflows/hamilton-main.yml | 3 +-- .github/workflows/hamilton-sdk.yml | 2 +- examples/hello_world/my_dag.png | Bin 68544 -> 63755 bytes examples/styling_visualization/dag.png | Bin 297452 -> 341276 bytes hamilton/node.py | 12 +++++++----- pyproject.toml | 5 ++--- tests/test_node.py | 7 +++---- 8 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.github/workflows/hamilton-lsp.yml b/.github/workflows/hamilton-lsp.yml index 83e17dc6..b1d23c22 100644 --- a/.github/workflows/hamilton-lsp.yml +++ b/.github/workflows/hamilton-lsp.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.9', '3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12', '3.13'] defaults: run: working-directory: dev_tools/language_server diff --git a/.github/workflows/hamilton-main.yml b/.github/workflows/hamilton-main.yml index 15495039..b1039c81 100644 --- a/.github/workflows/hamilton-main.yml +++ b/.github/workflows/hamilton-main.yml @@ -23,11 +23,10 @@ jobs: os: - ubuntu-latest python-version: - - '3.8' - - '3.9' - '3.10' - '3.11' - '3.12' + - '3.13' env: UV_PRERELEASE: "allow" HAMILTON_TELEMETRY_ENABLED: false diff --git a/.github/workflows/hamilton-sdk.yml b/.github/workflows/hamilton-sdk.yml index 5ec89e5a..d5f539f4 100644 --- a/.github/workflows/hamilton-sdk.yml +++ b/.github/workflows/hamilton-sdk.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.9', '3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12', '3.13'] defaults: run: working-directory: ui/sdk diff --git a/examples/hello_world/my_dag.png b/examples/hello_world/my_dag.png index 77e64e42..b77a634a 100644 Binary files a/examples/hello_world/my_dag.png and b/examples/hello_world/my_dag.png differ diff --git a/examples/styling_visualization/dag.png b/examples/styling_visualization/dag.png index ceda0c39..80da1082 100644 Binary files a/examples/styling_visualization/dag.png and b/examples/styling_visualization/dag.png differ diff --git a/hamilton/node.py b/hamilton/node.py index 5a0ba3f5..3122cafe 100644 --- a/hamilton/node.py +++ b/hamilton/node.py @@ -68,7 +68,7 @@ class Node(object): doc_string: str = "", callabl: Callable = None, node_source: NodeType = NodeType.STANDARD, - input_types: Dict[str, Union[Type, Tuple[Type, DependencyType]]] = None, + input_types: Optional[Dict[str, Union[Type, Tuple[Type, DependencyType]]]] = None, tags: Dict[str, Any] = None, namespace: Tuple[str, ...] = (), originating_functions: Optional[Tuple[Callable, ...]] = None, @@ -118,8 +118,9 @@ class Node(object): # assume optional values passed self._default_parameter_values = optional_values if optional_values else {} else: - # TODO -- remove this when we no longer support 3.8 -- 10/14/2024 - type_hint_kwargs = {} if sys.version_info < (3, 9) else {"include_extras": True} + type_hint_kwargs: dict[str, Any] = {"include_extras": True} + if sys.version_info >= (3, 13): + type_hint_kwargs["globalns"] = callabl.__globals__ input_types = typing.get_type_hints(callabl, **type_hint_kwargs) signature = inspect.signature(callabl) for key, value in signature.parameters.items(): @@ -291,8 +292,9 @@ class Node(object): """ if name is None: name = fn.__name__ - # TODO -- remove this when we no longer support 3.8 -- 10/14/2024 - type_hint_kwargs = {} if sys.version_info < (3, 9) else {"include_extras": True} + type_hint_kwargs = {"include_extras": True} + if sys.version_info >= (3, 13): + type_hint_kwargs["globalns"] = fn.__globals__ return_type = typing.get_type_hints(fn, **type_hint_kwargs).get("return") if return_type is None: raise ValueError(f"Missing type hint for return value in function {fn.__qualname__}.") diff --git a/pyproject.toml b/pyproject.toml index 2e58e897..bfa3f85e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ version = "1.89.0" # NOTE: keep this in sync with hamilton/version.py # dynamic = ["version"] description = "Hamilton, the micro-framework for creating dataframes." readme = "README.md" -requires-python = ">=3.8.1, <4" +requires-python = ">=3.10.1, <4" license = {text = "Apache-2.0"} keywords = ["hamilton"] authors = [ @@ -39,11 +39,10 @@ classifiers = [ "Natural Language :: English", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13" ] dependencies = [ "numpy", diff --git a/tests/test_node.py b/tests/test_node.py index 0411f055..111870f0 100644 --- a/tests/test_node.py +++ b/tests/test_node.py @@ -65,7 +65,6 @@ np_version = np.__version__ major, minor, _ = map(int, np_version.split(".")) [email protected](sys.version_info < (3, 9), reason="requires python 3.9 or higher") def test_node_handles_annotated(): from typing import Annotated @@ -77,7 +76,7 @@ def test_node_handles_annotated(): node = Node.from_fn(annotated_func) assert node.name == "annotated_func" - if major == 2 and minor > 1 and sys.version_info > (3, 9): # greater that 2.1 + if major == 2 and minor > 1 and sys.version_info < (3, 13): # greater that 2.1 expected = { "first": ( Annotated[np.ndarray[tuple[int, ...], np.dtype[np.float64]], Literal["N"]], @@ -89,12 +88,12 @@ def test_node_handles_annotated(): else: expected = { "first": ( - Annotated[np.ndarray[Any, np.dtype[np.float64]], Literal["N"]], + Annotated[np.ndarray[tuple[Any, ...], np.dtype[np.float64]], Literal["N"]], DependencyType.REQUIRED, ), "other": (float, DependencyType.OPTIONAL), } - expected_type = Annotated[np.ndarray[Any, np.dtype[np.float64]], Literal["N"]] + expected_type = Annotated[np.ndarray[tuple[Any, ...], np.dtype[np.float64]], Literal["N"]] assert node.input_types == expected assert node.type == expected_type
