From 10f13e5912496c7e652adf8a92468ba2fbfb1be6 Mon Sep 17 00:00:00 2001 From: Marcelo Duarte Trevisani Date: Wed, 13 Nov 2024 08:19:32 +0000 Subject: [PATCH] Remove support for python 3.8 (#579) * Remove support for python 3.8 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update type annotations to Python 3.9+ syntax Replace legacy type annotations with Python 3.9+ syntax for improved readability and conciseness. Additionally, update the pre-commit hook version for ruff to v0.7.3 for linting purposes. Signed-off-by: Marcelo Trevisani * Update Python version in GHA workflows Set Sphinx workflow to use Python 3.12 explicitly and removed testing for Python 3.9 in the tests workflow. Signed-off-by: Marcelo Trevisani * Update CLIConfig type hint for singleton instance Replaced the type hint for the `__instance` attribute in `CLIConfig` from `"CLIConfig"` to `Self` to improve type checking and readability. Signed-off-by: Marcelo Trevisani * Handle missing 'Self' in typing import Added a fallback mechanism to import 'TypeVar' and define 'Self' if 'Self' is not available in the typing module. This ensures compatibility with older Python versions that do not support 'Self'. Signed-off-by: Marcelo Trevisani --------- Signed-off-by: Marcelo Trevisani Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/sphinx.yml | 2 +- .github/workflows/tests.yml | 2 +- .pre-commit-config.yaml | 2 +- grayskull/base/github.py | 4 ++-- grayskull/base/track_packages.py | 12 +++++----- grayskull/cli/__init__.py | 9 ++++++-- grayskull/cli/parser.py | 3 +-- grayskull/cli/stdout.py | 3 +-- grayskull/config.py | 26 +++++++++++----------- grayskull/license/data/__init__.py | 3 +-- grayskull/strategy/parse_poetry_version.py | 5 ++--- grayskull/strategy/py_base.py | 5 ++--- grayskull/strategy/py_toml.py | 4 ++-- grayskull/strategy/pypi.py | 3 ++- grayskull/utils.py | 12 +++++----- pyproject.toml | 2 +- tests/license/test_discovery.py | 3 +-- 17 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index 7f3b270a9..cce1c4421 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: "3.x" + python-version: "3.12" architecture: "x64" cache: "pip" - name: Build Documentation diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2d5929313..0b866ec83 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - py_ver: ["3.8", "3.9", "3.10", "3.11", "3.12"] + py_ver: ["3.10", "3.11", "3.12"] env: OS: ${{ matrix.os }} PYTHON: ${{ matrix.py_ver }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e5ea5a878..223475620 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - id: debug-statements language_version: python3 - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.2 + rev: v0.7.3 hooks: # Run the linter - id: ruff diff --git a/grayskull/base/github.py b/grayskull/base/github.py index 34683416f..5de68f7e5 100644 --- a/grayskull/base/github.py +++ b/grayskull/base/github.py @@ -1,6 +1,6 @@ import logging import subprocess -from typing import Any, Tuple, Union +from typing import Any, Union from urllib.parse import urlparse, urlunparse import requests @@ -88,7 +88,7 @@ def closest_match(tag): def handle_gh_version( name: str, version: str, url: str, tag: str -) -> Tuple[Union[str, Any], Any, Any]: +) -> tuple[Union[str, Any], Any, Any]: """Method responsible for handling the version of the GitHub package. If version is specified, gets the closest tag in the repo. If not, gets the latest version. diff --git a/grayskull/base/track_packages.py b/grayskull/base/track_packages.py index 86ade424f..340647b23 100644 --- a/grayskull/base/track_packages.py +++ b/grayskull/base/track_packages.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from functools import lru_cache from pathlib import Path -from typing import Dict, List, Optional, Tuple, Union +from typing import Union from pkg_resources import parse_version # noqa from ruamel.yaml import YAML @@ -33,8 +33,8 @@ def track_package(pkg_name: str, config_file: Union[Path, str]) -> ConfigPkg: def solve_list_pkg_name( - list_pkg: List[str], config_file: Union[Path, str] -) -> List[str]: + list_pkg: list[str], config_file: Union[Path, str] +) -> list[str]: re_norm = re.compile(r",\s+") return [re_norm.sub(",", solve_pkg_name(pkg, config_file)) for pkg in list_pkg] @@ -51,7 +51,7 @@ def solve_pkg_name(pkg: str, config_file: Union[Path, str]) -> str: @lru_cache(maxsize=5) -def _get_track_info_from_file(config_file: Union[Path, str]) -> Dict: +def _get_track_info_from_file(config_file: Union[Path, str]) -> dict: yaml = YAML() with open(config_file, encoding="utf_8") as yaml_file: return yaml.load(yaml_file) @@ -72,7 +72,7 @@ def solve_version_delimiter(delimiter_exp: str, pkg_cfg: ConfigPkg) -> str: return ",".join(result) -def _version_solver(list_exp: List, pkg_cfg: ConfigPkg) -> List: +def _version_solver(list_exp: list, pkg_cfg: ConfigPkg) -> list: result = [] for op, version in list_exp: if op in ["==", ""]: @@ -98,7 +98,7 @@ def _version_solver(list_exp: List, pkg_cfg: ConfigPkg) -> List: return result -def parse_delimiter(delimiter_exp: str) -> List[Optional[Tuple[str, str]]]: +def parse_delimiter(delimiter_exp: str) -> list[tuple[str, str] | None]: re_search = re.compile(r"([!=><]+)\s*([a-z0-9\-\.\_]+)", re.IGNORECASE) result = re_search.findall(delimiter_exp) if not result: diff --git a/grayskull/cli/__init__.py b/grayskull/cli/__init__.py index 65a4d1d69..faaad5935 100644 --- a/grayskull/cli/__init__.py +++ b/grayskull/cli/__init__.py @@ -1,4 +1,9 @@ -from typing import Optional +try: + from typing import Self +except ImportError: + from typing import TypeVar + + Self = TypeVar("Self", bound="CLIConfig") import progressbar @@ -13,7 +18,7 @@ class CLIConfig: - __instance: Optional["CLIConfig"] = None + __instance: Self | None = None def __new__(cls, stdout: bool = False, list_missing_deps: bool = False): if CLIConfig.__instance is None: diff --git a/grayskull/cli/parser.py b/grayskull/cli/parser.py index 7d2dc2e85..80764ea2f 100644 --- a/grayskull/cli/parser.py +++ b/grayskull/cli/parser.py @@ -1,13 +1,12 @@ import re from pathlib import Path -from typing import Optional, Tuple from grayskull.utils import origin_is_github, origin_is_local_sdist def parse_pkg_name_version( pkg_name: str, -) -> Tuple[str, str, Optional[str]]: +) -> tuple[str, str, str | None]: origin = "" if origin_is_local_sdist(pkg_name): # Try to get package name and version from sdist archive diff --git a/grayskull/cli/stdout.py b/grayskull/cli/stdout.py index 17a653328..a143fa7d8 100644 --- a/grayskull/cli/stdout.py +++ b/grayskull/cli/stdout.py @@ -1,7 +1,6 @@ import re from contextlib import contextmanager from copy import deepcopy -from typing import Dict, List import progressbar from colorama import Fore, Style @@ -62,7 +61,7 @@ def update(self, *args, **kargs): def print_requirements( - requirements: Dict[str, List[str]], optional_requirements: Dict[str, List[str]] + requirements: dict[str, list[str]], optional_requirements: dict[str, list[str]] ) -> set: all_missing_deps = set() re_search = re.compile(r"^\s*([a-z0-9\.\-\_]+)(.*)", re.IGNORECASE | re.DOTALL) diff --git a/grayskull/config.py b/grayskull/config.py index c278f9634..31915da5f 100644 --- a/grayskull/config.py +++ b/grayskull/config.py @@ -1,5 +1,5 @@ +from collections.abc import Iterable from dataclasses import dataclass, field -from typing import Dict, Iterable, List, Optional, Tuple from grayskull.cli.parser import parse_pkg_name_version from grayskull.utils import PyVer @@ -12,8 +12,8 @@ class Configuration: name: str version: str = "" - files_to_copy: List = field(default_factory=list) - supported_py: List[PyVer] = field( + files_to_copy: list = field(default_factory=list) + supported_py: list[PyVer] = field( default_factory=lambda: [ PyVer(2, 7), PyVer(3, 6), @@ -25,7 +25,7 @@ class Configuration: PyVer(3, 12), ] ) - py_cf_supported: List[PyVer] = field( + py_cf_supported: list[PyVer] = field( default_factory=lambda: [ PyVer(3, 7), PyVer(3, 8), @@ -36,27 +36,27 @@ class Configuration: ] ) is_strict_cf: bool = False - pkg_need_c_compiler: Tuple = field( + pkg_need_c_compiler: tuple = field( default_factory=lambda: ("cython", "cython-blis", "blis") ) - pkg_need_cxx_compiler: Tuple = field(default_factory=lambda: ("pybind11",)) + pkg_need_cxx_compiler: tuple = field(default_factory=lambda: ("pybind11",)) url_pypi: str = DEFAULT_PYPI_URL url_pypi_metadata: str = DEFAULT_PYPI_META_URL download: bool = False is_arch: bool = False - repo_github: Optional[str] = None + repo_github: str | None = None from_local_sdist: bool = False - local_sdist: Optional[str] = None + local_sdist: str | None = None missing_deps: set = field(default_factory=set) - extras_require_test: Optional[str] = None - github_release_tag: Optional[str] = None + extras_require_test: str | None = None + github_release_tag: str | None = None extras_require_include: Iterable[str] = tuple() extras_require_exclude: Iterable[str] = tuple() extras_require_all: bool = False extras_require_split: bool = False licence_exclude_folders: Iterable[str] = tuple() - def get_oldest_py3_version(self, list_py_ver: List[PyVer]) -> PyVer: + def get_oldest_py3_version(self, list_py_ver: list[PyVer]) -> PyVer: list_py_ver = sorted(list_py_ver) min_python_version = ( self.py_cf_supported[0] if self.is_strict_cf else PyVer(3, 0) @@ -67,8 +67,8 @@ def get_oldest_py3_version(self, list_py_ver: List[PyVer]) -> PyVer: return min_python_version def get_py_version_available( - self, req_python: List[Tuple[str, str, str]] - ) -> Dict[PyVer, bool]: + self, req_python: list[tuple[str, str, str]] + ) -> dict[PyVer, bool]: """Get the python version available given the requires python received :param req_python: Requires python diff --git a/grayskull/license/data/__init__.py b/grayskull/license/data/__init__.py index 16c587c61..1cb259843 100644 --- a/grayskull/license/data/__init__.py +++ b/grayskull/license/data/__init__.py @@ -1,10 +1,9 @@ import os from functools import lru_cache -from typing import List @lru_cache(maxsize=2) -def get_all_licenses() -> List: +def get_all_licenses() -> list: data_folder = os.path.dirname(__file__) all_licenses = [] for license_file in os.listdir(data_folder): diff --git a/grayskull/strategy/parse_poetry_version.py b/grayskull/strategy/parse_poetry_version.py index 72b2a6c6f..f5066dbad 100644 --- a/grayskull/strategy/parse_poetry_version.py +++ b/grayskull/strategy/parse_poetry_version.py @@ -1,5 +1,4 @@ import re -from typing import Dict, Optional import semver @@ -21,7 +20,7 @@ class InvalidVersion(BaseException): pass -def parse_version(version: str) -> Dict[str, Optional[int]]: +def parse_version(version: str) -> dict[str, int | None]: """ Parses a version string (not necessarily semver) to a dictionary with keys "major", "minor", and "patch". "minor" and "patch" are possibly None. @@ -45,7 +44,7 @@ def parse_version(version: str) -> Dict[str, Optional[int]]: } -def vdict_to_vinfo(version_dict: Dict[str, Optional[int]]) -> semver.VersionInfo: +def vdict_to_vinfo(version_dict: dict[str, int | None]) -> semver.VersionInfo: """ Coerces version dictionary to a semver.VersionInfo object. If minor or patch numbers are missing, 0 is substituted in their place. diff --git a/grayskull/strategy/py_base.py b/grayskull/strategy/py_base.py index 873c995ad..ea28ed649 100644 --- a/grayskull/strategy/py_base.py +++ b/grayskull/strategy/py_base.py @@ -6,14 +6,13 @@ import shutil import sys from collections import defaultdict -from contextlib import contextmanager +from contextlib import AbstractContextManager, contextmanager from copy import deepcopy from distutils import core from glob import glob from pathlib import Path from subprocess import check_output from tempfile import mkdtemp -from typing import ContextManager from urllib.parse import urlparse import requests @@ -324,7 +323,7 @@ def get_setup_cfg(source_path: str) -> dict: @contextmanager -def injection_distutils(folder: str) -> ContextManager[dict]: +def injection_distutils(folder: str) -> AbstractContextManager[dict]: """This is a bit of "dark magic", please don't do it at home. It is injecting code in the distutils.core.setup and replacing the setup function by the inner function __fake_distutils_setup. diff --git a/grayskull/strategy/py_toml.py b/grayskull/strategy/py_toml.py index 36fd6e006..2023a83af 100644 --- a/grayskull/strategy/py_toml.py +++ b/grayskull/strategy/py_toml.py @@ -2,7 +2,7 @@ from collections import defaultdict from functools import singledispatch from pathlib import Path -from typing import Tuple, Union +from typing import Union from grayskull.strategy.parse_poetry_version import encode_poetry_version from grayskull.utils import nested_dict @@ -37,7 +37,7 @@ def __get_constrained_dep_str(dep_spec: str, dep_name: str) -> str: return f"{dep_name} {conda_version}" -def encode_poetry_deps(poetry_deps: dict) -> Tuple[list, list]: +def encode_poetry_deps(poetry_deps: dict) -> tuple[list, list]: run = [] run_constrained = [] for dep_name, dep_spec in poetry_deps.items(): diff --git a/grayskull/strategy/pypi.py b/grayskull/strategy/pypi.py index d4ce0434b..c1cddcc6f 100644 --- a/grayskull/strategy/pypi.py +++ b/grayskull/strategy/pypi.py @@ -5,9 +5,10 @@ import logging import os import re +from collections.abc import Iterable, MutableMapping from pathlib import Path from tempfile import mkdtemp -from typing import Iterable, MutableMapping, TypedDict +from typing import TypedDict import requests from colorama import Fore diff --git a/grayskull/utils.py b/grayskull/utils.py index fa423ea56..e3d1dbf0a 100644 --- a/grayskull/utils.py +++ b/grayskull/utils.py @@ -9,7 +9,7 @@ from glob import glob from pathlib import Path from shutil import copyfile -from typing import Final, List, Optional, Union +from typing import Final, Union from ruamel.yaml import YAML from ruamel.yaml.comments import CommentedMap @@ -29,7 +29,7 @@ @lru_cache(maxsize=10) -def get_std_modules() -> List: +def get_std_modules() -> list: from stdlib_list import stdlib_list all_libs = set() @@ -65,7 +65,7 @@ def visit_ImportFrom(node): return modules -def get_vendored_dependencies(script_file: str) -> List: +def get_vendored_dependencies(script_file: str) -> list: """Get all third part dependencies which are being in use in the setup.py :param script_file: Path to the setup.py @@ -82,7 +82,7 @@ def get_vendored_dependencies(script_file: str) -> List: @lru_cache(maxsize=20) -def get_local_modules(sdist_folder: str) -> List: +def get_local_modules(sdist_folder: str) -> list: result = [] for py_file in glob(f"{sdist_folder}/*.py"): py_file = os.path.basename(py_file) @@ -120,7 +120,7 @@ def string_similarity(a, b): return SequenceMatcher(None, a, b).ratio() -def rm_duplicated_deps(all_requirements: Union[list, set, None]) -> Optional[list]: +def rm_duplicated_deps(all_requirements: Union[list, set, None]) -> list | None: if not all_requirements: return None # Keep track of requirements which have already been added to the list. @@ -157,7 +157,7 @@ def rm_duplicated_deps(all_requirements: Union[list, set, None]) -> Optional[lis return [re.sub(r"\s+(#)", " \\1", v.strip()) for v in new_reqs.values()] -def format_dependencies(all_dependencies: List, name: str) -> List: +def format_dependencies(all_dependencies: list, name: str) -> list: """Just format the given dependency to a string which is valid for the recipe diff --git a/pyproject.toml b/pyproject.toml index a60696274..55c66c63f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ readme = "README.md" keywords = ["conda"] license = { text = "Apache-2.0" } dynamic = ["version"] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "beautifulsoup4", "colorama", diff --git a/tests/license/test_discovery.py b/tests/license/test_discovery.py index d5d0615a9..fd1cb7077 100644 --- a/tests/license/test_discovery.py +++ b/tests/license/test_discovery.py @@ -1,5 +1,4 @@ import os -from typing import List from unittest.mock import patch import pytest @@ -29,7 +28,7 @@ def license_pytest_path(data_dir) -> str: @fixture -def spdx_org_license_mit() -> List: +def spdx_org_license_mit() -> list: return [ { "reference": "./MIT.html",