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 86af3701f309b39fb677ba8310da7a11cc5f77a3
Author: Elijah ben Izzy <[email protected]>
AuthorDate: Sun Oct 19 10:50:54 2025 -0700

    Removes all 3.8/3.9 references
---
 examples/hello_world/my_dag.png                    | Bin 63755 -> 68544 bytes
 examples/styling_visualization/dag.png             | Bin 341276 -> 297452 bytes
 hamilton/cli/__main__.py                           |   8 +----
 hamilton/htypes.py                                 |   6 +---
 hamilton/plugins/h_spark.py                        |   6 +---
 plugin_tests/h_spark/test_h_spark.py               |   2 --
 tests/function_modifiers/test_adapters.py          |  14 ++------
 tests/function_modifiers/test_expanders.py         |  36 +++++++++------------
 .../pandera/test_pandera_data_quality.py           |   3 --
 tests/plugins/test_dlt_extensions.py               |  19 +++--------
 tests/plugins/test_huggingface_extensions.py       |   3 --
 tests/plugins/test_mlflow_extension.py             |  15 ++-------
 tests/resources/nodes_with_future_annotation.py    |   8 +----
 tests/test_graph_types.py                          |   7 ----
 tests/test_type_utils.py                           |  34 +++----------------
 15 files changed, 33 insertions(+), 128 deletions(-)

diff --git a/examples/hello_world/my_dag.png b/examples/hello_world/my_dag.png
index b77a634a..77e64e42 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 80da1082..ceda0c39 100644
Binary files a/examples/styling_visualization/dag.png and 
b/examples/styling_visualization/dag.png differ
diff --git a/hamilton/cli/__main__.py b/hamilton/cli/__main__.py
index 41c411c6..e5321bcb 100644
--- a/hamilton/cli/__main__.py
+++ b/hamilton/cli/__main__.py
@@ -19,16 +19,10 @@ import dataclasses
 import json
 import logging
 import os
-import sys
 import warnings
 from pathlib import Path
 from pprint import pprint
-from typing import Any, Callable, List, Optional
-
-if sys.version_info < (3, 9):
-    from typing_extensions import Annotated
-else:
-    from typing import Annotated
+from typing import Annotated, Any, Callable, List, Optional
 
 import typer
 
diff --git a/hamilton/htypes.py b/hamilton/htypes.py
index 6849ed56..ad374243 100644
--- a/hamilton/htypes.py
+++ b/hamilton/htypes.py
@@ -18,14 +18,10 @@
 import inspect
 import sys
 import typing
-from typing import Any, Iterable, Optional, Protocol, Tuple, Type, TypeVar, 
Union
+from typing import Any, Iterable, Literal, Optional, Protocol, Tuple, Type, 
TypeVar, Union
 
 import typing_inspect
 
-if sys.version_info >= (3, 9):
-    from typing import Literal
-else:
-    Literal = None
 from hamilton.registry import COLUMN_TYPE, DF_TYPE_AND_COLUMN_TYPES
 
 BASE_ARGS_FOR_GENERICS = (typing.T,)
diff --git a/hamilton/plugins/h_spark.py b/hamilton/plugins/h_spark.py
index 3d0f7e33..ae494319 100644
--- a/hamilton/plugins/h_spark.py
+++ b/hamilton/plugins/h_spark.py
@@ -18,7 +18,6 @@
 import functools
 import inspect
 import logging
-import sys
 from types import CodeType, FunctionType, ModuleType
 from typing import Any, Callable, Collection, Dict, List, Optional, Set, 
Tuple, Type, Union
 
@@ -229,10 +228,7 @@ def python_to_spark_type(python_type: Type[Union[int, 
float, bool, str, bytes]])
         raise ValueError("Unsupported Python type: " + str(python_type))
 
 
-if sys.version_info < (3, 9):
-    _list = (List[int], List[float], List[bool], List[str], List[bytes])
-else:
-    _list = (list[int], list[float], list[bool], list[str], list[bytes])
+_list = (list[int], list[float], list[bool], list[str], list[bytes])
 
 
 def get_spark_type(return_type: Any) -> types.DataType:
