From c0d286fc27352c2251dce93a33d3e87cd7db7718 Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 11:56:29 -0400 Subject: [PATCH 01/13] fix ci checks --- .github/workflows/build.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4837aca..c4894de 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,12 +26,16 @@ jobs: uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4 with: python-version: ${{ matrix.python-version }} - - name: Check isort, black, and flake8 - run: | - pip install black flake8 isort - isort --profile black . - black . - flake8 . + - name: + run: pip install black autoflake isort docformatter + - name: Check import order (isort) + run: isort --check --profile black . + - name: Check style + run: black --check --line-length 79 . + - name: Check linting + run: autoflake --check . + - name: check docstring formatting + run: docformatter . --check --docstring-length 1 79 - name: Install poetry run: | python -m pip install poetry==1.4.2 From ba6fcb7e09aebfee5f07e1a9f002718fa91ab28c Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 12:00:12 -0400 Subject: [PATCH 02/13] add style and lint dev dependencies --- poetry.lock | 173 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 4 ++ 2 files changed, 176 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 6345397..d1936e1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -11,6 +11,21 @@ files = [ {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, ] +[[package]] +name = "autoflake" +version = "2.2.1" +description = "Removes unused imports and unused variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "autoflake-2.2.1-py3-none-any.whl", hash = "sha256:265cde0a43c1f44ecfb4f30d95b0437796759d07be7706a2f70e4719234c0f79"}, + {file = "autoflake-2.2.1.tar.gz", hash = "sha256:62b7b6449a692c3c9b0c916919bbc21648da7281e8506bcf8d3f8280e431ebc1"}, +] + +[package.dependencies] +pyflakes = ">=3.0.0" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + [[package]] name = "babel" version = "2.13.1" @@ -28,6 +43,48 @@ setuptools = {version = "*", markers = "python_version >= \"3.12\""} [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] +[[package]] +name = "black" +version = "23.10.1" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.10.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:ec3f8e6234c4e46ff9e16d9ae96f4ef69fa328bb4ad08198c8cee45bb1f08c69"}, + {file = "black-23.10.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:1b917a2aa020ca600483a7b340c165970b26e9029067f019e3755b56e8dd5916"}, + {file = "black-23.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c74de4c77b849e6359c6f01987e94873c707098322b91490d24296f66d067dc"}, + {file = "black-23.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:7b4d10b0f016616a0d93d24a448100adf1699712fb7a4efd0e2c32bbb219b173"}, + {file = "black-23.10.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b15b75fc53a2fbcac8a87d3e20f69874d161beef13954747e053bca7a1ce53a0"}, + {file = "black-23.10.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:e293e4c2f4a992b980032bbd62df07c1bcff82d6964d6c9496f2cd726e246ace"}, + {file = "black-23.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d56124b7a61d092cb52cce34182a5280e160e6aff3137172a68c2c2c4b76bcb"}, + {file = "black-23.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:3f157a8945a7b2d424da3335f7ace89c14a3b0625e6593d21139c2d8214d55ce"}, + {file = "black-23.10.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:cfcce6f0a384d0da692119f2d72d79ed07c7159879d0bb1bb32d2e443382bf3a"}, + {file = "black-23.10.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:33d40f5b06be80c1bbce17b173cda17994fbad096ce60eb22054da021bf933d1"}, + {file = "black-23.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:840015166dbdfbc47992871325799fd2dc0dcf9395e401ada6d88fe11498abad"}, + {file = "black-23.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:037e9b4664cafda5f025a1728c50a9e9aedb99a759c89f760bd83730e76ba884"}, + {file = "black-23.10.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:7cb5936e686e782fddb1c73f8aa6f459e1ad38a6a7b0e54b403f1f05a1507ee9"}, + {file = "black-23.10.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:7670242e90dc129c539e9ca17665e39a146a761e681805c54fbd86015c7c84f7"}, + {file = "black-23.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed45ac9a613fb52dad3b61c8dea2ec9510bf3108d4db88422bacc7d1ba1243d"}, + {file = "black-23.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:6d23d7822140e3fef190734216cefb262521789367fbdc0b3f22af6744058982"}, + {file = "black-23.10.1-py3-none-any.whl", hash = "sha256:d431e6739f727bb2e0495df64a6c7a5310758e87505f5f8cde9ff6c0f2d7e4fe"}, + {file = "black-23.10.1.tar.gz", hash = "sha256:1f8ce316753428ff68749c65a5f7844631aa18c8679dfd3ca9dc1a289979c258"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "cbor2" version = "5.5.0" @@ -200,6 +257,20 @@ files = [ {file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"}, ] +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -286,6 +357,24 @@ files = [ {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, ] +[[package]] +name = "docformatter" +version = "1.7.5" +description = "Formats docstrings to follow PEP 257" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "docformatter-1.7.5-py3-none-any.whl", hash = "sha256:a24f5545ed1f30af00d106f5d85dc2fce4959295687c24c8f39f5263afaf9186"}, + {file = "docformatter-1.7.5.tar.gz", hash = "sha256:ffed3da0daffa2e77f80ccba4f0e50bfa2755e1c10e130102571c890a61b246e"}, +] + +[package.dependencies] +charset_normalizer = ">=3.0.0,<4.0.0" +untokenize = ">=0.1.1,<0.2.0" + +[package.extras] +tomli = ["tomli (>=2.0.0,<3.0.0)"] + [[package]] name = "docutils" version = "0.18.1" @@ -379,6 +468,23 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +[[package]] +name = "isort" +version = "5.12.0" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, +] + +[package.extras] +colors = ["colorama (>=0.4.3)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + [[package]] name = "jinja2" version = "3.1.2" @@ -465,6 +571,17 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + [[package]] name = "nodeenv" version = "1.8.0" @@ -490,6 +607,17 @@ files = [ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] +[[package]] +name = "pathspec" +version = "0.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + [[package]] name = "platformdirs" version = "3.11.0" @@ -523,6 +651,17 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "pyflakes" +version = "3.1.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, +] + [[package]] name = "pygments" version = "2.16.1" @@ -816,6 +955,38 @@ Sphinx = ">=5" lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, +] + +[[package]] +name = "untokenize" +version = "0.1.1" +description = "Transforms tokens into original source code (while preserving whitespace)." +optional = false +python-versions = "*" +files = [ + {file = "untokenize-0.1.1.tar.gz", hash = "sha256:3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2"}, +] + [[package]] name = "urllib3" version = "2.0.7" @@ -871,4 +1042,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "1a33692cc1b1e0ab8d55780e0799509905491d02ea572f60258e2b7eeff47f49" +content-hash = "9ca1b72e3a8499baabc4a8386fe20b3c571591bd1ba8b713af8f8f845aa0792c" diff --git a/pyproject.toml b/pyproject.toml index 6928ca0..4488fca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,10 @@ html2text = "^2020.1.16" pre-commit = "^3.0.0" sphinx = "^7.0.0" sphinx-rtd-theme = "^1.1.1" +isort = "^5.12.0" +black = "^23.10.1" +autoflake = "^2.2.1" +docformatter = "^1.7.5" [build-system] From ed318236f765f83e86707c12898927f5676e64b2 Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 12:01:40 -0400 Subject: [PATCH 03/13] sort imports --- src/arcaflow_plugin_sdk/atp.py | 8 +++----- src/arcaflow_plugin_sdk/predefined_schemas.py | 1 - src/arcaflow_plugin_sdk/test_atp.py | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/arcaflow_plugin_sdk/atp.py b/src/arcaflow_plugin_sdk/atp.py index 2c8fd5b..6ed2e4d 100644 --- a/src/arcaflow_plugin_sdk/atp.py +++ b/src/arcaflow_plugin_sdk/atp.py @@ -16,19 +16,17 @@ import dataclasses import io import os +import signal import sys -import typing import threading -import signal import traceback +import typing +from enum import IntEnum import cbor2 -from enum import IntEnum - from arcaflow_plugin_sdk import schema - ATP_SERVER_VERSION = 3 diff --git a/src/arcaflow_plugin_sdk/predefined_schemas.py b/src/arcaflow_plugin_sdk/predefined_schemas.py index 5c59a16..43bbdc4 100644 --- a/src/arcaflow_plugin_sdk/predefined_schemas.py +++ b/src/arcaflow_plugin_sdk/predefined_schemas.py @@ -2,7 +2,6 @@ from arcaflow_plugin_sdk import schema - cancel_signal_input_schema = schema.ScopeSchema( { "cancelInput": schema.ObjectSchema( diff --git a/src/arcaflow_plugin_sdk/test_atp.py b/src/arcaflow_plugin_sdk/test_atp.py index f9aacb6..b8719ec 100644 --- a/src/arcaflow_plugin_sdk/test_atp.py +++ b/src/arcaflow_plugin_sdk/test_atp.py @@ -4,7 +4,7 @@ import time import unittest from threading import Event -from typing import TextIO, Tuple, Union, List +from typing import List, TextIO, Tuple, Union from arcaflow_plugin_sdk import atp, plugin, schema From 99d413f5ccb7952f97a23ab0058beb039599e90c Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 12:32:55 -0400 Subject: [PATCH 04/13] add flake8 check to ci --- .github/workflows/build.yml | 18 +++++++++++------ poetry.lock | 40 ++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c4894de..1b572a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,16 +26,22 @@ jobs: uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4 with: python-version: ${{ matrix.python-version }} - - name: - run: pip install black autoflake isort docformatter + - name: install python style and linting tools + run: pip install black flake8 autoflake isort docformatter + - name: Check unused code + run: autoflake --remove-all-unused-imports --remove-unused-variables --check . - name: Check import order (isort) - run: isort --check --profile black . + run: isort --check --profile black --line-length 79 . - name: Check style run: black --check --line-length 79 . - - name: Check linting - run: autoflake --check . - - name: check docstring formatting + - name: Check docstring formatting run: docformatter . --check --docstring-length 1 79 + - name: Error linting + id: flake8 + run: flake8 --max-line-length 79 --ignore E203 . + - name: Check error linting + if: steps.flake8.outputs.number > 0 + run: exit 1 - name: Install poetry run: | python -m pip install poetry==1.4.2 diff --git a/poetry.lock b/poetry.lock index d1936e1..6642c77 100644 --- a/poetry.lock +++ b/poetry.lock @@ -402,6 +402,22 @@ docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] typing = ["typing-extensions (>=4.7.1)"] +[[package]] +name = "flake8" +version = "6.1.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" + [[package]] name = "html2text" version = "2020.1.16" @@ -571,6 +587,17 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -651,6 +678,17 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + [[package]] name = "pyflakes" version = "3.1.0" @@ -1042,4 +1080,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "9ca1b72e3a8499baabc4a8386fe20b3c571591bd1ba8b713af8f8f845aa0792c" +content-hash = "c730f712cb131107efaa65ca92c6931bdf4833003ec715b287713312655269d3" diff --git a/pyproject.toml b/pyproject.toml index 4488fca..2918907 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ isort = "^5.12.0" black = "^23.10.1" autoflake = "^2.2.1" docformatter = "^1.7.5" +flake8 = "^6.1.0" [build-system] From 24b76be8b4aa896b1ae4f84cd590a61ce33b7340 Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 12:34:02 -0400 Subject: [PATCH 05/13] execute style formatting --- src/arcaflow_plugin_sdk/atp.py | 182 +++++-- src/arcaflow_plugin_sdk/plugin.py | 37 +- src/arcaflow_plugin_sdk/schema.py | 528 ++++++++++++++++----- src/arcaflow_plugin_sdk/serialization.py | 12 +- src/arcaflow_plugin_sdk/test_atp.py | 164 +++++-- src/arcaflow_plugin_sdk/test_jsonschema.py | 17 +- src/arcaflow_plugin_sdk/test_plugin.py | 8 +- src/arcaflow_plugin_sdk/test_schema.py | 159 +++++-- test_example_plugin.py | 16 +- 9 files changed, 829 insertions(+), 294 deletions(-) diff --git a/src/arcaflow_plugin_sdk/atp.py b/src/arcaflow_plugin_sdk/atp.py index 6ed2e4d..614a1d1 100644 --- a/src/arcaflow_plugin_sdk/atp.py +++ b/src/arcaflow_plugin_sdk/atp.py @@ -36,6 +36,7 @@ class MessageType(IntEnum): in the data field. The corresponding class can then be used to deserialize the inner data. Look at the go SDK for the reference data structure. """ + WORK_START = 1 WORK_DONE = 2 SIGNAL = 3 @@ -81,10 +82,10 @@ class ATPServer: running_threads: typing.List[threading.Thread] def __init__( - self, - input_pipe: io.FileIO, - output_pipe: io.FileIO, - stderr: io.FileIO, + self, + input_pipe: io.FileIO, + output_pipe: io.FileIO, + stderr: io.FileIO, ) -> None: self.input_pipe = input_pipe self.output_pipe = output_pipe @@ -100,7 +101,9 @@ def run_plugin( """ This function wraps running a plugin. """ - signal.signal(signal.SIGINT, signal.SIG_IGN) # Ignore sigint. Only care about arcaflow signals. + signal.signal( + signal.SIGINT, signal.SIG_IGN + ) # Ignore sigint. Only care about arcaflow signals. if os.isatty(self.output_pipe.fileno()): print("Cannot run plugin in ATP mode on an interactive terminal.") return 1 @@ -133,8 +136,12 @@ def handle_handshake(self): self.decoder.decode() # Serialize then send HelloMessage - start_hello_message = HelloMessage(ATP_SERVER_VERSION, self.plugin_schema) - serialized_message = _HELLO_MESSAGE_SCHEMA.serialize(start_hello_message) + start_hello_message = HelloMessage( + ATP_SERVER_VERSION, self.plugin_schema + ) + serialized_message = _HELLO_MESSAGE_SCHEMA.serialize( + start_hello_message + ) self.send_message(serialized_message) def run_server_read_loop(self) -> None: @@ -145,8 +152,12 @@ def run_server_read_loop(self) -> None: msg_id = runtime_msg.get("id", None) # Validate if msg_id is None: - self.send_error_message("", step_fatal=False, server_fatal=True, - error_msg="Runtime message is missing the 'id' field.") + self.send_error_message( + "", + step_fatal=False, + server_fatal=True, + error_msg="Runtime message is missing the 'id' field.", + ) return run_id = runtime_msg["run_id"] # Then take action @@ -155,67 +166,121 @@ def run_server_read_loop(self) -> None: try: self.handle_work_start(run_id, work_start_msg) except Exception as e: - self.send_error_message(run_id, step_fatal=True, server_fatal=False, - error_msg="Exception while handling work start: " - f"{e} {traceback.format_exc()}") + self.send_error_message( + run_id, + step_fatal=True, + server_fatal=False, + error_msg="Exception while handling work start: " + f"{e} {traceback.format_exc()}", + ) elif msg_id == MessageType.SIGNAL: signal_msg = runtime_msg["data"] try: self.handle_signal(run_id, signal_msg) except Exception as e: - self.send_error_message(run_id, step_fatal=False, server_fatal=False, - error_msg=f"Exception while handling signal: {e} {traceback.format_exc()}") + self.send_error_message( + run_id, + step_fatal=False, + server_fatal=False, + error_msg=f"Exception while handling signal: {e} {traceback.format_exc()}", + ) elif msg_id == MessageType.CLIENT_DONE: return else: - self.send_error_message(run_id, step_fatal=False, server_fatal=False, - error_msg=f"Unknown runtime message ID: {msg_id}") - self.stderr.write(f"Unknown kind of runtime message: {msg_id}") + self.send_error_message( + run_id, + step_fatal=False, + server_fatal=False, + error_msg=f"Unknown runtime message ID: {msg_id}", + ) + self.stderr.write( + f"Unknown kind of runtime message: {msg_id}" + ) except cbor2.CBORDecodeError as err: self.stderr.write(f"Error while decoding CBOR: {err}") - self.send_error_message("", step_fatal=False, server_fatal=True, - error_msg=f"Error occurred while decoding CBOR: {err} {traceback.format_exc()}") + self.send_error_message( + "", + step_fatal=False, + server_fatal=True, + error_msg=f"Error occurred while decoding CBOR: {err} {traceback.format_exc()}", + ) except Exception as e: - self.send_error_message("", step_fatal=False, server_fatal=True, - error_msg=f"Exception occurred in ATP server read loop: {e} {traceback.format_exc()}") + self.send_error_message( + "", + step_fatal=False, + server_fatal=True, + error_msg=f"Exception occurred in ATP server read loop: {e} {traceback.format_exc()}", + ) def handle_signal(self, run_id, signal_msg): saved_step_id = self.step_ids[run_id] received_signal_id = signal_msg["signal_id"] - unserialized_data = self.plugin_schema.unserialize_signal_handler_input( - saved_step_id, - received_signal_id, - signal_msg["data"] + unserialized_data = ( + self.plugin_schema.unserialize_signal_handler_input( + saved_step_id, received_signal_id, signal_msg["data"] + ) ) # The data is verified and unserialized. Now call the signal in its own thread. - run_thread = threading.Thread(target=self.run_signal, - args=(run_id, saved_step_id, received_signal_id, unserialized_data)) + run_thread = threading.Thread( + target=self.run_signal, + args=( + run_id, + saved_step_id, + received_signal_id, + unserialized_data, + ), + ) self.running_threads.append(run_thread) run_thread.start() - def run_signal(self, run_id: str, step_id: str, signal_id: str, unserialized_input_param: any): + def run_signal( + self, + run_id: str, + step_id: str, + signal_id: str, + unserialized_input_param: any, + ): try: - self.plugin_schema.call_step_signal(run_id, step_id, signal_id, unserialized_input_param) + self.plugin_schema.call_step_signal( + run_id, step_id, signal_id, unserialized_input_param + ) except Exception as e: - self.send_error_message(run_id, step_fatal=False, server_fatal=False, - error_msg=f"Error while calling signal for step with run ID {run_id}: {e} " - f"{traceback.format_exc()}" - ) + self.send_error_message( + run_id, + step_fatal=False, + server_fatal=False, + error_msg=f"Error while calling signal for step with run ID {run_id}: {e} " + f"{traceback.format_exc()}", + ) - def handle_work_start(self, run_id: str, work_start_msg: typing.Dict[str, any]): + def handle_work_start( + self, run_id: str, work_start_msg: typing.Dict[str, any] + ): if work_start_msg is None: - self.send_error_message(run_id, step_fatal=True, server_fatal=False, - error_msg="Work start message is None.") + self.send_error_message( + run_id, + step_fatal=True, + server_fatal=False, + error_msg="Work start message is None.", + ) return if "id" not in work_start_msg: - self.send_error_message(run_id, step_fatal=True, server_fatal=False, - error_msg="Work start message is missing the 'id' field.") + self.send_error_message( + run_id, + step_fatal=True, + server_fatal=False, + error_msg="Work start message is missing the 'id' field.", + ) return if "config" not in work_start_msg: - self.send_error_message(run_id, step_fatal=True, server_fatal=False, - error_msg="Work start message is missing the 'config' field.") + self.send_error_message( + run_id, + step_fatal=True, + server_fatal=False, + error_msg="Work start message is missing the 'config' field.", + ) return # Save for later self.step_ids[run_id] = work_start_msg["id"] @@ -227,9 +292,11 @@ def handle_work_start(self, run_id: str, work_start_msg: typing.Dict[str, any]): run_id, work_start_msg["id"], work_start_msg["config"], - ) + ), ) - self.running_threads.append(run_thread) # Save so that we can join with it at the end. + self.running_threads.append( + run_thread + ) # Save so that we can join with it at the end. run_thread.start() def start_step(self, run_id: str, step_id: str, config: typing.Any): @@ -237,7 +304,7 @@ def start_step(self, run_id: str, step_id: str, config: typing.Any): output_id, output_data = self.plugin_schema.call_step( run_id, step_id, - self.plugin_schema.unserialize_step_input(step_id, config) + self.plugin_schema.unserialize_step_input(step_id, config), ) # Send WorkDoneMessage @@ -253,9 +320,13 @@ def start_step(self, run_id: str, step_id: str, config: typing.Any): }, ) except Exception as e: - self.send_error_message(run_id, step_fatal=True, server_fatal=False, - error_msg=f"Error while calling step {run_id}/{step_id}:" - f"{e} {traceback.format_exc()}") + self.send_error_message( + run_id, + step_fatal=True, + server_fatal=False, + error_msg=f"Error while calling step {run_id}/{step_id}:" + f"{e} {traceback.format_exc()}", + ) return def send_message(self, data: any): @@ -263,7 +334,9 @@ def send_message(self, data: any): self.encoder.encode(data) self.output_pipe.flush() # Sends it to the ATP client immediately. - def send_runtime_message(self, message_type: MessageType, run_id: str, data: any): + def send_runtime_message( + self, message_type: MessageType, run_id: str, data: any + ): self.send_message( { "id": message_type, @@ -272,7 +345,9 @@ def send_runtime_message(self, message_type: MessageType, run_id: str, data: any } ) - def send_error_message(self, run_id: str, step_fatal: bool, server_fatal: bool, error_msg: str): + def send_error_message( + self, run_id: str, step_fatal: bool, server_fatal: bool, error_msg: str + ): self.send_runtime_message( MessageType.ERROR, run_id, @@ -346,7 +421,7 @@ def start_work(self, run_id: str, step_id: str, config: any): { "id": step_id, "config": config, - } + }, ) def send_signal(self, run_id: str, signal_id: str, input_data: any): @@ -359,13 +434,15 @@ def send_signal(self, run_id: str, signal_id: str, input_data: any): { "signal_id": signal_id, "data": input_data, - } + }, ) def send_client_done(self): self.send_runtime_message(MessageType.CLIENT_DONE, "", {}) - def send_runtime_message(self, message_type: MessageType, run_id: str, data: any): + def send_runtime_message( + self, message_type: MessageType, run_id: str, data: any + ): self.encoder.encode( { "id": message_type, @@ -407,7 +484,8 @@ def read_single_result(self) -> StepResult: continue elif msg_id == MessageType.ERROR: raise PluginClientStateException( - "Error received from ATP Server (plugin): " + str(runtime_msg['data']).replace('\\n', '\n') + "Error received from ATP Server (plugin): " + + str(runtime_msg["data"]).replace("\\n", "\n") ) else: raise PluginClientStateException( diff --git a/src/arcaflow_plugin_sdk/plugin.py b/src/arcaflow_plugin_sdk/plugin.py index fb43843..b283671 100644 --- a/src/arcaflow_plugin_sdk/plugin.py +++ b/src/arcaflow_plugin_sdk/plugin.py @@ -45,7 +45,9 @@ def signal_handler( :return: A schema for the signal. """ - def signal_decorator(func: _step_decorator_param) -> schema.SignalHandlerType: + def signal_decorator( + func: _step_decorator_param, + ) -> schema.SignalHandlerType: if id == "": raise BadArgumentException("Signals cannot have empty IDs") if name == "": @@ -53,8 +55,8 @@ def signal_decorator(func: _step_decorator_param) -> schema.SignalHandlerType: sig = inspect.signature(func) if len(sig.parameters) != 2: raise BadArgumentException( - "The '%s' (id: %s) signal must have exactly two parameters, including self. Currently has %s" % - (name, id, str(sig.parameters)) + "The '%s' (id: %s) signal must have exactly two parameters, including self. Currently has %s" + % (name, id, str(sig.parameters)) ) input_param = list(sig.parameters.values())[1] if input_param.annotation is inspect.Parameter.empty: @@ -107,6 +109,7 @@ def step_with_signals( :param icon: SVG icon for this step. :return: A schema for the step. """ + def step_decorator(func: _step_object_decorator_param) -> schema.StepType: if id == "": raise BadArgumentException("Steps cannot have an empty ID") @@ -115,8 +118,8 @@ def step_decorator(func: _step_object_decorator_param) -> schema.StepType: sig = inspect.signature(func) if len(sig.parameters) != 2: raise BadArgumentException( - "The '%s' (id: %s) step must have exactly two parameters, including self. Currently has %d" % - (name, id, len(sig.parameters)) + "The '%s' (id: %s) step must have exactly two parameters, including self. Currently has %d" + % (name, id, len(sig.parameters)) ) input_param = list(sig.parameters.values())[1] if input_param.annotation is inspect.Parameter.empty: @@ -186,7 +189,8 @@ def step_decorator(func: _step_decorator_param) -> schema.StepType: sig = inspect.signature(func) if len(sig.parameters) != 1: raise BadArgumentException( - "The '%s' (id: %s) step must have exactly one parameter: step input object" % (name, id) + "The '%s' (id: %s) step must have exactly one parameter: step input object" + % (name, id) ) input_param = list(sig.parameters.values())[0] if input_param.annotation is inspect.Parameter.empty: @@ -317,13 +321,17 @@ def run( if options.json_schema is not None: if action is not None: raise _ExitException( - 64, "--{} and --json-schema cannot be used together".format(action) + 64, + "--{} and --json-schema cannot be used together".format( + action + ), ) action = "json-schema" if options.schema is not None: if action is not None: raise _ExitException( - 64, "--{} and --schema cannot be used together".format(action) + 64, + "--{} and --schema cannot be used together".format(action), ) action = "schema" if options.atp is not None: @@ -355,7 +363,10 @@ def run( return _execute_file(step_id, s, options, stdin, stdout, stderr) elif action == "atp": from arcaflow_plugin_sdk import atp - atp_server = atp.ATPServer(stdin.buffer, stdout.buffer, stdout.buffer) + + atp_server = atp.ATPServer( + stdin.buffer, stdout.buffer, stdout.buffer + ) return atp_server.run_plugin(s) elif action == "json-schema": return _print_json_schema(step_id, s, options, stdout) @@ -487,14 +498,18 @@ def _print_json_schema(step_id, s, options, stdout): if step_id not in s.steps: raise _ExitException( 64, - 'Unknown step "{}". Steps: {}'.format(step_id, str(list(s.steps.keys()))), + 'Unknown step "{}". Steps: {}'.format( + step_id, str(list(s.steps.keys())) + ), ) if options.json_schema == "input": data = jsonschema.step_input(s.steps[step_id]) elif options.json_schema == "output": data = jsonschema.step_outputs(s.steps[step_id]) else: - raise _ExitException(64, "--json-schema must be one of 'input' or 'output'") + raise _ExitException( + 64, "--json-schema must be one of 'input' or 'output'" + ) stdout.write(json.dumps(data, indent=" ")) return 0 diff --git a/src/arcaflow_plugin_sdk/schema.py b/src/arcaflow_plugin_sdk/schema.py index 5020483..b9aa6a9 100644 --- a/src/arcaflow_plugin_sdk/schema.py +++ b/src/arcaflow_plugin_sdk/schema.py @@ -102,7 +102,9 @@ class ConstraintException(Exception): def __str__(self): if len(self.path) == 0: return "Validation failed: {}".format(self.msg) - return "Validation failed for '{}': {}".format(" -> ".join(self.path), self.msg) + return "Validation failed for '{}': {}".format( + " -> ".join(self.path), self.msg + ) @dataclass @@ -433,7 +435,9 @@ def call(t): ): raise InvalidAnnotationException( "units", - "expected int or float schema, found {}".format(type(t).__name__), + "expected int or float schema, found {}".format( + type(t).__name__ + ), ) effective_t.units = units return t @@ -483,7 +487,8 @@ def example( marshalled_example = json.dumps(example) except Exception as e: raise InvalidAnnotationException( - "example", "expected a JSON-serializable type, {}".format(e.__str__()) + "example", + "expected a JSON-serializable type, {}".format(e.__str__()), ) from e def call(t): @@ -505,17 +510,20 @@ def call(t): discriminatorT = typing.TypeVar( "discriminatorT", bound=typing.Union[ - typing.ForwardRef("OneOfStringSchema"), typing.ForwardRef("OneOfIntSchema") + typing.ForwardRef("OneOfStringSchema"), + typing.ForwardRef("OneOfIntSchema"), ], ) discriminatorFunc = typing.Callable[ [ typing.Union[ - typing.ForwardRef("OneOfStringSchema"), typing.ForwardRef("OneOfIntSchema") + typing.ForwardRef("OneOfStringSchema"), + typing.ForwardRef("OneOfIntSchema"), ] ], typing.Union[ - typing.ForwardRef("OneOfStringSchema"), typing.ForwardRef("OneOfIntSchema") + typing.ForwardRef("OneOfStringSchema"), + typing.ForwardRef("OneOfIntSchema"), ], ] @@ -565,7 +573,9 @@ def call(t): :param typing.Union[OneOfStringSchema, OneOfIntSchema] t: :return typing.Union[OneOfStringSchema, OneOfIntSchema]: """ - if not isinstance(t, OneOfStringSchema) and not isinstance(t, OneOfIntSchema): + if not isinstance(t, OneOfStringSchema) and not isinstance( + t, OneOfIntSchema + ): raise InvalidAnnotationException( "discriminator", "expected a property or object type with union member, found {}".format( @@ -608,7 +618,9 @@ def call(t): _discriminator = discriminator -def discriminator_value(discriminator_value: typing.Union[str, int, enum.Enum]): +def discriminator_value( + discriminator_value: typing.Union[str, int, enum.Enum] +): """ This annotation adds a custom value for an instance of a discriminator. The value must match the discriminator field This annotation works only when used in conjunction with discriminator(). @@ -1153,13 +1165,19 @@ def call(t): name("Float"), ], typing.Annotated[ - typing.ForwardRef("BoolSchema"), discriminator_value("bool"), name("Bool") + typing.ForwardRef("BoolSchema"), + discriminator_value("bool"), + name("Bool"), ], typing.Annotated[ - typing.ForwardRef("ListSchema"), discriminator_value("list"), name("List") + typing.ForwardRef("ListSchema"), + discriminator_value("list"), + name("List"), ], typing.Annotated[ - typing.ForwardRef("MapSchema"), discriminator_value("map"), name("Map") + typing.ForwardRef("MapSchema"), + discriminator_value("map"), + name("Map"), ], typing.Annotated[ typing.ForwardRef("ScopeSchema"), @@ -1187,7 +1205,9 @@ def call(t): name("Object reference"), ], typing.Annotated[ - typing.ForwardRef("AnySchema"), discriminator_value("any"), name("Any") + typing.ForwardRef("AnySchema"), + discriminator_value("any"), + name("Any"), ], ], discriminator("type_id"), @@ -1239,9 +1259,15 @@ def call(t): _description("Example values for this property, encoded as JSON."), ] _OBJECT_LIKE = typing.Union[ - typing.Annotated[typing.ForwardRef("RefSchema"), discriminator_value("ref")], - typing.Annotated[typing.ForwardRef("ScopeSchema"), discriminator_value("scope")], - typing.Annotated[typing.ForwardRef("ObjectSchema"), discriminator_value("object")], + typing.Annotated[ + typing.ForwardRef("RefSchema"), discriminator_value("ref") + ], + typing.Annotated[ + typing.ForwardRef("ScopeSchema"), discriminator_value("scope") + ], + typing.Annotated[ + typing.ForwardRef("ObjectSchema"), discriminator_value("object") + ], ] _id_type_inverse_re = re.compile("[^$@a-zA-Z0-9-_]") @@ -1261,6 +1287,7 @@ def _id_typeize(input: str) -> str: """ return re.sub(_id_type_inverse_re, "_", input) + # endregion # region Schema @@ -1339,7 +1366,9 @@ class Unit: name_short_plural: typing.Annotated[ str, name("Short name (plural)"), - description("Short name that can be printed in a few characters, plural form."), + description( + "Short name that can be printed in a few characters, plural form." + ), example("B"), example("chars"), ] @@ -1490,7 +1519,9 @@ class Units: multipliers: typing.Annotated[ Optional[Dict[int, Unit]], _name("Multipliers"), - _description("A set of multiplies that describe multiple units of scale."), + _description( + "A set of multiplies that describe multiple units of scale." + ), _example( { 1024: { @@ -1509,7 +1540,9 @@ class Units: ), ] = None - def __init__(self, base_unit: Unit, multipliers: Optional[Dict[int, Unit]] = None): + def __init__( + self, base_unit: Unit, multipliers: Optional[Dict[int, Unit]] = None + ): self.base_unit = base_unit self.multipliers = multipliers self.__unit_re_cache = None @@ -1543,7 +1576,8 @@ def parse(self, data: str) -> typing.Union[int, float]: """ if data.strip() == "": raise UnitParseException( - "Empty string cannot be parsed as " + self.base_unit.name_long_plural + "Empty string cannot be parsed as " + + self.base_unit.name_long_plural ) if self.__unit_re_cache is None: parts = [] @@ -1586,7 +1620,9 @@ def parse(self, data: str) -> typing.Union[int, float]: "Cannot parse '{}' as '{}': invalid format, valid unit types are: '{}'".format( data, self.base_unit.name_long_plural, - "', '".join(collections.OrderedDict.fromkeys(valid_units).keys()), + "', '".join( + collections.OrderedDict.fromkeys(valid_units).keys() + ), ) ) number = 0 @@ -1840,7 +1876,11 @@ def _to_openapi_fragment( This method generates an OpenAPI fragment for string enums. See: https://spec.openapis.org/oas/v3.1.0#data-types """ - return {"type": "integer", "format": "int64", "enum": list(self.values.keys())} + return { + "type": "integer", + "format": "int64", + "enum": list(self.values.keys()), + } @dataclass @@ -2272,7 +2312,9 @@ def _to_jsonschema_fragment( result = { "type": "object", "propertyNames": self.keys._to_jsonschema_fragment(scope, defs), - "additionalProperties": self.values._to_jsonschema_fragment(scope, defs), + "additionalProperties": self.values._to_jsonschema_fragment( + scope, defs + ), } # Sadly, these properties are not supported by JSON schema @@ -2299,7 +2341,9 @@ def _to_openapi_fragment( result = { "type": "object", "propertyNames": self.keys._to_openapi_fragment(scope, defs), - "additionalProperties": self.values._to_openapi_fragment(scope, defs), + "additionalProperties": self.values._to_openapi_fragment( + scope, defs + ), } # Sadly, these properties are not supported by JSON schema @@ -2352,7 +2396,9 @@ class PropertySchema(_JSONSchemaGenerator, _OpenAPIGenerator): """ type: typing.Annotated[ - VALUE_TYPE, _name("Type"), _description("Type definition for this field.") + VALUE_TYPE, + _name("Type"), + _description("Type definition for this field."), ] display: DISPLAY_TYPE = None default: DEFAULT_TYPE = None @@ -2458,7 +2504,9 @@ class ObjectSchema(_JSONSchemaGenerator, _OpenAPIGenerator): id: typing.Annotated[ ID_TYPE, _name("ID"), - _description("Unique identifier for this object within the current scope."), + _description( + "Unique identifier for this object within the current scope." + ), ] properties: typing.Annotated[ Dict[str, PropertySchema], @@ -2608,11 +2656,15 @@ def _to_jsonschema_fragment( discriminated_object = defs.defs[v.id] - discriminated_object["properties"][self.discriminator_field_name] = { + discriminated_object["properties"][ + self.discriminator_field_name + ] = { "type": "string", "const": k, } - discriminated_object["required"].insert(0, self.discriminator_field_name) + discriminated_object["required"].insert( + 0, self.discriminator_field_name + ) if v.display is not None: if v.display.name is not None: discriminated_object["title"] = v.display.name @@ -2636,10 +2688,14 @@ def _to_openapi_fragment( discriminated_object = defs.components[v.id] - discriminated_object["properties"][self.discriminator_field_name] = { + discriminated_object["properties"][ + self.discriminator_field_name + ] = { "type": "string", } - discriminated_object["required"].insert(0, self.discriminator_field_name) + discriminated_object["required"].insert( + 0, self.discriminator_field_name + ) if v.display is not None: if v.display.name is not None: discriminated_object["title"] = v.display.name @@ -2730,11 +2786,15 @@ def _to_jsonschema_fragment( discriminated_object = defs.defs[v.id] - discriminated_object["properties"][self.discriminator_field_name] = { + discriminated_object["properties"][ + self.discriminator_field_name + ] = { "type": "string", "const": str(k), } - discriminated_object["required"].insert(0, self.discriminator_field_name) + discriminated_object["required"].insert( + 0, self.discriminator_field_name + ) if v.display is not None: if v.display.name is not None: discriminated_object["title"] = v.display.name @@ -2758,10 +2818,14 @@ def _to_openapi_fragment( discriminated_object = defs.components[v.id] - discriminated_object["properties"][self.discriminator_field_name] = { + discriminated_object["properties"][ + self.discriminator_field_name + ] = { "type": "string", } - discriminated_object["required"].insert(0, self.discriminator_field_name) + discriminated_object["required"].insert( + 0, self.discriminator_field_name + ) if v.display is not None: if v.display.name is not None: discriminated_object["title"] = v.display.name @@ -3003,7 +3067,9 @@ class StepOutputSchema(_JSONSchemaGenerator, _OpenAPIGenerator): error: typing.Annotated[ bool, _name("Error"), - _description("If set to true, this output will be treated as an error output."), + _description( + "If set to true, this output will be treated as an error output." + ), ] = False def to_jsonschema(self): @@ -3092,7 +3158,9 @@ def to_openapi(self): """ return self._to_openapi_fragment(self.schema, _OpenAPIComponents()) - def _to_jsonschema_fragment(self, scope: ScopeSchema, defs: _JSONSchemaDefs) -> any: + def _to_jsonschema_fragment( + self, scope: ScopeSchema, defs: _JSONSchemaDefs + ) -> any: # noinspection PyProtectedMember return self.schema._to_jsonschema_fragment(scope, defs) @@ -3111,15 +3179,12 @@ class SignalSchema: To create, set the ID, and create a scope for the data input or output. """ + id: typing.Annotated[ - ID_TYPE, - _name("ID"), - _description("Machine identifier for this step.") + ID_TYPE, _name("ID"), _description("Machine identifier for this step.") ] data_schema: typing.Annotated[ - ScopeSchema, - _name("Data"), - _description("Input or output data schema") + ScopeSchema, _name("Data"), _description("Input or output data schema") ] display: DISPLAY_TYPE = None @@ -3196,7 +3261,6 @@ class StepSchema: Dict[ID_TYPE, SignalSchema], _name("Signal handlers"), _description("Signals that are input by the step."), - ] = None signal_emitters: typing.Annotated[ Dict[ID_TYPE, SignalSchema], @@ -3233,7 +3297,9 @@ class AbstractType(Generic[TypeT]): """ @abstractmethod - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> TypeT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> TypeT: """ This function takes the underlying raw data and decodes it into the underlying advanced data type (e.g. dataclass) for usage. @@ -3257,7 +3323,9 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): """ @abstractmethod - def serialize(self, data: TypeT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: TypeT, path: typing.Tuple[str] = tuple([]) + ) -> Any: """ This function serializes the passed data into it's raw form for transport, e.g. string, int, dicts, list. @@ -3279,7 +3347,9 @@ class _EnumType(AbstractType, Generic[EnumT]): _type: Type[EnumT] - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> EnumT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> EnumT: if isinstance(data, Enum): if data not in self._type: raise ConstraintException( @@ -3295,7 +3365,9 @@ def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> EnumT: return v raise ConstraintException( path, - "'{}' is not a valid value for '{}'".format(data, self._type.__name__), + "'{}' is not a valid value for '{}'".format( + data, self._type.__name__ + ), ) def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): @@ -3313,10 +3385,14 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): return raise ConstraintException( path, - "'{}' is not a valid value for '{}'".format(data, self._type.__name__), + "'{}' is not a valid value for '{}'".format( + data, self._type.__name__ + ), ) - def serialize(self, data: EnumT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: EnumT, path: typing.Tuple[str] = tuple([]) + ) -> Any: if data not in self._type: raise ConstraintException( path, @@ -3461,7 +3537,9 @@ class BoolType(BoolSchema, AbstractType): Now you can use the type to unseralize, validate, or serialize values. """ - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> TypeT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> TypeT: """ This function unserializes a bool value from a variety of types. @@ -3588,7 +3666,9 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): path, "Boolean value expected, {} found".format(type(data)) ) - def serialize(self, data: TypeT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: TypeT, path: typing.Tuple[str] = tuple([]) + ) -> Any: """ This function serializes a bool value. @@ -3641,7 +3721,9 @@ class StringType(StringSchema, AbstractType): You can now unserialize, validate, or serialize the data. """ - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> str: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> str: """ Unserializes the current string from an integer or string. @@ -3713,7 +3795,10 @@ def validate(self, data: str, path: typing.Tuple[str] = tuple([])): ) if self.pattern is not None and not self.pattern.match(string): raise ConstraintException( - path, "String must match the pattern {}".format(self.pattern.pattern) + path, + "String must match the pattern {}".format( + self.pattern.pattern + ), ) def serialize(self, data: str, path: typing.Tuple[str] = tuple([])) -> any: @@ -3736,7 +3821,9 @@ class PatternType(PatternSchema, AbstractType): You can now unserialize, validate, or serialize the data. """ - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> re.Pattern: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> re.Pattern: """ This function unserializes a regular expression from a string. @@ -3804,7 +3891,9 @@ def validate(self, data: re.Pattern, path: typing.Tuple[str] = tuple([])): if not isinstance(data, re.Pattern): raise ConstraintException(path, "Not a regular expression") - def serialize(self, data: re.Pattern, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: re.Pattern, path: typing.Tuple[str] = tuple([]) + ) -> Any: if not isinstance(data, re.Pattern): raise ConstraintException(path, "Must be a re.Pattern") return data.pattern @@ -3831,7 +3920,9 @@ class IntType(IntSchema, AbstractType): Now you can use this type to unserialize, validate, or serialize. """ - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> int: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> int: """ This function can unserialize a number for a integers or strings. If the passed data is a string, it can take the unit of the current type into account. @@ -3888,7 +3979,9 @@ def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> int: try: data = int(data) except ValueError as e: - raise ConstraintException(path, "Must be an integer") from e + raise ConstraintException( + path, "Must be an integer" + ) from e self.validate(data, path) return data @@ -3932,7 +4025,8 @@ def validate(self, data: int, path: typing.Tuple[str] = tuple([])): """ if not isinstance(data, int): raise ConstraintException( - path, "Must be an integer, {} given".format(type(data).__name__) + path, + "Must be an integer, {} given".format(type(data).__name__), ) integer = int(data) if self.min is not None and integer < self.min: @@ -4020,7 +4114,9 @@ class FloatType(FloatSchema, AbstractType): Now you can use this type to unserialize, validate, or serialize. """ - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> float: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> float: """ This function can unserialize a number for a integers or strings. If the passed data is a string, it can take the unit of the current type into account. @@ -4154,7 +4250,9 @@ def validate(self, data: float, path: typing.Tuple[str] = tuple([])): ) raise ConstraintException(path, "Must be at most {}".format(num)) - def serialize(self, data: float, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: float, path: typing.Tuple[str] = tuple([]) + ) -> Any: """ This function will return a float for the base unit of this value. @@ -4222,7 +4320,9 @@ class ListType(ListSchema, AbstractType, Generic[ListT]): Now you can use the list type to unserialize, validate, and serialize. """ - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> ListT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> ListT: """ This function unserializes the list itself, and also unserializes the underlying type. @@ -4322,7 +4422,9 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): new_path.append("item " + str(i)) self.items.validate(data[i], tuple(new_path)) - def serialize(self, data: ListT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: ListT, path: typing.Tuple[str] = tuple([]) + ) -> Any: """ This function serializes the list elements into a list for transport. @@ -4362,11 +4464,16 @@ def _validate(self, data, path): if self.min is not None and len(data) < self.min: raise ConstraintException( path, - "Must have at least {} items, {} given".format(self.min, len(data)), + "Must have at least {} items, {} given".format( + self.min, len(data) + ), ) if self.max is not None and len(data) > self.max: raise ConstraintException( - path, "Must have at most {} items, {} given".format(self.max, len(data)) + path, + "Must have at most {} items, {} given".format( + self.max, len(data) + ), ) @@ -4396,7 +4503,9 @@ class MapType(MapSchema, AbstractType, Generic[MapT]): Now you can use the map type to unserialize, validate, or serialize data. """ - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> MapT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> MapT: """ Unserialize a map (dict) type as defined with the underlying types. @@ -4454,7 +4563,9 @@ def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> MapT: ) value_path = list(tuple(new_path)) value_path.append("value") - result[unserialized_key] = self.values.unserialize(value, tuple(value_path)) + result[unserialized_key] = self.values.unserialize( + value, tuple(value_path) + ) return result def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): @@ -4511,7 +4622,9 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): value_path.append("value") self.values.validate(value, tuple(value_path)) - def serialize(self, data: MapT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: MapT, path: typing.Tuple[str] = tuple([]) + ) -> Any: """ This function serializes the data into the transportable system. @@ -4716,7 +4829,9 @@ class ObjectType(ObjectSchema, AbstractType, Generic[ObjectT]): _cls: Type[ObjectT] properties: Dict[str, PropertyType] - def __init__(self, cls: Type[ObjectT], properties: Dict[str, PropertyType]): + def __init__( + self, cls: Type[ObjectT], properties: Dict[str, PropertyType] + ): super().__init__(cls.__name__, properties) self._cls = cls self._validate_config(cls, properties) @@ -4726,7 +4841,9 @@ def cls(self) -> Type[ObjectT]: return self._cls @staticmethod - def _validate_config(cls_type: Type[ObjectT], properties: Dict[str, PropertyType]): + def _validate_config( + cls_type: Type[ObjectT], properties: Dict[str, PropertyType] + ): if not isinstance(cls_type, type): raise BadArgumentException( "The passed class argument '{}' is not a type. Please pass a type.".format( @@ -4845,7 +4962,9 @@ def _resolve_class_type_hints( "Too many parent classes of {}".format(cls_type.__name__) ) - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> ObjectT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> ObjectT: """ This function unserializes a dict into a dataclass. @@ -4967,7 +5086,9 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): field_id = property_id if property_field.field_override != "": field_id = property_field.field_override - new_path, value = self._validate_property(data, path, field_id, property_id) + new_path, value = self._validate_property( + data, path, field_id, property_id + ) if value is not None: property_field.type.validate(value, tuple(new_path)) values[property_id] = value @@ -5015,7 +5136,9 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): ), ) - def serialize(self, data: ObjectT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: ObjectT, path: typing.Tuple[str] = tuple([]) + ) -> Any: if not isinstance(data, self._cls): raise ConstraintException( path, @@ -5029,7 +5152,9 @@ def serialize(self, data: ObjectT, path: typing.Tuple[str] = tuple([])) -> Any: property_field: PropertyType = self.properties[property_id] if property_field.field_override != "": field_id = property_field.field_override - new_path, value = self._validate_property(data, path, field_id, property_id) + new_path, value = self._validate_property( + data, path, field_id, property_id + ) if value is not None: result[property_id] = property_field.type.serialize( getattr(data, field_id), tuple(new_path) @@ -5037,7 +5162,11 @@ def serialize(self, data: ObjectT, path: typing.Tuple[str] = tuple([])) -> Any: return result def _validate_property( - self, data: TypeT, path: typing.Tuple[str], field_id: str, property_id: str + self, + data: TypeT, + path: typing.Tuple[str], + field_id: str, + property_id: str, ): new_path = list(path) new_path.append(property_id) @@ -5048,7 +5177,9 @@ def _validate_property( return new_path, value @staticmethod - def _validate_not_set(data, object_property: PropertyType, path: typing.Tuple[str]): + def _validate_not_set( + data, object_property: PropertyType, path: typing.Tuple[str] + ): """ Validate required_if and required_if_not constraints on a property in the given data object. If a constraint has been broken, then raise a ConstraintException. @@ -5067,8 +5198,13 @@ def _validate_not_set(data, object_property: PropertyType, path: typing.Tuple[st raise ConstraintException(path, "This field is required") if object_property.required_if is not None: for required_if in object_property.required_if: - if (isinstance(data, dict) and required_if in data and data[required_if] is not None) or ( - hasattr(data, required_if) and getattr(data, required_if) is not None + if ( + isinstance(data, dict) + and required_if in data + and data[required_if] is not None + ) or ( + hasattr(data, required_if) + and getattr(data, required_if) is not None ): # (here, required_if refers to its value) # if data is a dict, has this required_if as a key, and the @@ -5088,7 +5224,11 @@ def _validate_not_set(data, object_property: PropertyType, path: typing.Tuple[st ): none_set = True for required_if_not in object_property.required_if_not: - if (isinstance(data, dict) and required_if_not in data and data[required_if_not] is not None) or ( + if ( + isinstance(data, dict) + and required_if_not in data + and data[required_if_not] is not None + ) or ( hasattr(data, required_if_not) and getattr(data, required_if_not) is not None ): @@ -5159,7 +5299,9 @@ def __init__( self._scope = scope self.discriminator_field_name = discriminator_field_name - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> OneOfT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> OneOfT: if not isinstance(data, dict): raise ConstraintException( path, "Must be a dict, got {}".format(type(data).__name__) @@ -5170,12 +5312,15 @@ def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> OneOfT: raise ConstraintException( tuple(new_path), "Required discriminator field not found" ) - unserialized_discriminator_field: str = data[self.discriminator_field_name] + unserialized_discriminator_field: str = data[ + self.discriminator_field_name + ] if not isinstance(unserialized_discriminator_field, self._t): raise ConstraintException( tuple(new_path), "{} required, {} found".format( - self._t.__name__, type(unserialized_discriminator_field).__name__ + self._t.__name__, + type(unserialized_discriminator_field).__name__, ), ) if unserialized_discriminator_field not in self.types: @@ -5201,7 +5346,10 @@ def validate(self, data: OneOfT, path: typing.Tuple[str] = tuple([])): if isinstance(data, object_schema.cls): item_schema.validate(data) if self.discriminator_field_name in object_schema.properties: - if getattr(data, self.discriminator_field_name) != discriminator: + if ( + getattr(data, self.discriminator_field_name) + != discriminator + ): new_path = list(path) new_path.append(self.discriminator_field_name) raise ConstraintException( @@ -5220,7 +5368,9 @@ def validate(self, data: OneOfT, path: typing.Tuple[str] = tuple([])): ), ) - def serialize(self, data: OneOfT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: OneOfT, path: typing.Tuple[str] = tuple([]) + ) -> Any: types = [] for discriminator, item_schema in self.types.items(): item_schema: RefType @@ -5229,7 +5379,10 @@ def serialize(self, data: OneOfT, path: typing.Tuple[str] = tuple([])) -> Any: if isinstance(data, object_schema.cls): serialized_data = item_schema.serialize(data) if self.discriminator_field_name in object_schema.properties: - if getattr(data, self.discriminator_field_name) != discriminator: + if ( + getattr(data, self.discriminator_field_name) + != discriminator + ): new_path = list(path) new_path.append(self.discriminator_field_name) raise ConstraintException( @@ -5241,7 +5394,9 @@ def serialize(self, data: OneOfT, path: typing.Tuple[str] = tuple([])) -> Any: ), ) else: - serialized_data[self.discriminator_field_name] = discriminator + serialized_data[ + self.discriminator_field_name + ] = discriminator return serialized_data raise ConstraintException( tuple(path), @@ -5251,10 +5406,14 @@ def serialize(self, data: OneOfT, path: typing.Tuple[str] = tuple([])) -> Any: ) -class OneOfStringType(OneOfStringSchema, _OneOfType[OneOfT, str], Generic[OneOfT]): +class OneOfStringType( + OneOfStringSchema, _OneOfType[OneOfT, str], Generic[OneOfT] +): def __init__( self, - types: Dict[str, typing.Annotated[_OBJECT_LIKE, discriminator("type_id")]], + types: Dict[ + str, typing.Annotated[_OBJECT_LIKE, discriminator("type_id")] + ], scope: typing.ForwardRef("ScopeType"), discriminator_field_name: str = "_type", ): @@ -5355,7 +5514,9 @@ def id(self) -> str: ) return self.objects[self.root].id - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> TypeT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> TypeT: if self.root is None: raise ConstraintException( path, "Cannot unserialize, root object is not set on scope." @@ -5375,7 +5536,9 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): new_path.append(root_object.cls.__name__) return root_object.validate(data, tuple(new_path)) - def serialize(self, data: TypeT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: TypeT, path: typing.Tuple[str] = tuple([]) + ) -> Any: if self.root is None: raise ConstraintException( path, "Cannot serialize, root object is not set on scope." @@ -5401,13 +5564,17 @@ def __init__(self, id: str, scope: ScopeType): def properties(self): return self._scope[self.id].properties - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> TypeT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> TypeT: return self._scope.objects[self.id].unserialize(data, path) def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): return self._scope.objects[self.id].validate(data, path) - def serialize(self, data: TypeT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: TypeT, path: typing.Tuple[str] = tuple([]) + ) -> Any: return self._scope.objects[self.id].serialize(data, path) @@ -5471,7 +5638,9 @@ class AnyType(AnySchema, AbstractType): arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Unsupported data type for 'any' type: TestClass """ - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> Any: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> Any: self._check(data, path) return data @@ -5538,13 +5707,17 @@ class StepOutputType(StepOutputSchema, AbstractType): display: Optional[DisplayValue] = None error: bool = False - def unserialize(self, data: Any, path: typing.Tuple[str] = tuple([])) -> TypeT: + def unserialize( + self, data: Any, path: typing.Tuple[str] = tuple([]) + ) -> TypeT: return self.schema.unserialize(data, path) def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): return self.schema.validate(data, path) - def serialize(self, data: TypeT, path: typing.Tuple[str] = tuple([])) -> Any: + def serialize( + self, data: TypeT, path: typing.Tuple[str] = tuple([]) + ) -> Any: return self.schema.serialize(data, path) @@ -5559,6 +5732,7 @@ class SignalHandlerType(SignalSchema): """ SignalHandlerType describes a callable signal type. """ + _handler: Callable[[StepObjectT, SignalDataT], type(None)] def __init__( @@ -5594,6 +5768,7 @@ class _StepLocalData: and the data needed to synchronize and notify steps of the step being ready. """ + initialized_object: StepObjectT step_running: bool = False # So signals to wait if sent before the step. step_running_condition: threading.Condition = threading.Condition() @@ -5608,7 +5783,9 @@ class StepType(StepSchema): possible outputs identified by a string. """ - _handler: Callable[[StepObjectT, StepInputT], typing.Tuple[str, StepOutputT]] + _handler: Callable[ + [StepObjectT, StepInputT], typing.Tuple[str, StepOutputT] + ] _step_object_constructor: step_object_constructor_param input: ScopeType outputs: Dict[ID_TYPE, StepOutputType] @@ -5621,7 +5798,9 @@ class StepType(StepSchema): def __init__( self, id: str, - handler: Callable[[StepObjectT, StepInputT], typing.Tuple[str, StepOutputT]], + handler: Callable[ + [StepObjectT, StepInputT], typing.Tuple[str, StepOutputT] + ], step_object_constructor: step_object_constructor_param or None, input: ScopeType, outputs: Dict[ID_TYPE, StepOutputType], @@ -5629,7 +5808,14 @@ def __init__( signal_emitters: Optional[Dict[ID_TYPE, SignalSchema]] = None, display: Optional[DisplayValue] = None, ): - super().__init__(id, input, outputs, signal_handlers=None, signal_emitters=signal_emitters, display=display) + super().__init__( + id, + input, + outputs, + signal_handlers=None, + signal_emitters=signal_emitters, + display=display, + ) self._handler = handler self._step_object_constructor = step_object_constructor self.signal_handler_method_names = signal_handler_method_names @@ -5697,7 +5883,10 @@ def inspect_methods(self): Retrieves the schemas from the method names given in the constructor. """ # Abort if required components are not set. - if self._step_object_constructor is None or self.signal_handler_method_names is None: + if ( + self._step_object_constructor is None + or self.signal_handler_method_names is None + ): return # Constructs an instance of the class in order to retrieve attributes from it object_instance = self._step_object_constructor() @@ -5729,7 +5918,9 @@ def get_signal(self, step_id: str, signal_id: str): raise NoSuchSignalException(step_id, signal_id) return step.signal_handlers[signal_id] - def unserialize_step_input(self, step_id: str, serialized_data: Any) -> Any: + def unserialize_step_input( + self, step_id: str, serialized_data: Any + ) -> Any: """ This function unserializes the input from a raw data to data structures, such as dataclasses. This function is automatically called by ``__call__`` before running the step with the unserialized input. @@ -5738,7 +5929,9 @@ def unserialize_step_input(self, step_id: str, serialized_data: Any) -> Any: :param serialized_data: The raw data to unserialize. :return: The unserialized data in the structure the step expects it. """ - return self._unserialize_step_input(self.get_step(step_id), serialized_data) + return self._unserialize_step_input( + self.get_step(step_id), serialized_data + ) @staticmethod def _unserialize_step_input(step: StepType, serialized_data: Any) -> Any: @@ -5747,7 +5940,9 @@ def _unserialize_step_input(step: StepType, serialized_data: Any) -> Any: except ConstraintException as e: raise InvalidInputException(e) from e - def unserialize_signal_handler_input(self, step_id: str, signal_id: str, serialized_data: Any) -> Any: + def unserialize_signal_handler_input( + self, step_id: str, signal_id: str, serialized_data: Any + ) -> Any: """ This function unserializes the input from a raw data to data structures, such as dataclasses. This function is automatically called by ``__call__`` before running the step with the unserialized input. @@ -5756,16 +5951,22 @@ def unserialize_signal_handler_input(self, step_id: str, signal_id: str, seriali :param serialized_data: The raw data to unserialize. :return: The unserialized data in the structure the step expects it. """ - return self._unserialize_signal_handler_input(self.get_signal(step_id, signal_id), serialized_data) + return self._unserialize_signal_handler_input( + self.get_signal(step_id, signal_id), serialized_data + ) @staticmethod - def _unserialize_signal_handler_input(signal: SignalHandlerType, data: Any) -> Any: + def _unserialize_signal_handler_input( + signal: SignalHandlerType, data: Any + ) -> Any: try: return signal.data_schema.unserialize(data) except ConstraintException as e: raise InvalidInputException(e) from e - def call_step(self, run_id: str, step_id: str, input_param: Any) -> typing.Tuple[str, Any]: + def call_step( + self, run_id: str, step_id: str, input_param: Any + ) -> typing.Tuple[str, Any]: """ This function calls a specific step with the input parameter that has already been unserialized. It expects the data to be already valid, use unserialize_step_input to produce a valid input. This function is automatically @@ -5778,7 +5979,13 @@ def call_step(self, run_id: str, step_id: str, input_param: Any) -> typing.Tuple """ return self._call_step(self.get_step(step_id), run_id, input_param) - def call_step_signal(self, run_id: str, step_id: str, signal_id: str, unserialized_input_param: Any): + def call_step_signal( + self, + run_id: str, + step_id: str, + signal_id: str, + unserialized_input_param: Any, + ): """ This function calls a specific step's signal with the input parameter that has already been unserialized. It expects the data to be already valid, use unserialize_signal_input to produce a valid input. @@ -5796,7 +6003,9 @@ def call_step_signal(self, run_id: str, step_id: str, signal_id: str, unserializ if not local_step_data.step_running: # wait to be notified of it being ready. Test this by adding a sleep before the step call. local_step_data.step_running_condition.wait() - return signal(local_step_data.initialized_object, unserialized_input_param) + return signal( + local_step_data.initialized_object, unserialized_input_param + ) @staticmethod def _call_step( @@ -5813,7 +6022,9 @@ def _call_step( skip_output_validation=skip_output_validation, ) - def serialize_output(self, step_id: str, output_id: str, output_data: Any) -> Any: + def serialize_output( + self, step_id: str, output_id: str, output_data: Any + ) -> Any: """ This function takes an output ID (e.g. "error") and structured output_data and serializes them into a format suitable for wire transport. This function is automatically called by ``__call__`` after the step is run. @@ -5823,7 +6034,9 @@ def serialize_output(self, step_id: str, output_id: str, output_data: Any) -> An :param output_data: The data structure returned from the step. :return: """ - return self._serialize_output(self.get_step(step_id), output_id, output_data) + return self._serialize_output( + self.get_step(step_id), output_id, output_data + ) @staticmethod def _serialize_output(step, output_id: str, output_data: Any) -> Any: @@ -5833,7 +6046,11 @@ def _serialize_output(step, output_id: str, output_data: Any) -> Any: raise InvalidOutputException(e) from e def __call__( - self, run_id: str, step_id: str, data: Any, skip_serialization: bool = False + self, + run_id: str, + step_id: str, + data: Any, + skip_serialization: bool = False, ) -> typing.Tuple[str, Any]: """ This function takes the input data, unserializes it for the specified step, calls the specified step, and, @@ -5857,7 +6074,9 @@ def __call__( if skip_serialization: step.outputs[output_id].validate(output_data) return output_id, output_data - serialized_output_data = self._serialize_output(step, output_id, output_data) + serialized_output_data = self._serialize_output( + step, output_id, output_data + ) return output_id, serialized_output_data @@ -5889,7 +6108,9 @@ def _resolve_abstract_type( new_path: List[str] = list(path) name = res.display.name if name is None: - raise SchemaBuildException(path, "BUG: resolved property with no name") + raise SchemaBuildException( + path, "BUG: resolved property with no name" + ) new_path.append(name) raise SchemaBuildException( tuple(new_path), @@ -5901,7 +6122,11 @@ def _resolve_abstract_type( @classmethod def _resolve_field( - cls, t: any, type_hints: type, path: typing.Tuple[str], scope: ScopeType + cls, + t: any, + type_hints: type, + path: typing.Tuple[str], + scope: ScopeType, ) -> PropertyType: result = cls._resolve(t, type_hints, path, scope) if not isinstance(result, PropertyType): @@ -5944,7 +6169,8 @@ def _resolve( return cls._resolve_annotated(t, type_hints, path, scope) else: raise SchemaBuildException( - path, "Unable to resolve underlying type: %s" % type(t).__name__ + path, + "Unable to resolve underlying type: %s" % type(t).__name__, ) @classmethod @@ -6012,15 +6238,22 @@ def _resolve_dataclass_field( underlying_type.display.name = getattr(underlying_type, "__name") elif hasattr(base_type, "__name"): underlying_type.display.name = getattr(base_type, "__name") - if underlying_type.display.name == "" or underlying_type.display.name is None: + if ( + underlying_type.display.name == "" + or underlying_type.display.name is None + ): meta_name = t.metadata.get("name") if meta_name != "" and meta_name is not None: underlying_type.display.name = meta_name if hasattr(underlying_type, "__description"): - underlying_type.display.description = getattr(underlying_type, "__description") + underlying_type.display.description = getattr( + underlying_type, "__description" + ) elif hasattr(base_type, "__description"): - underlying_type.display.description = getattr(base_type, "__description") + underlying_type.display.description = getattr( + base_type, "__description" + ) if ( underlying_type.display.description == "" or underlying_type.display.description is None @@ -6033,7 +6266,10 @@ def _resolve_dataclass_field( underlying_type.display.icon = getattr(underlying_type, "__icon") elif hasattr(base_type, "__icon"): underlying_type.display.icon = getattr(base_type, "__icon") - if underlying_type.display.icon == "" or underlying_type.display.icon is None: + if ( + underlying_type.display.icon == "" + or underlying_type.display.icon is None + ): meta_icon = t.metadata.get("icon") if meta_icon != "" and meta_icon is not None: underlying_type.display.icon = meta_icon @@ -6065,7 +6301,10 @@ def _resolve_dataclass_field( if meta_id is not None and meta_id != t.name: underlying_type.field_override = t.name - if t.default != dataclasses.MISSING or t.default_factory != dataclasses.MISSING: + if ( + t.default != dataclasses.MISSING + or t.default_factory != dataclasses.MISSING + ): underlying_type.required = False if t.default != dataclasses.MISSING: default = t.default @@ -6079,7 +6318,9 @@ def _resolve_dataclass_field( except ConstraintException as e: raise SchemaBuildException( path, - "Failed to serialize default value: {}".format(e.__str__()), + "Failed to serialize default value: {}".format( + e.__str__() + ), ) elif not underlying_type.required: raise SchemaBuildException( @@ -6116,13 +6357,18 @@ def _resolve_class( types.GenericAlias: "generic aliases", types.ModuleType: "modules", } - for unsupported_type, unsupported_type_name in unsupported_types.items(): + for ( + unsupported_type, + unsupported_type_name, + ) in unsupported_types.items(): if isinstance(t, unsupported_type) or t == unsupported_type: raise SchemaBuildException( path, "{} are not supported by the Arcaflow typing system and cannot be used in input or output data" "types. Please use one of the supported types, or file an issue at {} with your use case to " - "get them included.".format(unsupported_type_name, _issue_url), + "get them included.".format( + unsupported_type_name, _issue_url + ), ) raise SchemaBuildException( path, @@ -6293,7 +6539,8 @@ def _resolve_annotated( args_hints = (type_hints,) if len(args) < 2: raise SchemaBuildException( - path, "At least one validation parameter required for typing.Annotated" + path, + "At least one validation parameter required for typing.Annotated", ) new_path = list(path) new_path.append("typing.Annotated") @@ -6311,7 +6558,9 @@ def _resolve_annotated( except Exception as e: raise SchemaBuildException( tuple(new_path), - "Failed to execute Annotated argument: {}".format(e.__str__()), + "Failed to execute Annotated argument: {}".format( + e.__str__() + ), ) from e return underlying_t @@ -6362,10 +6611,14 @@ def _resolve_list_annotation( new_path.append("items") try: return ListType( - cls._resolve_abstract_type(args[0], type_hints, tuple(new_path), scope) + cls._resolve_abstract_type( + args[0], type_hints, tuple(new_path), scope + ) ) except Exception as e: - raise SchemaBuildException(path, "Failed to create list type") from e + raise SchemaBuildException( + path, "Failed to create list type" + ) from e @classmethod def _resolve_dict( @@ -6445,7 +6698,9 @@ def _resolve_dict_annotation( value_schema, ) except Exception as e: - raise SchemaBuildException(path, "Failed to create map type") from e + raise SchemaBuildException( + path, "Failed to create map type" + ) from e @classmethod def _resolve_union( @@ -6460,7 +6715,9 @@ def _resolve_union( try: # noinspection PyTypeHints if isinstance(None, args[0]): - raise SchemaBuildException(path, "None types are not supported.") + raise SchemaBuildException( + path, "None types are not supported." + ) except TypeError: pass try: @@ -6468,7 +6725,9 @@ def _resolve_union( if isinstance(None, args[1]): new_path = list(path) new_path.append("typing.Optional") - result = cls._resolve_field(args[0], arg_hints[0], tuple(path), scope) + result = cls._resolve_field( + args[0], arg_hints[0], tuple(path), scope + ) result.required = False return result except TypeError: @@ -6479,10 +6738,13 @@ def _resolve_union( new_path = list(path) new_path.append("typing.Union") new_path.append(str(i)) - f = cls._resolve_field(args[i], arg_hints[i], tuple(new_path), scope) + f = cls._resolve_field( + args[i], arg_hints[i], tuple(new_path), scope + ) if not f.required: raise SchemaBuildException( - tuple(new_path), "Union types cannot contain optional values." + tuple(new_path), + "Union types cannot contain optional values.", ) if f.required_if is not None and len(f.required_if) != 0: raise SchemaBuildException( @@ -6542,7 +6804,9 @@ def _resolve_pattern(cls, t, type_hints: type, path, scope: ScopeType): try: return PatternType() except Exception as e: - raise SchemaBuildException(path, "Failed to create pattern type") from e + raise SchemaBuildException( + path, "Failed to create pattern type" + ) from e def build_object_schema(t, _skip_validation: bool = False) -> ScopeType: @@ -6558,7 +6822,9 @@ def build_object_schema(t, _skip_validation: bool = False) -> ScopeType: r = _SchemaBuilder.resolve(t, scope) if not isinstance(r, RefType): - raise SchemaBuildException(tuple({}), "Response type is not an object.") + raise SchemaBuildException( + tuple({}), "Response type is not an object." + ) if not _skip_validation: SCOPE_SCHEMA.validate(scope) diff --git a/src/arcaflow_plugin_sdk/serialization.py b/src/arcaflow_plugin_sdk/serialization.py index a2950c2..e7f8d11 100644 --- a/src/arcaflow_plugin_sdk/serialization.py +++ b/src/arcaflow_plugin_sdk/serialization.py @@ -18,7 +18,9 @@ def load_from_file(file_name: str) -> Any: return json.load(f) except BaseException as e: raise LoadFromFileException( - "Failed to load JSON from {}: {}".format(file_name, e.__str__()) + "Failed to load JSON from {}: {}".format( + file_name, e.__str__() + ) ) from e elif file_name.endswith(".yaml") or file_name.endswith(".yml"): try: @@ -26,10 +28,14 @@ def load_from_file(file_name: str) -> Any: return yaml.safe_load(f) except BaseException as e: raise LoadFromFileException( - "Failed to load YAML from {}: {}".format(file_name, e.__str__()) + "Failed to load YAML from {}: {}".format( + file_name, e.__str__() + ) ) from e else: - raise LoadFromFileException("Unsupported file extension: {}".format(file_name)) + raise LoadFromFileException( + "Unsupported file extension: {}".format(file_name) + ) def load_from_stdin(stdin: io.TextIOWrapper) -> Any: diff --git a/src/arcaflow_plugin_sdk/test_atp.py b/src/arcaflow_plugin_sdk/test_atp.py index b8719ec..b3f0784 100644 --- a/src/arcaflow_plugin_sdk/test_atp.py +++ b/src/arcaflow_plugin_sdk/test_atp.py @@ -76,7 +76,9 @@ def __init__(self): signal_emitters=[], step_object_constructor=lambda: SignalTestStep(), ) - def signal_test_step(self, params: StepTestInput) -> Tuple[str, Union[SignalTestOutput]]: + def signal_test_step( + self, params: StepTestInput + ) -> Tuple[str, Union[SignalTestOutput]]: self.exit_event.wait(params.wait_time_seconds) return "success", SignalTestOutput(self.signal_values) @@ -134,7 +136,9 @@ def _execute_plugin(self, schema) -> Tuple[int, TextIO, TextIO]: else: self.fail("Fork failed") - def _cleanup(self, pid, stdin_writer, stdout_reader, can_fail: bool = False): + def _cleanup( + self, pid, stdin_writer, stdout_reader, can_fail: bool = False + ): stdin_writer.close() stdout_reader.close() time.sleep(0.1) @@ -142,13 +146,17 @@ def _cleanup(self, pid, stdin_writer, stdout_reader, can_fail: bool = False): stop_info = os.waitpid(pid, 0) exit_status = os.waitstatus_to_exitcode(stop_info[1]) if exit_status != 0 and not can_fail: - self.fail("Plugin exited with non-zero status: {}".format(exit_status)) + self.fail( + "Plugin exited with non-zero status: {}".format(exit_status) + ) def test_step_simple(self): pid, stdin_writer, stdout_reader = self._execute_plugin(test_schema) try: - client = atp.PluginClient(stdin_writer.buffer.raw, stdout_reader.buffer.raw) + client = atp.PluginClient( + stdin_writer.buffer.raw, stdout_reader.buffer.raw + ) client.start_output() hello_message = client.read_hello() self.assertEqual(3, hello_message.version) @@ -169,10 +177,14 @@ def test_step_simple(self): self._cleanup(pid, stdin_writer, stdout_reader) def test_step_with_signals(self): - pid, stdin_writer, stdout_reader = self._execute_plugin(test_signals_schema) + pid, stdin_writer, stdout_reader = self._execute_plugin( + test_signals_schema + ) try: - client = atp.PluginClient(stdin_writer.buffer.raw, stdout_reader.buffer.raw) + client = atp.PluginClient( + stdin_writer.buffer.raw, stdout_reader.buffer.raw + ) client.start_output() hello_message = client.read_hello() self.assertEqual(3, hello_message.version) @@ -182,22 +194,32 @@ def test_step_with_signals(self): schema.SCHEMA_SCHEMA.serialize(hello_message.schema), ) - client.start_work(self.id(), "signal_test_step", {"wait_time_seconds": "5"}) - client.send_signal(self.id(), "record_value", - {"final": "false", "value": "1"}, - ) - client.send_signal(self.id(), "record_value", - {"final": "false", "value": "2"}, - ) - client.send_signal(self.id(), "record_value", - {"final": "true", "value": "3"}, - ) + client.start_work( + self.id(), "signal_test_step", {"wait_time_seconds": "5"} + ) + client.send_signal( + self.id(), + "record_value", + {"final": "false", "value": "1"}, + ) + client.send_signal( + self.id(), + "record_value", + {"final": "false", "value": "2"}, + ) + client.send_signal( + self.id(), + "record_value", + {"final": "true", "value": "3"}, + ) result = client.read_single_result() self.assertEqual(result.run_id, self.id()) client.send_client_done() self.assertEqual(result.debug_logs, "") self.assertEqual(result.output_id, "success") - self.assertListEqual(result.output_data["signals_received"], [1, 2, 3]) + self.assertListEqual( + result.output_data["signals_received"], [1, 2, 3] + ) finally: self._cleanup(pid, stdin_writer, stdout_reader) @@ -206,10 +228,14 @@ def test_multi_step_with_signals(self): Starts two steps simultaneously, sends them separate data from signals, then verifies that each step got the dats intended for it. """ - pid, stdin_writer, stdout_reader = self._execute_plugin(test_signals_schema) + pid, stdin_writer, stdout_reader = self._execute_plugin( + test_signals_schema + ) try: - client = atp.PluginClient(stdin_writer.buffer.raw, stdout_reader.buffer.raw) + client = atp.PluginClient( + stdin_writer.buffer.raw, stdout_reader.buffer.raw + ) client.start_output() hello_message = client.read_hello() self.assertEqual(3, hello_message.version) @@ -221,29 +247,47 @@ def test_multi_step_with_signals(self): step_a_id = self.id() + "_a" step_b_id = self.id() + "_b" - client.start_work(step_a_id, "signal_test_step", {"wait_time_seconds": "5"}) - client.start_work(step_b_id, "signal_test_step", {"wait_time_seconds": "5"}) - client.send_signal(step_a_id, "record_value", - {"final": "false", "value": "1"}, - ) - client.send_signal(step_b_id, "record_value", - {"final": "true", "value": "2"}, - ) + client.start_work( + step_a_id, "signal_test_step", {"wait_time_seconds": "5"} + ) + client.start_work( + step_b_id, "signal_test_step", {"wait_time_seconds": "5"} + ) + client.send_signal( + step_a_id, + "record_value", + {"final": "false", "value": "1"}, + ) + client.send_signal( + step_b_id, + "record_value", + {"final": "true", "value": "2"}, + ) step_b_result = client.read_single_result() - client.send_signal(step_a_id, "record_value", - {"final": "true", "value": "3"}, - ) + client.send_signal( + step_a_id, + "record_value", + {"final": "true", "value": "3"}, + ) step_a_result = client.read_single_result() client.send_client_done() - self.assertEqual(step_a_result.run_id, step_a_id, "Expected 'a' run ID") - self.assertEqual(step_b_result.run_id, step_b_id, "Expected 'b' run ID") + self.assertEqual( + step_a_result.run_id, step_a_id, "Expected 'a' run ID" + ) + self.assertEqual( + step_b_result.run_id, step_b_id, "Expected 'b' run ID" + ) self.assertEqual(step_b_result.debug_logs, "") self.assertEqual(step_a_result.debug_logs, "") self.assertEqual(step_a_result.output_id, "success") self.assertEqual(step_b_result.output_id, "success") - self.assertListEqual(step_a_result.output_data["signals_received"], [1, 3]) - self.assertListEqual(step_b_result.output_data["signals_received"], [2]) + self.assertListEqual( + step_a_result.output_data["signals_received"], [1, 3] + ) + self.assertListEqual( + step_b_result.output_data["signals_received"], [2] + ) finally: self._cleanup(pid, stdin_writer, stdout_reader) @@ -252,14 +296,20 @@ def test_broken_step(self): Runs a step that throws an exception, which is something that should be caught by the plugin, but we need to test for it since the uncaught exceptions are the hardest to debug without proper handling. """ - pid, stdin_writer, stdout_reader = self._execute_plugin(test_broken_schema) + pid, stdin_writer, stdout_reader = self._execute_plugin( + test_broken_schema + ) try: - client = atp.PluginClient(stdin_writer.buffer.raw, stdout_reader.buffer.raw) + client = atp.PluginClient( + stdin_writer.buffer.raw, stdout_reader.buffer.raw + ) client.start_output() client.read_hello() - client.start_work(self.id(), "hello-world-broken", {"name": "Arca Lot"}) + client.start_work( + self.id(), "hello-world-broken", {"name": "Arca Lot"} + ) with self.assertRaises(atp.PluginClientStateException) as context: _, _, _, _ = client.read_single_result() @@ -275,7 +325,9 @@ def test_wrong_step(self): pid, stdin_writer, stdout_reader = self._execute_plugin(test_schema) try: - client = atp.PluginClient(stdin_writer.buffer.raw, stdout_reader.buffer.raw) + client = atp.PluginClient( + stdin_writer.buffer.raw, stdout_reader.buffer.raw + ) client.start_output() client.read_hello() @@ -295,7 +347,9 @@ def test_invalid_runtime_message_id(self): pid, stdin_writer, stdout_reader = self._execute_plugin(test_schema) try: - client = atp.PluginClient(stdin_writer.buffer.raw, stdout_reader.buffer.raw) + client = atp.PluginClient( + stdin_writer.buffer.raw, stdout_reader.buffer.raw + ) client.start_output() client.read_hello() @@ -304,15 +358,21 @@ def test_invalid_runtime_message_id(self): with self.assertRaises(atp.PluginClientStateException) as context: _, _, _, _ = client.read_single_result() client.send_client_done() - self.assertIn("Unknown runtime message ID: 1000", str(context.exception)) + self.assertIn( + "Unknown runtime message ID: 1000", str(context.exception) + ) finally: self._cleanup(pid, stdin_writer, stdout_reader, True) def test_error_in_signal(self): - pid, stdin_writer, stdout_reader = self._execute_plugin(test_signals_schema) + pid, stdin_writer, stdout_reader = self._execute_plugin( + test_signals_schema + ) try: - client = atp.PluginClient(stdin_writer.buffer.raw, stdout_reader.buffer.raw) + client = atp.PluginClient( + stdin_writer.buffer.raw, stdout_reader.buffer.raw + ) client.start_output() hello_message = client.read_hello() self.assertEqual(3, hello_message.version) @@ -322,13 +382,19 @@ def test_error_in_signal(self): schema.SCHEMA_SCHEMA.serialize(hello_message.schema), ) - client.start_work(self.id(), "signal_test_step", {"wait_time_seconds": "5"}) - client.send_signal(self.id(), "record_value", - {"final": "false", "value": "1"}, - ) - client.send_signal(self.id(), "record_value", - {"final": "false", "value": "-1"}, - ) + client.start_work( + self.id(), "signal_test_step", {"wait_time_seconds": "5"} + ) + client.send_signal( + self.id(), + "record_value", + {"final": "false", "value": "1"}, + ) + client.send_signal( + self.id(), + "record_value", + {"final": "false", "value": "-1"}, + ) result = client.read_single_result() self.assertEqual(result.run_id, self.id()) self.assertEqual(result.debug_logs, "") diff --git a/src/arcaflow_plugin_sdk/test_jsonschema.py b/src/arcaflow_plugin_sdk/test_jsonschema.py index 1ece16c..39d9d5b 100644 --- a/src/arcaflow_plugin_sdk/test_jsonschema.py +++ b/src/arcaflow_plugin_sdk/test_jsonschema.py @@ -70,7 +70,10 @@ def noop_handler(input: Request) -> Tuple[str, typing.Union[Response]]: "type": "object", "title": "Test step input", "description": "This is just a test", - "properties": {"a": {"type": "string"}, "field-b": {"type": "integer"}}, + "properties": { + "a": {"type": "string"}, + "field-b": {"type": "integer"}, + }, "required": ["a", "field-b"], "dependentRequired": {}, "additionalProperties": False, @@ -83,7 +86,9 @@ def test_step_outputs(self): { "Request": schema.ObjectSchema( id="Request", - properties={"a": schema.PropertySchema(schema.StringSchema())}, + properties={ + "a": schema.PropertySchema(schema.StringSchema()) + }, ) }, "Request", @@ -96,7 +101,9 @@ def test_step_outputs(self): "Response1": schema.ObjectSchema( id="Response1", properties={ - "b": schema.PropertySchema(schema.StringSchema()) + "b": schema.PropertySchema( + schema.StringSchema() + ) }, ) }, @@ -109,7 +116,9 @@ def test_step_outputs(self): "Response1": schema.ObjectSchema( id="Response1", properties={ - "c": schema.PropertySchema(schema.StringSchema()) + "c": schema.PropertySchema( + schema.StringSchema() + ) }, ) }, diff --git a/src/arcaflow_plugin_sdk/test_plugin.py b/src/arcaflow_plugin_sdk/test_plugin.py index b5e787f..3ca1896 100644 --- a/src/arcaflow_plugin_sdk/test_plugin.py +++ b/src/arcaflow_plugin_sdk/test_plugin.py @@ -23,7 +23,9 @@ class StdoutTestOutput: "A test for writing to stdout.", {"success": StdoutTestOutput}, ) -def stdout_test_step(input: StdoutTestInput) -> typing.Tuple[str, StdoutTestOutput]: +def stdout_test_step( + input: StdoutTestInput, +) -> typing.Tuple[str, StdoutTestOutput]: print("Hello world!") return "success", StdoutTestOutput() @@ -43,7 +45,9 @@ def cleanup(): i = io.StringIO() o = io.StringIO() e = io.StringIO() - exit_code = plugin.run(s, ["test.py", "-f", tmp.name, "--debug"], i, o, e) + exit_code = plugin.run( + s, ["test.py", "-f", tmp.name, "--debug"], i, o, e + ) self.assertEqual(0, exit_code) self.assertEqual("Hello world!\n", e.getvalue()) diff --git a/src/arcaflow_plugin_sdk/test_schema.py b/src/arcaflow_plugin_sdk/test_schema.py index 0f8fcd4..e1a5071 100644 --- a/src/arcaflow_plugin_sdk/test_schema.py +++ b/src/arcaflow_plugin_sdk/test_schema.py @@ -527,7 +527,9 @@ class OneOfData2: self.assertIsInstance(unserialized_data, OneOfData1) self.assertEqual(unserialized_data.a, "Hello world!") - unserialized_data2: OneOfData2 = s_type.unserialize({"_type": "b", "b": 42}) + unserialized_data2: OneOfData2 = s_type.unserialize( + {"_type": "b", "b": 42} + ) self.assertIsInstance(unserialized_data2, OneOfData2) self.assertEqual(unserialized_data2.b, 42) @@ -717,7 +719,8 @@ class KillPodConfig: namespace_pattern: re.Pattern name_pattern: typing.Annotated[ - typing.Optional[re.Pattern], schema.required_if_not("label_selector") + typing.Optional[re.Pattern], + schema.required_if_not("label_selector"), ] = None kill: typing.Annotated[int, schema.min(1)] = dataclasses.field( @@ -738,7 +741,8 @@ class KillPodConfig: schema.test_object_serialization( KillPodConfig( - namespace_pattern=re.compile(".*"), name_pattern=re.compile(".*") + namespace_pattern=re.compile(".*"), + name_pattern=re.compile(".*"), ), self.fail, ) @@ -746,7 +750,9 @@ class KillPodConfig: def test_required_if(self): @dataclasses.dataclass class TestData1: - A: typing.Annotated[typing.Optional[str], schema.required_if("B")] = None + A: typing.Annotated[ + typing.Optional[str], schema.required_if("B") + ] = None B: typing.Optional[int] = None s = schema.build_object_schema(TestData1) @@ -776,7 +782,9 @@ def test_required_if_not(self): @dataclasses.dataclass class TestData1: A: typing.Optional[str] = None - B: typing.Annotated[typing.Optional[str], schema.required_if_not("A")] = None + B: typing.Annotated[ + typing.Optional[str], schema.required_if_not("A") + ] = None s = schema.build_object_schema(TestData1) @@ -801,7 +809,11 @@ class TestData1: class TestData2: A: typing.Optional[str] = None B: typing.Optional[str] = None - C: typing.Annotated[typing.Optional[str], schema.required_if_not("A"), schema.required_if_not("B")] = None + C: typing.Annotated[ + typing.Optional[str], + schema.required_if_not("A"), + schema.required_if_not("B"), + ] = None s = schema.build_object_schema(TestData2) @@ -970,7 +982,9 @@ def test_map(self): {}, "a", ) - resolved_type = schema._SchemaBuilder.resolve(typing.Dict[str, str], scope) + resolved_type = schema._SchemaBuilder.resolve( + typing.Dict[str, str], scope + ) self.assertIsInstance(resolved_type, schema.MapType) self.assertIsInstance(resolved_type.keys, schema.StringType) self.assertIsInstance(resolved_type.values, schema.StringType) @@ -1017,23 +1031,32 @@ class TestData: self.assertIsInstance(object_schema, schema.ObjectType) self.assertIsNone(object_schema.properties["a"].display.name) self.assertTrue(object_schema.properties["a"].required) - self.assertIsInstance(object_schema.properties["a"].type, schema.StringType) + self.assertIsInstance( + object_schema.properties["a"].type, schema.StringType + ) self.assertIsNone(object_schema.properties["b"].display.name) self.assertTrue(object_schema.properties["b"].required) - self.assertIsInstance(object_schema.properties["b"].type, schema.IntType) + self.assertIsInstance( + object_schema.properties["b"].type, schema.IntType + ) self.assertIsNone(object_schema.properties["c"].display.name) self.assertTrue(object_schema.properties["c"].required) - self.assertIsInstance(object_schema.properties["c"].type, schema.FloatType) + self.assertIsInstance( + object_schema.properties["c"].type, schema.FloatType + ) self.assertIsNone(object_schema.properties["d"].display.name) self.assertTrue(object_schema.properties["d"].required) - self.assertIsInstance(object_schema.properties["d"].type, schema.BoolType) + self.assertIsInstance( + object_schema.properties["d"].type, schema.BoolType + ) @dataclasses.dataclass class TestData: a: str = "foo" b: int = 5 c: str = dataclasses.field( - default="bar", metadata={"name": "C", "description": "A string"} + default="bar", + metadata={"name": "C", "description": "A string"}, ) d: bool = True @@ -1048,17 +1071,27 @@ class TestData: self.assertIsInstance(object_schema, schema.ObjectType) self.assertIsNone(object_schema.properties["a"].display.name) self.assertFalse(object_schema.properties["a"].required) - self.assertIsInstance(object_schema.properties["a"].type, schema.StringType) + self.assertIsInstance( + object_schema.properties["a"].type, schema.StringType + ) self.assertIsNone(object_schema.properties["b"].display.name) self.assertFalse(object_schema.properties["b"].required) - self.assertIsInstance(object_schema.properties["b"].type, schema.IntType) + self.assertIsInstance( + object_schema.properties["b"].type, schema.IntType + ) self.assertEqual("C", object_schema.properties["c"].display.name) - self.assertEqual("A string", object_schema.properties["c"].display.description) + self.assertEqual( + "A string", object_schema.properties["c"].display.description + ) self.assertFalse(object_schema.properties["c"].required) - self.assertIsInstance(object_schema.properties["c"].type, schema.StringType) + self.assertIsInstance( + object_schema.properties["c"].type, schema.StringType + ) self.assertIsNone(object_schema.properties["d"].display.name) self.assertFalse(object_schema.properties["d"].required) - self.assertIsInstance(object_schema.properties["d"].type, schema.BoolType) + self.assertIsInstance( + object_schema.properties["d"].type, schema.BoolType + ) def test_union(self): @dataclasses.dataclass @@ -1080,7 +1113,8 @@ class TestData: self.assertIsInstance(scope.objects["B"], schema.ObjectType) self.assertIsInstance( - scope.objects["TestData"].properties["a"].type, schema.OneOfStringType + scope.objects["TestData"].properties["a"].type, + schema.OneOfStringType, ) one_of_type: schema.OneOfStringType = ( scope.objects["TestData"].properties["a"].type @@ -1141,7 +1175,9 @@ class TestData: resolved_type = scope.objects["TestData"] self.assertFalse(resolved_type.properties["a"].required) - self.assertIsInstance(resolved_type.properties["a"].type, schema.StringType) + self.assertIsInstance( + resolved_type.properties["a"].type, schema.StringType + ) def test_annotated(self): scope = schema.ScopeType( @@ -1185,7 +1221,9 @@ class TestData: def test_annotated_required_if(self): @dataclasses.dataclass class TestData2: - a: typing.Annotated[typing.Optional[str], schema.required_if("b")] = None + a: typing.Annotated[ + typing.Optional[str], schema.required_if("b") + ] = None b: typing.Optional[str] = None scope = schema.ScopeType( @@ -1243,7 +1281,9 @@ def _execute_test_cases(self, test_cases): input = test_cases[name][0] expected = test_cases[name][1] - self.assertEqual(expected, input._to_jsonschema_fragment(scope, defs)) + self.assertEqual( + expected, input._to_jsonschema_fragment(scope, defs) + ) def test_bool(self): defs = schema._JSONSchemaDefs() @@ -1257,10 +1297,18 @@ def test_bool(self): self.assertEqual(s["anyOf"][2]["type"], "integer") def test_string(self): - test_cases: typing.Dict[str, typing.Tuple[schema.StringType, typing.Dict]] = { + test_cases: typing.Dict[ + str, typing.Tuple[schema.StringType, typing.Dict] + ] = { "base": (schema.StringType(), {"type": "string"}), - "min": (schema.StringType(min=5), {"type": "string", "minLength": 5}), - "max": (schema.StringType(max=5), {"type": "string", "maxLength": 5}), + "min": ( + schema.StringType(min=5), + {"type": "string", "minLength": 5}, + ), + "max": ( + schema.StringType(max=5), + {"type": "string", "maxLength": 5}, + ), "pattern": ( schema.StringType(pattern=re.compile("^[a-z]+$")), {"type": "string", "pattern": "^[a-z]+$"}, @@ -1270,7 +1318,9 @@ def test_string(self): self._execute_test_cases(test_cases) def test_int(self): - test_cases: typing.Dict[str, typing.Tuple[schema.IntType, typing.Dict]] = { + test_cases: typing.Dict[ + str, typing.Tuple[schema.IntType, typing.Dict] + ] = { "base": (schema.IntType(), {"type": "integer"}), "min": (schema.IntType(min=5), {"type": "integer", "minimum": 5}), "max": (schema.IntType(max=5), {"type": "integer", "maximum": 5}), @@ -1279,10 +1329,18 @@ def test_int(self): self._execute_test_cases(test_cases) def test_float(self): - test_cases: typing.Dict[str, typing.Tuple[schema.FloatType, typing.Dict]] = { + test_cases: typing.Dict[ + str, typing.Tuple[schema.FloatType, typing.Dict] + ] = { "base": (schema.FloatType(), {"type": "number"}), - "min": (schema.FloatType(min=5.0), {"type": "number", "minimum": 5.0}), - "max": (schema.FloatType(max=5.0), {"type": "number", "maximum": 5.0}), + "min": ( + schema.FloatType(min=5.0), + {"type": "number", "minimum": 5.0}, + ), + "max": ( + schema.FloatType(max=5.0), + {"type": "number", "maximum": 5.0}, + ), } self._execute_test_cases(test_cases) @@ -1295,18 +1353,25 @@ class Fibonacci(enum.Enum): FIRST = 1 SECOND = 2 - test_cases: typing.Dict[str, typing.Tuple[schema._EnumType, typing.Dict]] = { + test_cases: typing.Dict[ + str, typing.Tuple[schema._EnumType, typing.Dict] + ] = { "string": ( schema.StringEnumType(Color), {"type": "string", "enum": ["red"]}, ), - "int": (schema.IntEnumType(Fibonacci), {"type": "integer", "enum": [1, 2]}), + "int": ( + schema.IntEnumType(Fibonacci), + {"type": "integer", "enum": [1, 2]}, + ), } self._execute_test_cases(test_cases) def test_list(self): - test_cases: typing.Dict[str, typing.Tuple[schema.ListType, typing.Dict]] = { + test_cases: typing.Dict[ + str, typing.Tuple[schema.ListType, typing.Dict] + ] = { "base": ( schema.ListType(schema.IntType()), {"type": "array", "items": {"type": "integer"}}, @@ -1323,7 +1388,9 @@ def test_list(self): self._execute_test_cases(test_cases) def test_map(self): - test_cases: typing.Dict[str, typing.Tuple[schema.MapType, typing.Dict]] = { + test_cases: typing.Dict[ + str, typing.Tuple[schema.MapType, typing.Dict] + ] = { "base": ( schema.MapType(schema.IntType(), schema.StringType()), { @@ -1387,7 +1454,11 @@ class TestData: "TestData": { "type": "object", "properties": { - "a": {"type": "string", "title": "A", "description": "A string"} + "a": { + "type": "string", + "title": "A", + "description": "A string", + } }, "required": ["a"], "additionalProperties": False, @@ -1396,7 +1467,11 @@ class TestData: }, "type": "object", "properties": { - "a": {"type": "string", "title": "A", "description": "A string"} + "a": { + "type": "string", + "title": "A", + "description": "A string", + } }, "required": ["a"], "additionalProperties": False, @@ -1438,8 +1513,12 @@ class TestData: ) }, ), - "A": schema.ObjectType(A, {"a": schema.PropertyType(schema.StringType())}), - "B": schema.ObjectType(B, {"b": schema.PropertyType(schema.StringType())}), + "A": schema.ObjectType( + A, {"a": schema.PropertyType(schema.StringType())} + ), + "B": schema.ObjectType( + B, {"b": schema.PropertyType(schema.StringType())} + ), } defs = schema._JSONSchemaDefs() json_schema = scope._to_jsonschema_fragment(scope, defs) @@ -1451,8 +1530,12 @@ class TestData: "properties": { "a": { "oneOf": [ - {"$ref": "#/$defs/A_discriminated_string_a"}, - {"$ref": "#/$defs/B_discriminated_string_b"}, + { + "$ref": "#/$defs/A_discriminated_string_a" + }, + { + "$ref": "#/$defs/B_discriminated_string_b" + }, ] } }, diff --git a/test_example_plugin.py b/test_example_plugin.py index e01ab63..b476cf7 100755 --- a/test_example_plugin.py +++ b/test_example_plugin.py @@ -9,7 +9,9 @@ class ExamplePluginTest(unittest.TestCase): @staticmethod def test_serialization(): plugin.test_object_serialization( - example_plugin.InputParams(name=example_plugin.FullName("Arca", "Lot")) + example_plugin.InputParams( + name=example_plugin.FullName("Arca", "Lot") + ) ) plugin.test_object_serialization( @@ -21,15 +23,21 @@ def test_serialization(): ) def test_functional(self): - step_input = example_plugin.InputParams(name=example_plugin.FullName("Arca", "Lot")) + step_input = example_plugin.InputParams( + name=example_plugin.FullName("Arca", "Lot") + ) # Note: The call to hello_world is to the output of the decorator, not the function itself. # So it's calling the StepType - output_id, output_data = example_plugin.hello_world(self.id(), step_input) + output_id, output_data = example_plugin.hello_world( + self.id(), step_input + ) # The example plugin always returns an error: self.assertEqual("success", output_id) - self.assertEqual(output_data, example_plugin.SuccessOutput("Hello, Arca Lot!")) + self.assertEqual( + output_data, example_plugin.SuccessOutput("Hello, Arca Lot!") + ) if __name__ == "__main__": From abb2c212011c7697a6b5757107430e1c0d53fe34 Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 12:59:36 -0400 Subject: [PATCH 06/13] update ci checks --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1b572a5..b4cd75a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,10 +32,10 @@ jobs: run: autoflake --remove-all-unused-imports --remove-unused-variables --check . - name: Check import order (isort) run: isort --check --profile black --line-length 79 . + - name: Check docstring formatting + run: docformatter --check --recursive --black --docstring-length 1 79 --wrap-descriptions 79 --wrap-summaries 79 . - name: Check style run: black --check --line-length 79 . - - name: Check docstring formatting - run: docformatter . --check --docstring-length 1 79 - name: Error linting id: flake8 run: flake8 --max-line-length 79 --ignore E203 . From 5afa7f4c994d517dc75e3d585b376328c4fec291 Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 13:00:22 -0400 Subject: [PATCH 07/13] apply formatting --- example_plugin.py | 34 +- poetry.lock | 17 +- pyproject.toml | 1 + src/arcaflow_plugin_sdk/atp.py | 102 +- src/arcaflow_plugin_sdk/jsonschema.py | 10 +- src/arcaflow_plugin_sdk/plugin.py | 84 +- src/arcaflow_plugin_sdk/predefined_schemas.py | 5 +- src/arcaflow_plugin_sdk/schema.py | 960 +++++++++--------- src/arcaflow_plugin_sdk/serialization.py | 7 +- src/arcaflow_plugin_sdk/test_atp.py | 26 +- src/arcaflow_plugin_sdk/test_schema.py | 12 +- 11 files changed, 642 insertions(+), 616 deletions(-) diff --git a/example_plugin.py b/example_plugin.py index 72b53d6..e342fd9 100755 --- a/example_plugin.py +++ b/example_plugin.py @@ -9,9 +9,7 @@ @dataclass class FullName: - """ - A full name holds the first and last name of an individual. - """ + """A full name holds the first and last name of an individual.""" first_name: typing.Annotated[ str, @@ -37,9 +35,8 @@ def __str__(self) -> str: @dataclass class Nickname: - """ - A nickname is a simplified form of the name that only holds the preferred name of an individual. - """ + """A nickname is a simplified form of the name that only holds the + preferred name of an individual.""" nick: typing.Annotated[ str, @@ -58,9 +55,8 @@ def __str__(self) -> str: @dataclass class InputParams: - """ - This is the data structure for the input parameters of the step defined below. - """ + """This is the data structure for the input parameters of the step defined + below.""" name: typing.Annotated[ typing.Union[ @@ -96,24 +92,20 @@ class InputParams: @dataclass class SuccessOutput: - """ - This is the output data structure for the success case. - """ + """This is the output data structure for the success case.""" message: str @dataclass class ErrorOutput: - """ - This is the output data structure in the error case. - """ + """This is the output data structure in the error case.""" error: str -# The following is a decorator (starting with @). We add this in front of our function to define the metadata for our -# step. +# The following is a decorator (starting with @). We add this in front of +# our function to define the metadata for our step. @plugin.step( id="hello-world", name="Hello world!", @@ -123,13 +115,13 @@ class ErrorOutput: def hello_world( params: InputParams, ) -> typing.Tuple[str, typing.Union[SuccessOutput, ErrorOutput]]: - """ - The function is the implementation for the step. It needs the decorator above to make it into a step. The type - hints for the params are required. + """The function is the implementation for the step. It needs the decorator + above to make it into a step. The type hints for the params are required. :param params: - :return: the string identifying which output it is, as well the output structure + :return: the string identifying which output it is, as well the output + structure """ return "success", SuccessOutput("Hello, {}!".format(params.name)) diff --git a/poetry.lock b/poetry.lock index 6642c77..0f39459 100644 --- a/poetry.lock +++ b/poetry.lock @@ -26,6 +26,21 @@ files = [ pyflakes = ">=3.0.0" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" +tomli = {version = "*", markers = "python_version < \"3.11\""} + [[package]] name = "babel" version = "2.13.1" @@ -1080,4 +1095,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "c730f712cb131107efaa65ca92c6931bdf4833003ec715b287713312655269d3" +content-hash = "b8c21417679a614f2b8ffde727f28c9298c15102f37a101466ac822a4c07b0eb" diff --git a/pyproject.toml b/pyproject.toml index 2918907..59aaec5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ black = "^23.10.1" autoflake = "^2.2.1" docformatter = "^1.7.5" flake8 = "^6.1.0" +autopep8 = "^2.0.4" [build-system] diff --git a/src/arcaflow_plugin_sdk/atp.py b/src/arcaflow_plugin_sdk/atp.py index 614a1d1..eaf8d81 100644 --- a/src/arcaflow_plugin_sdk/atp.py +++ b/src/arcaflow_plugin_sdk/atp.py @@ -1,7 +1,7 @@ -""" -The Arcaflow Transport Protocol is a protocol to communicate between the Engine and a plugin. This library provides -the services to do so. The main use-case is running over STDIO, so the communication protocol is designed to run 1:1, -it is not possible to use multiple clients (engines) with a plugin. +"""The Arcaflow Transport Protocol is a protocol to communicate between the +Engine and a plugin. This library provides the services to do so. The main use- +case is running over STDIO, so the communication protocol is designed to run +1:1, it is not possible to use multiple clients (engines) with a plugin. The message flow is as follows: @@ -31,10 +31,11 @@ class MessageType(IntEnum): - """ - An integer ID that indicates the type of runtime message that is stored - in the data field. The corresponding class can then be used to deserialize - the inner data. Look at the go SDK for the reference data structure. + """An integer ID that indicates the type of runtime message that is stored + in the data field. + + The corresponding class can then be used to deserialize the inner data. + Look at the go SDK for the reference data structure. """ WORK_START = 1 @@ -46,9 +47,8 @@ class MessageType(IntEnum): @dataclasses.dataclass class HelloMessage: - """ - This message is the initial greeting message a plugin sends to the output. - """ + """This message is the initial greeting message a plugin sends to the + output.""" version: typing.Annotated[ int, @@ -60,8 +60,8 @@ class HelloMessage: schema.Schema, schema.name("Schema"), schema.description( - "Schema information describing the required inputs and outputs, as well as the steps offered by this " - "plugin." + "Schema information describing the required inputs and outputs, as" + " well as the steps offered by this plugin." ), ] @@ -98,9 +98,7 @@ def run_plugin( self, plugin_schema: schema.SchemaType, ) -> int: - """ - This function wraps running a plugin. - """ + """This function wraps running a plugin.""" signal.signal( signal.SIGINT, signal.SIG_IGN ) # Ignore sigint. Only care about arcaflow signals. @@ -170,8 +168,10 @@ def run_server_read_loop(self) -> None: run_id, step_fatal=True, server_fatal=False, - error_msg="Exception while handling work start: " - f"{e} {traceback.format_exc()}", + error_msg=( + "Exception while handling work start: " + f"{e} {traceback.format_exc()}" + ), ) elif msg_id == MessageType.SIGNAL: signal_msg = runtime_msg["data"] @@ -182,7 +182,10 @@ def run_server_read_loop(self) -> None: run_id, step_fatal=False, server_fatal=False, - error_msg=f"Exception while handling signal: {e} {traceback.format_exc()}", + error_msg=( + "Exception while handling signal:" + f" {e} {traceback.format_exc()}" + ), ) elif msg_id == MessageType.CLIENT_DONE: return @@ -203,14 +206,20 @@ def run_server_read_loop(self) -> None: "", step_fatal=False, server_fatal=True, - error_msg=f"Error occurred while decoding CBOR: {err} {traceback.format_exc()}", + error_msg=( + "Error occurred while decoding CBOR:" + f" {err} {traceback.format_exc()}" + ), ) except Exception as e: self.send_error_message( "", step_fatal=False, server_fatal=True, - error_msg=f"Exception occurred in ATP server read loop: {e} {traceback.format_exc()}", + error_msg=( + "Exception occurred in ATP server read loop:" + f" {e} {traceback.format_exc()}" + ), ) def handle_signal(self, run_id, signal_msg): @@ -251,8 +260,10 @@ def run_signal( run_id, step_fatal=False, server_fatal=False, - error_msg=f"Error while calling signal for step with run ID {run_id}: {e} " - f"{traceback.format_exc()}", + error_msg=( + "Error while calling signal for step with run ID" + f" {run_id}: {e} {traceback.format_exc()}" + ), ) def handle_work_start( @@ -324,8 +335,10 @@ def start_step(self, run_id: str, step_id: str, config: typing.Any): run_id, step_fatal=True, server_fatal=False, - error_msg=f"Error while calling step {run_id}/{step_id}:" - f"{e} {traceback.format_exc()}", + error_msg=( + f"Error while calling step {run_id}/{step_id}:" + f"{e} {traceback.format_exc()}" + ), ) return @@ -360,9 +373,8 @@ def send_error_message( class PluginClientStateException(Exception): - """ - This exception is for client ATP client errors, like problems decoding - """ + """This exception is for client ATP client errors, like problems + decoding.""" msg: str @@ -382,9 +394,10 @@ class StepResult: class PluginClient: - """ - This is a rudimentary client that reads information from a plugin and starts work on the plugin. The functions - must be executed in order. + """This is a rudimentary client that reads information from a plugin and + starts work on the plugin. + + The functions must be executed in order. """ to_server_pipe: io.FileIO # Usually the stdin of the sub-process @@ -405,16 +418,13 @@ def start_output(self) -> None: self.encoder.encode(None) def read_hello(self) -> HelloMessage: - """ - This function reads the initial "Hello" message from the plugin. - """ + """This function reads the initial "Hello" message from the plugin.""" message = self.decoder.decode() return _HELLO_MESSAGE_SCHEMA.unserialize(message) def start_work(self, run_id: str, step_id: str, config: any): - """ - After the Hello message has been read, this function starts work in a plugin with the specified data. - """ + """After the Hello message has been read, this function starts work in + a plugin with the specified data.""" self.send_runtime_message( MessageType.WORK_START, run_id, @@ -425,9 +435,7 @@ def start_work(self, run_id: str, step_id: str, config: any): ) def send_signal(self, run_id: str, signal_id: str, input_data: any): - """ - This function sends any signals to the plugin. - """ + """This function sends any signals to the plugin.""" self.send_runtime_message( MessageType.SIGNAL, run_id, @@ -453,9 +461,8 @@ def send_runtime_message( self.to_server_pipe.flush() def read_single_result(self) -> StepResult: - """ - This function reads until it gets the next result of an execution from the plugin, or an error. - """ + """This function reads until it gets the next result of an execution + from the plugin, or an error.""" while True: runtime_msg = self.decoder.decode() msg_id = runtime_msg["id"] @@ -463,15 +470,18 @@ def read_single_result(self) -> StepResult: signal_msg = runtime_msg["data"] if signal_msg["output_id"] is None: raise PluginClientStateException( - "Missing 'output_id' in CBOR message. Possibly wrong order of calls?" + "Missing 'output_id' in CBOR message. Possibly wrong" + " order of calls?" ) if signal_msg["output_data"] is None: raise PluginClientStateException( - "Missing 'output_data' in CBOR message. Possibly wrong order of calls?" + "Missing 'output_data' in CBOR message. Possibly wrong" + " order of calls?" ) if signal_msg["debug_logs"] is None: raise PluginClientStateException( - "Missing 'output_data' in CBOR message. Possibly wrong order of calls?" + "Missing 'output_data' in CBOR message. Possibly wrong" + " order of calls?" ) return StepResult( run_id=runtime_msg["run_id"], diff --git a/src/arcaflow_plugin_sdk/jsonschema.py b/src/arcaflow_plugin_sdk/jsonschema.py index 1fee397..7f8719b 100644 --- a/src/arcaflow_plugin_sdk/jsonschema.py +++ b/src/arcaflow_plugin_sdk/jsonschema.py @@ -2,8 +2,9 @@ def step_input(t: schema.StepSchema) -> dict: - """ - This function takes a schema step and creates a JSON schema object from the input parameter. + """This function takes a schema step and creates a JSON schema object from + the input parameter. + :return: the JSON schema represented as a dict. """ result = t.input.to_jsonschema() @@ -15,8 +16,9 @@ def step_input(t: schema.StepSchema) -> dict: def step_outputs(t: schema.StepSchema): - """ - This function takes a schema step and creates a JSON schema object from the output parameters. + """This function takes a schema step and creates a JSON schema object from + the output parameters. + :return: the JSON schema represented as a dict. """ result = { diff --git a/src/arcaflow_plugin_sdk/plugin.py b/src/arcaflow_plugin_sdk/plugin.py index b283671..7ae9d8e 100644 --- a/src/arcaflow_plugin_sdk/plugin.py +++ b/src/arcaflow_plugin_sdk/plugin.py @@ -34,9 +34,9 @@ def signal_handler( description: str, icon: typing.Optional[str] = None, ) -> Callable[[_signal_handler_decorator_param], schema.SignalHandlerType]: - """ - ``@plugin.signal_handler`` is a decorator that takes a function with a single parameter and creates a schema for it - that you can use with ``plugin.step``. + """``@plugin.signal_handler`` is a decorator that takes a function with a + single parameter and creates a schema for it that you can use with + ``plugin.step``. :param id: The identifier for the signal handler. :param name: The human-readable name for the signal handler. @@ -55,20 +55,21 @@ def signal_decorator( sig = inspect.signature(func) if len(sig.parameters) != 2: raise BadArgumentException( - "The '%s' (id: %s) signal must have exactly two parameters, including self. Currently has %s" + "The '%s' (id: %s) signal must have exactly two parameters," + " including self. Currently has %s" % (name, id, str(sig.parameters)) ) input_param = list(sig.parameters.values())[1] if input_param.annotation is inspect.Parameter.empty: raise BadArgumentException( - "The '%s' (id: %s) signal parameter must have a type annotation" - % (name, id) + "The '%s' (id: %s) signal parameter must have a type" + " annotation" % (name, id) ) if isinstance(input_param.annotation, str): raise BadArgumentException( - "Stringized type annotation encountered in %s (id: %s). Please make sure you " - "don't import annotations from __future__ to avoid this problem." - % (name, id) + "Stringized type annotation encountered in %s (id: %s). Please" + " make sure you don't import annotations from __future__ to" + " avoid this problem." % (name, id) ) return schema.SignalHandlerType( @@ -95,17 +96,19 @@ def step_with_signals( step_object_constructor: schema.step_object_constructor_param, icon: typing.Optional[str] = None, ) -> Callable[[_step_object_decorator_param], schema.StepType]: - """ - ``@plugin.step_with_signals`` is a decorator that takes a class and a method with a single parameter (plus self) - and creates a schema for it that you can use with ``plugin.build_schema``. + """``@plugin.step_with_signals`` is a decorator that takes a class and a + method with a single parameter (plus self) and creates a schema for it that + you can use with ``plugin.build_schema``. :param id: The identifier for the step. :param name: The human-readable name for the step. :param description: The human-readable description for the step. :param outputs: A dict linking response IDs to response object types. - :param signal_handler_method_names: A list of methods for all signal handlers. + :param signal_handler_method_names: A list of methods for all signal + handlers. :param signal_emitters: A list of signal schemas for signal emitters. - :param step_object_constructor: A constructor lambda for the object with the step and signal methods. + :param step_object_constructor: A constructor lambda for the object with + the step and signal methods. :param icon: SVG icon for this step. :return: A schema for the step. """ @@ -118,7 +121,8 @@ def step_decorator(func: _step_object_decorator_param) -> schema.StepType: sig = inspect.signature(func) if len(sig.parameters) != 2: raise BadArgumentException( - "The '%s' (id: %s) step must have exactly two parameters, including self. Currently has %d" + "The '%s' (id: %s) step must have exactly two parameters," + " including self. Currently has %d" % (name, id, len(sig.parameters)) ) input_param = list(sig.parameters.values())[1] @@ -129,9 +133,9 @@ def step_decorator(func: _step_object_decorator_param) -> schema.StepType: ) if isinstance(input_param.annotation, str): raise BadArgumentException( - "Stringized type annotation encountered in %s (id: %s). Please make sure you " - "don't import annotations from __future__ to avoid this problem." - % (name, id) + "Stringized type annotation encountered in %s (id: %s). Please" + " make sure you don't import annotations from __future__ to" + " avoid this problem." % (name, id) ) new_responses: Dict[str, schema.StepOutputType] = {} @@ -169,9 +173,9 @@ def step( outputs: Dict[str, Type], icon: typing.Optional[str] = None, ) -> Callable[[_step_decorator_param], schema.StepType]: - """ - ``@plugin.step`` is a decorator that takes a function with a single parameter and creates a schema for it that you can - use with ``plugin.build_schema``. + """``@plugin.step`` is a decorator that takes a function with a single + parameter and creates a schema for it that you can use with + ``plugin.build_schema``. :param id: The identifier for the step. :param name: The human-readable name for the step. @@ -189,8 +193,8 @@ def step_decorator(func: _step_decorator_param) -> schema.StepType: sig = inspect.signature(func) if len(sig.parameters) != 1: raise BadArgumentException( - "The '%s' (id: %s) step must have exactly one parameter: step input object" - % (name, id) + "The '%s' (id: %s) step must have exactly one parameter: step" + " input object" % (name, id) ) input_param = list(sig.parameters.values())[0] if input_param.annotation is inspect.Parameter.empty: @@ -200,9 +204,9 @@ def step_decorator(func: _step_decorator_param) -> schema.StepType: ) if isinstance(input_param.annotation, str): raise BadArgumentException( - "Stringized type annotation encountered in %s (id: %s). Please make sure you " - "don't import annotations from __future__ to avoid this problem." - % (name, id) + "Stringized type annotation encountered in %s (id: %s). Please" + " make sure you don't import annotations from __future__ to" + " avoid this problem." % (name, id) ) new_responses: Dict[str, schema.StepOutputType] = {} @@ -254,9 +258,9 @@ def run( stdout: io.TextIOWrapper = stdout, stderr: io.TextIOWrapper = stderr, ) -> int: - """ - Run takes a schema and runs it as a command line utility. It returns the exit code of the program. It is intended - to be used as an entry point for your plugin. + """Run takes a schema and runs it as a command line utility. It returns the + exit code of the program. It is intended to be used as an entry point for + your plugin. :param s: the schema to run :param argv: command line arguments @@ -271,7 +275,10 @@ def run( "-f", "--file", dest="filename", - help="Configuration file to read configuration from. Pass - to read from stdin.", + help=( + "Configuration file to read configuration from. Pass - to read" + " from stdin." + ), metavar="FILE", ) parser.add_option( @@ -343,7 +350,8 @@ def run( if action is None: raise _ExitException( 64, - "At least one of --file, --json-schema, or --schema must be specified", + "At least one of --file, --json-schema, or --schema must be" + " specified", ) if action == "file" or action == "json-schema": @@ -381,8 +389,8 @@ def run( def build_schema(*args: schema.StepType) -> schema.SchemaType: - """ - This function takes functions annotated with ``@plugin.step`` and creates a schema from them. + """This function takes functions annotated with ``@plugin.step`` and + creates a schema from them. :param args: the steps to be added to the schema :return: a callable schema @@ -469,9 +477,8 @@ def _execute_file( return 0 except InvalidInputException as e: stderr.write( - "Invalid input encountered while executing step '{}' from file '{}':\n {}\n\n".format( - step_id, filename, e.__str__() - ) + "Invalid input encountered while executing step '{}' from file" + " '{}':\n {}\n\n".format(step_id, filename, e.__str__()) ) if options.debug: traceback.print_exc(chain=True) @@ -480,9 +487,8 @@ def _execute_file( return 65 except InvalidOutputException as e: stderr.write( - "Bug: invalid output encountered while executing step '{}' from file '{}':\n {}\n\n".format( - step_id, filename, e.__str__() - ) + "Bug: invalid output encountered while executing step '{}' from" + " file '{}':\n {}\n\n".format(step_id, filename, e.__str__()) ) if options.debug: traceback.print_exc(chain=True) diff --git a/src/arcaflow_plugin_sdk/predefined_schemas.py b/src/arcaflow_plugin_sdk/predefined_schemas.py index 43bbdc4..657ca67 100644 --- a/src/arcaflow_plugin_sdk/predefined_schemas.py +++ b/src/arcaflow_plugin_sdk/predefined_schemas.py @@ -17,7 +17,10 @@ data_schema=cancel_signal_input_schema, display=schema.DisplayValue( name="Step Cancel", - description="The signal that instructs the plugin to finish execution gracefully.", + description=( + "The signal that instructs the plugin to finish execution" + " gracefully." + ), icon=None, ), ) diff --git a/src/arcaflow_plugin_sdk/schema.py b/src/arcaflow_plugin_sdk/schema.py index b9aa6a9..cf7d61c 100644 --- a/src/arcaflow_plugin_sdk/schema.py +++ b/src/arcaflow_plugin_sdk/schema.py @@ -1,5 +1,5 @@ -""" -This module provides the tools to build a schema, either by hand, or automatically from type hints and annotations. +"""This module provides the tools to build a schema, either by hand, or +automatically from type hints and annotations. Using ===== @@ -90,10 +90,12 @@ @dataclass class ConstraintException(Exception): - """ - ``ConstraintException`` indicates that the passed data violated one or more constraints defined in the schema. - The message holds the exact path of the problematic field, as well as a message explaining the error. - If this error is not easily understood, please open an issue on the Arcaflow plugin SDK. + """``ConstraintException`` indicates that the passed data violated one or + more constraints defined in the schema. + + The message holds the exact path of the problematic field, as well as a + message explaining the error. If this error is not easily understood, + please open an issue on the Arcaflow plugin SDK. """ path: typing.Tuple[str] = tuple([]) @@ -109,9 +111,8 @@ def __str__(self): @dataclass class NoSuchStepException(Exception): - """ - ``NoSuchStepException`` indicates that the given step is not supported by the plugin. - """ + """``NoSuchStepException`` indicates that the given step is not supported + by the plugin.""" step: str @@ -121,9 +122,8 @@ def __str__(self): @dataclass class NoSuchSignalException(Exception): - """ - ``NoSuchSignalException`` indicates that the given signal is not supported by the plugin's step. - """ + """``NoSuchSignalException`` indicates that the given signal is not + supported by the plugin's step.""" step: str signal: str @@ -134,10 +134,11 @@ def __str__(self): @dataclass class BadArgumentException(Exception): - """ - BadArgumentException indicates that an invalid configuration was passed to a schema component. The message will - explain what exactly the problem is, but may not be able to locate the exact error as the schema may be manually - built. + """BadArgumentException indicates that an invalid configuration was passed + to a schema component. + + The message will explain what exactly the problem is, but may not be able + to locate the exact error as the schema may be manually built. """ msg: str @@ -148,9 +149,8 @@ def __str__(self): @dataclass class InvalidAnnotationException(Exception): - """ - ``InvalidAnnotationException`` indicates that an annotation was used on a type it does not support. - """ + """``InvalidAnnotationException`` indicates that an annotation was used on + a type it does not support.""" annotation: str msg: str @@ -160,10 +160,12 @@ def __str__(self): class SchemaBuildException(Exception): - """ - SchemaBuildException indicates an error while building the schema using type inspection. This exception holds the - path to the parameter that caused the problem. The message should be easily understood, if you are having trouble - with an error message, please open a ticket on the Arcaflow plugin SDK. + """SchemaBuildException indicates an error while building the schema using + type inspection. + + This exception holds the path to the parameter that caused the problem. The + message should be easily understood, if you are having trouble with an + error message, please open a ticket on the Arcaflow plugin SDK. """ def __init__(self, path: typing.Tuple[str], msg: str): @@ -180,8 +182,10 @@ def __str__(self) -> str: class InvalidInputException(Exception): - """ - This exception indicates that the input data for a given step didn't match the schema. The embedded + """This exception indicates that the input data for a given step didn't + match the schema. + + The embedded ``ConstraintException`` holds the details of this failure. """ @@ -195,9 +199,10 @@ def __str__(self): class InvalidOutputException(Exception): - """ - This exception indicates that the output of a schema was invalid. This is always a bug in the plugin and should - be reported to the plugin author. + """This exception indicates that the output of a schema was invalid. + + This is always a bug in the plugin and should be reported to the plugin + author. """ constraint: ConstraintException @@ -210,9 +215,7 @@ def __str__(self): class UnitParseException(Exception): - """ - This exception indicates that it failed to parse a unit string. - """ + """This exception indicates that it failed to parse a unit string.""" msg: str @@ -229,9 +232,9 @@ def __str__(self) -> str: def id(id: str): - """ - The id annotation can be used to change the serialized name of an object field. This is useful when a field - must be serialized to a name that is not a valid Python field. + """The id annotation can be used to change the serialized name of an object + field. This is useful when a field must be serialized to a name that is not + a valid Python field. **Example:** @@ -274,9 +277,9 @@ def call(t): def name(name: str): - """ - The name annotation can be applied on any dataclass field, or on Union types to add a human-readable name to the - field. It is used as a form field or as part of a dropdown box in a form. + """The name annotation can be applied on any dataclass field, or on Union + types to add a human-readable name to the field. It is used as a form field + or as part of a dropdown box in a form. **Example:** @@ -315,9 +318,9 @@ def call(t): def description(description: str): - """ - The description annotation can be applied on any dataclass field, or on Union types to add a human-readable - description to the field. It can contain line breaks and links for formatting. It is used as a form field + """The description annotation can be applied on any dataclass field, or on + Union types to add a human-readable description to the field. It can + contain line breaks and links for formatting. It is used as a form field description text or as part of a dropdown box in a form. **Example:** @@ -357,9 +360,10 @@ def call(t): def icon(icon: str): - """ - The icon annotation can be applied to any dataclass field, or on Union types to add a 64x64 pixel SVG icon to the - item on display. However, the SVG must not contain any external sources or namespaces in order to work correctly. + """The icon annotation can be applied to any dataclass field, or on Union + types to add a 64x64 pixel SVG icon to the item on display. However, the + SVG must not contain any external sources or namespaces in order to work + correctly. **Example:** @@ -395,9 +399,9 @@ def call(t): def units(units: typing.ForwardRef("Units")): - """ - This annotation lets you add unit definitions to int and float fields. This helps with determining how to treat that - number, but also contains scaling information for creating a nicely formatted string, such as 5m30s. + """This annotation lets you add unit definitions to int and float fields. + This helps with determining how to treat that number, but also contains + scaling information for creating a nicely formatted string, such as 5m30s. **Example:** @@ -423,8 +427,8 @@ def units(units: typing.ForwardRef("Units")): """ def call(t): - """ - :param typing.Union[IntSchema, FloatSchema] t: + """:param typing.Union[IntSchema, FloatSchema] t: + :return typing.Union[IntSchema, FloatSchema]: """ effective_t = t @@ -453,9 +457,9 @@ def example( ) -> typing.Callable[ [typing.ForwardRef("PropertySchema")], typing.ForwardRef("PropertySchema") ]: - """ - This annotation provides the option to add an example to a type. - :param example: the example as raw type, serializable by json.dumps. Do not use dataclasses + """This annotation provides the option to add an example to a type. :param + example: the example as raw type, serializable by json.dumps. Do not use + dataclasses. **Example:** @@ -529,10 +533,9 @@ def call(t): def discriminator(discriminator_field_name: str) -> discriminatorFunc: - """ - This annotation is used to manually set the discriminator field on a Union type. - :param discriminator_field_name: the name of the discriminator field. - :return: the callable decorator + """This annotation is used to manually set the discriminator field on a + Union type. :param discriminator_field_name: the name of the discriminator + field. :return: the callable decorator. **Example:** @@ -569,8 +572,8 @@ def discriminator(discriminator_field_name: str) -> discriminatorFunc: """ def call(t): - """ - :param typing.Union[OneOfStringSchema, OneOfIntSchema] t: + """:param typing.Union[OneOfStringSchema, OneOfIntSchema] t: + :return typing.Union[OneOfStringSchema, OneOfIntSchema]: """ if not isinstance(t, OneOfStringSchema) and not isinstance( @@ -578,7 +581,8 @@ def call(t): ): raise InvalidAnnotationException( "discriminator", - "expected a property or object type with union member, found {}".format( + "expected a property or object type with union member," + " found {}".format( type(t).__name__, ), ) @@ -621,9 +625,9 @@ def call(t): def discriminator_value( discriminator_value: typing.Union[str, int, enum.Enum] ): - """ - This annotation adds a custom value for an instance of a discriminator. The value must match the discriminator field - This annotation works only when used in conjunction with discriminator(). + """This annotation adds a custom value for an instance of a discriminator. + The value must match the discriminator field This annotation works only + when used in conjunction with discriminator(). :param discriminator_value: The value for the discriminator field. :return: The callable decorator @@ -671,9 +675,8 @@ def call(t): ): raise InvalidAnnotationException( "discriminator_value", - "discriminator_value is only valid for object types, not {}".format( - type(t).__name__ - ), + "discriminator_value is only valid for object types, not {}" + .format(type(t).__name__), ) t.__discriminator_value = discriminator_value return t @@ -699,9 +702,8 @@ def call(t): def min(param: typing.Union[int, float]) -> Validator: - """ - This decorator creates a minimum length (strings), minimum number (int, float), or minimum element count (lists and - maps) validation. + """This decorator creates a minimum length (strings), minimum number (int, + float), or minimum element count (lists and maps) validation. :param: The minimum number :return: the validator @@ -746,9 +748,8 @@ def call(t): effective_t.min = param else: raise BadArgumentException( - "min is valid only for STRING, INT, FLOAT, LIST, and MAP types, not for {} types.".format( - t.__name__ - ) + "min is valid only for STRING, INT, FLOAT, LIST, and MAP" + " types, not for {} types.".format(t.__name__) ) if isinstance(t, PropertySchema): t.type = effective_t @@ -761,9 +762,8 @@ def call(t): def max(param: int) -> Validator: - """ - This decorator creates a maximum length (strings), maximum number (int, float), or maximum element count (lists and - maps) validation. + """This decorator creates a maximum length (strings), maximum number (int, + float), or maximum element count (lists and maps) validation. :param param: The maximum number :return: the validator @@ -802,9 +802,11 @@ def max(param: int) -> Validator: """ def call(t): - """ - :param typing.Union[IntSchema, FloatSchema, StringSchema, ListSchema, MapSchema, PropertySchema] t: - :return typing.Union[IntSchema, FloatSchema, StringSchema, ListSchema, MapSchema, PropertySchema]: + """:param typing.Union[IntSchema, FloatSchema, StringSchema, + ListSchema, MapSchema, PropertySchema] t: + + :return typing.Union[IntSchema, FloatSchema, StringSchema, ListSchema, + MapSchema, PropertySchema]: """ effective_t = t if isinstance(t, PropertySchema): @@ -813,9 +815,8 @@ def call(t): effective_t.max = param else: raise BadArgumentException( - "max is valid only for STRING, INT, FLOAT, LIST, and MAP types, not for {} types.".format( - t.__name__ - ) + "max is valid only for STRING, INT, FLOAT, LIST, and MAP" + " types, not for {} types.".format(t.__name__) ) if isinstance(t, PropertySchema): t.type = effective_t @@ -828,8 +829,8 @@ def call(t): def pattern(pattern: Pattern) -> Validator: - """ - This decorator creates a regular expression pattern validation for strings. + """This decorator creates a regular expression pattern validation for + strings. :param pattern: The regular expression. :return: the validator @@ -868,8 +869,8 @@ def pattern(pattern: Pattern) -> Validator: """ def call(t): - """ - :param typing.Union[StringSchema,PropertySchema] t: + """:param typing.Union[StringSchema,PropertySchema] t: + :return typing.Union[StringSchema,PropertySchema]: """ effective_t = t @@ -879,9 +880,8 @@ def call(t): effective_t.pattern = pattern else: raise BadArgumentException( - "pattern is valid only for STRING types, not for {} types.".format( - t.__name__ - ) + "pattern is valid only for STRING types, not for {} types." + .format(t.__name__) ) if isinstance(t, PropertySchema): t.type = effective_t @@ -894,8 +894,8 @@ def call(t): def required_if(required_if: str) -> Validator: - """ - This decorator creates a that marks the current field as required if the specified field is set. + """This decorator creates a that marks the current field as required if the + specified field is set. :param required_if: The other field to use. :return: the validator @@ -965,10 +965,10 @@ def call(t: PropertySchema) -> PropertySchema: def required_if_not(required_if_not: str) -> Validator: - """ - This decorator creates a validation that marks the current field as required if the specified field is not set. If - there are multiple of these validators, the current field is only marked as required if none of the specified fields - are provided. + """This decorator creates a validation that marks the current field as + required if the specified field is not set. If there are multiple of these + validators, the current field is only marked as required if none of the + specified fields are provided. :param required_if_not: The other field to use. :return: the validator @@ -1044,9 +1044,8 @@ def call(t: PropertySchema) -> PropertySchema: def conflicts(conflicts: str) -> Validator: - """ - This decorator creates a validation that triggers if the current field on an object is set in parallel with the - specified field. + """This decorator creates a validation that triggers if the current field + on an object is set in parallel with the specified field. :param conflicts: The field to conflict with. :return: the validator @@ -1249,8 +1248,8 @@ def call(t): Optional[str], _name("Default"), _description( - "Default value for this property in JSON encoding. The value must be unserializable by the type specified " - "in the type field. " + "Default value for this property in JSON encoding. The value must be" + " unserializable by the type specified in the type field. " ), ] EXAMPLES_TYPE = typing.Annotated[ @@ -1274,8 +1273,7 @@ def call(t): def _id_typeize(input: str) -> str: - """ - This function creates an ID-safe representation of a string. + """This function creates an ID-safe representation of a string. **Example:** @@ -1302,9 +1300,7 @@ def __init__(self): class _OpenAPIGenerator(ABC): - """ - This class describes a method to generate OpenAPI 3.0 documents. - """ + """This class describes a method to generate OpenAPI 3.0 documents.""" @abstractmethod def _to_openapi_fragment( @@ -1322,16 +1318,15 @@ def __init__(self): class _JSONSchemaGenerator(ABC): - """ - This class prescribes a method to generate JSON schema documents. - """ + """This class prescribes a method to generate JSON schema documents.""" @abstractmethod def _to_jsonschema_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _JSONSchemaDefs ) -> any: - """ - This method creates a JSON schema version 2020-12 representation of this schema object. + """This method creates a JSON schema version 2020-12 representation of + this schema object. + :return: """ pass @@ -1339,9 +1334,9 @@ def _to_jsonschema_fragment( @dataclass class Unit: - """ - A unit is a description of a single scale of measurement, such as a "second". If there are multiple scales, such as - "minute", "second", etc. then multiple of these unit classes can be composed into units. + """A unit is a description of a single scale of measurement, such as a + "second". If there are multiple scales, such as "minute", "second", etc. + then multiple of these unit classes can be composed into units. **Example:** @@ -1358,7 +1353,8 @@ class Unit: str, name("Short name (singular)"), description( - "Short name that can be printed in a few characters, singular form." + "Short name that can be printed in a few characters, singular" + " form." ), example("B"), example("char"), @@ -1390,8 +1386,7 @@ class Unit: def format_short( self, amount: typing.Union[int, float], display_zero: bool = True ) -> str: - """ - This function formats an amount according to this unit. + """This function formats an amount according to this unit. **Example:** @@ -1417,8 +1412,7 @@ def format_short( def format_long( self, amount: typing.Union[int, float], display_zero: bool = True ) -> str: - """ - This function formats an amount according to this unit. + """This function formats an amount according to this unit. **Example:** @@ -1448,8 +1442,8 @@ def format_long( @dataclass class Units: - """ - Units holds several scales of magnitude of the same unit, for example 5m30s. + """Units holds several scales of magnitude of the same unit, for example + 5m30s. **Example:** @@ -1498,14 +1492,14 @@ class Units: >>> t.format_short(305000000) '5m5s' - """ base_unit: typing.Annotated[ Unit, _name("Base unit"), _description( - "The base unit is the smallest unit of scale for this set of units." + "The base unit is the smallest unit of scale for this set of" + " units." ), _example( { @@ -1548,8 +1542,8 @@ def __init__( self.__unit_re_cache = None def parse(self, data: str) -> typing.Union[int, float]: - """ - This function parses a string of units into the base number representation. + """This function parses a string of units into the base number + representation. :raises UnitParseException: if the data string fails to parse. @@ -1617,7 +1611,8 @@ def parse(self, data: str) -> typing.Union[int, float]: valid_units.append(multiplier.name_short_plural) valid_units.append(multiplier.name_short_singular) raise UnitParseException( - "Cannot parse '{}' as '{}': invalid format, valid unit types are: '{}'".format( + "Cannot parse '{}' as '{}': invalid format, valid unit types" + " are: '{}'".format( data, self.base_unit.name_long_plural, "', '".join( @@ -1640,9 +1635,8 @@ def parse(self, data: str) -> typing.Union[int, float]: return number def format_short(self, data: typing.Union[int, float]) -> str: - """ - This function takes an integer and formats it so that it is the most readable based on the current set of - units. + """This function takes an integer and formats it so that it is the most + readable based on the current set of units. **Example:** @@ -1673,9 +1667,8 @@ def format_short(self, data: typing.Union[int, float]) -> str: return output def format_long(self, data: typing.Union[int, float]) -> str: - """ - This function takes an integer and formats it so that it is the most readable based on the current set of - units. + """This function takes an integer and formats it so that it is the most + readable based on the current set of units. **Example:** @@ -1736,8 +1729,8 @@ def format_long(self, data: typing.Union[int, float]) -> str: @dataclass class DisplayValue: - """ - This class holds the fields related to displaying an item in a user interface. + """This class holds the fields related to displaying an item in a user + interface. **Example:** @@ -1766,8 +1759,9 @@ class DisplayValue: Optional[str], _name("Icon"), _description( - "SVG icon for this item. Must have the declared size of 64x64, must not include " - "additional namespaces, and must not reference external resources." + "SVG icon for this item. Must have the declared size of 64x64," + " must not include additional namespaces, and must not reference" + " external resources." ), _example(""), _min(1), @@ -1776,9 +1770,8 @@ class DisplayValue: @dataclass class StringEnumSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class specifically holds an enum that has string values. The values field maps the underlying values to - display values for easy presentation. + """This class specifically holds an enum that has string values. The values + field maps the underlying values to display values for easy presentation. This dataclass only has the ability to hold the configuration but cannot serialize, unserialize or validate. For that functionality please use StringEnumType. @@ -1800,8 +1793,8 @@ class StringEnumSchema(_JSONSchemaGenerator, _OpenAPIGenerator): _min(1), _name("Values"), _description( - "Mapping where the left side of the map holds the possible value and the right side holds the display " - "value for forms, etc." + "Mapping where the left side of the map holds the possible value" + " and the right side holds the display value for forms, etc." ), _example({"apple": {"name": "Apple"}, "orange": {"name": "Orange"}}), ] @@ -1812,8 +1805,9 @@ def valid_values(self) -> List[str]: def _to_jsonschema_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _JSONSchemaDefs ) -> any: - """ - This method generates an JSON schema fragment for enumerated strings. + """This method generates an JSON schema fragment for enumerated + strings. + See: https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values """ return {"type": "string", "enum": list(self.values.keys())} @@ -1821,8 +1815,8 @@ def _to_jsonschema_fragment( def _to_openapi_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _OpenAPIComponents ) -> any: - """ - This method generates an OpenAPI fragment for string enums. + """This method generates an OpenAPI fragment for string enums. + See: https://spec.openapis.org/oas/v3.1.0#data-types """ return {"type": "string", "enum": list(self.values.keys())} @@ -1830,8 +1824,7 @@ def _to_openapi_fragment( @dataclass class IntEnumSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class specifically holds an enum that has integer values. + """This class specifically holds an enum that has integer values. This dataclass only has the ability to hold the configuration but cannot serialize, unserialize or validate. For that functionality please use :class:`IntEnumType`. @@ -1863,8 +1856,9 @@ def valid_values(self) -> List[int]: def _to_jsonschema_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _JSONSchemaDefs ) -> any: - """ - This method generates an JSON schema fragment for enumerated integers. + """This method generates an JSON schema fragment for enumerated + integers. + See: https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values """ return {"type": "integer", "enum": list(self.values.keys())} @@ -1872,8 +1866,8 @@ def _to_jsonschema_fragment( def _to_openapi_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _OpenAPIComponents ) -> any: - """ - This method generates an OpenAPI fragment for string enums. + """This method generates an OpenAPI fragment for string enums. + See: https://spec.openapis.org/oas/v3.1.0#data-types """ return { @@ -1885,9 +1879,9 @@ def _to_openapi_fragment( @dataclass class StringSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds schema information for strings. This dataclass only has the ability to hold the configuration but - cannot serialize, unserialize or validate. For that functionality please use :class:`StringType`. + """This class holds schema information for strings. This dataclass only has + the ability to hold the configuration but cannot serialize, unserialize or + validate. For that functionality please use :class:`StringType`. **Example:** @@ -1926,8 +1920,8 @@ class StringSchema(_JSONSchemaGenerator, _OpenAPIGenerator): def _to_jsonschema_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _JSONSchemaDefs ) -> any: - """ - This method generates an JSON schema fragment for strings. + """This method generates an JSON schema fragment for strings. + See: https://json-schema.org/understanding-json-schema/reference/string.html """ result = {"type": "string"} @@ -1942,8 +1936,8 @@ def _to_jsonschema_fragment( def _to_openapi_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _OpenAPIComponents ) -> any: - """ - This method generates an OpenAPI fragment for strings. + """This method generates an OpenAPI fragment for strings. + See: https://swagger.io/docs/specification/data-models/data-types/#string """ result = {"type": "string"} @@ -1958,9 +1952,9 @@ def _to_openapi_fragment( @dataclass class PatternSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the schema information for regular expression patterns. This dataclass only has the ability to - hold the configuration but cannot serialize, unserialize or validate. For that functionality please use + """This class holds the schema information for regular expression patterns. + This dataclass only has the ability to hold the configuration but cannot + serialize, unserialize or validate. For that functionality please use :class:`PatternType`. **Example:** @@ -1982,9 +1976,10 @@ def _to_openapi_fragment( @dataclass class IntSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the schema information for 64-bit integers. This dataclass only has the ability to hold the - configuration but cannot serialize, unserialize or validate. For that functionality please use :class:`IntType`. + """This class holds the schema information for 64-bit integers. This + dataclass only has the ability to hold the configuration but cannot + serialize, unserialize or validate. For that functionality please use + :class:`IntType`. **Example:** @@ -2047,10 +2042,10 @@ def _to_openapi_fragment( @dataclass class FloatSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the schema information for 64-bit floating point numbers. This dataclass only has the ability to - hold the configuration but cannot serialize, unserialize or validate. For that functionality please use - :class:`FloatType`. + """This class holds the schema information for 64-bit floating point + numbers. This dataclass only has the ability to hold the configuration but + cannot serialize, unserialize or validate. For that functionality please + use :class:`FloatType`. **Example:** @@ -2113,9 +2108,10 @@ def _to_openapi_fragment( @dataclass class BoolSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the schema information for boolean types. This dataclass only has the ability to hold the - configuration but cannot serialize, unserialize or validate. For that functionality please use :class:`BoolType`. + """This class holds the schema information for boolean types. This + dataclass only has the ability to hold the configuration but cannot + serialize, unserialize or validate. For that functionality please use + :class:`BoolType`. **Example:** @@ -2200,9 +2196,9 @@ def _to_openapi_fragment( @dataclass class ListSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the schema definition for lists. This dataclass only has the ability to hold the configuration but - cannot serialize, unserialize or validate. For that functionality please use :class:`ListType`. + """This class holds the schema definition for lists. This dataclass only + has the ability to hold the configuration but cannot serialize, unserialize + or validate. For that functionality please use :class:`ListType`. **Example:** @@ -2265,9 +2261,10 @@ def _to_openapi_fragment( @dataclass class MapSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the schema definition for key-value associations. This dataclass only has the ability to hold the - configuration but cannot serialize, unserialize or validate. For that functionality please use :class:`MapType`. + """This class holds the schema definition for key-value associations. This + dataclass only has the ability to hold the configuration but cannot + serialize, unserialize or validate. For that functionality please use + :class:`MapType`. **Example:** @@ -2366,10 +2363,10 @@ def _to_openapi_fragment( @dataclass class PropertySchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the schema definition for a single object property. It is usable in conjunction with ``ObjectSchema``. - This dataclass only has the ability to hold the configuration but cannot serialize, unserialize or validate. For - that functionality please use :class:`PropertyType`. + """This class holds the schema definition for a single object property. It + is usable in conjunction with ``ObjectSchema``. This dataclass only has the + ability to hold the configuration but cannot serialize, unserialize or + validate. For that functionality please use :class:`PropertyType`. **Example:** @@ -2407,28 +2404,32 @@ class PropertySchema(_JSONSchemaGenerator, _OpenAPIGenerator): bool, _name("Required"), _description( - "When set to true, the value for this field must be provided under all circumstances." + "When set to true, the value for this field must be provided under" + " all circumstances." ), ] = True required_if: typing.Annotated[ Optional[List[str]], _name("Required if"), _description( - "Sets the current property to required if any of the properties in this list are set." + "Sets the current property to required if any of the properties in" + " this list are set." ), ] = None required_if_not: typing.Annotated[ Optional[List[str]], _name("Required if not"), _description( - "Sets the current property to be required if none of the properties in this list are set." + "Sets the current property to be required if none of the" + " properties in this list are set." ), ] = None conflicts: typing.Annotated[ Optional[List[str]], _name("Conflicts"), _description( - "The current property cannot be set if any of the listed properties are set." + "The current property cannot be set if any of the listed" + " properties are set." ), ] = None @@ -2472,9 +2473,9 @@ def _to_openapi_fragment( @dataclass class ObjectSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the definition for objects comprised of defined fields. This dataclass only has the ability to hold - the configuration but cannot serialize, unserialize or validate. For that functionality please use + """This class holds the definition for objects comprised of defined fields. + This dataclass only has the ability to hold the configuration but cannot + serialize, unserialize or validate. For that functionality please use :class:`PropertyType`. **Example:** @@ -2569,10 +2570,11 @@ def _to_openapi_fragment( @dataclass class OneOfStringSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the definition of variable types with a string discriminator. This type acts as a split for a case - where multiple possible object types can be present in a field. This type requires that there be a common field - (the discriminator) which tells a parsing party which type it is. The field type in this case is a string. + """This class holds the definition of variable types with a string + discriminator. This type acts as a split for a case where multiple possible + object types can be present in a field. This type requires that there be a + common field (the discriminator) which tells a parsing party which type it + is. The field type in this case is a string. This dataclass only has the ability to hold the configuration but cannot serialize, unserialize or validate. For that functionality please use :class:`OneOfStringType`. @@ -2641,8 +2643,9 @@ class OneOfStringSchema(_JSONSchemaGenerator, _OpenAPIGenerator): str, _name("Discriminator field name"), _description( - "Name of the field used to discriminate between possible values. If this field is" - "present on any of the component objects it must also be a string." + "Name of the field used to discriminate between possible values." + " If this field ispresent on any of the component objects it must" + " also be a string." ), ] = "_type" @@ -2715,10 +2718,11 @@ def _to_openapi_fragment( @dataclass class OneOfIntSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the definition of variable types with an integer discriminator. This type acts as a split for a - case where multiple possible object types can be present in a field. This type requires that there be a common field - (the discriminator) which tells a parsing party which type it is. The field type in this case is a string. + """This class holds the definition of variable types with an integer + discriminator. This type acts as a split for a case where multiple possible + object types can be present in a field. This type requires that there be a + common field (the discriminator) which tells a parsing party which type it + is. The field type in this case is a string. This dataclass only has the ability to hold the configuration but cannot serialize, unserialize or validate. For that functionality please use :class:`OneOfIntType`. @@ -2771,8 +2775,9 @@ class OneOfIntSchema(_JSONSchemaGenerator, _OpenAPIGenerator): str, _name("Discriminator field name"), _description( - "Name of the field used to discriminate between possible values. If this field is" - "present on any of the component objects it must also be an int." + "Name of the field used to discriminate between possible values." + " If this field ispresent on any of the component objects it must" + " also be an int." ), ] = "_type" @@ -2845,10 +2850,10 @@ def _to_openapi_fragment( @dataclass class RefSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the definition of a reference to a scope-wide object. The ref must always be inside a scope, - either directly or indirectly. If several scopes are embedded within each other, the Ref references the object - in the current scope. + """This class holds the definition of a reference to a scope-wide object. + The ref must always be inside a scope, either directly or indirectly. If + several scopes are embedded within each other, the Ref references the + object in the current scope. This dataclass only has the ability to hold the configuration but cannot serialize, unserialize or validate. For that functionality please use :class:`RefType`. @@ -2902,9 +2907,8 @@ def _to_openapi_fragment( @dataclass class AnySchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class stores the details of the "any" type, which allows all lists, dicts, integers, floats, strings, and bools - in a value. + """This class stores the details of the "any" type, which allows all lists, + dicts, integers, floats, strings, and bools in a value. **Example:** @@ -2930,11 +2934,11 @@ def _to_openapi_fragment( @dataclass class ScopeSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - A scope is a container for holding objects that can be referenced. It also optionally holds a reference to the - root object of the current scope. References within the scope must always reference IDs in a scope. Scopes can be - embedded into other objects, and scopes can have subscopes. Each RefSchema will reference objects in its current - scope. + """A scope is a container for holding objects that can be referenced. It + also optionally holds a reference to the root object of the current scope. + References within the scope must always reference IDs in a scope. Scopes + can be embedded into other objects, and scopes can have subscopes. Each + RefSchema will reference objects in its current scope. This dataclass only has the ability to hold the configuration but cannot serialize, unserialize or validate. For that functionality please use :class:`ScopeType`. @@ -2972,7 +2976,8 @@ class ScopeSchema(_JSONSchemaGenerator, _OpenAPIGenerator): Dict[ID_TYPE, ObjectSchema], _name("Objects"), _description( - "A set of referenceable objects. These objects may contain references themselves." + "A set of referenceable objects. These objects may contain" + " references themselves." ), ] root: typing.Annotated[ @@ -3025,8 +3030,8 @@ def _to_openapi_fragment( @dataclass class StepOutputSchema(_JSONSchemaGenerator, _OpenAPIGenerator): - """ - This class holds the possible outputs of a step and the metadata information related to these outputs. + """This class holds the possible outputs of a step and the metadata + information related to these outputs. This dataclass only has the ability to hold the configuration but cannot serialize, unserialize or validate. For that functionality please use :class:`StepOutputType`. @@ -3073,9 +3078,9 @@ class StepOutputSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ] = False def to_jsonschema(self): - """ - Creates a JSON schema fragment for this output. This is useful when combining the output with other outputs into - a comprehensive JSON schema output. + """Creates a JSON schema fragment for this output. This is useful when + combining the output with other outputs into a comprehensive JSON + schema output. **Example:** @@ -3116,9 +3121,9 @@ def to_jsonschema(self): return self._to_jsonschema_fragment(self.schema, _JSONSchemaDefs()) def to_openapi(self): - """ - Creates a OpenAPI fragment for this output. This is useful when combining the output with other outputs into - a comprehensive OpenAPI document. + """Creates a OpenAPI fragment for this output. This is useful when + combining the output with other outputs into a comprehensive OpenAPI + document. **Example:** @@ -3173,11 +3178,10 @@ def _to_openapi_fragment( @dataclass class SignalSchema: - """ - Holds the definition for a single signal. This can be used for input or output signals. + """Holds the definition for a single signal. This can be used for input or + output signals. To create, set the ID, and create a scope for the data input or output. - """ id: typing.Annotated[ @@ -3191,8 +3195,8 @@ class SignalSchema: @dataclass class StepSchema: - """ - This class holds the definition for a single step, it's input and output definitions. + """This class holds the definition for a single step, it's input and output + definitions. **Example:** @@ -3272,9 +3276,7 @@ class StepSchema: @dataclass class Schema: - """ - This is a collection of steps supported by a plugin. - """ + """This is a collection of steps supported by a plugin.""" steps: typing.Annotated[ Dict[ID_TYPE, StepSchema], @@ -3292,47 +3294,47 @@ class Schema: class AbstractType(Generic[TypeT]): - """ - This class is an abstract class describing the methods needed to implement a type. - """ + """This class is an abstract class describing the methods needed to + implement a type.""" @abstractmethod def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) ) -> TypeT: - """ - This function takes the underlying raw data and decodes it into the underlying advanced data type (e.g. - dataclass) for usage. + """This function takes the underlying raw data and decodes it into the + underlying advanced data type (e.g. dataclass) for usage. :param data: the raw data. - :param path: the list of structural elements that lead to this point for error messages. - :return: the advanced datatype. - :raise ConstraintException: if the passed data was not valid. + :param path: the list of structural elements that lead to this point + for error messages. + :return: the advanced datatype. :raise ConstraintException: if the + passed data was not valid. """ pass @abstractmethod def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): - """ - This function validates an already unserialized data type and raises an exception if it does not match - the type definition. + """This function validates an already unserialized data type and raises + an exception if it does not match the type definition. :param data: the unserialized data. - :param path: the path that lead to this validation call, in order to produce a nice error message - :raise ConstraintException: if the passed data was not valid. + :param path: the path that lead to this validation call, in order to + produce a nice error message :raise ConstraintException: if the + passed data was not valid. """ @abstractmethod def serialize( self, data: TypeT, path: typing.Tuple[str] = tuple([]) ) -> Any: - """ - This function serializes the passed data into it's raw form for transport, e.g. string, int, dicts, list. + """This function serializes the passed data into it's raw form for + transport, e.g. string, int, dicts, list. :param data: the underlying data type to be serialized. - :param path: the list of structural elements that lead to this point for error messages. - :return: the raw datatype. - :raise ConstraintException: if the passed data was not valid. + :param path: the list of structural elements that lead to this point + for error messages. + :return: the raw datatype. :raise ConstraintException: if the passed + data was not valid. """ pass @@ -3341,9 +3343,7 @@ def serialize( class _EnumType(AbstractType, Generic[EnumT]): - """ - StringEnumType is an implementation of StringEnumSchema. - """ + """StringEnumType is an implementation of StringEnumSchema.""" _type: Type[EnumT] @@ -3404,8 +3404,7 @@ def serialize( class StringEnumType(_EnumType, StringEnumSchema): - """ - This class represents an enum type that is a string. + """This class represents an enum type that is a string. **Example:** @@ -3462,8 +3461,7 @@ def __init__(self, t: Type[EnumT]): class IntEnumType(_EnumType, IntEnumSchema): - """ - This class represents an enum type that is an integer. + """This class represents an enum type that is an integer. **Example:** @@ -3521,8 +3519,8 @@ def __init__(self, t: Type[EnumT]): class BoolType(BoolSchema, AbstractType): - """ - This type represents a boolean value with a multitude of unserialization options. + """This type represents a boolean value with a multitude of unserialization + options. **Example:** @@ -3540,8 +3538,7 @@ class BoolType(BoolSchema, AbstractType): def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) ) -> TypeT: - """ - This function unserializes a bool value from a variety of types. + """This function unserializes a bool value from a variety of types. **Example:** @@ -3636,8 +3633,7 @@ def unserialize( ) def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): - """ - This function validates a bool value. + """This function validates a bool value. **Example:** @@ -3669,8 +3665,7 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): def serialize( self, data: TypeT, path: typing.Tuple[str] = tuple([]) ) -> Any: - """ - This function serializes a bool value. + """This function serializes a bool value. **Example:** @@ -3705,8 +3700,7 @@ def serialize( @dataclass class StringType(StringSchema, AbstractType): - """ - StringType represents a string of characters for human consumption. + """StringType represents a string of characters for human consumption. **Example:** @@ -3724,8 +3718,7 @@ class StringType(StringSchema, AbstractType): def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) ) -> str: - """ - Unserializes the current string from an integer or string. + """Unserializes the current string from an integer or string. **Example:** @@ -3745,8 +3738,8 @@ def unserialize( return data def validate(self, data: str, path: typing.Tuple[str] = tuple([])): - """ - Validates the given string for conformance with the rules set up in this object. + """Validates the given string for conformance with the rules set up in + this object. **Example:** @@ -3802,16 +3795,14 @@ def validate(self, data: str, path: typing.Tuple[str] = tuple([])): ) def serialize(self, data: str, path: typing.Tuple[str] = tuple([])) -> any: - """ - This function returns the string as-is after validating its contents. - """ + """This function returns the string as-is after validating its + contents.""" self.validate(data, path) return data class PatternType(PatternSchema, AbstractType): - """ - PatternType represents a regular expression. + """PatternType represents a regular expression. **Example:** @@ -3824,8 +3815,7 @@ class PatternType(PatternSchema, AbstractType): def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) ) -> re.Pattern: - """ - This function unserializes a regular expression from a string. + """This function unserializes a regular expression from a string. **Example:** @@ -3866,8 +3856,7 @@ def unserialize( ) def validate(self, data: re.Pattern, path: typing.Tuple[str] = tuple([])): - """ - This function validates a regular expression as such. + """This function validates a regular expression as such. **Example:** @@ -3900,8 +3889,8 @@ def serialize( class IntType(IntSchema, AbstractType): - """ - ``IntType`` represents an integer type, both positive or negative. It is designed to take a 64 bit value. + """``IntType`` represents an integer type, both positive or negative. It is + designed to take a 64 bit value. **Example:** @@ -3923,9 +3912,9 @@ class IntType(IntSchema, AbstractType): def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) ) -> int: - """ - This function can unserialize a number for a integers or strings. If the passed data is a string, it can take - the unit of the current type into account. + """This function can unserialize a number for a integers or strings. If + the passed data is a string, it can take the unit of the current type + into account. **Example:** @@ -3987,8 +3976,8 @@ def unserialize( return data def validate(self, data: int, path: typing.Tuple[str] = tuple([])): - """ - This function validates the passed number for conformity with the schema. + """This function validates the passed number for conformity with the + schema. **Example:** @@ -4051,8 +4040,8 @@ def validate(self, data: int, path: typing.Tuple[str] = tuple([])): raise ConstraintException(path, "Must be at most {}".format(num)) def serialize(self, data: int, path: typing.Tuple[str] = tuple([])) -> Any: - """ - This function will return an integer for the base unit of this value. + """This function will return an integer for the base unit of this + value. **Example:** @@ -4094,8 +4083,7 @@ def serialize(self, data: int, path: typing.Tuple[str] = tuple([])) -> Any: @dataclass class FloatType(FloatSchema, AbstractType): - """ - This type represents a 64-bit floating point / real number. + """This type represents a 64-bit floating point / real number. **Example:** @@ -4117,9 +4105,9 @@ class FloatType(FloatSchema, AbstractType): def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) ) -> float: - """ - This function can unserialize a number for a integers or strings. If the passed data is a string, it can take - the unit of the current type into account. + """This function can unserialize a number for a integers or strings. If + the passed data is a string, it can take the unit of the current type + into account. **Example:** @@ -4184,8 +4172,8 @@ def unserialize( return data def validate(self, data: float, path: typing.Tuple[str] = tuple([])): - """ - This function validates the passed number for conformity with the schema. + """This function validates the passed number for conformity with the + schema. **Example:** @@ -4253,8 +4241,7 @@ def validate(self, data: float, path: typing.Tuple[str] = tuple([])): def serialize( self, data: float, path: typing.Tuple[str] = tuple([]) ) -> Any: - """ - This function will return a float for the base unit of this value. + """This function will return a float for the base unit of this value. **Example:** @@ -4299,9 +4286,8 @@ def serialize( @dataclass class ListType(ListSchema, AbstractType, Generic[ListT]): - """ - ``ListType`` is a strongly typed list that can have elements of only one type. The typical Python equivalent would be - ``typing.List[sometype]``. + """``ListType`` is a strongly typed list that can have elements of only one + type. The typical Python equivalent would be ``typing.List[sometype]``. **Example:** @@ -4323,8 +4309,8 @@ class ListType(ListSchema, AbstractType, Generic[ListT]): def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) ) -> ListT: - """ - This function unserializes the list itself, and also unserializes the underlying type. + """This function unserializes the list itself, and also unserializes + the underlying type. **Example:** @@ -4376,8 +4362,8 @@ def unserialize( return data def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): - """ - This function validates the data type. It also validates the underlying data type. + """This function validates the data type. It also validates the + underlying data type. **Example:** @@ -4425,8 +4411,8 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): def serialize( self, data: ListT, path: typing.Tuple[str] = tuple([]) ) -> Any: - """ - This function serializes the list elements into a list for transport. + """This function serializes the list elements into a list for + transport. **Example:** @@ -4482,8 +4468,7 @@ def _validate(self, data, path): @dataclass class MapType(MapSchema, AbstractType, Generic[MapT]): - """ - MapType is a key-value dict with fixed types for both. + """MapType is a key-value dict with fixed types for both. **Example:** @@ -4506,8 +4491,7 @@ class MapType(MapSchema, AbstractType, Generic[MapT]): def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) ) -> MapT: - """ - Unserialize a map (dict) type as defined with the underlying types. + """Unserialize a map (dict) type as defined with the underlying types. **Example:** @@ -4569,8 +4553,7 @@ def unserialize( return result def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): - """ - This function validates the map and its underlying types. + """This function validates the map and its underlying types. **Example:** @@ -4625,8 +4608,7 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): def serialize( self, data: MapT, path: typing.Tuple[str] = tuple([]) ) -> Any: - """ - This function serializes the data into the transportable system. + """This function serializes the data into the transportable system. **Example:** @@ -4709,8 +4691,8 @@ def _validate(self, data, path): class PropertyType(PropertySchema, Generic[PropertyT]): - """ - This class holds the schema definition for a single object property . It is usable in conjunction with ``ObjectType``. + """This class holds the schema definition for a single object property . It + is usable in conjunction with ``ObjectType``. **Example:** @@ -4783,9 +4765,9 @@ def __init__( @dataclass class ObjectType(ObjectSchema, AbstractType, Generic[ObjectT]): - """ - ``ObjectType`` represents an object with predefined fields. The property declaration must match the fields in the class. - The type currently does not validate if the properties match the provided class. + """``ObjectType`` represents an object with predefined fields. The property + declaration must match the fields in the class. The type currently does not + validate if the properties match the provided class. **Example:** @@ -4846,24 +4828,21 @@ def _validate_config( ): if not isinstance(cls_type, type): raise BadArgumentException( - "The passed class argument '{}' is not a type. Please pass a type.".format( - type(cls_type).__name__ - ) + "The passed class argument '{}' is not a type. Please pass a" + " type.".format(type(cls_type).__name__) ) if not isinstance(properties, dict): raise BadArgumentException( - "The properties parameter to 'ObjectType' must be a 'dict', '{}' given".format( - type(properties).__name__ - ) + "The properties parameter to 'ObjectType' must be a 'dict'," + " '{}' given".format(type(properties).__name__) ) try: # noinspection PyDataclass dataclasses.fields(cls_type) except Exception as e: raise BadArgumentException( - "The passed class '{}' is not a dataclass. Please use a dataclass.".format( - cls_type.__name__ - ) + "The passed class '{}' is not a dataclass. Please use a" + " dataclass.".format(cls_type.__name__) ) from e class_type_hints = typing.get_type_hints(cls_type) @@ -4876,10 +4855,12 @@ def _validate_config( del init_type_hints["return"] if len(properties) != len(init_type_hints): raise BadArgumentException( - "The '{}' class has an invalid number of parameters in the '__init__' function. Expected: {} got: {}\n" - "The declared '__init__' parameters are the following: '{}'\n" - "The '__init__' parameters must match your declared parameters exactly so the Arcaflow plugin SDK can " - "inject the data values.".format( + "The '{}' class has an invalid number of parameters in the" + " '__init__' function. Expected: {} got: {}\nThe declared" + " '__init__' parameters are the following: '{}'\nThe" + " '__init__' parameters must match your declared parameters" + " exactly so the Arcaflow plugin SDK can inject the data" + " values.".format( cls_type.__name__, len(init_type_hints), len(properties), @@ -4898,10 +4879,12 @@ def _validate_config( # Check if the property can be properly set: if field_id not in init_type_hints: raise BadArgumentException( - "The '__init__' function for the class '{}' does not contain a parameter called '{}' as " - "required by the property '{}'. Please make sure all declared properties are settable from the" - "'__init__' function so the Arcaflow SDK can fill your dataclass with the unserialized data.\n" - "Init parameters: '{}'".format( + "The '__init__' function for the class '{}' does not" + " contain a parameter called '{}' as required by the" + " property '{}'. Please make sure all declared" + " properties are settable from the'__init__' function" + " so the Arcaflow SDK can fill your dataclass with the" + " unserialized data.\nInit parameters: '{}'".format( cls_type.__name__, field_id, property_id, @@ -4918,11 +4901,12 @@ def _validate_config( # Check if the property can be read: if field_id not in class_type_hints: raise BadArgumentException( - "The '{}' class does not contain a property called '{}' as " - "required by the declared property '{}'. Please make sure all declared properties are " - "gettable from the class so the Arcaflow SDK can fill your dataclass with the unserialized " - "data.\n" - "Class properties: '{}'".format( + "The '{}' class does not contain a property called" + " '{}' as required by the declared property '{}'." + " Please make sure all declared properties are" + " gettable from the class so the Arcaflow SDK can fill" + " your dataclass with the unserialized data.\nClass" + " properties: '{}'".format( cls_type.__name__, field_id, property_id, @@ -4965,8 +4949,7 @@ def _resolve_class_type_hints( def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) ) -> ObjectT: - """ - This function unserializes a dict into a dataclass. + """This function unserializes a dict into a dataclass. **Example:** @@ -5032,17 +5015,16 @@ def unserialize( if conflict in data: raise ConstraintException( tuple(new_path), - "Field conflicts '{}', set one of the two, not both".format( - conflict - ), + "Field conflicts '{}', set one of the two, not" + " both".format(conflict), ) else: self._validate_not_set(data, object_property, tuple(new_path)) return self._cls(**kwargs) def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): - """ - This function will validate the dataclass and all underlying types as well. + """This function will validate the dataclass and all underlying types + as well. **Example:** @@ -5121,7 +5103,8 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): if not found: raise ConstraintException( tuple(new_path), - "Field is required because none of '{}' are set".format( + "Field is required because none of '{}' are set" + .format( "', '".join(property_field.required_if_not) ), ) @@ -5131,7 +5114,8 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): if required_if in values.keys(): raise ConstraintException( tuple(new_path), - "Field is required because none of '{}' are set".format( + "Field is required because none of '{}'" + " are set".format( "', '".join(property_field.required_if_not) ), ) @@ -5180,9 +5164,9 @@ def _validate_property( def _validate_not_set( data, object_property: PropertyType, path: typing.Tuple[str] ): - """ - Validate required_if and required_if_not constraints on a property in the given - data object. If a constraint has been broken, then raise a ConstraintException. + """Validate required_if and required_if_not constraints on a property + in the given data object. If a constraint has been broken, then raise a + ConstraintException. For a description of the required_if constraint visit [https://arcalot.io/arcaflow/plugins/python/schema/?h=required_if#objecttype]. @@ -5244,15 +5228,13 @@ def _validate_not_set( if len(object_property.required_if_not) == 1: raise ConstraintException( path, - "This field is required because '{}' is not set".format( - object_property.required_if_not[0] - ), + "This field is required because '{}' is not set" + .format(object_property.required_if_not[0]), ) raise ConstraintException( path, - "This field is required because none of '{}' are set".format( - "', '".join(object_property.required_if_not) - ), + "This field is required because none of '{}' are set" + .format("', '".join(object_property.required_if_not)), ) @@ -5275,15 +5257,13 @@ def __init__( ): if not isinstance(scope, ScopeType): raise BadArgumentException( - "The 'scope' parameter for OneOf*Type must be a ScopeType, {} given".format( - type(scope).__name__ - ) + "The 'scope' parameter for OneOf*Type must be a ScopeType, {}" + " given".format(type(scope).__name__) ) if not isinstance(types, dict): raise BadArgumentException( - "The 'scope' parameter for OneOf*Type must be a ScopeType, {} given".format( - type(scope).__name__ - ) + "The 'scope' parameter for OneOf*Type must be a ScopeType, {}" + " given".format(type(scope).__name__) ) for k, v in types.items(): if ( @@ -5292,8 +5272,9 @@ def __init__( and not isinstance(v, ScopeType) ): raise BadArgumentException( - "The 'types' parameter of OneOf*Type must contain RefTypes, ObjectTypes, or ScopeTypes, " - "{} found for key {}".format(type(v).__name__, v) + "The 'types' parameter of OneOf*Type must contain" + " RefTypes, ObjectTypes, or ScopeTypes, {} found for" + " key {}".format(type(v).__name__, v) ) self._t = t self._scope = scope @@ -5354,7 +5335,8 @@ def validate(self, data: OneOfT, path: typing.Tuple[str] = tuple([])): new_path.append(self.discriminator_field_name) raise ConstraintException( tuple(new_path), - "Invalid value for '{}' on '{}', should be: '{}'".format( + "Invalid value for '{}' on '{}', should be: '{}'" + .format( self.discriminator_field_name, object_schema.cls.__name__, discriminator, @@ -5387,7 +5369,8 @@ def serialize( new_path.append(self.discriminator_field_name) raise ConstraintException( tuple(new_path), - "Invalid value for '{}' on '{}', should be: '{}'".format( + "Invalid value for '{}' on '{}', should be: '{}'" + .format( self.discriminator_field_name, object_schema.cls.__name__, discriminator, @@ -5463,10 +5446,12 @@ def __init__( class ScopeType(ScopeSchema, AbstractType): - """ - A scope is a container object for an object structure. Its main purpose is to hold objects that can be referenced, - even in cases where circular references are desired. It mimics the ObjectType and provides several properties that - proxy through to the underlying root object if set. + """A scope is a container object for an object structure. + + Its main purpose is to hold objects that can be referenced, even in cases + where circular references are desired. It mimics the ObjectType and + provides several properties that proxy through to the underlying root + object if set. """ objects: Dict[ID_TYPE, ObjectType] @@ -5550,9 +5535,7 @@ def serialize( class RefType(RefSchema, AbstractType): - """ - A ref is a reference to an object in a Scope. - """ + """A ref is a reference to an object in a Scope.""" _scope: ScopeType @@ -5579,8 +5562,7 @@ def serialize( class AnyType(AnySchema, AbstractType): - """ - This type is a serializable version of the "any" type. + """This type is a serializable version of the "any" type. **Examples:** @@ -5659,7 +5641,8 @@ def _check(self, data: Any, path: typing.Tuple[str] = tuple([])): if type(item) is not list_type_base: raise ConstraintException( tuple(path), - "non-uniform type found in list: '{}' is not of list type '{}'".format( + "non-uniform type found in list: '{}' is not of" + " list type '{}'".format( type(item), list_type_base ), ) @@ -5699,9 +5682,8 @@ def _check(self, data: Any, path: typing.Tuple[str] = tuple([])): class StepOutputType(StepOutputSchema, AbstractType): - """ - This class holds the possible outputs of a step and the metadata information related to these outputs. - """ + """This class holds the possible outputs of a step and the metadata + information related to these outputs.""" schema: ScopeType display: Optional[DisplayValue] = None @@ -5729,9 +5711,7 @@ def serialize( class SignalHandlerType(SignalSchema): - """ - SignalHandlerType describes a callable signal type. - """ + """SignalHandlerType describes a callable signal type.""" _handler: Callable[[StepObjectT, SignalDataT], type(None)] @@ -5763,11 +5743,9 @@ def __call__( class _StepLocalData: - """ - Data associated with a single step, including the constructed object, - and the data needed to synchronize and notify steps of the step - being ready. - """ + """Data associated with a single step, including the constructed object, + and the data needed to synchronize and notify steps of the step being + ready.""" initialized_object: StepObjectT step_running: bool = False # So signals to wait if sent before the step. @@ -5778,9 +5756,10 @@ def __init__(self, initialized_object: StepObjectT): class StepType(StepSchema): - """ - StepSchema describes the schema for a single step. The input is always one ObjectType, while there are multiple - possible outputs identified by a string. + """StepSchema describes the schema for a single step. + + The input is always one ObjectType, while there are multiple possible + outputs identified by a string. """ _handler: Callable[ @@ -5863,15 +5842,14 @@ def __call__( result = self._handler(step_local_data.initialized_object, params) if len(result) != 2: raise BadArgumentException( - "The step returned {} results instead of 2. Did your step return the correct results?".format( - len(result) - ) + "The step returned {} results instead of 2. Did your step" + " return the correct results?".format(len(result)) ) output_id, output_data = result if output_id not in self.outputs: raise BadArgumentException( - "The step returned an undeclared output ID: %s, please return one of: '%s'" - % (output_id, "', '".join(self.outputs.keys())) + "The step returned an undeclared output ID: %s, please return" + " one of: '%s'" % (output_id, "', '".join(self.outputs.keys())) ) output: StepOutputType = self.outputs[output_id] if not skip_output_validation: @@ -5879,9 +5857,8 @@ def __call__( return output_id, output_data def inspect_methods(self): - """ - Retrieves the schemas from the method names given in the constructor. - """ + """Retrieves the schemas from the method names given in the + constructor.""" # Abort if required components are not set. if ( self._step_object_constructor is None @@ -5900,8 +5877,9 @@ def inspect_methods(self): class SchemaType(Schema): - """ - A schema is a definition of one or more steps that can be executed. The step has a defined input and output + """A schema is a definition of one or more steps that can be executed. + + The step has a defined input and output """ steps: Dict[str, StepType] @@ -5921,11 +5899,12 @@ def get_signal(self, step_id: str, signal_id: str): def unserialize_step_input( self, step_id: str, serialized_data: Any ) -> Any: - """ - This function unserializes the input from a raw data to data structures, such as dataclasses. This function is - automatically called by ``__call__`` before running the step with the unserialized input. + """This function unserializes the input from a raw data to data + structures, such as dataclasses. This function is automatically called + by ``__call__`` before running the step with the unserialized input. - :param step_id: The step ID to use to look up the schema for unserialization. + :param step_id: The step ID to use to look up the schema for + unserialization. :param serialized_data: The raw data to unserialize. :return: The unserialized data in the structure the step expects it. """ @@ -5943,11 +5922,12 @@ def _unserialize_step_input(step: StepType, serialized_data: Any) -> Any: def unserialize_signal_handler_input( self, step_id: str, signal_id: str, serialized_data: Any ) -> Any: - """ - This function unserializes the input from a raw data to data structures, such as dataclasses. This function is - automatically called by ``__call__`` before running the step with the unserialized input. + """This function unserializes the input from a raw data to data + structures, such as dataclasses. This function is automatically called + by ``__call__`` before running the step with the unserialized input. - :param step_id: The step ID to use to look up the schema for unserialization. + :param step_id: The step ID to use to look up the schema for + unserialization. :param serialized_data: The raw data to unserialize. :return: The unserialized data in the structure the step expects it. """ @@ -5967,15 +5947,16 @@ def _unserialize_signal_handler_input( def call_step( self, run_id: str, step_id: str, input_param: Any ) -> typing.Tuple[str, Any]: - """ - This function calls a specific step with the input parameter that has already been unserialized. It expects the - data to be already valid, use unserialize_step_input to produce a valid input. This function is automatically - called by ``__call__`` after unserializing the input. + """This function calls a specific step with the input parameter that + has already been unserialized. It expects the data to be already valid, + use unserialize_step_input to produce a valid input. This function is + automatically called by ``__call__`` after unserializing the input. :param run_id: A unique ID for the run. :param step_id: The ID of the input step to run. :param input_param: The unserialized data structure the step expects. - :return: The ID of the output, and the data structure returned from the step. + :return: The ID of the output, and the data structure returned from the + step. """ return self._call_step(self.get_step(step_id), run_id, input_param) @@ -5986,15 +5967,18 @@ def call_step_signal( signal_id: str, unserialized_input_param: Any, ): - """ - This function calls a specific step's signal with the input parameter that has already been unserialized. It expects the - data to be already valid, use unserialize_signal_input to produce a valid input. + """This function calls a specific step's signal with the input + parameter that has already been unserialized. It expects the data to be + already valid, use unserialize_signal_input to produce a valid input. - :param run_id: A unique ID for the run, which must match signals associated with this step execution. + :param run_id: A unique ID for the run, which must match signals + associated with this step execution. :param step_id: The ID of the input step to run. :param signal_id: The signal ID as defined by the plugin. - :param unserialized_input_param: The unserialized data structure the step expects. - :return: The ID of the output, and the data structure returned from the step. + :param unserialized_input_param: The unserialized data structure the + step expects. + :return: The ID of the output, and the data structure returned from the + step. """ step = self.get_step(step_id) signal = self.get_signal(step_id, signal_id) @@ -6025,11 +6009,13 @@ def _call_step( def serialize_output( self, step_id: str, output_id: str, output_data: Any ) -> Any: - """ - This function takes an output ID (e.g. "error") and structured output_data and serializes them into a format - suitable for wire transport. This function is automatically called by ``__call__`` after the step is run. + """This function takes an output ID (e.g. "error") and structured + output_data and serializes them into a format suitable for wire + transport. This function is automatically called by ``__call__`` after + the step is run. - :param step_id: The step ID to use to look up the schema for serialization. + :param step_id: The step ID to use to look up the schema for + serialization. :param output_id: The string identifier for the output data structure. :param output_data: The data structure returned from the step. :return: @@ -6052,14 +6038,15 @@ def __call__( data: Any, skip_serialization: bool = False, ) -> typing.Tuple[str, Any]: - """ - This function takes the input data, unserializes it for the specified step, calls the specified step, and, - unless skip_serialization is set, serializes the return data. + """This function takes the input data, unserializes it for the + specified step, calls the specified step, and, unless + skip_serialization is set, serializes the return data. :param step_id: the step to execute :param data: input data :param skip_serialization: skip result serialization to basic types - :return: the result ID, and the resulting data in the structure matching the result ID + :return: the result ID, and the resulting data in the structure + matching the result ID """ step = self.get_step(step_id) input_param = self._unserialize_step_input(step, data) @@ -6114,8 +6101,9 @@ def _resolve_abstract_type( new_path.append(name) raise SchemaBuildException( tuple(new_path), - "Unsupported attribute combination, you can only use typing.Optional, etc. in classes, but not in " - "lists, dicts, etc.", + "Unsupported attribute combination, you can only use" + " typing.Optional, etc. in classes, but not in lists, dicts," + " etc.", ) res: AbstractType = result return res @@ -6325,8 +6313,8 @@ def _resolve_dataclass_field( elif not underlying_type.required: raise SchemaBuildException( path, - "Field is marked as optional, but does not have a default value set. " - "Please set a default value for this field.", + "Field is marked as optional, but does not have a default" + " value set. Please set a default value for this field.", ) return meta_id, underlying_type @@ -6364,17 +6352,19 @@ def _resolve_class( if isinstance(t, unsupported_type) or t == unsupported_type: raise SchemaBuildException( path, - "{} are not supported by the Arcaflow typing system and cannot be used in input or output data" - "types. Please use one of the supported types, or file an issue at {} with your use case to " - "get them included.".format( - unsupported_type_name, _issue_url - ), + "{} are not supported by the Arcaflow typing system" + " and cannot be used in input or output datatypes." + " Please use one of the supported types, or file an" + " issue at {} with your use case to get them included." + .format(unsupported_type_name, _issue_url), ) raise SchemaBuildException( path, - "{} is not a dataclass or a supported type. Please use the @dataclasses.dataclass decorator on your " - "class or use a supported native type. If this is a native Python type and you want to request support " - "for it in the Arcaflow SDK, please open an issue at {} to get it included.".format( + "{} is not a dataclass or a supported type. Please use the" + " @dataclasses.dataclass decorator on your class or use a" + " supported native type. If this is a native Python type and" + " you want to request support for it in the Arcaflow SDK," + " please open an issue at {} to get it included.".format( t.__name__, _issue_url ), ) from e @@ -6540,7 +6530,8 @@ def _resolve_annotated( if len(args) < 2: raise SchemaBuildException( path, - "At least one validation parameter required for typing.Annotated", + "At least one validation parameter required for" + " typing.Annotated", ) new_path = list(path) new_path.append("typing.Annotated") @@ -6574,8 +6565,8 @@ def _resolve_list( ) -> AbstractType: raise SchemaBuildException( path, - "List type without item type definition encountered, please declare your lists like this: " - "typing.List[str]", + "List type without item type definition encountered, please" + " declare your lists like this: typing.List[str]", ) @classmethod @@ -6588,8 +6579,8 @@ def _resolve_list_type( ) -> AbstractType: raise SchemaBuildException( path, - "List type without item type definition encountered, please declare your lists like this: " - "typing.List[str]", + "List type without item type definition encountered, please" + " declare your lists like this: typing.List[str]", ) @classmethod @@ -6604,8 +6595,8 @@ def _resolve_list_annotation( if len(args) != 1: raise SchemaBuildException( path, - "List type without item type definition encountered, please declare your lists like this: " - "typing.List[str]", + "List type without item type definition encountered, please" + " declare your lists like this: typing.List[str]", ) new_path = list(path) new_path.append("items") @@ -6630,8 +6621,8 @@ def _resolve_dict( ) -> AbstractType: raise SchemaBuildException( path, - "Dict type without item type definition encountered, please declare your dicts like this: " - "typing.Dict[str, int]", + "Dict type without item type definition encountered, please" + " declare your dicts like this: typing.Dict[str, int]", ) @classmethod @@ -6658,8 +6649,8 @@ def _resolve_dict_type( ) -> AbstractType: raise SchemaBuildException( path, - "Dict type without item type definition encountered, please declare your dicts like this: " - "typing.Dict[str, int]", + "Dict type without item type definition encountered, please" + " declare your dicts like this: typing.Dict[str, int]", ) @classmethod @@ -6677,8 +6668,8 @@ def _resolve_dict_annotation( if len(args) != 2: raise SchemaBuildException( path, - "Dict type without item type definition encountered, please declare your dicts like this: " - "typing.Dict[str, int]", + "Dict type without item type definition encountered, please" + " declare your dicts like this: typing.Dict[str, int]", ) keys_path = list(path) keys_path.append("keys") @@ -6749,17 +6740,20 @@ def _resolve_union( if f.required_if is not None and len(f.required_if) != 0: raise SchemaBuildException( tuple(new_path), - "Union types cannot simultaneously contain require_if fields", + "Union types cannot simultaneously contain require_if" + " fields", ) if f.required_if_not is not None and len(f.required_if_not) != 0: raise SchemaBuildException( tuple(new_path), - "Union types cannot simultaneously contain require_if_not fields", + "Union types cannot simultaneously contain require_if_not" + " fields", ) if f.conflicts is not None and len(f.conflicts) != 0: raise SchemaBuildException( tuple(new_path), - "Union types cannot simultaneously contain conflicts fields", + "Union types cannot simultaneously contain conflicts" + " fields", ) if not isinstance(f.type, RefType): raise SchemaBuildException( @@ -6776,8 +6770,9 @@ def _resolve_union( and type(discriminator_value) is not discriminator_type ): raise BadArgumentException( - "Invalid discriminator value type: {}, the value type has been previously set to {}. Please make " - "sure all your discriminator values have the same type.".format( + "Invalid discriminator value type: {}, the value type has" + " been previously set to {}. Please make sure all your" + " discriminator values have the same type.".format( type(discriminator_value), discriminator_type ) ) @@ -6810,9 +6805,9 @@ def _resolve_pattern(cls, t, type_hints: type, path, scope: ScopeType): def build_object_schema(t, _skip_validation: bool = False) -> ScopeType: - """ - This function builds a schema for a single object. This is useful when serializing input parameters into a file - for underlying tools to use, or unserializing responses from underlying tools into output data types. + """This function builds a schema for a single object. This is useful when + serializing input parameters into a file for underlying tools to use, or + unserializing responses from underlying tools into output data types. :param t: the type to build a schema for. :param _skip_validation: Skip schema validation. For internal use only when constructing ``SCOPE_SCHEMA``. @@ -6837,15 +6832,19 @@ def build_object_schema(t, _skip_validation: bool = False) -> ScopeType: # region Schema schemas SCOPE_SCHEMA = build_object_schema(ScopeSchema, True) -""" -This variable holds a constructed, serializable/unserializable schema for a scope. You can use it to send schemas to -the engine, or to build an engine replacement. (This is normally handled by the plugin module.) +"""This variable holds a constructed, serializable/unserializable schema for a +scope. + +You can use it to send schemas to the engine, or to build an engine +replacement. (This is normally handled by the plugin module.) """ SCHEMA_SCHEMA = build_object_schema(Schema) -""" -This variable holds a constructed, serializable/unserializable schema for an entire schema. You can use it to send -schemas to the engine, or to build an engine replacement. (This is normally handled by the plugin module.) +"""This variable holds a constructed, serializable/unserializable schema for an +entire schema. + +You can use it to send schemas to the engine, or to build an engine +replacement. (This is normally handled by the plugin module.) """ @@ -6854,9 +6853,9 @@ def test_object_serialization( fail: typing.Optional[Callable[[str], None]] = None, t: typing.Optional[ObjectType] = None, ): - """ - This function aids serialization by first serializing, then unserializing the passed parameter according to the - passed schema. It then compares that the two objects are equal. + """This function aids serialization by first serializing, then + unserializing the passed parameter according to the passed schema. It then + compares that the two objects are equal. :param dc: the dataclass to use for tests. :param t: the schema for the dataclass. If none is passed, the schema is built automatically using @@ -6871,27 +6870,28 @@ def test_object_serialization( unserialized_data = t.unserialize(serialized_data, path) if unserialized_data != dc: raise Exception( - "After serializing and unserializing {}, the data mismatched. Serialized data was: {}".format( - dc.__name__, serialized_data - ) + "After serializing and unserializing {}, the data mismatched." + " Serialized data was: {}".format(dc.__name__, serialized_data) ) except Exception as e: result = ( - "Your object serialization test for {} failed.\n\n" - "This means that your object cannot be properly serialized by the SDK. There are three possible " - "reasons for this:\n\n" - "1. Your data class has a field type in it that the SDK doesn't support\n" - "2. Your sample data is invalid according to your own rules\n" - "3. There is a bug in the SDK (please report it)\n\n" - "Check the error message below for details.\n\n" - "---\n\n{}".format(type(dc).__name__, traceback.extract_stack()) + "Your object serialization test for {} failed.\n\nThis means that" + " your object cannot be properly serialized by the SDK. There are" + " three possible reasons for this:\n\n1. Your data class has a" + " field type in it that the SDK doesn't support\n2. Your sample" + " data is invalid according to your own rules\n3. There is a bug" + " in the SDK (please report it)\n\nCheck the error message below" + " for details.\n\n---\n\n{}".format( + type(dc).__name__, traceback.extract_stack() + ) ) result += "Error message:\n" + e.__str__() + "\n\n" # noinspection PyDataclass result += "Input:\n" + pprint.pformat(dataclasses.asdict(dc)) + "\n\n" result += "---\n\n" - result += "Your object serialization test for {} failed. Please scroll up for details.\n\n".format( - type(dc).__name__ + result += ( + "Your object serialization test for {} failed. Please scroll up" + " for details.\n\n".format(type(dc).__name__) ) if fail is None: print(result) diff --git a/src/arcaflow_plugin_sdk/serialization.py b/src/arcaflow_plugin_sdk/serialization.py index e7f8d11..94385f0 100644 --- a/src/arcaflow_plugin_sdk/serialization.py +++ b/src/arcaflow_plugin_sdk/serialization.py @@ -6,8 +6,7 @@ def load_from_file(file_name: str) -> Any: - """ - This function loads a YAML or JSON structure from a file. + """This function loads a YAML or JSON structure from a file. :param file_name: File name ending in JSON or YAML. :return: the decoded structure. @@ -39,8 +38,8 @@ def load_from_file(file_name: str) -> Any: def load_from_stdin(stdin: io.TextIOWrapper) -> Any: - """ - This function reads from the standard input and returns a Python data structure. + """This function reads from the standard input and returns a Python data + structure. :param stdin: the standard input :return: the decoded structure. diff --git a/src/arcaflow_plugin_sdk/test_atp.py b/src/arcaflow_plugin_sdk/test_atp.py index b3f0784..07f6f9b 100644 --- a/src/arcaflow_plugin_sdk/test_atp.py +++ b/src/arcaflow_plugin_sdk/test_atp.py @@ -85,7 +85,10 @@ def signal_test_step( @plugin.signal_handler( id="record_value", name="record value", - description="Records the value, and optionally ends the step. Throws error if it's less than 0, for testing.", + description=( + "Records the value, and optionally ends the step. Throws error if" + " it's less than 0, for testing." + ), ) def signal_test_signal_handler(self, signal_input: SignalTestInput): if signal_input.value < 0: @@ -224,10 +227,8 @@ def test_step_with_signals(self): self._cleanup(pid, stdin_writer, stdout_reader) def test_multi_step_with_signals(self): - """ - Starts two steps simultaneously, sends them separate data from signals, then verifies - that each step got the dats intended for it. - """ + """Starts two steps simultaneously, sends them separate data from + signals, then verifies that each step got the dats intended for it.""" pid, stdin_writer, stdout_reader = self._execute_plugin( test_signals_schema ) @@ -292,10 +293,9 @@ def test_multi_step_with_signals(self): self._cleanup(pid, stdin_writer, stdout_reader) def test_broken_step(self): - """ - Runs a step that throws an exception, which is something that should be caught by the plugin, but - we need to test for it since the uncaught exceptions are the hardest to debug without proper handling. - """ + """Runs a step that throws an exception, which is something that should + be caught by the plugin, but we need to test for it since the uncaught + exceptions are the hardest to debug without proper handling.""" pid, stdin_writer, stdout_reader = self._execute_plugin( test_broken_schema ) @@ -319,9 +319,7 @@ def test_broken_step(self): self._cleanup(pid, stdin_writer, stdout_reader, True) def test_wrong_step(self): - """ - Tests the error reporting due to an invalid step being called. - """ + """Tests the error reporting due to an invalid step being called.""" pid, stdin_writer, stdout_reader = self._execute_plugin(test_schema) try: @@ -341,9 +339,7 @@ def test_wrong_step(self): self._cleanup(pid, stdin_writer, stdout_reader, True) def test_invalid_runtime_message_id(self): - """ - Tests the error reporting due to an invalid step being called. - """ + """Tests the error reporting due to an invalid step being called.""" pid, stdin_writer, stdout_reader = self._execute_plugin(test_schema) try: diff --git a/src/arcaflow_plugin_sdk/test_schema.py b/src/arcaflow_plugin_sdk/test_schema.py index e1a5071..66a5be4 100644 --- a/src/arcaflow_plugin_sdk/test_schema.py +++ b/src/arcaflow_plugin_sdk/test_schema.py @@ -1531,10 +1531,14 @@ class TestData: "a": { "oneOf": [ { - "$ref": "#/$defs/A_discriminated_string_a" + "$ref": ( + "#/$defs/A_discriminated_string_a" + ) }, { - "$ref": "#/$defs/B_discriminated_string_b" + "$ref": ( + "#/$defs/B_discriminated_string_b" + ) }, ] } @@ -1602,9 +1606,7 @@ class TestData: def load_tests(loader, tests, ignore): - """ - This function adds the doctests to the discovery process. - """ + """This function adds the doctests to the discovery process.""" tests.addTests(doctest.DocTestSuite(schema)) return tests From 2fecde7bf588e327238fc1494604d37be4a2a60e Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 13:01:34 -0400 Subject: [PATCH 08/13] ignore pep W503 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b4cd75a..5c0a4cf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: run: black --check --line-length 79 . - name: Error linting id: flake8 - run: flake8 --max-line-length 79 --ignore E203 . + run: flake8 --max-line-length 79 --ignore E203,W503 . - name: Check error linting if: steps.flake8.outputs.number > 0 run: exit 1 From 8b9a890afb962356817dbd93e11e1823d2c6f070 Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 13:38:25 -0400 Subject: [PATCH 09/13] fix long lines --- src/arcaflow_plugin_sdk/atp.py | 12 +- src/arcaflow_plugin_sdk/plugin.py | 5 +- src/arcaflow_plugin_sdk/schema.py | 212 +++++++++++++++------------- src/arcaflow_plugin_sdk/test_atp.py | 8 +- test_example_plugin.py | 4 +- 5 files changed, 132 insertions(+), 109 deletions(-) diff --git a/src/arcaflow_plugin_sdk/atp.py b/src/arcaflow_plugin_sdk/atp.py index eaf8d81..9c492e9 100644 --- a/src/arcaflow_plugin_sdk/atp.py +++ b/src/arcaflow_plugin_sdk/atp.py @@ -110,7 +110,8 @@ def run_plugin( self.plugin_schema = plugin_schema self.handle_handshake() - # First replace stdout so that prints are handled by us, instead of potentially interfering with the atp pipes. + # First replace stdout so that prints are handled by us, instead of + # potentially interfering with the atp pipes. original_stdout = sys.stdout original_stderr = sys.stderr self.user_out_buffer = io.StringIO() @@ -119,11 +120,13 @@ def run_plugin( # Run the read loop. This blocks to wait for the loop to finish. self.run_server_read_loop() - # Wait for the step/signal threads to finish. If it gets stuck here then there is another thread blocked. + # Wait for the step/signal threads to finish. If it gets stuck here + # then there is another thread blocked. for thread in self.running_threads: thread.join() - # Don't reset stdout/stderr until after the read and step/signal threads are done. + # Don't reset stdout/stderr until after the read and step/signal + # threads are done. sys.stdout = original_stdout sys.stderr = original_stderr @@ -231,7 +234,8 @@ def handle_signal(self, run_id, signal_msg): saved_step_id, received_signal_id, signal_msg["data"] ) ) - # The data is verified and unserialized. Now call the signal in its own thread. + # The data is verified and unserialized. Now call the signal in its own + # thread. run_thread = threading.Thread( target=self.run_signal, args=( diff --git a/src/arcaflow_plugin_sdk/plugin.py b/src/arcaflow_plugin_sdk/plugin.py index 7ae9d8e..ec8c4bb 100644 --- a/src/arcaflow_plugin_sdk/plugin.py +++ b/src/arcaflow_plugin_sdk/plugin.py @@ -433,12 +433,13 @@ def build_schema(*args: schema.StepType) -> schema.SchemaType: >>> plugin_schema("hello-world", {"name": "Arca Lot"}) ('success', {'message': 'Hello, Arca Lot!'}) - """ + """ # noqa: E501 steps_by_id: Dict[str, schema.StepType] = {} for step in args: if step.id in steps_by_id: raise BadArgumentException("Duplicate step ID %s" % step.id) - step.inspect_methods() # Allows it to retrieve the signal schemas from their method names + # Allows it to retrieve the signal schemas from their method names + step.inspect_methods() steps_by_id[step.id] = step return schema.SchemaType(steps_by_id) diff --git a/src/arcaflow_plugin_sdk/schema.py b/src/arcaflow_plugin_sdk/schema.py index cf7d61c..3bb09ee 100644 --- a/src/arcaflow_plugin_sdk/schema.py +++ b/src/arcaflow_plugin_sdk/schema.py @@ -62,7 +62,7 @@ This region holds two variables, ``SCOPE_SCHEMA`` and ``SCHEMA_SCHEMA``. You can use these variables to work with schemas themselves. For example, you can create a client that calls Arcaflow plugins and use these classes to unserialize schemas that the plugins send. -""" +""" # noqa: E501 import collections import dataclasses import enum @@ -347,7 +347,7 @@ def description(description: str): :param description: The description to apply :return: Callable - """ + """ # noqa: E501 def call(t): t.__description = description @@ -569,7 +569,7 @@ def discriminator(discriminator_field_name: str) -> discriminatorFunc: >>> unserialized_data = s.unserialize({"some_field": {"foo": "A", "a": "Hello world!"}}) >>> unserialized_data.some_field.a 'Hello world!' - """ + """ # noqa: E501 def call(t): """:param typing.Union[OneOfStringSchema, OneOfIntSchema] t: @@ -664,7 +664,7 @@ def discriminator_value( >>> unserialized_data = s.unserialize({"some_field": {"_type": "Foo", "a": "Hello world!"}}) >>> unserialized_data.some_field.a 'Hello world!' - """ + """ # noqa: E501 def call(t): if ( @@ -738,7 +738,7 @@ def min(param: typing.Union[int, float]) -> Validator: >>> unserialized_data = s.unserialize({"some_field": 42}) >>> unserialized_data.some_field 42 - """ + """ # noqa: E501 def call(t): effective_t = t @@ -799,7 +799,7 @@ def max(param: int) -> Validator: >>> unserialized_data = s.unserialize({"some_field": 4}) >>> unserialized_data.some_field 4 - """ + """ # noqa: E501 def call(t): """:param typing.Union[IntSchema, FloatSchema, StringSchema, @@ -866,7 +866,7 @@ def pattern(pattern: Pattern) -> Validator: >>> unserialized_data = s.unserialize({"some_field": "asdf"}) >>> unserialized_data.some_field 'asdf' - """ + """ # noqa: E501 def call(t): """:param typing.Union[StringSchema,PropertySchema] t: @@ -943,7 +943,7 @@ def required_if(required_if: str) -> Validator: 'bar' >>> unserialized_data.some_other_field 'foo' - """ + """ # noqa: E501 def call(t: PropertySchema) -> PropertySchema: if not isinstance(t, PropertySchema): @@ -1022,7 +1022,7 @@ def required_if_not(required_if_not: str) -> Validator: 'foo' >>> unserialized_data.some_field 'bar' - """ + """ # noqa: E501 def call(t: PropertySchema) -> PropertySchema: if not isinstance(t, PropertySchema): @@ -1094,7 +1094,7 @@ def conflicts(conflicts: str) -> Validator: >>> unserialized_data = s.unserialize({"some_field": "bar"}) >>> unserialized_data.some_field 'bar' - """ + """ # noqa: E501 def call(t): """ @@ -1282,7 +1282,7 @@ def _id_typeize(input: str) -> str: 'Hello_world_' >>> _id_typeize('Hello\\nworld') 'Hello_world' - """ + """ # noqa: E501 return re.sub(_id_type_inverse_re, "_", input) @@ -1347,7 +1347,7 @@ class Unit: ... "nanosecond", ... "nanoseconds" ... ) - """ + """ # noqa: E501 name_short_singular: typing.Annotated[ str, @@ -1786,7 +1786,7 @@ class StringEnumSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... }) >>> s.valid_values() ['apple', 'banana', 'orange'] - """ + """ # noqa: E501 values: typing.Annotated[ Dict[str, DisplayValue], @@ -1809,7 +1809,7 @@ def _to_jsonschema_fragment( strings. See: https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values - """ + """ # noqa: E501 return {"type": "string", "enum": list(self.values.keys())} def _to_openapi_fragment( @@ -1818,7 +1818,7 @@ def _to_openapi_fragment( """This method generates an OpenAPI fragment for string enums. See: https://spec.openapis.org/oas/v3.1.0#data-types - """ + """ # noqa: E501 return {"type": "string", "enum": list(self.values.keys())} @@ -1839,7 +1839,7 @@ class IntEnumSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... }) >>> s.valid_values() [1, 2, 3] - """ + """ # noqa: E501 values: typing.Annotated[ Dict[int, DisplayValue], @@ -1860,7 +1860,7 @@ def _to_jsonschema_fragment( integers. See: https://json-schema.org/understanding-json-schema/reference/generic.html#enumerated-values - """ + """ # noqa: E501 return {"type": "integer", "enum": list(self.values.keys())} def _to_openapi_fragment( @@ -1869,7 +1869,7 @@ def _to_openapi_fragment( """This method generates an OpenAPI fragment for string enums. See: https://spec.openapis.org/oas/v3.1.0#data-types - """ + """ # noqa: E501 return { "type": "integer", "format": "int64", @@ -1892,7 +1892,7 @@ class StringSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... max=5, ... pattern=re.compile("^[a-z]+$") ... ) - """ + """ # noqa: E501 min: typing.Annotated[ Optional[int], @@ -1923,7 +1923,7 @@ def _to_jsonschema_fragment( """This method generates an JSON schema fragment for strings. See: https://json-schema.org/understanding-json-schema/reference/string.html - """ + """ # noqa: E501 result = {"type": "string"} if self.min is not None: result["minLength"] = self.min @@ -1939,7 +1939,7 @@ def _to_openapi_fragment( """This method generates an OpenAPI fragment for strings. See: https://swagger.io/docs/specification/data-models/data-types/#string - """ + """ # noqa: E501 result = {"type": "string"} if self.min is not None: result["minLength"] = self.min @@ -1961,7 +1961,7 @@ class PatternSchema(_JSONSchemaGenerator, _OpenAPIGenerator): >>> from arcaflow_plugin_sdk import schema >>> s = schema.PatternSchema() - """ + """ # noqa: E501 def _to_jsonschema_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _JSONSchemaDefs @@ -1989,7 +1989,7 @@ class IntSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... max=5, ... units=schema.UNIT_BYTE ... ) - """ + """ # noqa: E501 min: typing.Annotated[ Optional[int], @@ -2055,7 +2055,7 @@ class FloatSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... max=100, ... units=schema.UNIT_PERCENT ... ) - """ + """ # noqa: E501 min: typing.Annotated[ Optional[float], @@ -2121,7 +2121,7 @@ class BoolSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... max=100, ... units=schema.UNIT_PERCENT ... ) - """ + """ # noqa: E501 def _to_jsonschema_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _JSONSchemaDefs @@ -2208,7 +2208,7 @@ class ListSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... min=2, ... max=3, ... ) - """ + """ # noqa: E501 items: typing.Annotated[ VALUE_TYPE, @@ -2521,7 +2521,8 @@ def _to_jsonschema_fragment( if self.id in defs.defs: return {"$ref": "#/$defs/" + self.id} - # Add the object in the beginning to avoid an endless loop. Properties will be filled up below + # Add the object in the beginning to avoid an endless loop. Properties + # will be filled up below defs.defs[self.id] = { "type": "object", "properties": {}, @@ -2550,7 +2551,8 @@ def _to_openapi_fragment( if self.id in defs.components: return {"$ref": "#/components/schemas/" + self.id} - # Add the object in the beginning to avoid an endless loop. Properties will be filled up below + # Add the object in the beginning to avoid an endless loop. Properties + # will be filled up below defs.components[self.id] = { "type": "object", "properties": {}, @@ -2636,7 +2638,7 @@ class OneOfStringSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... ) ... } ... ) - """ + """ # noqa: E501 types: Dict[str, typing.Annotated[_OBJECT_LIKE, discriminator("type_id")]] discriminator_field_name: typing.Annotated[ @@ -2768,7 +2770,7 @@ class OneOfIntSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... }, ... "C", ... ) - """ + """ # noqa: E501 types: Dict[int, typing.Annotated[_OBJECT_LIKE, discriminator("type_id")]] discriminator_field_name: typing.Annotated[ @@ -2885,7 +2887,7 @@ class RefSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... "a": schema.PropertySchema(ref) ... } ... ) - """ + """ # noqa: E501 id: typing.Annotated[ ID_TYPE, @@ -2919,7 +2921,7 @@ class AnySchema(_JSONSchemaGenerator, _OpenAPIGenerator): Create an any schema: >>> ref = schema.AnySchema() - """ + """ # noqa: E501 def _to_jsonschema_fragment( self, scope: typing.ForwardRef("ScopeSchema"), defs: _JSONSchemaDefs @@ -2970,7 +2972,7 @@ class ScopeSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... "a": schema.PropertySchema(ref) ... } ... ) - """ + """ # noqa: E501 objects: typing.Annotated[ Dict[ID_TYPE, ObjectSchema], @@ -3061,7 +3063,7 @@ class StepOutputSchema(_JSONSchemaGenerator, _OpenAPIGenerator): ... schema.DisplayValue("Test output"), ... error=False, ... ) - """ + """ # noqa: E501 schema: typing.Annotated[ ScopeSchema, @@ -3117,7 +3119,7 @@ def to_jsonschema(self): 'object' >>> json_schema["properties"]["foo"]["type"] 'string' - """ + """ # noqa: E501 return self._to_jsonschema_fragment(self.schema, _JSONSchemaDefs()) def to_openapi(self): @@ -3160,7 +3162,7 @@ def to_openapi(self): '#/components/schemas/a' >>> openapi["components"]["schemas"]["a"]["properties"]["foo"]["type"] 'string' - """ + """ # noqa: E501 return self._to_openapi_fragment(self.schema, _OpenAPIComponents()) def _to_jsonschema_fragment( @@ -3439,7 +3441,7 @@ class StringEnumType(_EnumType, StringEnumSchema): Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: 'plum' is not a valid value for 'Fruits' - """ + """ # noqa: E501 def __init__(self, t: Type[EnumT]): self._type = t @@ -3497,7 +3499,7 @@ class IntEnumType(_EnumType, IntEnumSchema): Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: '4' is not a valid value for 'PrimeNumbers' - """ + """ # noqa: E501 def __init__(self, t: Type[EnumT]): self._type = t @@ -3533,7 +3535,7 @@ class BoolType(BoolSchema, AbstractType): >>> bool_type = schema.BoolType() Now you can use the type to unseralize, validate, or serialize values. - """ + """ # noqa: E501 def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) @@ -3591,7 +3593,7 @@ def unserialize( Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Boolean value expected, string found () - """ + """ # noqa: E501 if isinstance(data, bool): return data if isinstance(data, int): @@ -3656,7 +3658,7 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Boolean value expected, found - """ + """ # noqa: E501 if not isinstance(data, bool): raise ConstraintException( path, "Boolean value expected, {} found".format(type(data)) @@ -3690,7 +3692,7 @@ def serialize( Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Boolean value expected, found - """ + """ # noqa: E501 if isinstance(data, bool): return data raise ConstraintException( @@ -3713,7 +3715,7 @@ class StringType(StringSchema, AbstractType): ... ) You can now unserialize, validate, or serialize the data. - """ + """ # noqa: E501 def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) @@ -3731,7 +3733,7 @@ def unserialize( ... ) >>> s.unserialize(123) '123' - """ + """ # noqa: E501 if isinstance(data, int): data = str(data) self.validate(data, path) @@ -3766,7 +3768,7 @@ def validate(self, data: str, path: typing.Tuple[str] = tuple([])): Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: String must be at most 5 characters, 6 given - """ + """ # noqa: E501 if not isinstance(data, str): raise ConstraintException( path, "Must be a string, {} given".format(type(data)) @@ -3837,7 +3839,7 @@ def unserialize( ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Invalid regular expression (unterminated \ character set at position 0) - """ + """ # noqa: E501 if not isinstance(data, str): raise ConstraintException(path, "Must be a string") try: @@ -3876,7 +3878,7 @@ def validate(self, data: re.Pattern, path: typing.Tuple[str] = tuple([])): Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Not a regular expression - """ + """ # noqa: E501 if not isinstance(data, re.Pattern): raise ConstraintException(path, "Not a regular expression") @@ -3907,7 +3909,7 @@ class IntType(IntSchema, AbstractType): ... ) Now you can use this type to unserialize, validate, or serialize. - """ + """ # noqa: E501 def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) @@ -3957,7 +3959,7 @@ def unserialize( Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Must be at least 1s (1000000ns) - """ + """ # noqa: E501 if isinstance(data, str): if self.units is not None: try: @@ -4011,7 +4013,7 @@ def validate(self, data: int, path: typing.Tuple[str] = tuple([])): Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Must be at least 1s (1000000ns) - """ + """ # noqa: E501 if not isinstance(data, int): raise ConstraintException( path, @@ -4076,7 +4078,7 @@ def serialize(self, data: int, path: typing.Tuple[str] = tuple([])) -> Any: Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Must be at least 1s (1000000ns) - """ + """ # noqa: E501 self.validate(data, path) return data @@ -4100,7 +4102,7 @@ class FloatType(FloatSchema, AbstractType): ... ) Now you can use this type to unserialize, validate, or serialize. - """ + """ # noqa: E501 def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) @@ -4154,7 +4156,7 @@ def unserialize( Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Must be at least 1s (1000000.0ns) - """ + """ # noqa: E501 if isinstance(data, str): if self.units is not None: try: @@ -4211,7 +4213,7 @@ def validate(self, data: float, path: typing.Tuple[str] = tuple([])): Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Must be at least 1s (1000000.0ns) - """ + """ # noqa: E501 if not isinstance(data, float): raise ConstraintException( path, "Must be an float, {} given".format(type(data).__name__) @@ -4276,7 +4278,7 @@ def serialize( Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Must be at least 1s (1000000.0ns) - """ + """ # noqa: E501 self.validate(data, path) return data @@ -4304,7 +4306,7 @@ class ListType(ListSchema, AbstractType, Generic[ListT]): ... ) Now you can use the list type to unserialize, validate, and serialize. - """ + """ # noqa: E501 def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) @@ -4349,7 +4351,7 @@ def unserialize( ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed for 'item 0': String must be at least 1 \ characters, 0 given - """ + """ # noqa: E501 if not isinstance(data, list): raise ConstraintException( path, "Must be a list, {} given".format(type(data).__name__) @@ -4401,7 +4403,7 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed for 'item 0': String must be at least 1 \ characters, 0 given - """ + """ # noqa: E501 self._validate(data, path) for i in range(len(data)): new_path = list(path) @@ -4433,7 +4435,7 @@ def serialize( >>> list_type.serialize(["a"]) ['a'] - """ + """ # noqa: E501 self._validate(data, path) result = [] for i in range(len(data)): @@ -4486,7 +4488,7 @@ class MapType(MapSchema, AbstractType, Generic[MapT]): ... ) Now you can use the map type to unserialize, validate, or serialize data. - """ + """ # noqa: E501 def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) @@ -4531,7 +4533,7 @@ def unserialize( Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Must have at least 1 elements, 0 given. - """ + """ # noqa: E501 entries = self._validate(data, path) result: MapT = {} for key in entries.keys(): @@ -4592,7 +4594,7 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Must have at least 1 elements, 0 given. - """ + """ # noqa: E501 self._validate(data, path) for key in data.keys(): value = data[key] @@ -4648,7 +4650,7 @@ def serialize( Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Must have at least 1 elements, 0 given. - """ + """ # noqa: E501 entries = self._validate(data, path) result = {} for key in entries.keys(): @@ -4729,7 +4731,7 @@ class PropertyType(PropertySchema, Generic[PropertyT]): >>> object_type.properties["a"].type StringType(min=None, max=None, pattern=None) - """ + """ # noqa: E501 field_override: str = "" @@ -4806,7 +4808,7 @@ class ObjectType(ObjectSchema, AbstractType, Generic[ObjectT]): StringType(min=None, max=None, pattern=None) You can now use the object_type to unserialize, validate, and serialize properties. - """ + """ # noqa: E501 _cls: Type[ObjectT] properties: Dict[str, PropertyType] @@ -4870,8 +4872,9 @@ def _validate_config( if len(properties) > 0: for property_id, property in properties.items(): - # Determine what the field is called we are supposed to access. (This may be different from the - # serialized representation.) + # Determine what the field is called we are supposed to + # access. (This may be different from the serialized + # representation.) field_id = property_id if property.field_override != "": field_id = property.field_override @@ -4891,12 +4894,16 @@ def _validate_config( "', '".join(init_type_hints.keys()), ) ) - # TODO in the future we should check if the data types of the constructor match with the property. - # This can be a source of bugs when the code assumes the data is of one type, but the actual data - # type is different. This is not a problem if the schema is auto-generated, but hand-written schemas - # can accidentally introduce this issue. + # TODO in the future we should check if the data types + # of the constructor match with the property. This can + # be a source of bugs when the code assumes the data is + # of one type, but the actual data type is different. + # This is not a problem if the schema is auto-generated, + # but hand-written schemas can accidentally introduce + # this issue. # - # See: https://github.com/arcalot/arcaflow-plugin-sdk-python/issues/46 + # See: + # https://github.com/arcalot/arcaflow-plugin-sdk-python/issues/46 # Check if the property can be read: if field_id not in class_type_hints: @@ -4913,12 +4920,15 @@ def _validate_config( "', '".join(class_type_hints.keys()), ) ) - # TODO in the future we should check if the data types of the properties match with the property. - # This can be a source of bugs when the code assumes the data is of one type, but the actual data - # type is different. This is not a problem if the schema is auto-generated, but hand-written schemas - # can accidentally introduce this issue. + # TODO in the future we should check if the data types of the + # properties match with the property. This can be a source of + # bugs when the code assumes the data is of one type, but the + # actual data type is different. This is not a problem if the + # schema is auto-generated, but hand-written schemas can + # accidentally introduce this issue. # - # See: https://github.com/arcalot/arcaflow-plugin-sdk-python/issues/46 + # See: + # https://github.com/arcalot/arcaflow-plugin-sdk-python/issues/46 @classmethod def _resolve_class_type_hints( @@ -4979,7 +4989,7 @@ def unserialize( Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed for 'TestData': Must be a dict, got str - """ + """ # noqa: E501 if not isinstance(data, dict): raise ConstraintException( path, "Must be a dict, got {}".format(type(data).__name__) @@ -5054,7 +5064,7 @@ def validate(self, data: TypeT, path: typing.Tuple[str] = tuple([])): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed for 'TestData -> a': String must be at least \ 1 characters, 0 given - """ + """ # noqa: E501 if not isinstance(data, self._cls): raise ConstraintException( path, @@ -5177,7 +5187,7 @@ def _validate_not_set( :param data: a dictionary representation of an ObjectType :param object_property: a property of an ObjectType :param path: a traversal from data to object_property - """ + """ # noqa: E501 if object_property.required: raise ConstraintException(path, "This field is required") if object_property.required_if is not None: @@ -5217,11 +5227,10 @@ def _validate_not_set( and getattr(data, required_if_not) is not None ): # (here, required_if_not refers to its value) - # if data is a dict, has this required_if_not as a key, and the - # dict value paired with this required_if_not key is not None - # or - # if data is an object with attribute required_if_not, and - # data.required_if_not is not None + # if data is a dict, has this required_if_not as a key, + # and the dict value paired with this required_if_not + # key is not None or if data is an object with attribute + # required_if_not, and data.required_if_not is not None none_set = False break if none_set: @@ -5618,7 +5627,7 @@ class AnyType(AnySchema, AbstractType): Traceback (most recent call last): ... arcaflow_plugin_sdk.schema.ConstraintException: Validation failed: Unsupported data type for 'any' type: TestClass - """ + """ # noqa: E501 def unserialize( self, data: Any, path: typing.Tuple[str] = tuple([]) @@ -5638,7 +5647,7 @@ def _check(self, data: Any, path: typing.Tuple[str] = tuple([])): if len(data) != 0: list_type_base = type(data[0]) for item in data: - if type(item) is not list_type_base: + if not isinstance(item, list_type_base): raise ConstraintException( tuple(path), "non-uniform type found in list: '{}' is not of" @@ -5731,7 +5740,8 @@ def __call__( signal_input: SignalDataT, ): """ - :param step_data: The instantiated object that stores step run-specific data. + :param step_data: The instantiated object that stores step + run-specific data. :param params: Input data parameter for the signal handler. """ input: ScopeType = self.data_schema @@ -5823,14 +5833,17 @@ def __call__( ) -> typing.Tuple[str, StepOutputT]: """ :param params: Input parameter for the step. - :param skip_input_validation: Do not perform input data type validation. Use at your own risk. - :param skip_output_validation: Do not validate returned output data. Use at your own risk. + :param skip_input_validation: Do not perform input data type + validation. Use at your own risk. + :param skip_output_validation: Do not validate returned output data. + Use at your own risk. :return: The ID for the output datatype, and the output itself. """ # Initialize the step object step_local_data: _StepLocalData = self.setup_run_data(run_id) # Notify potentially waiting signals that the step is running - # Ideally, this would be done after, but just before is the only realistic option without more threads or sleep + # Ideally, this would be done after, but just before is the only + # realistic option without more threads or sleep step_local_data.step_running = True with step_local_data.step_running_condition: step_local_data.step_running_condition.notify_all() @@ -5865,7 +5878,8 @@ def inspect_methods(self): or self.signal_handler_method_names is None ): return - # Constructs an instance of the class in order to retrieve attributes from it + # Constructs an instance of the class in order to retrieve attributes + # from it object_instance = self._step_object_constructor() # Create a map to populate signal_handlers_map = {} @@ -5985,7 +5999,8 @@ def call_step_signal( local_step_data: _StepLocalData = step.setup_run_data(run_id) with local_step_data.step_running_condition: if not local_step_data.step_running: - # wait to be notified of it being ready. Test this by adding a sleep before the step call. + # wait to be notified of it being ready. Test this by adding a + # sleep before the step call. local_step_data.step_running_condition.wait() return signal( local_step_data.initialized_object, unserialized_input_param @@ -6765,9 +6780,8 @@ def _resolve_union( discriminator_value = f.type.id if hasattr(f.type, "__discriminator_value"): discriminator_value = getattr(f.type, "__discriminator_value") - if ( - discriminator_type is not None - and type(discriminator_value) is not discriminator_type + if discriminator_type is not None and not isinstance( + discriminator_value, discriminator_type ): raise BadArgumentException( "Invalid discriminator value type: {}, the value type has" @@ -6810,7 +6824,8 @@ def build_object_schema(t, _skip_validation: bool = False) -> ScopeType: unserializing responses from underlying tools into output data types. :param t: the type to build a schema for. - :param _skip_validation: Skip schema validation. For internal use only when constructing ``SCOPE_SCHEMA``. + :param _skip_validation: Skip schema validation. For internal use only + when constructing ``SCOPE_SCHEMA``. :return: the built object schema """ scope = ScopeType({}, t.__name__) @@ -6858,7 +6873,8 @@ def test_object_serialization( compares that the two objects are equal. :param dc: the dataclass to use for tests. - :param t: the schema for the dataclass. If none is passed, the schema is built automatically using + :param t: the schema for the dataclass. If none is passed, the schema is + built automatically using ``schema.build_object_schema()`` """ try: diff --git a/src/arcaflow_plugin_sdk/test_atp.py b/src/arcaflow_plugin_sdk/test_atp.py index 07f6f9b..1e5c008 100644 --- a/src/arcaflow_plugin_sdk/test_atp.py +++ b/src/arcaflow_plugin_sdk/test_atp.py @@ -62,8 +62,9 @@ class SignalTestStep: exit_event: Event def __init__(self): - # Due to the way Python works, this MUST be done here, and not inlined above, or else it will be - # shared by all objects, resulting in a shared list and event, which would cause problems. + # Due to the way Python works, this MUST be done here, and not inlined + # above, or else it will be shared by all objects, resulting in a + # shared list and event, which would cause problems. self.signal_values = [] self.exit_event = Event() @@ -397,7 +398,8 @@ def test_error_in_signal(self): self.assertEqual(result.output_id, "success") self.assertListEqual(result.output_data["signals_received"], [1]) - # Note: The exception is raised after the step finishes in the test class + # Note: The exception is raised after the step finishes in the test + # class with self.assertRaises(atp.PluginClientStateException) as context: _, _, _, _ = client.read_single_result() client.send_client_done() diff --git a/test_example_plugin.py b/test_example_plugin.py index b476cf7..3672ddc 100755 --- a/test_example_plugin.py +++ b/test_example_plugin.py @@ -27,8 +27,8 @@ def test_functional(self): name=example_plugin.FullName("Arca", "Lot") ) - # Note: The call to hello_world is to the output of the decorator, not the function itself. - # So it's calling the StepType + # Note: The call to hello_world is to the output of the decorator, not + # the function itself, so it's calling the StepType output_id, output_data = example_plugin.hello_world( self.id(), step_input ) From 76e8f586c00ff44a39171953a1ca4d7ec41dd426 Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 16:09:17 -0400 Subject: [PATCH 10/13] add pytest repeat plugin --- poetry.lock | 78 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 0f39459..5174c25 100644 --- a/poetry.lock +++ b/poetry.lock @@ -401,6 +401,20 @@ files = [ {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, ] +[[package]] +name = "exceptiongroup" +version = "1.1.3" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "filelock" version = "3.12.4" @@ -499,6 +513,17 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "isort" version = "5.12.0" @@ -675,6 +700,21 @@ files = [ docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +[[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "pre-commit" version = "3.5.0" @@ -729,6 +769,42 @@ files = [ [package.extras] plugins = ["importlib-metadata"] +[[package]] +name = "pytest" +version = "7.4.3" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, + {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-repeat" +version = "0.9.3" +description = "pytest plugin for repeating tests" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest_repeat-0.9.3-py3-none-any.whl", hash = "sha256:26ab2df18226af9d5ce441c858f273121e92ff55f5bb311d25755b8d7abdd8ed"}, + {file = "pytest_repeat-0.9.3.tar.gz", hash = "sha256:ffd3836dfcd67bb270bec648b330e20be37d2966448c4148c4092d1e8aba8185"}, +] + +[package.dependencies] +pytest = "*" + [[package]] name = "pyyaml" version = "6.0.1" @@ -1095,4 +1171,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "b8c21417679a614f2b8ffde727f28c9298c15102f37a101466ac822a4c07b0eb" +content-hash = "12f16c8df7fcf7cf24094f81873980b8c1a94326627b078d422df56474243c3d" diff --git a/pyproject.toml b/pyproject.toml index 59aaec5..4d1c5cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ autoflake = "^2.2.1" docformatter = "^1.7.5" flake8 = "^6.1.0" autopep8 = "^2.0.4" +pytest-repeat = "^0.9.3" [build-system] From e11e7c24d389a230ea4a0469b7a6515bb91a290f Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 17:03:58 -0400 Subject: [PATCH 11/13] apply docformatter to all docstrings --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5c0a4cf..39a7d98 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: - name: Check import order (isort) run: isort --check --profile black --line-length 79 . - name: Check docstring formatting - run: docformatter --check --recursive --black --docstring-length 1 79 --wrap-descriptions 79 --wrap-summaries 79 . + run: docformatter --check --recursive --black --wrap-descriptions 79 --wrap-summaries 79 . - name: Check style run: black --check --line-length 79 . - name: Error linting From c1862e40c6937ad8519d138ee3c085747d67c112 Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 17:17:20 -0400 Subject: [PATCH 12/13] add dev tool configuration to pyproject --- .github/workflows/build.yml | 4 +--- pyproject.toml | 8 ++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39a7d98..422e5df 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,9 +34,7 @@ jobs: run: isort --check --profile black --line-length 79 . - name: Check docstring formatting run: docformatter --check --recursive --black --wrap-descriptions 79 --wrap-summaries 79 . - - name: Check style - run: black --check --line-length 79 . - - name: Error linting + - name: Error and style linting id: flake8 run: flake8 --max-line-length 79 --ignore E203,W503 . - name: Check error linting diff --git a/pyproject.toml b/pyproject.toml index 4d1c5cc..d1704d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,14 @@ flake8 = "^6.1.0" autopep8 = "^2.0.4" pytest-repeat = "^0.9.3" +[tool.isort] +profile = 'black' + +[tool.docformatter] +recursive = true +wrap-summaries = 79 +wrap-descriptions = 79 + [build-system] requires = ["poetry-core"] From 802bc6bef2ff3d940cd9768a9f202febfe688ab8 Mon Sep 17 00:00:00 2001 From: mleader Date: Thu, 26 Oct 2023 17:41:37 -0400 Subject: [PATCH 13/13] align sphinx field indentation --- src/arcaflow_plugin_sdk/schema.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/arcaflow_plugin_sdk/schema.py b/src/arcaflow_plugin_sdk/schema.py index 3bb09ee..33a71cb 100644 --- a/src/arcaflow_plugin_sdk/schema.py +++ b/src/arcaflow_plugin_sdk/schema.py @@ -6874,8 +6874,7 @@ def test_object_serialization( :param dc: the dataclass to use for tests. :param t: the schema for the dataclass. If none is passed, the schema is - built automatically using - ``schema.build_object_schema()`` + built automatically using ``schema.build_object_schema()`` """ try: if t is None: