diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9c2413028..4ce430044 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,10 +19,10 @@ jobs: with: fetch-depth: 0 - - name: Set up Python 3.11 + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.12 - name: Install dependencies run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 70835d175..43d14ae39 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.8', '3.10', '3.11', '3.12'] + python-version: ['3.10', '3.11', '3.12', '3.13'] runs-on: ubuntu-latest @@ -32,7 +32,7 @@ jobs: - name: Setup Komodo run: | - pip install -U pip wheel setuptools + pip install -U pip pip install ".[dev]" - name: Unit tests @@ -48,13 +48,13 @@ jobs: run: | komodo-transpiler transpile \ --matrix-file="ci/${{env.RELEASE_NAME}}.yml" \ - --output ci --matrix-coordinates "{rhel: ['7'], py: ['${{matrix.python-version}}']}" + --output ci --matrix-coordinates "{rhel: ['8'], py: ['${{matrix.python-version}}']}" - name: Full integration test run: | py_version_number=$(echo "${{ matrix.python-version }}" | sed 's/\.//g') kmd \ - ci/${{env.RELEASE_NAME}}-py$py_version_number-rhel7.yml \ + ci/${{env.RELEASE_NAME}}-py$py_version_number-rhel8.yml \ ci/${{env.REPOSITORY}} \ --workspace ${{ runner.temp }}/kmd-ws \ --prefix ${{ runner.temp }}/prefix \ diff --git a/ci/release-matrix.yml b/ci/release-matrix.yml index c80e6a745..17657bda9 100644 --- a/ci/release-matrix.yml +++ b/ci/release-matrix.yml @@ -1,9 +1,8 @@ numpy: - py38: 1.23.5 - py39: 1.23.5 py310: 1.23.5 py311: 1.23.5 py312: 1.26.4 + py313: 2.2.0 python: 3-builtin -setuptools: 69.0.3 -wheel: 0.42.0 +setuptools: 75.6.0 +wheel: 0.45.1 diff --git a/ci/repository.yml b/ci/repository.yml index 4385d3c55..ec1496265 100644 --- a/ci/repository.yml +++ b/ci/repository.yml @@ -1,4 +1,8 @@ numpy: + 2.2.0: + source: pypi + make: pip + maintainer: ci 1.26.4: source: pypi make: pip @@ -16,13 +20,13 @@ python: makeopts: --virtualenv-interpreter python3 setuptools: - 69.0.3: + 75.6.0: source: pypi make: pip maintainer: ci wheel: - 0.42.0: + 0.45.1: source: pypi make: pip maintainer: ci diff --git a/komodo/build.py b/komodo/build.py index 1b9af1b34..20e18fefe 100644 --- a/komodo/build.py +++ b/komodo/build.py @@ -5,7 +5,6 @@ import re import stat from pathlib import Path -from typing import Dict import requests @@ -184,7 +183,7 @@ def pypaths(prefix, version): def make( - pkgs: Dict[str, str], + pkgs: dict[str, str], repo, data, prefix, @@ -228,7 +227,7 @@ def make( def resolve(input_str): return input_str.replace("$(prefix)", prefix) - for package_name, path in zip(pkgorder, pkgpaths): + for package_name, path in zip(pkgorder, pkgpaths, strict=False): ver = pkgs[package_name] current = repo[package_name][ver] make = current["make"] diff --git a/komodo/check_unused_package.py b/komodo/check_unused_package.py index e01c34762..077cee04d 100644 --- a/komodo/check_unused_package.py +++ b/komodo/check_unused_package.py @@ -2,7 +2,6 @@ import argparse import os import sys -from typing import Dict import yaml @@ -16,7 +15,7 @@ def check_for_unused_package( release_file: ReleaseFile, package_status_file: str, repository: RepositoryFile, - builtin_python_versions: Dict[str, str], + builtin_python_versions: dict[str, str], ): package_status = load_yaml(package_status_file) public_and_plugin_packages = [ diff --git a/komodo/check_up_to_date_pypi.py b/komodo/check_up_to_date_pypi.py index db22d7660..76f328a05 100644 --- a/komodo/check_up_to_date_pypi.py +++ b/komodo/check_up_to_date_pypi.py @@ -3,7 +3,6 @@ import pathlib import re import sys -from typing import Dict, List import requests import ruamel.yaml @@ -57,7 +56,7 @@ def get_python_requirement(sources: list): return "" -def is_platform_compatible(build_info: List[Dict], platform: str) -> bool: +def is_platform_compatible(build_info: list[dict], platform: str) -> bool: if platform == "darwin": platform = "macos" if platform == "linux2": @@ -73,7 +72,7 @@ def is_platform_compatible(build_info: List[Dict], platform: str) -> bool: def compatible_versions( releases: dict, python_version, platform: str -) -> List[get_version.Version]: +) -> list[get_version.Version]: compatible_versions = [] for version_str, build_info in releases.items(): try: diff --git a/komodo/cleanup.py b/komodo/cleanup.py index 8839cf193..d2cc080d8 100755 --- a/komodo/cleanup.py +++ b/komodo/cleanup.py @@ -1,12 +1,11 @@ #!/usr/bin/env python import sys -from typing import List from komodo.yaml_file_types import ReleaseFile, RepositoryFile -def cleanup(repository_file_path: str, release_files_path: List[str]): +def cleanup(repository_file_path: str, release_files_path: list[str]): repository_file = RepositoryFile()(repository_file_path) repository = repository_file.content diff --git a/komodo/cli.py b/komodo/cli.py index e47e836bf..2291e616b 100755 --- a/komodo/cli.py +++ b/komodo/cli.py @@ -4,8 +4,9 @@ import os import sys import uuid +from collections.abc import Callable, Mapping, Sequence from pathlib import Path -from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Union +from typing import Any import jinja2 from ruamel.yaml import YAML @@ -40,7 +41,7 @@ class KomodoNamespace(argparse.Namespace): cmake: str pip: str workspace: str - extra_data_dirs: List[str] + extra_data_dirs: list[str] postinst: str @@ -88,7 +89,7 @@ def create_enable_scripts(komodo_prefix: str, komodo_release: str) -> None: def _print_timing( - timing_element: Tuple[str, datetime.timedelta], + timing_element: tuple[str, datetime.timedelta], adjust: bool = False, ) -> None: if adjust: @@ -125,10 +126,10 @@ def is_download_only(args: KomodoNamespace) -> bool: @profile_time("Fetching all packages") def download_packages( release_file_content: Mapping[str, str], - repository_file_content: Mapping[str, Mapping[str, Union[str, Sequence[str]]]], + repository_file_content: Mapping[str, Mapping[str, str | Sequence[str]]], download_destination: str, pip_executable: str = "pip", -) -> Dict[str, str]: +) -> dict[str, str]: """Downloads all PyPI packages to destination. Tries to download other packages to destination too. Git packages are collected, and a dict of all git hashes is returned. @@ -205,14 +206,14 @@ def generate_release_root(release_path: Path) -> Path: def generate_release_manifest( release_name: Path, release_file_content: Mapping[str, str], - repository_file_content: Mapping[str, Mapping[str, Union[str, Sequence[str]]]], - git_hashes: Optional[Mapping[str, str]] = None, + repository_file_content: Mapping[str, Mapping[str, str | Sequence[str]]], + git_hashes: Mapping[str, str] | None = None, ) -> None: releasedoc = Path(release_name) / Path(release_name) with open(releasedoc, mode="w", encoding="utf-8") as filehandle: - release: Dict[str, Dict[str, str]] = {} + release: dict[str, dict[str, str]] = {} for package, version in release_file_content.items(): - entry: Dict[str, str] = repository_file_content[package][version] + entry: dict[str, str] = repository_file_content[package][version] maintainer = repository_file_content[package][version]["maintainer"] if entry.get("fetch") == "git": version = git_hashes[package] @@ -250,7 +251,7 @@ def delete_old_previously_moved_releases(prefix_path: Path, release_name: Path) ) -def apply_fallback_tmpdir_for_pip_if_set(tmp_dir: Optional[str] = None): +def apply_fallback_tmpdir_for_pip_if_set(tmp_dir: str | None = None): """Allows e.g. pip to use this folder as a destination for "pip download", instead of in some cases falling back to /tmp, which is undesired when building on nfs. @@ -265,7 +266,7 @@ def apply_fallback_tmpdir_for_pip_if_set(tmp_dir: Optional[str] = None): @profile_time("pip install to final destination") def install_previously_downloaded_pip_packages( release_file_content: Mapping[str, str], - repository_file_content: Mapping[str, Mapping[str, Union[str, Sequence[str]]]], + repository_file_content: Mapping[str, Mapping[str, str | Sequence[str]]], downloads_directory: str, pip_executable: str, release_root: Path, @@ -295,7 +296,7 @@ def install_previously_downloaded_pip_packages( def run_post_installation_scripts_if_set( - postinst: Optional[str], release_path: Path + postinst: str | None, release_path: Path ) -> None: if postinst: timing_func = profile_time("Running post-install scripts") @@ -328,7 +329,7 @@ def compile_python_bytecode_files_and_fix_permissions( compile_python_bytecode_files(release_root) -timings: List[Tuple[str, datetime.timedelta]] = [] +timings: list[tuple[str, datetime.timedelta]] = [] def _main(args: KomodoNamespace) -> None: @@ -416,7 +417,7 @@ def cli_main(): _main(args) -def parse_args(args: List[str]) -> KomodoNamespace: +def parse_args(args: list[str]) -> KomodoNamespace: """Parse the arguments from the command line into an `argparse.Namespace`. Having a separated function makes it easier to test the CLI. diff --git a/komodo/deployed.py b/komodo/deployed.py index 73ae92af9..82997949a 100644 --- a/komodo/deployed.py +++ b/komodo/deployed.py @@ -1,7 +1,6 @@ import argparse import json import os -from typing import List, Optional from komodo.matrix import get_matrix_base from komodo.yaml_file_types import ReleaseDir @@ -35,13 +34,13 @@ def _fetch_non_deployed_releases(install_root, release_folder): def fetch_non_deployed( install_root: str, releases_folder: str, - limit: Optional[int] = None, -) -> List[str]: + limit: int | None = None, +) -> list[str]: non_deployed = _fetch_non_deployed_releases(install_root, releases_folder) return non_deployed[:limit] -def output_formatter(release_list: List[str], do_json: bool = False) -> str: +def output_formatter(release_list: list[str], do_json: bool = False) -> str: if do_json: return json.dumps(release_list, separators=(",", ":")) return "\n".join(release_list) diff --git a/komodo/insert_proposals.py b/komodo/insert_proposals.py index 500f1c96e..a66c43752 100644 --- a/komodo/insert_proposals.py +++ b/komodo/insert_proposals.py @@ -3,8 +3,8 @@ import difflib import os from base64 import b64decode +from collections.abc import Mapping, MutableSet from datetime import datetime -from typing import Dict, Mapping, MutableSet, Optional, Union import github from github import Github, UnknownObjectException @@ -21,8 +21,8 @@ def recursive_update( - to_be_upgraded: Mapping[str, Union[str, Mapping[str, str]]], - upgrades: Optional[Mapping[str, Union[str, Mapping[str, str]]]], + to_be_upgraded: Mapping[str, str | Mapping[str, str]], + upgrades: Mapping[str, str | Mapping[str, str]] | None, ) -> None: """Updates release in place with upgrades/additions of packages from upgrades. @@ -115,7 +115,7 @@ def get_upgrade_key(target: str) -> str: def validate_upgrades( - upgrade_section: Dict[str, str], repofile: RepositoryFile + upgrade_section: dict[str, str], repofile: RepositoryFile ) -> None: errors = set() for package_name, package_version in upgrade_section.items(): @@ -128,7 +128,7 @@ def validate_upgrades( def recursive_validate_package_entries( package_name, - package_version_or_matrix: Union[str, Mapping, None], + package_version_or_matrix: str | Mapping | None, repository_file: RepositoryFile, errors: MutableSet, ) -> None: @@ -151,7 +151,7 @@ def recursive_validate_package_entries( def generate_contents_of_new_release_matrix( base_release_matrix_content: Mapping, repofile: RepositoryFile, - upgrade: Optional[Dict], + upgrade: dict | None, ) -> str: if upgrade: validate_upgrades(upgrade, repofile) @@ -207,7 +207,7 @@ def create_pr_with_changes( tmp_target: str, tmp_ref: GitRef, pr_msg: str, - rhel8: Optional[bool] = False, + rhel8: bool | None = False, ): commit_title = ( f"Add release {target} (+rhel8)" if rhel8 else f"Add release {target}" @@ -258,7 +258,7 @@ def insert_proposals( git_ref: str, jobname: str, joburl: str, - rhel8: Optional[bool] = False, + rhel8: bool | None = False, ) -> None: tmp_target = target + ".tmp" @@ -274,7 +274,7 @@ def insert_proposals( base_file = f"releases/matrices/{base}.yml" release_file_yaml_string = load_yaml_from_repo(base_file, repo, git_ref) release_matrix_file = ReleaseMatrixFile.from_yaml_string(release_file_yaml_string) - upgrade: Dict[str, str] = proposal_file.content.get(upgrade_key) + upgrade: dict[str, str] = proposal_file.content.get(upgrade_key) repofile_yaml_string = load_yaml_from_repo("repository.yml", repo, git_ref) repofile = RepositoryFile().from_yaml_string(repofile_yaml_string) diff --git a/komodo/lint_maturity.py b/komodo/lint_maturity.py index ec1779a23..e5ed25bb6 100644 --- a/komodo/lint_maturity.py +++ b/komodo/lint_maturity.py @@ -3,7 +3,6 @@ import argparse import os import warnings -from typing import List import yaml from packaging.version import InvalidVersion, Version @@ -126,7 +125,7 @@ def get_release_version(release_basename, tag_exceptions_release): return release_version -def run(files_to_lint: List[str], tag_exceptions): +def run(files_to_lint: list[str], tag_exceptions): system_exit_msg = "" system_warning_msg = "" @@ -181,7 +180,7 @@ def read_yaml_file_and_convert_to_release_file(release_file_path: str) -> Releas return ReleaseFile().from_yaml_string(value=release_file_yaml_string) -def get_files_to_lint(release_folder: str, release_file: str) -> List[str]: +def get_files_to_lint(release_folder: str, release_file: str) -> list[str]: if release_folder is None: files_to_lint = [release_file] else: diff --git a/komodo/lint_symlink_config.py b/komodo/lint_symlink_config.py index aea691e7e..fb9cd2021 100644 --- a/komodo/lint_symlink_config.py +++ b/komodo/lint_symlink_config.py @@ -3,8 +3,8 @@ import os import re import sys +from collections.abc import Mapping from pathlib import Path -from typing import Mapping, Union from komodo.symlink.sanity_check import assert_root_nodes @@ -21,7 +21,7 @@ def parse_args(): return parser.parse_args() -def lint_symlink_config(link_dict: Mapping[str, Union[Mapping, str]]): +def lint_symlink_config(link_dict: Mapping[str, Mapping | str]): assert_root_nodes(link_dict) links = link_dict["links"] diff --git a/komodo/maintainer.py b/komodo/maintainer.py index d2e9ff84b..66d08c961 100755 --- a/komodo/maintainer.py +++ b/komodo/maintainer.py @@ -6,9 +6,10 @@ def maintainers(pkgfile, repofile): - with open(pkgfile, encoding="utf-8") as package_file_stream, open( - repofile, encoding="utf-8" - ) as repository_file_stream: + with ( + open(pkgfile, encoding="utf-8") as package_file_stream, + open(repofile, encoding="utf-8") as repository_file_stream, + ): yml = YAML() pkgs, repo = yml.load(package_file_stream), yml.load(repository_file_stream) diff --git a/komodo/matrix.py b/komodo/matrix.py index a1c3bee9f..c8d89621f 100644 --- a/komodo/matrix.py +++ b/komodo/matrix.py @@ -6,13 +6,13 @@ import itertools import re -from typing import Iterator, Sequence, Tuple +from collections.abc import Iterator, Sequence def get_matrix( rhel_versions: Sequence[str], py_versions: Sequence[str], -) -> Iterator[Tuple[str, str]]: +) -> Iterator[tuple[str, str]]: """Return tuples of rhel version and Python version, representing the current release matrix. """ diff --git a/komodo/pypi_dependencies.py b/komodo/pypi_dependencies.py index 501584b70..eb13b7b9d 100644 --- a/komodo/pypi_dependencies.py +++ b/komodo/pypi_dependencies.py @@ -3,7 +3,6 @@ import subprocess import sys from tempfile import TemporaryDirectory -from typing import Dict, List, Set, Tuple import pkginfo import yaml @@ -40,8 +39,8 @@ def format_full_version(info) -> str: class PypiDependencies: def __init__( self, - pypi_dependencies: Dict[str, str], - to_install: Dict[str, str], + pypi_dependencies: dict[str, str], + to_install: dict[str, str], python_version: str, cachefile: str = "./pypi_dependencies.yml", ) -> None: @@ -49,7 +48,7 @@ def __init__( environment["python_full_version"] = python_version environment["python_version"] = ".".join(python_version.split(".")[0:2]) self._satisfied_requirements = set() - self._failed_requirements: Dict[Requirement, str] = {} + self._failed_requirements: dict[Requirement, str] = {} self._used_packages = set() self._install_names = {canonicalize_name(name): name for name in to_install} @@ -111,7 +110,7 @@ def used_packages(self, packages=None): def add_user_specified( self, package_name: str, - depends: List[str], + depends: list[str], ) -> None: canonical = canonicalize_name(package_name) # It is necessary to set the version to the installed @@ -135,7 +134,7 @@ def dump_cache(self): def _get_requirements( self, package_name: str, package_version: str - ) -> List[Requirement]: + ) -> list[Requirement]: """ >>> from packaging.requirements import Requirement >>> PypiDependencies({}, {}, python_version="3.8.11")._get_requirements( @@ -219,8 +218,8 @@ def _is_necessary(self, requirement, extras): def _satisfied( self, - requirements: List[Requirement], - ) -> Tuple[bool, List[Tuple[List[Requirement], Set[str]]]]: + requirements: list[Requirement], + ) -> tuple[bool, list[tuple[list[Requirement], set[str]]]]: installed = self._to_install transient_requirements = [] for requirement in requirements: diff --git a/komodo/release_transpiler.py b/komodo/release_transpiler.py index dcfd8affa..eaba06244 100755 --- a/komodo/release_transpiler.py +++ b/komodo/release_transpiler.py @@ -2,7 +2,7 @@ import argparse import os -from typing import Dict, Sequence, Union +from collections.abc import Sequence import yaml @@ -56,7 +56,7 @@ def _pick_package_versions_for_release( def _check_version_exists_for_coordinates( - pkg_versions: Union[dict, str], + pkg_versions: dict | str, rhel_coordinate: str, py_coordinate: str, ) -> None: @@ -164,7 +164,7 @@ def transpile(args): transpile_releases(args.matrix_file, args.output_folder, args.matrix_coordinates) -def transpile_for_pip(args: Dict): +def transpile_for_pip(args: dict): transpile_releases_for_pip( args.matrix_file, args.output_folder, diff --git a/komodo/show_version.py b/komodo/show_version.py index 100eba833..dc1a10ee9 100644 --- a/komodo/show_version.py +++ b/komodo/show_version.py @@ -6,7 +6,6 @@ import sys import textwrap from pathlib import Path -from typing import Dict, List, Optional, Union from ruamel.yaml import YAML @@ -44,7 +43,7 @@ def get_release() -> str: sys.exit(message) -def read_config(fname: Union[str, Path]) -> dict: +def read_config(fname: str | Path) -> dict: """Read an INI file (aka config file) that does not have sections. Sections are part of the INI file format specification and cannot be read by a `configparser.ConfigParser` without them. This function creates a @@ -114,7 +113,7 @@ def get_komodo_path(release: str) -> Path: return Path(path) -def get_version(pkg: str, manifest: Optional[Dict] = None) -> str: +def get_version(pkg: str, manifest: dict | None = None) -> str: """Get the release number (or git commit hash) for a package in a komodo release file. If no file is specified, the current release is used. If no environment is active, the path to the release @@ -154,7 +153,7 @@ def get_version(pkg: str, manifest: Optional[Dict] = None) -> str: return package.get("version") -def parse_args(args: List[str]) -> argparse.Namespace: +def parse_args(args: list[str]) -> argparse.Namespace: """Parse the arguments from the command line into an `argparse.Namespace`. Having a separated function makes it easier to test the CLI. diff --git a/komodo/snyk_reporting.py b/komodo/snyk_reporting.py index 8ed3b6b00..462315b38 100644 --- a/komodo/snyk_reporting.py +++ b/komodo/snyk_reporting.py @@ -2,7 +2,7 @@ import html import os import sys -from typing import Any, Dict, List, Optional +from typing import Any from snyk import SnykClient from snyk.models import Organization, Vulnerability @@ -41,7 +41,7 @@ def main() -> None: print(_format_console(vulnerabilities=vulnerabilities)) -def parse_args(args: List[str]) -> argparse.Namespace: +def parse_args(args: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( description="Test a release for security and license issues.", formatter_class=argparse.ArgumentDefaultsHelpFormatter, @@ -81,9 +81,9 @@ def parse_args(args: List[str]) -> argparse.Namespace: def filter_pip_packages( - packages: Dict[str, str], - repository: Dict[str, Any], -) -> Dict[str, str]: + packages: dict[str, str], + repository: dict[str, Any], +) -> dict[str, str]: return { package_name: version for (package_name, version) in packages.items() @@ -91,13 +91,13 @@ def filter_pip_packages( } -def create_snyk_search_string(packages: Dict[str, str]) -> str: +def create_snyk_search_string(packages: dict[str, str]) -> str: return "\n".join( [f"{package_name}=={version}" for package_name, version in packages.items()], ) -def get_unique_issues(issues: List[Vulnerability]) -> List[Vulnerability]: +def get_unique_issues(issues: list[Vulnerability]) -> list[Vulnerability]: ids = set() result = [] for issue in issues: @@ -109,7 +109,7 @@ def get_unique_issues(issues: List[Vulnerability]) -> List[Vulnerability]: def filter_vulnerability_issues( - snyk_issues: List[Vulnerability], release_packages: Dict[str, str] + snyk_issues: list[Vulnerability], release_packages: dict[str, str] ): filtered_vulnerability_issues = [] for snyk_issue in snyk_issues: @@ -121,10 +121,10 @@ def filter_vulnerability_issues( def find_vulnerabilities( - releases: Dict[str, Dict[str, str]], - repository: Dict[str, Any], + releases: dict[str, dict[str, str]], + repository: dict[str, Any], org: Organization, -) -> Dict[str, List[Vulnerability]]: +) -> dict[str, list[Vulnerability]]: result = {} for release_name, packages in releases.items(): @@ -139,7 +139,7 @@ def find_vulnerabilities( return result -def _format_console(vulnerabilities: Dict[str, List[Vulnerability]]) -> str: +def _format_console(vulnerabilities: dict[str, list[Vulnerability]]) -> str: result = "Security Vulnerabilities:\n" for release, vulns in vulnerabilities.items(): result += release + "\n" @@ -160,7 +160,7 @@ def _format_console(vulnerabilities: Dict[str, List[Vulnerability]]) -> str: return result -def _format_github(vulnerabilities: Dict[str, List[Vulnerability]]) -> str: +def _format_github(vulnerabilities: dict[str, list[Vulnerability]]) -> str: result = "Snyk Security Report\n==\n\n" for release, vulns in vulnerabilities.items(): result += f"{release}\n--\n" @@ -191,11 +191,11 @@ def _get_org(api_token: str, org_id: str) -> Organization: def snyk_main( - releases: Dict[str, Dict[str, str]], - repository: Dict[str, Any], - api_token: Optional[str], + releases: dict[str, dict[str, str]], + repository: dict[str, Any], + api_token: str | None, org_id: str, -) -> Dict[str, List[Vulnerability]]: +) -> dict[str, list[Vulnerability]]: if api_token is None: msg = "No api token given, please set the environment variable SNYK_API_TOKEN." raise ValueError( diff --git a/komodo/switch.py b/komodo/switch.py index e1c282620..9345e3521 100644 --- a/komodo/switch.py +++ b/komodo/switch.py @@ -43,11 +43,10 @@ def create_activator_switch(data, prefix, release): os.makedirs(release_path) - with open( - os.path.join(release_path, "enable"), "w", encoding="utf-8" - ) as activator, open( - data.get("activator_switch.tmpl"), encoding="utf-8" - ) as activator_tmpl: + with ( + open(os.path.join(release_path, "enable"), "w", encoding="utf-8") as activator, + open(data.get("activator_switch.tmpl"), encoding="utf-8") as activator_tmpl, + ): activator.write( Template(activator_tmpl.read(), keep_trailing_newline=True).render( py_version=py_version, @@ -57,11 +56,12 @@ def create_activator_switch(data, prefix, release): ), ) - with open( - os.path.join(release_path, "enable.csh"), "w", encoding="utf-8" - ) as activator, open( - data.get("activator_switch.csh.tmpl"), encoding="utf-8" - ) as activator_tmpl: + with ( + open( + os.path.join(release_path, "enable.csh"), "w", encoding="utf-8" + ) as activator, + open(data.get("activator_switch.csh.tmpl"), encoding="utf-8") as activator_tmpl, + ): activator.write( Template(activator_tmpl.read(), keep_trailing_newline=True).render( py_version=py_version, diff --git a/komodo/symlink/create_links.py b/komodo/symlink/create_links.py index 67766479d..3fb37942c 100644 --- a/komodo/symlink/create_links.py +++ b/komodo/symlink/create_links.py @@ -3,7 +3,6 @@ import os import sys from contextlib import contextmanager -from typing import List from .sanity_check import verify_integrity @@ -18,7 +17,7 @@ def working_dir(path): os.chdir(prev_dir) -def get_implicitly_moved_symlinks(key: str, link_dict: dict) -> List[str]: +def get_implicitly_moved_symlinks(key: str, link_dict: dict) -> list[str]: sources = [src for src, dst in link_dict.items() if dst == key] implicitly_moved_symlinks = [] for source in sources: diff --git a/komodo/symlink/suggester/cli.py b/komodo/symlink/suggester/cli.py index 698c18e50..76d6dc4a2 100755 --- a/komodo/symlink/suggester/cli.py +++ b/komodo/symlink/suggester/cli.py @@ -5,7 +5,6 @@ import sys from base64 import b64decode from datetime import datetime -from typing import Optional from github import Github from github.GithubException import UnknownObjectException @@ -61,7 +60,7 @@ def _parse_args(): return parser.parse_args() -def _get_repo(token: Optional[str], fork: str, repo: str) -> Repository: +def _get_repo(token: str | None, fork: str, repo: str) -> Repository: client = Github(token) try: return client.get_repo(f"{fork}/{repo}") @@ -74,7 +73,7 @@ def suggest_symlink_configuration( args: argparse.Namespace, repo: Repository, dry_run: bool = False, -) -> Optional[Repository]: +) -> Repository | None: """Returns a pull request if the symlink configuration could be updated, or None if no update was possible. """ diff --git a/komodo/symlink/suggester/configuration.py b/komodo/symlink/suggester/configuration.py index 153256e87..4152fb26f 100644 --- a/komodo/symlink/suggester/configuration.py +++ b/komodo/symlink/suggester/configuration.py @@ -1,5 +1,4 @@ import json -from typing import List, Tuple from komodo.symlink.sanity_check import suggest_missing_roots from komodo.symlink.suggester.release import Release @@ -20,7 +19,7 @@ def _get_concrete_release(self, link): release = Release(self.links[repr(release)]) return release - def update(self, release, mode, python_versions: List[str]): + def update(self, release, mode, python_versions: list[str]): for python_version in python_versions: python_version = python_version.strip() release.set_python_version(python_version) @@ -66,8 +65,8 @@ def from_json(conf_json_str): def update( - symlink_configuration, release_id, mode, python_versions: List[str] = None -) -> Tuple[str, bool]: + symlink_configuration, release_id, mode, python_versions: list[str] = None +) -> tuple[str, bool]: """Return a tuple of a string representing the new symlink config json, and whether an update was made. This function assumes the release_id is in the yyyy.mm.[part ...] format and will look for python suffix (-py38, -py311) diff --git a/komodo/yaml_file_types.py b/komodo/yaml_file_types.py index 68746e71a..ee2e13b3e 100644 --- a/komodo/yaml_file_types.py +++ b/komodo/yaml_file_types.py @@ -1,8 +1,8 @@ import argparse import os from collections import namedtuple +from collections.abc import Mapping, MutableSet, Sequence from pathlib import Path -from typing import Dict, List, Mapping, MutableSet, Sequence, Union from ruamel.yaml import YAML from ruamel.yaml.constructor import DuplicateKeyError @@ -137,7 +137,7 @@ def validate_release_matrix_file(release_matrix_file_content: Mapping) -> None: class ReleaseDir: - def __call__(self, value: str) -> Dict[str, YamlFile]: + def __call__(self, value: str) -> dict[str, YamlFile]: if not os.path.isdir(value): raise NotADirectoryError(value) result = {} @@ -155,7 +155,7 @@ def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.content: dict = None - def __call__(self, value: str) -> Dict[str, Dict[str, str]]: + def __call__(self, value: str) -> dict[str, dict[str, str]]: yml = super().__call__(value) self.validate_manifest_file(yml) return yml @@ -272,7 +272,7 @@ def validate_repository_file(self) -> None: handle_validation_errors(errors, message) - def validate_versions(self, package_name: str, versions: dict) -> List[str]: + def validate_versions(self, package_name: str, versions: dict) -> list[str]: """Validates versions-dictionary of a package and returns a list of error messages.""" errors = [] for version, version_metadata in versions.items(): @@ -308,7 +308,7 @@ def validate_package_properties( package_version: str, package_property: str, package_property_value: str, - ) -> List[str]: + ) -> list[str]: """Validates package properties of the specified package and returns a list of error messages. """ @@ -355,7 +355,7 @@ def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.content: dict = None - def __call__(self, value: str) -> Dict[str, Dict[str, str]]: + def __call__(self, value: str) -> dict[str, dict[str, str]]: yml = super().__call__(value) self.validate_upgrade_proposals_file(yml) self.content: dict = yml @@ -484,7 +484,7 @@ def validate_package_name(package_name: str) -> None: @staticmethod def validate_package_version( - package_name: Union[str, None], + package_name: str | None, package_version: str, is_matrix_file: bool = False, ) -> None: @@ -512,7 +512,7 @@ def validate_package_entry_with_errors( package_name: str, package_version: str, is_matrix_file: bool = False, - ) -> List[str]: + ) -> list[str]: """Validates package name and version, and returns a list of error messages.""" errors = [] try: @@ -541,7 +541,7 @@ def validate_package_importance(package_name: str, package_importance: str) -> N def validate_package_importance_with_errors( package_name, package_importance: str, - ) -> List[str]: + ) -> list[str]: """Validates package importance of a package and returns a list of error messages.""" errors = [] try: @@ -582,7 +582,7 @@ def validate_package_maturity(package_name: str, package_maturity: str) -> None: def validate_package_maturity_with_errors( package_name: str, package_maturity: str, - ) -> List[str]: + ) -> list[str]: """Validates package maturity of a package and returns a list of error messages.""" errors = [] try: @@ -614,7 +614,7 @@ def validate_package_make_with_errors( package_name: str, package_version: str, package_make: str, - ) -> List[str]: + ) -> list[str]: """Validates make of a package and returns a list of error messages.""" errors = [] try: @@ -641,7 +641,7 @@ def validate_package_maintainer_with_errors( package_name: str, package_version: str, package_maintainer: str, - ) -> List[str]: + ) -> list[str]: """Validates maintainer of a package and returns a list of error messages.""" errors = [] try: @@ -660,7 +660,7 @@ def validate_package_source( package_version: str, package_source: str, ) -> None: - if isinstance(package_source, (str, type(None))): + if isinstance(package_source, (str | type(None))): return msg = f"Package '{package_name}' version {package_version} has invalid source type ({package_source})" raise TypeError( @@ -672,7 +672,7 @@ def validate_package_source_with_errors( package_name: str, package_version: str, package_source: str, - ) -> List[str]: + ) -> list[str]: """Validates source of a package and returns a list of error messages.""" errors = [] try: @@ -718,7 +718,7 @@ def load_repository_file(repository_file_string): def _recursive_validate_version_matrix( - version_or_matrix: Union[dict, str], package_name: str, errors: MutableSet + version_or_matrix: dict | str, package_name: str, errors: MutableSet ) -> None: if isinstance(version_or_matrix, Mapping): for nested_version_or_matrix in version_or_matrix.values(): diff --git a/pyproject.toml b/pyproject.toml index 849fccebf..172e212a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,18 +7,17 @@ name = "komodo" authors = [{ name="Equinor ASA", email="fg_sib-scout@equinor.com" },] description = "Komodo is a software distribution system." dynamic = ["version"] -requires-python = ">=3.8" +requires-python = ">=3.10" license = {file = "LICENSE"} readme = "README.md" classifiers = [ "Intended Audience :: Science/Research", "Development Status :: 4 - Beta", "Natural Language :: English", - "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", "License :: OSI Approved :: License :: OSI Approved :: GNU Affero General Public License v3", "Operating System :: OS Independent", ] diff --git a/tests/test_cleanup.py b/tests/test_cleanup.py index 3dd2e3de6..33bdaa3d9 100644 --- a/tests/test_cleanup.py +++ b/tests/test_cleanup.py @@ -1,7 +1,6 @@ import os import sys from contextlib import contextmanager -from typing import List import pytest @@ -72,8 +71,8 @@ def _write_file(file_path: str, file_content: str) -> str: def _create_tmp_test_files( repository_file_content: str, - release_files_contents: List[str], -) -> (str, List[str]): + release_files_contents: list[str], +) -> (str, list[str]): folder_name = os.path.join(os.getcwd(), "test_cleanup/") os.mkdir(folder_name) repository_file_path = _write_file( @@ -109,7 +108,7 @@ def _create_tmp_test_files( ) def test_cleanup_main( repository_file_content: str, - release_files_content: List[str], + release_files_content: list[str], expected_print, monkeypatch, tmpdir, @@ -146,7 +145,7 @@ def test_cleanup_main( ) def test_cleanup_main_invalid_input_files( repository_file_content: str, - release_files_content: List[str], + release_files_content: list[str], expectation, monkeypatch, tmpdir, diff --git a/tests/test_lint_maturity.py b/tests/test_lint_maturity.py index 95c671230..d5e8bb784 100644 --- a/tests/test_lint_maturity.py +++ b/tests/test_lint_maturity.py @@ -294,9 +294,12 @@ def test_lint_maturity_run(tmpdir): """ # noqa for list_file in list_files: - with pytest.raises(SystemExit) as exit_info, warnings.catch_warnings( - record=True, - ) as warning_info: + with ( + pytest.raises(SystemExit) as exit_info, + warnings.catch_warnings( + record=True, + ) as warning_info, + ): lint_maturity.run( files_to_lint=[list_file], tag_exceptions={ diff --git a/tests/test_release_transpiler_argument_types.py b/tests/test_release_transpiler_argument_types.py index e9f312824..82d647b7a 100644 --- a/tests/test_release_transpiler_argument_types.py +++ b/tests/test_release_transpiler_argument_types.py @@ -1,7 +1,6 @@ import sys from contextlib import contextmanager from os.path import abspath, dirname -from typing import List import pytest @@ -72,7 +71,7 @@ def does_not_raise(): ), ], ) -def test_transpile_py_matrix_file_type(args: List[str], expectation, monkeypatch): +def test_transpile_py_matrix_file_type(args: list[str], expectation, monkeypatch): monkeypatch.setattr( sys, "argv", @@ -131,7 +130,7 @@ def test_transpile_py_matrix_file_type(args: List[str], expectation, monkeypatch ), ], ) -def test_transpile_py_output_folder_type(args: List[str], expectation, monkeypatch): +def test_transpile_py_output_folder_type(args: list[str], expectation, monkeypatch): monkeypatch.setattr( sys, "argv", @@ -179,7 +178,7 @@ def test_transpile_py_output_folder_type(args: List[str], expectation, monkeypat ], ) def test_transpile_py_matrix_coordinates_type( - args: List[str], + args: list[str], expectation, monkeypatch, ): diff --git a/tests/test_snyk_reporting.py b/tests/test_snyk_reporting.py index fee4a46aa..bade721d2 100644 --- a/tests/test_snyk_reporting.py +++ b/tests/test_snyk_reporting.py @@ -1,4 +1,4 @@ -from typing import Mapping, Sequence +from collections.abc import Mapping, Sequence from unittest.mock import Mock, patch import pytest