diff --git a/plugin_tests/h_spark/test_h_spark.py 
b/plugin_tests/h_spark/test_h_spark.py
index 0fcb4456..8a36d5ab 100644
--- a/plugin_tests/h_spark/test_h_spark.py
+++ b/plugin_tests/h_spark/test_h_spark.py
@@ -15,7 +15,6 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import sys
 
 import numpy as np
 import pandas as pd
@@ -356,7 +355,6 @@ def test_get_spark_type_basic_types(return_type, 
expected_spark_type):
 
 
 # 2. Lists of basic Python types
[email protected](sys.version_info < (3, 9), reason="requires python 3.9 or 
higher")
 @pytest.mark.parametrize(
     "return_type,expected_spark_type",
     [
diff --git a/tests/function_modifiers/test_adapters.py 
b/tests/function_modifiers/test_adapters.py
index e95311a4..56052d5b 100644
--- a/tests/function_modifiers/test_adapters.py
+++ b/tests/function_modifiers/test_adapters.py
@@ -711,14 +711,8 @@ def test_load_from_with_multiple_inputs():
     assert len(fg) == 5
 
 
-import sys
-
-if sys.version_info >= (3, 9):
-    dict_ = dict
-    tuple_ = tuple
-else:
-    dict_ = Dict
-    tuple_ = Tuple
+dict_ = dict
+tuple_ = tuple
 
 
 # Mock functions for dataloader & datasaver testing
@@ -770,10 +764,6 @@ def test_dl_validate_incorrect_functions(func):
         dl.validate(func)
 
 
[email protected](
-    sys.version_info < (3, 9, 0),
-    reason="dataloader not guarenteed to work with subscripted tuples on 3.8",
-)
 def test_dl_validate_with_correct_function():
     dl = dataloader()
     try:
diff --git a/tests/function_modifiers/test_expanders.py 
b/tests/function_modifiers/test_expanders.py
index ee9d1b17..bbc2fef2 100644
--- a/tests/function_modifiers/test_expanders.py
+++ b/tests/function_modifiers/test_expanders.py
@@ -38,10 +38,6 @@ from hamilton.node import DependencyType
 
 # TODO: Move/refactor for more general use
 skipif = pytest.mark.skipif
-prior_to_py39 = {
-    "condition": sys.version_info < (3, 9, 0),
-    "reason": "Python 3.9+ required for this test",
-}
 prior_to_py311 = {
     "condition": sys.version_info < (3, 11, 0),
     "reason": "Python 3.11+ required for this test",
@@ -377,9 +373,9 @@ class MyDictInheritanceBadCase(TypedDict):
         ("MyDict", ()),
         ("MyDict", {"test2": str}),
         ("MyDictInheritance", {"test": InheritedObject}),
-        pytest.param("dict[str, int]", ("A", "B"), 
marks=skipif(**prior_to_py39)),
-        pytest.param("dict[str, int]", (["A", "B"]), 
marks=skipif(**prior_to_py39)),
-        pytest.param("dict", {"A": str, "B": int}, 
marks=skipif(**prior_to_py39)),
+        ("dict[str, int]", ("A", "B")),
+        ("dict[str, int]", (["A", "B"])),
+        ("dict", {"A": str, "B": int}),
     ],
 )
 def test_extract_fields_valid_annotations_for_inferred_types(return_type_str, 
fields):
@@ -408,10 +404,10 @@ def 
test_extract_fields_valid_annotations_for_inferred_types(return_type_str, fi
         ("pd.DataFrame", {"A": int}),
         ("MyDictBad", {"A": int}),
         ("MyDictInheritanceBadCase", {"A": SomeObject}),
-        pytest.param("dict", ("A", "B"), marks=skipif(**prior_to_py39)),
-        pytest.param("dict", (["A", "B"]), marks=skipif(**prior_to_py39)),
-        pytest.param("dict", (["A"]), marks=skipif(**prior_to_py39)),
-        pytest.param("dict", (["A", "B", "C"]), marks=skipif(**prior_to_py39)),
+        ("dict", ("A", "B")),
+        ("dict", (["A", "B"])),
+        ("dict", (["A"])),
+        ("dict", (["A", "B", "C"])),
     ],
 )
 def 
test_extract_fields_invalid_annotations_for_inferred_types(return_type_str, 
fields):
@@ -781,9 +777,9 @@ def test_unpack_fields_transform_on_indeterminate_tuple():
         ("Tuple[int, int]", ("A", "B")),
         ("Tuple[int, int, str]", ("A", "B", "C")),
         ("Tuple[int, ...]", ("A", "B")),
-        pytest.param("tuple[int, int]", ("A", "B"), 
marks=skipif(**prior_to_py39)),
-        pytest.param("tuple[int, int, str]", ("A", "B", "C"), 
marks=skipif(**prior_to_py39)),
-        pytest.param("tuple[int, ...]", ("A", "B"), 
marks=skipif(**prior_to_py39)),
+        ("tuple[int, int]", ("A", "B")),
+        ("tuple[int, int, str]", ("A", "B", "C")),
+        ("tuple[int, ...]", ("A", "B")),
     ],
 )
 def test_unpack_fields_valid_type_annotations(return_type_str, fields):
