diff --git a/.coveragerc b/.coveragerc index 43bbe587..ea98bab7 100644 --- a/.coveragerc +++ b/.coveragerc @@ -21,6 +21,5 @@ exclude_lines = if __name__ == .__main__.: ignore_errors = True +omit = orquesta/tests/* -omit = - tests/* diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index b47e8ca1..3467dea6 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -12,11 +12,10 @@ on: jobs: tests: - #python 3.6 not available on ubuntu-latest runs-on: ubuntu-20.04 strategy: matrix: - python-version: ["3.6", "3.8"] + python-version: ["3.6", "3.8", "3.9", "3.10", "3.11"] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -30,5 +29,4 @@ jobs: pip install -r requirements-ci.txt make clean reqs schemas - name: "Run tox for ${{ matrix.python-version }}" - run: | - make check + run: make check diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 97f513e4..1b0c9b74 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,12 @@ In Development Changed ~~~~~~~ +* Update deprecated `collections` imports to `collections.abc` to be forward-compatible with Python3.10 + Contributed by @AndroxxTraxxon +* Migrate from `nosetest` to `pytest` for Python test runner. + Contributed by @AndroxxTraxxon +* Add Python versions 3.9, 3.10, and 3.11 to the test matrix + Contributed by @AndroxxTraxxon * Update networkx >=2.6 for Python 3.8 to fix insecure deserialization #255 (improvement) Contributed by @Stealthii * Update jsonschema requirements to allow 3.2 (improvement) diff --git a/orquesta/conducting.py b/orquesta/conducting.py index 34fb3567..a0c07d1d 100644 --- a/orquesta/conducting.py +++ b/orquesta/conducting.py @@ -370,7 +370,6 @@ def log_entry( result=None, data=None, ): - # Check entry type. if entry_type not in ["info", "warn", "error"]: raise exc.WorkflowLogEntryError('The log entry type "%s" is not valid.' % entry_type) diff --git a/orquesta/expressions/functions/workflow.py b/orquesta/expressions/functions/workflow.py index 971b3ad4..32d2abe6 100644 --- a/orquesta/expressions/functions/workflow.py +++ b/orquesta/expressions/functions/workflow.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import collections +import collections.abc from orquesta import constants from orquesta import exceptions as exc @@ -117,8 +117,8 @@ def item_(context, key=None): if not key: return current_item - if not isinstance(current_item, collections.Mapping): - raise exc.ExpressionEvaluationException("Item is not type of collections.Mapping.") + if not isinstance(current_item, collections.abc.Mapping): + raise exc.ExpressionEvaluationException("Item is not type of collections.abc.Mapping.") if key not in current_item: raise exc.ExpressionEvaluationException('Item does not have key "%s".' % key) diff --git a/orquesta/specs/base.py b/orquesta/specs/base.py index 3e7f977f..d9367031 100644 --- a/orquesta/specs/base.py +++ b/orquesta/specs/base.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import collections +import collections.abc import inspect import json import jsonschema @@ -613,7 +613,7 @@ def update(self, *args, **kwargs): raise NotImplementedError() -class SequenceSpec(Spec, collections.MutableSequence): +class SequenceSpec(Spec, collections.abc.MutableSequence): def __init__(self, spec, name=None, member=False): super(SequenceSpec, self).__init__(spec, name=name, member=member) diff --git a/orquesta/tests/hacking/import_aliases_rule.py b/orquesta/tests/hacking/import_aliases_rule.py index f6eb2539..8114bb58 100644 --- a/orquesta/tests/hacking/import_aliases_rule.py +++ b/orquesta/tests/hacking/import_aliases_rule.py @@ -59,7 +59,6 @@ def get_alias(logical_line): - parts = logical_line.split() if ( @@ -68,7 +67,6 @@ def get_alias(logical_line): and parts[1] != "__future__" and not core.is_import_exception(parts[1]) ): - # from path.to.module import module if len(parts) == 4: return ".".join([parts[1], parts[3]]), None diff --git a/orquesta/tests/unit/base.py b/orquesta/tests/unit/base.py index a4aa287f..d0cca988 100644 --- a/orquesta/tests/unit/base.py +++ b/orquesta/tests/unit/base.py @@ -156,7 +156,6 @@ def format_task_item( items_count=None, items_concurrency=None, ): - if not actions and items_count is None: actions = [{"action": spec.action, "input": spec.input}] @@ -285,7 +284,6 @@ def assert_task_items( concurrency=None, mock_ac_ex_results=None, ): - # Set up test cases. tests = list(zip(mock_ac_ex_statuses, expected_task_statuses, expected_workflow_statuses)) tk_ex_result = [None] * len(items) diff --git a/orquesta/tests/unit/conducting/native/test_task_rendering_for_with_items.py b/orquesta/tests/unit/conducting/native/test_task_rendering_for_with_items.py index 740a0067..47213a49 100644 --- a/orquesta/tests/unit/conducting/native/test_task_rendering_for_with_items.py +++ b/orquesta/tests/unit/conducting/native/test_task_rendering_for_with_items.py @@ -81,7 +81,7 @@ def test_bad_item_type(self): "type": "error", "message": ( "YaqlEvaluationException: Unable to evaluate expression '<% item(x) %>'. " - "ExpressionEvaluationException: Item is not type of collections.Mapping." + "ExpressionEvaluationException: Item is not type of collections.abc.Mapping." ), "task_id": "task1", "route": 0, diff --git a/orquesta/tests/unit/specs/native/test_workflow_rehearsal_spec.py b/orquesta/tests/unit/specs/native/test_workflow_rehearsal_spec.py index 94fd90a6..93181d2d 100644 --- a/orquesta/tests/unit/specs/native/test_workflow_rehearsal_spec.py +++ b/orquesta/tests/unit/specs/native/test_workflow_rehearsal_spec.py @@ -211,8 +211,7 @@ def test_init_test_spec_with_mock_action_execution_yml_result_path(self): def test_init_test_spec_with_base_path(self): shutil.copy(self.get_wf_file_path("sequential"), "/tmp/sequential.yaml") - fd, path = tempfile.mkstemp(suffix=".json") - + fd, path = tempfile.mkstemp(suffix=".json", dir="/tmp") with os.fdopen(fd, "w") as tmp: tmp.write('{"foo": "bar"}\n') diff --git a/requirements-ci.txt b/requirements-ci.txt index 3fa96f9a..0e1df7ba 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -1,4 +1,4 @@ # Different versions of tox are required by python version tox-gh-actions tox==3.28.0 ; python_version == "3.6" -tox==4.6.4 ; python_version == "3.8" +tox==4.11.3; python_version >= "3.8" diff --git a/requirements-test-py27.txt b/requirements-test-py27.txt deleted file mode 100644 index 17835402..00000000 --- a/requirements-test-py27.txt +++ /dev/null @@ -1,9 +0,0 @@ -coverage -flake8<2.7.0,>=2.6.0 -hacking -mock>=1.0 -nose -nosexcover -pep8>=1.6.0,<1.7 -pylint>=1.4.3,<1.5 -unittest2 diff --git a/requirements-test.txt b/requirements-test.txt index dd206521..8cc235e2 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,8 +3,8 @@ coverage flake8<3.9.0,>=3.8.0 hacking mock>=1.0 -nose -nosexcover +pytest +pytest-cov pep8>=1.6.0,<1.7 pylint>=2.5.2,<2.6 twine diff --git a/tox.ini b/tox.ini index 40c149af..ee984eb0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,15 @@ [tox] -envlist = py36,py38,pep8,docs +envlist = py36,py38,py39,py310,py311,pep8,docs minversion = 1.6 skipsdist = True [gh-actions] python = - 3.6: py36,pep8,docs - 3.8: py38 + 3.6: py36 + 3.8: py38,pep8,docs + 3.9: py39 + 3.10: py310 + 3.11: py311 [testenv] usedevelop = True @@ -14,10 +17,11 @@ deps = -r{toxinidir}/requirements.txt -r{toxinidir}/requirements-test.txt commands = - nosetests -sv --with-xcoverage --cover-package=orquesta orquesta.tests + pytest --cov=orquesta --cov-report=term orquesta/tests [testenv:pep8] deps = + -r{toxinidir}/requirements.txt -r{toxinidir}/requirements-test.txt commands = pylint -E --rcfile={toxinidir}/.pylintrc orquesta @@ -26,7 +30,9 @@ commands = [testenv:docs] deps = + -r{toxinidir}/requirements.txt -r{toxinidir}/requirements-docs.txt + -e{toxinidir} commands = python {toxinidir}/bin/orquesta-generate-schemas sphinx-build -W -b html docs/source docs/build/html