diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index ef178eacd..f2f53b8bf 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -32,8 +32,18 @@ jobs: strategy: matrix: py-ver-major: [3] - py-ver-minor: [8, 9, 10, 11, 12] + py-ver-minor: [7, 8, 9, 10, 11, 12] step: [lint, unit, bandit, mypy] + exclude: + - py-ver-major: 3 + py-ver-minor: 7 + step: mypy + - py-ver-major: 3 + py-ver-minor: 7 + step: lint + - py-ver-major: 3 + py-ver-minor: 7 + step: bandit env: py-semver: ${{ format('{0}.{1}', matrix.py-ver-major, matrix.py-ver-minor) }} diff --git a/cwltool/checker.py b/cwltool/checker.py index c11561719..1132adebe 100644 --- a/cwltool/checker.py +++ b/cwltool/checker.py @@ -5,7 +5,6 @@ Dict, Iterator, List, - Literal, MutableMapping, MutableSequence, Optional, @@ -13,6 +12,7 @@ Union, cast, ) +from typing_extensions import Literal from schema_salad.exceptions import ValidationException from schema_salad.sourceline import SourceLine, bullets, strip_dup_lineno @@ -509,7 +509,8 @@ def get_step_id(field_id: str) -> str: def is_conditional_step(param_to_step: Dict[str, CWLObjectType], parm_id: str) -> bool: - if (source_step := param_to_step.get(parm_id)) is not None: + source_step = param_to_step.get(parm_id) + if source_step is not None: if source_step.get("when") is not None: return True return False diff --git a/cwltool/context.py b/cwltool/context.py index 7f30e4c30..d0bf98a07 100644 --- a/cwltool/context.py +++ b/cwltool/context.py @@ -12,12 +12,12 @@ Dict, Iterable, List, - Literal, Optional, TextIO, Tuple, Union, ) +from typing_extensions import Literal from ruamel.yaml.comments import CommentedMap from schema_salad.avro.schema import Names diff --git a/cwltool/cwlprov/__init__.py b/cwltool/cwlprov/__init__.py index b8ff8d14d..b7db6ab33 100644 --- a/cwltool/cwlprov/__init__.py +++ b/cwltool/cwlprov/__init__.py @@ -6,7 +6,8 @@ import re import uuid from getpass import getuser -from typing import IO, Any, Callable, Dict, List, Optional, Tuple, TypedDict, Union +from typing import IO, Any, Callable, Dict, List, Optional, Tuple, Union +from typing_extensions import TypedDict def _whoami() -> Tuple[str, str]: diff --git a/cwltool/software_requirements.py b/cwltool/software_requirements.py index 4587b97a8..7e114bb86 100644 --- a/cwltool/software_requirements.py +++ b/cwltool/software_requirements.py @@ -91,8 +91,9 @@ def build_job_script(self, builder: "Builder", command: List[str]) -> str: resolution_config_dict=resolution_config_dict, conf_file=self.dependency_resolvers_config_file, ) - handle_dependencies: str = "" - if dependencies := get_dependencies(builder): + dependencies = get_dependencies(builder) + handle_dependencies = "" # str + if dependencies: handle_dependencies = "\n".join( tool_dependency_manager.dependency_shell_commands( dependencies, job_directory=builder.tmpdir diff --git a/cwltool/utils.py b/cwltool/utils.py index 25d6c8873..58613e088 100644 --- a/cwltool/utils.py +++ b/cwltool/utils.py @@ -1,7 +1,6 @@ """Shared functions and other definitions.""" import collections import fcntl -import importlib.metadata import os import random import shutil @@ -28,17 +27,16 @@ Generator, Iterable, List, - Literal, MutableMapping, MutableSequence, NamedTuple, Optional, Set, Tuple, - TypedDict, Union, cast, ) +from typing_extensions import Literal, TypedDict import requests from cachecontrol import CacheControl @@ -47,6 +45,11 @@ from schema_salad.exceptions import ValidationException from schema_salad.ref_resolver import Loader +if sys.version_info >= (3, 8): + import importlib.metadata as importlib_metadata +else: + import importlib_metadata + if TYPE_CHECKING: from .command_line_tool import CallbackJob, ExpressionJob from .job import CommandLineJob, JobBase @@ -117,7 +120,8 @@ class WorkflowStateItem(NamedTuple): def versionstring() -> str: """Version of CWLtool used to execute the workflow.""" - if pkg := importlib.metadata.version("cwltool"): + pkg = importlib_metadata.version("cwltool") + if pkg: return f"{sys.argv[0]} {pkg}" return "{} {}".format(sys.argv[0], "unknown version") diff --git a/docs/conf.py b/docs/conf.py index fbc089caa..94c3e1e64 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,7 +14,6 @@ import sys from datetime import datetime import time -import importlib.metadata sys.path.insert(0, os.path.abspath("..")) @@ -82,7 +81,12 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] -release = importlib.metadata.version("cwltool") + +if sys.version_info >= (3, 8): + import importlib.metadata as importlib_metadata +else: + import importlib_metadata +release = importlib_metadata.version("cwltool") version = ".".join(release.split(".")[:2]) autoapi_dirs = ["../cwltool"] diff --git a/gittaggers.py b/gittaggers.py index c25dbe1af..ab54b3e05 100644 --- a/gittaggers.py +++ b/gittaggers.py @@ -2,13 +2,16 @@ import sys import time -import importlib.metadata +if sys.version_info >= (3, 8): + import importlib.metadata as importlib_metadata +else: + import importlib_metadata from typing import Any from setuptools.command.egg_info import egg_info -SETUPTOOLS_VER = importlib.metadata.version("setuptools").split(".") +SETUPTOOLS_VER = importlib_metadata.version("setuptools").split(".") RECENT_SETUPTOOLS = ( int(SETUPTOOLS_VER[0]) > 40 diff --git a/pyproject.toml b/pyproject.toml index 70b985255..bbdd251ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,4 +15,4 @@ build-backend = "setuptools.build_meta" [tool.black] line-length = 100 -target-version = [ "py38" ] +target-version = [ "py37" ] diff --git a/requirements.txt b/requirements.txt index d92936b34..34e8be3ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,9 @@ schema-salad>=8.4.20230426093816,<9 prov==1.5.1 mypy-extensions psutil>=5.6.6 +typing-extensions importlib_resources>=1.4 # equivalent to Python 3.9 +importlib_metadata;python_version<'3.8' # equivalent to Python 3.9 coloredlogs pydot>=1.4.1 argcomplete>=1.12.0 diff --git a/setup.py b/setup.py index a86b1a4ad..7ecc43dbd 100644 --- a/setup.py +++ b/setup.py @@ -120,7 +120,9 @@ "prov == 1.5.1", "mypy-extensions", "psutil >= 5.6.6", + "typing-extensions", "importlib_resources>=1.4", + "importlib_metadata;python_version<'3.8'", "coloredlogs", "pydot >= 1.4.1", "argcomplete", @@ -130,7 +132,7 @@ extras_require={ "deps": ["galaxy-tool-util >= 22.1.2, <23", "galaxy-util <23"], }, - python_requires=">=3.8, <4", + python_requires=">=3.7, <4", setup_requires=PYTEST_RUNNER, test_suite="tests", tests_require=[ @@ -156,6 +158,7 @@ "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", diff --git a/tox.ini b/tox.ini index 86bc7c0aa..6033a1a03 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = py3{8,9,10,11,12}-lint - py3{8,9,10,11,12}-unit + py3{7,8,9,10,11,12}-unit py3{8,9,10,11,12}-bandit py3{8,9,10,11,12}-mypy py311-lintreadme @@ -16,6 +16,7 @@ testpaths = tests [gh-actions] python = + 3.7: py37 3.8: py38 3.9: py39 3.10: py310 @@ -24,10 +25,10 @@ python = [testenv] skipsdist = - py3{8,9,10,11,12}-!{unit,mypy,lintreadme} = True + py3{7,8,9,10,11,12}-!{unit,mypy,lintreadme} = True description = - py3{8,9,10,11,12}-unit: Run the unit tests + py3{7,8,9,10,11,12}-unit: Run the unit tests py3{8,9,10,11,12}-lint: Lint the Python code py3{8,9,10,11,12}-bandit: Search for common security issues py3{8,9,10,11,12}-mypy: Check for type safety @@ -41,11 +42,11 @@ passenv = PROOT_NO_SECCOMP extras = - py3{8,9,10,11,12}-unit: deps + py3{7,8,9,10,11,12}-unit: deps deps = - py3{8,9,10,11,12}-{unit,lint,bandit,mypy}: -rrequirements.txt - py3{8,9,10,11,12}-{unit,mypy}: -rtest-requirements.txt + py3{7,8,9,10,11,12}-{unit,lint,bandit,mypy}: -rrequirements.txt + py3{7,8,9,10,11,12}-{unit,mypy}: -rtest-requirements.txt py3{8,9,10,11,12}-lint: -rlint-requirements.txt py3{8,9,10,11,12}-bandit: bandit py3{8,9,10,11,12}-bandit: importlib_metadata != 4.8.0 @@ -57,14 +58,14 @@ deps = py311-lintreadme: readme_renderer[rst] setenv = - py3{8,9,10,11,12}-unit: LC_ALL = C.UTF-8 + py3{7,8,9,10,11,12}-unit: LC_ALL = C.UTF-8 commands_pre = - py3{8,9,10,11,12}-unit: python -m pip install -U pip setuptools wheel + py3{7,8,9,10,11,12}-unit: python -m pip install -U pip setuptools wheel py311-lintreadme: python -m build --outdir {distdir} commands = - py3{8,9,10,11,12}-unit: make coverage-report coverage.xml PYTEST_EXTRA={posargs} + py3{7,8,9,10,11,12}-unit: make coverage-report coverage.xml PYTEST_EXTRA={posargs} py3{8,9,10,11,12}-bandit: bandit -r cwltool py3{8,9,10,11,12}-lint: make flake8 format-check codespell-check py3{8,9,10,11,12}-mypy: make mypy PYTEST_EXTRA={posargs} @@ -74,6 +75,6 @@ commands = py311-lintreadme: twine check {distdir}/* skip_install = - py3{8,9,10,11,12}-{bandit,lint,mypy,shellcheck,pydocstyle,lintreadme}: true + py3{7,8,9,10,11,12}-{bandit,lint,mypy,shellcheck,pydocstyle,lintreadme}: true allowlist_externals = make