@@ -807,11 +803,11 @@ def 
test_unpack_fields_valid_type_annotations(return_type_str, fields):
         pytest.param("Tuple[...]", ("A", "B", "C"), 
marks=skipif(**prior_to_py311)),
         pytest.param("Tuple[int, int, ...]", ("A", "B"), 
marks=skipif(**prior_to_py311)),
         pytest.param("Tuple[..., int, int]", ("A", "B"), 
marks=skipif(**prior_to_py311)),
-        pytest.param("tuple", ("A",), marks=skipif(**prior_to_py39)),
-        pytest.param("tuple[int, int]", ("A", "B", "C"), 
marks=skipif(**prior_to_py39)),
-        pytest.param("tuple[...]", ("A", "B", "C"), 
marks=skipif(**prior_to_py39)),
-        pytest.param("tuple[int, int, ...]", ("A", "B"), 
marks=skipif(**prior_to_py39)),
-        pytest.param("tuple[..., int, int]", ("A", "B"), 
marks=skipif(**prior_to_py39)),
+        ("tuple", ("A",)),
+        ("tuple[int, int]", ("A", "B", "C")),
+        ("tuple[...]", ("A", "B", "C")),
+        ("tuple[int, int, ...]", ("A", "B")),
+        ("tuple[..., int, int]", ("A", "B")),
     ],
 )
 def test_unpack_fields_invalid_type_annotations(return_type_str, fields):
@@ -1112,9 +1108,7 @@ def test_inject_misconfigured_param_type_dict():
         annotation.validate(foo)
 
 
[email protected](**prior_to_py39)
 def test_inject_misconfigured_param_untyped_generic_list():
-    # NOTE: Stricter typing rules for generics were introduced in Python 3.9.
     def foo(x: List) -> int:
         return sum(x)
 
diff --git a/tests/integrations/pandera/test_pandera_data_quality.py 
b/tests/integrations/pandera/test_pandera_data_quality.py
index ef346776..ac1a91ed 100644
--- a/tests/integrations/pandera/test_pandera_data_quality.py
+++ b/tests/integrations/pandera/test_pandera_data_quality.py
@@ -15,7 +15,6 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import sys
 
 import dask.dataframe as dd
 import numpy as np
@@ -165,7 +164,6 @@ def test_pandera_decorator_fails_without_annotation():
         h_pandera.check_output().get_validators(n)
 
 
