diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 25066be1..a7b704f8 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -17,18 +17,18 @@ jobs: fail-fast: false # need to see which ones fail matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python: ["3.7", "3.8", "3.9", "3.10"] + python: ["3.8", "3.9", "3.10"] # this is to make the CI run on different sklearn versions include: - - python: "3.7" - sklearn_version: "0.24.0" - python: "3.8" - sklearn_version: "1.0.0" + sklearn_version: "1.0" + # TODO: add sklearn 1.1 when we add 3.11 support - python: "3.9" - sklearn_version: "1.1.0" + sklearn_version: "1.2" - python: "3.10" sklearn_version: "nightly" + # Timeout: https://stackoverflow.com/a/59076067/4521646 timeout-minutes: 15 diff --git a/pyproject.toml b/pyproject.toml index b19d1aa4..c957920e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.black] line-length = 88 -target_version = ['py37', 'py38', 'py39', 'py310'] +target_version = ['py38', 'py39', 'py310', 'py311'] preview = true [tool.isort] @@ -17,6 +17,8 @@ filterwarnings = [ "ignore:The \\'sym_pos\\' keyword is deprecated and should be replaced:DeprecationWarning", # https://github.com/scikit-learn/scikit-learn/pull/23633 "ignore:Unlike other reduction functions:FutureWarning", + # https://github.com/scikit-learn/scikit-learn/pull/25157 + "ignore:\\w+ is deprecated. Use files\\(\\) instead:DeprecationWarning" ] markers = [ "network: marks tests as requiring internet (deselect with '-m \"not network\"')", diff --git a/setup.py b/setup.py index df25333b..2262dcea 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ # This is a bit (!) hackish: we are setting a global variable so that the # main modelcard __init__ can detect if it is being loaded by the setup # routine, to avoid attempting to load components. -builtins.__SKOPS_SETUP__ = True +builtins.__SKOPS_SETUP__ = True # type: ignore import skops # noqa @@ -58,13 +58,12 @@ def setup_package(): "Operating System :: Unix", "Operating System :: MacOS", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: Implementation :: CPython", ], - python_requires=">=3.7", + python_requires=">=3.8", install_requires=min_deps.tag_to_packages["install"], extras_require={ "docs": min_deps.tag_to_packages["docs"], diff --git a/skops/_min_dependencies.py b/skops/_min_dependencies.py index a60579c4..35219a12 100644 --- a/skops/_min_dependencies.py +++ b/skops/_min_dependencies.py @@ -26,7 +26,6 @@ "sphinx-issues": ("1.2.0", "docs", None), "matplotlib": ("3.3", "docs, tests", None), "pandas": ("1", "docs, tests", None), - "typing_extensions": ("3.7", "install", "python_full_version < '3.8'"), # required for persistence tests of external libraries "lightgbm": ("3", "tests", None), "xgboost": ("1.6", "tests", None), diff --git a/skops/hub_utils/_hf_hub.py b/skops/hub_utils/_hf_hub.py index 67d982a9..d76e1de3 100644 --- a/skops/hub_utils/_hf_hub.py +++ b/skops/hub_utils/_hf_hub.py @@ -9,13 +9,11 @@ import os import shutil from pathlib import Path -from typing import Any, List, MutableMapping, Optional, Union +from typing import Any, List, Literal, MutableMapping, Optional, Union import numpy as np from huggingface_hub import HfApi, InferenceApi, snapshot_download -from ..utils.fixes import Literal - SUPPORTED_TASKS = [ "tabular-classification", "tabular-regression", diff --git a/skops/hub_utils/tests/test_hf_hub.py b/skops/hub_utils/tests/test_hf_hub.py index ff2cf781..c4db9aee 100644 --- a/skops/hub_utils/tests/test_hf_hub.py +++ b/skops/hub_utils/tests/test_hf_hub.py @@ -5,6 +5,7 @@ import shutil import tempfile import warnings +from importlib import metadata from pathlib import Path from uuid import uuid4 @@ -37,7 +38,6 @@ ) from skops.hub_utils.tests.common import HF_HUB_TOKEN from skops.io import dump -from skops.utils.fixes import metadata, path_unlink iris = load_iris(as_frame=True, return_X_y=False) diabetes = load_diabetes(as_frame=True, return_X_y=False) @@ -91,7 +91,7 @@ def classifier(repo_path, config_json): dump(clf, path) yield path finally: - path_unlink(path, missing_ok=True) + path.unlink(missing_ok=True) CONFIG = { @@ -118,7 +118,7 @@ def config_json(repo_path, request): json.dump(CONFIG[request.param], f) yield path, request.param finally: - path_unlink(path, missing_ok=True) + path.unlink(missing_ok=True) def test_validate_format(classifier): @@ -337,7 +337,7 @@ def test_model_file_does_not_exist_raises(repo_path, config_json): task="tabular-classification", data=iris.data, ) - path_unlink(model_path, missing_ok=True) + model_path.unlink(missing_ok=True) def test_init_empty_model_file_errors(repo_path, config_json): @@ -360,7 +360,7 @@ def test_init_empty_model_file_errors(repo_path, config_json): task="tabular-classification", data=iris.data, ) - path_unlink(model_path, missing_ok=True) + model_path.unlink(missing_ok=True) @pytest.mark.network @@ -501,7 +501,7 @@ def test_inference( # cleanup client.delete_repo(repo_id=repo_id, token=HF_HUB_TOKEN) - path_unlink(model_path, missing_ok=True) + model_path.unlink(missing_ok=True) assert np.allclose(output, y_pred) diff --git a/skops/io/_audit.py b/skops/io/_audit.py index cb202cde..1e379308 100644 --- a/skops/io/_audit.py +++ b/skops/io/_audit.py @@ -2,9 +2,8 @@ import io from contextlib import contextmanager -from typing import Any, Generator, Sequence, Type, Union +from typing import Any, Generator, Literal, Sequence, Type, Union -from ..utils.fixes import Literal from ._trusted_types import PRIMITIVE_TYPE_NAMES from ._utils import LoadContext, get_module, get_type_paths from .exceptions import UntrustedTypesFoundException diff --git a/skops/utils/fixes.py b/skops/utils/fixes.py deleted file mode 100644 index e9d83558..00000000 --- a/skops/utils/fixes.py +++ /dev/null @@ -1,57 +0,0 @@ -# This file includes fixes which are usually required to handle multiple -# versions of a dependency. - -import sys -from contextlib import suppress -from pathlib import Path - -if sys.version_info >= (3, 8): - # py>=3.8 - from importlib import metadata # noqa -else: - # older pythons - import importlib_metadata as metadata # noqa - -if sys.version_info >= (3, 8): - # py>=3.8 - from typing import Literal # noqa -else: - # older pythons, this requires typing_extensions to be installed. - # if you're removing this, you should also remove the dependency from - # _min_dependencies.py - from typing_extensions import Literal # noqa - - -def path_unlink(path: Path, missing_ok: bool = False) -> None: - """Remove this file or symbolic link - - Parameters - ---------- - path : pathlib.Path - Path to the file to be removed - - missing_ok : bool (default=False) - If False, ``FileNotFoundError`` is raised if the path does not exist. If - True, ``FileNotFoundError`` exceptions will be ignored (same behavior as - the POSIX ``rm -f`` command). - - Raises - ------ - FileNotFoundError - Is raised if ``missing_ok`` is False and the file is missing. - - """ - # Python 3.7 does not support the missing_ok argument. - # One we move to Python >= 3.8, this function can just call - # Path.unlink(missing_ok) - if not missing_ok: # default behavior - path.unlink() - return - - if sys.version_info >= (3, 8): - path.unlink(missing_ok=missing_ok) - return - - # for Python 3.7, just catch the error - with suppress(FileNotFoundError): - path.unlink() diff --git a/skops/utils/tests/test_fixes.py b/skops/utils/tests/test_fixes.py deleted file mode 100644 index 2a1d9991..00000000 --- a/skops/utils/tests/test_fixes.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Tests for skops.utils.fixes.py""" - -import tempfile -from pathlib import Path - -import pytest - - -class TestPathUnlink: - @pytest.fixture(scope="class") - def path_unlink(self): - from skops.utils.fixes import path_unlink - - return path_unlink - - @pytest.fixture(scope="class") - def tempdir(self): - with tempfile.TemporaryDirectory(prefix="skops-test") as directory: - yield Path(directory) - - def test_path_unlink_file_exists(self, path_unlink, tempdir): - path = tempdir / "some-file" - path.touch() - assert path.exists() - - path_unlink(path) - assert not path.exists() - - def test_path_unlink_file_does_not_exist_raises(self, path_unlink, tempdir): - path = tempdir / "some-file" - assert not path.exists() - - with pytest.raises(FileNotFoundError): - path_unlink(path) - - def test_path_unlink_file_does_not_missing_ok(self, path_unlink, tempdir): - path = tempdir / "some-file" - assert not path.exists() - # does not raise an error - path_unlink(path, missing_ok=True)