[email protected](sys.version_info < (3, 9), reason="requires python3.9 or 
higher")
 def test_pandera_decorator_dask_df():
     """Validates that the function can be annotated with a dask dataframe type 
it'll work appropriately.
 
@@ -216,7 +214,6 @@ def test_pandera_decorator_dask_df():
     assert not result_success.passes
 
 
[email protected](sys.version_info < (3, 9), reason="requires python3.9 or 
higher")
 @pytest.mark.xfail(
     reason="some weird import issue leads to key error in pandera, can't 
recreate outside of the series decorator"
 )
diff --git a/tests/plugins/test_dlt_extensions.py 
b/tests/plugins/test_dlt_extensions.py
index fa899fc7..96795aae 100644
--- a/tests/plugins/test_dlt_extensions.py
+++ b/tests/plugins/test_dlt_extensions.py
@@ -15,24 +15,15 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import sys
 from pathlib import Path
 
-import pytest
-
-PY38_OR_BELOW = sys.version_info < (3, 9)
-pytestmark = pytest.mark.skipif(
-    PY38_OR_BELOW, reason="Breaks for python 3.8 and below due to backports 
dependency."
-)
-
-if not PY38_OR_BELOW:
-    import dlt
-    from dlt.destinations import filesystem
-
-    from hamilton.plugins.dlt_extensions import DltDestinationSaver, 
DltResourceLoader
-
+import dlt
 import pandas as pd
 import pyarrow as pa
+import pytest
+from dlt.destinations import filesystem
+
+from hamilton.plugins.dlt_extensions import DltDestinationSaver, 
DltResourceLoader
 
 
 def pandas_df():
diff --git a/tests/plugins/test_huggingface_extensions.py 
b/tests/plugins/test_huggingface_extensions.py
index 664ab122..7b52446a 100644
--- a/tests/plugins/test_huggingface_extensions.py
+++ b/tests/plugins/test_huggingface_extensions.py
@@ -16,11 +16,9 @@
 # under the License.
 
 import pathlib
-import sys
 
 import lancedb
 import numpy as np
-import pytest
 from datasets import Dataset, DatasetDict
 
 from hamilton.plugins import huggingface_extensions
@@ -59,7 +57,6 @@ def test_hfds_parquet_saver(tmp_path: pathlib.Path):
     assert saver.applies_to(Dataset)
 
 
[email protected](sys.version_info < (3, 9), reason="requires Python 3.9 or 
higher")
 def test_hfds_lancedb_saver(tmp_path: pathlib.Path):
     db_client = lancedb.connect(tmp_path / "lancedb")
     saver = huggingface_extensions.HuggingFaceDSLanceDBSaver(db_client, 
"test_table")
diff --git a/tests/plugins/test_mlflow_extension.py 
b/tests/plugins/test_mlflow_extension.py
index 8b89212b..d98d3ed4 100644
--- a/tests/plugins/test_mlflow_extension.py
+++ b/tests/plugins/test_mlflow_extension.py
@@ -15,27 +15,16 @@
 # specific language governing permissions and limitations
 # under the License.
 
-import sys
 from pathlib import Path
 
-import pytest
-
-PY38_OR_BELOW = sys.version_info < (3, 9)
-pytestmark = pytest.mark.skipif(
-    PY38_OR_BELOW, reason="Breaks for python 3.8 and below due to backports 
dependency."
-)
-
-if not PY38_OR_BELOW:
-    import mlflow
-
-    from hamilton.plugins.mlflow_extensions import MLFlowModelLoader, 
MLFlowModelSaver
-
+import mlflow
 import numpy as np
 import pytest
 from sklearn.base import BaseEstimator
 from sklearn.linear_model import LinearRegression
 
 from hamilton.io.materialization import from_, to
+from hamilton.plugins.mlflow_extensions import MLFlowModelLoader, 
MLFlowModelSaver
 
 # TODO move these tests to `plugin_tests` because the required read-writes can 
get
 # complicated and tests are time consuming.
diff --git a/tests/resources/nodes_with_future_annotation.py 
b/tests/resources/nodes_with_future_annotation.py
index f74ede77..c577e80d 100644
--- a/tests/resources/nodes_with_future_annotation.py
+++ b/tests/resources/nodes_with_future_annotation.py
@@ -17,17 +17,11 @@
 
 from __future__ import annotations
 
-import sys
-from typing import List, Tuple
-
 from hamilton.function_modifiers import dataloader
 from hamilton.htypes import Collect, Parallelizable
 
 """Tests future annotations with common node types"""
 
-tuple_ = Tuple if sys.version_info < (3, 9, 0) else tuple
-list_ = List if sys.version_info < (3, 9, 0) else list
-
 
 def parallelized() -> Parallelizable[int]:
     yield 1
@@ -44,6 +38,6 @@ def collected(standard: Collect[int]) -> int:
 
 
 @dataloader()
-def sample_dataloader() -> tuple_[list_[str], dict]:
+def sample_dataloader() -> tuple[list[str], dict]:
     """Grouping here as the rest test annotations"""
     return ["a", "b", "c"], {}
diff --git a/tests/test_graph_types.py b/tests/test_graph_types.py
index a152716f..a289a266 100644
--- a/tests/test_graph_types.py
+++ b/tests/test_graph_types.py
@@ -216,28 +216,24 @@ def test_json_serializable_dict():
             json.dumps(hamilton_node.as_dict())
 
 
[email protected](sys.version_info < (3, 9), reason="requires Python 3.9 or 
higher")
 def test_remove_docstring(func_a: str, func_a_docstring: str):
     func_a_no_whitespace = func_a.strip()
     stripped = graph_types._remove_docs_and_comments(func_a_docstring)
     assert func_a_no_whitespace == stripped
 
 
[email protected](sys.version_info < (3, 9), reason="requires Python 3.9 or 
higher")
 def test_remove_multiline(func_a: str, func_a_multiline: str):
     func_a_no_whitespace = func_a.strip()
     stripped = graph_types._remove_docs_and_comments(func_a_multiline)
     assert func_a_no_whitespace == stripped
 
 
[email protected](sys.version_info < (3, 9), reason="requires Python 3.9 or 
higher")
 def test_remove_comment(func_a: str, func_a_comment: str):
     func_a_no_whitespace = func_a.strip()
     stripped = graph_types._remove_docs_and_comments(func_a_comment)
     assert func_a_no_whitespace == stripped
 
 
[email protected](sys.version_info < (3, 9), reason="requires Python 3.9 or 
higher")
 @pytest.mark.parametrize("strip", [True, False])
 def test_different_hash_function_body(func_a: str, func_a_body: str, strip: 
bool):
     """Gives different hash for different function body"""
@@ -246,7 +242,6 @@ def test_different_hash_function_body(func_a: str, 
func_a_body: str, strip: bool
     assert func_a_hash != func_a_body_hash
 
 
[email protected](sys.version_info < (3, 9), reason="requires Python 3.9 or 
higher")
 @pytest.mark.parametrize("strip", [True, False])
 def test_different_hash_docstring(func_a: str, func_a_docstring: str, strip: 
bool):
     """Same hash if strip docstring, else different hash"""
@@ -255,7 +250,6 @@ def test_different_hash_docstring(func_a: str, 
func_a_docstring: str, strip: boo
     assert (func_a_hash == func_a_docstring_hash) is (True if strip else False)
 
 
[email protected](sys.version_info < (3, 9), reason="requires Python 3.9 or 
higher")
 @pytest.mark.parametrize("strip", [True, False])
 def test_different_hash_multiline_docstring(func_a: str, func_a_multiline: 
str, strip: bool):
     """Same hash if strip multiline docstring, else different hash"""
@@ -264,7 +258,6 @@ def test_different_hash_multiline_docstring(func_a: str, 
func_a_multiline: str,
     assert (func_a_hash == func_a_multiline_hash) is (True if strip else False)
 
 
[email protected](sys.version_info < (3, 9), reason="requires Python 3.9 or 
higher")
 @pytest.mark.parametrize("strip", [True, False])
 def test_different_hash_comment(func_a: str, func_a_comment: str, strip: bool):
     """Same hash if strip comment, else different hash"""
diff --git a/tests/test_type_utils.py b/tests/test_type_utils.py
index a49e47e1..5ff1cb13 100644
--- a/tests/test_type_utils.py
+++ b/tests/test_type_utils.py
@@ -16,9 +16,8 @@
 # under the License.
 
 import collections
-import sys
 import typing
-from typing import Any, Dict, List, Union
+from typing import Annotated, Any, Dict, List, Union
 
 import pandas as pd
 import pytest
@@ -26,11 +25,6 @@ import pytest
 from hamilton import htypes
 from hamilton.htypes import check_instance
 
-if sys.version_info >= (3, 9):
-    from typing import Annotated
-else:
-    from typing_extensions import Annotated
-
 
 class X:
     pass
@@ -308,24 +302,12 @@ def test_check_input_type_match(node_type, input_value):
     assert actual is True
 
 
-# We cannot parameterize this as the parameterization cannot be
-# included if the
[email protected](
-    sys.version_info < (3, 9, 0),
-    reason="Type hinting generics in standard collections " "is only supported 
in 3.9+",
-)
 def test_check_input_types_subscripted_generics_dict_str_Any():
     """Tests check_input_type of SimplePythonDataFrameGraphAdapter"""
     actual = htypes.check_input_type(dict[str, typing.Any], {})
     assert actual is True
 
 
-# We cannot parameterize this as the parameterization cannot be
-# included if the
[email protected](
-    sys.version_info < (3, 9, 0),
-    reason="Type hinting generics in standard collections " "is only supported 
in 3.9+",
-)
 def test_check_input_types_subscripted_generics_list_Any():
     """Tests check_input_type of SimplePythonDataFrameGraphAdapter"""
     actual = htypes.check_input_type(list[typing.Any], [])
@@ -340,10 +322,8 @@ def test_check_instance_with_non_generic_type():
 def test_check_instance_with_generic_list_type():
     assert check_instance([1, 2, 3], List[int])
     assert not check_instance([1, 2, "3"], List[int])
-    if sys.version_info >= (3, 9):
-        # skip 3.8 -- not worth fixing
-        assert check_instance([1, 2, 3], List)
-        assert check_instance([1, 2, "3"], List)
+    assert check_instance([1, 2, 3], List)
+    assert check_instance([1, 2, "3"], List)
 
 
 def test_check_instance_with_list_type():
@@ -354,10 +334,8 @@ def test_check_instance_with_list_type():
 def test_check_instance_with_generic_dict_type():
     assert check_instance({"key1": 1, "key2": 2}, Dict[str, int])
     assert not check_instance({"key1": 1, "key2": "2"}, Dict[str, int])
-    if sys.version_info >= (3, 9):
-        # skip 3.8 -- not worth fixing
-        assert check_instance({"key1": 1, "key2": 2}, Dict)
-        assert check_instance({"key1": 1, "key2": "2"}, Dict)
+    assert check_instance({"key1": 1, "key2": 2}, Dict)
+    assert check_instance({"key1": 1, "key2": "2"}, Dict)
 
 
 def test_check_instance_with_dict_type():
@@ -391,7 +369,6 @@ def test_check_instance_with_union_type():
     assert not check_instance({"key1": 1, "key2": 2}, Union[int, str])
 
 
[email protected](sys.version_info < (3, 9), reason="requires python 3.9 or 
higher")
 def test_check_instance_with_union_type_and_literal():
     from typing import Literal
 
@@ -400,7 +377,6 @@ def test_check_instance_with_union_type_and_literal():
     assert not check_instance("c", Union[Literal["a"], Literal["b"]])
 
 
[email protected](sys.version_info < (3, 9), reason="requires python 3.9 or 
higher")
 def test_non_generic_dict_and_list():
     assert check_instance([1, 2, 3], list[int])
     assert not check_instance([1, 2, "3"], list[int])

Reply via email to