From 3cf7c5df8a04acea407a28f3eee46bc49b1a80db Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Wed, 2 Nov 2022 18:00:05 +0100 Subject: [PATCH] feat(svc): workflow ui support (#3135) --- .vscode/settings.json | 3 + docker-compose.yml | 2 +- poetry.lock | 839 ++++++++++-------- pyproject.toml | 4 + renku/command/run.py | 9 +- renku/command/schema/composite_plan.py | 2 + renku/command/schema/plan.py | 2 + renku/command/view_model/agent.py | 54 ++ renku/command/view_model/composite_plan.py | 150 +++- renku/command/view_model/plan.py | 82 +- renku/core/errors.py | 11 +- renku/core/workflow/plan.py | 239 ++++- renku/core/workflow/plan_factory.py | 3 + renku/domain_model/workflow/composite_plan.py | 14 +- renku/domain_model/workflow/plan.py | 17 +- renku/infrastructure/gateway/plan_gateway.py | 2 +- renku/infrastructure/immutable.py | 7 + renku/ui/cli/dataset.py | 2 +- renku/ui/cli/run.py | 14 + renku/ui/cli/utils/terminal.py | 134 ++- renku/ui/cli/workflow.py | 116 +-- .../controllers/workflow_plans_list.py | 53 ++ .../controllers/workflow_plans_show.py | 51 ++ renku/ui/service/entrypoint.py | 2 + renku/ui/service/errors.py | 21 +- renku/ui/service/serializers/workflows.py | 193 ++++ renku/ui/service/views/api_versions.py | 7 +- renku/ui/service/views/error_handlers.py | 18 + renku/ui/service/views/workflow_plans.py | 88 ++ tests/cli/test_run.py | 30 + tests/cli/test_workflow.py | 12 +- tests/core/commands/test_graph.py | 69 +- tests/core/fixtures/core_serialization.py | 9 - tests/core/models/test_template.py | 17 - tests/data/dataset-v0.10.4-before-calamus.yml | 595 ------------- tests/fixtures/config.py | 4 +- tests/fixtures/domain_models.py | 8 + tests/service/fixtures/service_client.py | 23 - tests/service/fixtures/service_integration.py | 46 +- tests/service/fixtures/service_projects.py | 16 +- tests/service/views/test_dataset_views.py | 10 +- .../service/views/test_workflow_plan_views.py | 176 ++++ 42 files changed, 1929 insertions(+), 1225 deletions(-) create mode 100644 renku/command/view_model/agent.py create mode 100644 renku/ui/service/controllers/workflow_plans_list.py create mode 100644 renku/ui/service/controllers/workflow_plans_show.py create mode 100644 renku/ui/service/serializers/workflows.py create mode 100644 renku/ui/service/views/workflow_plans.py delete mode 100644 tests/data/dataset-v0.10.4-before-calamus.yml create mode 100644 tests/service/views/test_workflow_plan_views.py diff --git a/.vscode/settings.json b/.vscode/settings.json index ab6b97adda..6c48fd06f1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,4 +23,7 @@ "\\.eggs | \\.git | \\.hg | \\.mypy_cache | \\.tox | \\.venv | _build | buck-out | build | dist | docs/conf.py", ], "esbonio.sphinx.confDir": "", + "python.analysis.include": [ + "renku/**" + ], } diff --git a/docker-compose.yml b/docker-compose.yml index 5c2ca8043d..f3d5764178 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -117,7 +117,7 @@ services: image: traefik:v2.4 command: --api.insecure=true --providers.docker ports: - - "80:80" + - "81:81" # set the UI port to something other than 8080 since that's our service already - "8088:8080" volumes: diff --git a/poetry.lock b/poetry.lock index 245e317e9e..ffff315254 100644 --- a/poetry.lock +++ b/poetry.lock @@ -34,11 +34,11 @@ python-versions = ">=3.7" PyYAML = {version = ">=3.10", optional = true, markers = "extra == \"yaml\""} [package.extras] -dev = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.13.0)", "pytest", "mock", "flake8 (==4.0.1)", "flake8-bugbear (==22.4.25)", "pre-commit (>=2.4,<3.0)", "mypy (==0.950)", "types-pyyaml", "tox"] +dev = ["PyYAML (>=3.10)", "flake8 (==4.0.1)", "flake8-bugbear (==22.4.25)", "marshmallow (>=3.13.0)", "mock", "mypy (==0.950)", "prance[osv] (>=0.11)", "pre-commit (>=2.4,<3.0)", "pytest", "tox", "types-PyYAML"] docs = ["marshmallow (>=3.13.0)", "pyyaml (==6.0)", "sphinx (==4.5.0)", "sphinx-issues (==3.0.1)", "sphinx-rtd-theme (==1.0.0)"] -lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.4.25)", "pre-commit (>=2.4,<3.0)", "mypy (==0.950)", "types-pyyaml"] +lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.4.25)", "mypy (==0.950)", "pre-commit (>=2.4,<3.0)", "types-PyYAML"] marshmallow = ["marshmallow (>=3.13.0)"] -tests = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.13.0)", "pytest", "mock"] +tests = ["PyYAML (>=3.10)", "marshmallow (>=3.13.0)", "mock", "prance[osv] (>=0.11)", "pytest"] validation = ["prance[osv] (>=0.11)"] yaml = ["PyYAML (>=3.10)"] @@ -54,9 +54,9 @@ python-versions = ">=3.6" apispec = {version = ">=2.0.0", extras = ["yaml"]} [package.extras] -tests = ["bottle (==0.12.17)", "tornado", "Flask (==1.1.1)", "mock", "pytest"] -lint = ["pre-commit (>=1.18,<2.0)", "flake8-bugbear (==19.8.0)", "flake8 (==3.7.9)"] -dev = ["tox", "pre-commit (>=1.18,<2.0)", "flake8-bugbear (==19.8.0)", "flake8 (==3.7.9)", "bottle (==0.12.17)", "tornado", "Flask (==1.1.1)", "mock", "pytest"] +dev = ["Flask (==1.1.1)", "bottle (==0.12.17)", "flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "mock", "pre-commit (>=1.18,<2.0)", "pytest", "tornado", "tox"] +lint = ["flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)"] +tests = ["Flask (==1.1.1)", "bottle (==0.12.17)", "mock", "pytest", "tornado"] [[package]] name = "appdirs" @@ -78,7 +78,7 @@ python-versions = ">=3.6" importlib-metadata = {version = ">=0.23,<5", markers = "python_version == \"3.7\""} [package.extras] -test = ["wheel", "pexpect", "flake8", "coverage"] +test = ["coverage", "flake8", "pexpect", "wheel"] [[package]] name = "attrs" @@ -89,13 +89,13 @@ optional = false python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope-interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope-interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope-interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] +dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] +docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] +tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] -name = "babel" +name = "Babel" version = "2.10.3" description = "Internationalization utilities" category = "main" @@ -158,7 +158,7 @@ optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] -name = "btrees" +name = "BTrees" version = "4.10.1" description = "Scalable persistent object containers" category = "main" @@ -170,13 +170,13 @@ persistent = ">=4.1.0" "zope.interface" = ">=5.0.0" [package.extras] -ZODB = ["zodb"] -docs = ["sphinx", "repoze-sphinx-autointerface", "sphinx-rtd-theme"] -test = ["persistent (>=4.4.3)", "transaction", "zope-testrunner"] +ZODB = ["ZODB"] +docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx_rtd_theme"] +test = ["persistent (>=4.4.3)", "transaction", "zope.testrunner"] [[package]] -name = "cachecontrol" -version = "0.12.12" +name = "CacheControl" +version = "0.12.11" description = "httplib2 caching for requests" category = "main" optional = false @@ -187,7 +187,7 @@ msgpack = ">=0.5.2" requests = "*" [package.extras] -filecache = ["filelock (>=3.8.0)"] +filecache = ["lockfile (>=0.9)"] redis = ["redis (>=2.10.5)"] [[package]] @@ -200,7 +200,7 @@ python-versions = "~=3.7" [[package]] name = "calamus" -version = "0.3.14" +version = "0.4.1" description = "calamus is a library built on top of marshmallow to allow (de-)Serialization of Python classes to JSON-LD." category = "main" optional = false @@ -208,16 +208,16 @@ python-versions = ">=3.7.1,<4.0.0" [package.dependencies] lazy-object-proxy = ">=1.4.3,<2.0.0" -marshmallow = ">=3.5.1,<4.0.0" +marshmallow = ">=3.14.0,<4.0.0" pyld = ">=2.0.2,<3.0.0" rdflib = ">=6.0.0,<7.0.0" [package.extras] -docs = ["sphinx (>=3.0.3,<4.0.0)", "sphinx-rtd-theme (>=0.4.3,<0.5.0)", "sphinxcontrib-spelling (>=5.0.0,<6.0.0)"] +docs = ["Jinja2 (>=3.0.0,<3.1.0)", "sphinx (>=3.0.3,<4.0.0)", "sphinx-rtd-theme (>=0.4.3,<0.5.0)", "sphinxcontrib-spelling (>=5.0.0,<6.0.0)"] [[package]] name = "certifi" -version = "2022.9.14" +version = "2022.9.24" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -259,7 +259,7 @@ pyzmq = ">=17.0" tornado = ">=5.0.2" [package.extras] -test = ["nose", "nose-cov", "coverage", "mock", "circus-web", "gevent", "papa", "pyyaml", "pyzmq (>=17.0)", "flake8 (==2.1.0)"] +test = ["PyYAML", "circus-web", "coverage", "flake8 (==2.1.0)", "gevent", "mock", "nose", "nose-cov", "papa", "pyzmq (>=17.0)"] [[package]] name = "click" @@ -275,7 +275,7 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "click-option-group" -version = "0.5.3" +version = "0.5.4" description = "Option groups missing in Click" category = "main" optional = false @@ -285,8 +285,8 @@ python-versions = ">=3.6,<4" Click = ">=7.0,<9" [package.extras] -docs = ["sphinx (>=2.3,<3)", "pallets-sphinx-themes", "m2r"] -tests = ["coverage (<6)", "pytest", "pytest-cov", "coveralls"] +docs = ["Pallets-Sphinx-Themes", "m2r2", "sphinx (>=3.0,<6)"] +tests = ["coverage (<6)", "coveralls", "pytest", "pytest-cov"] [[package]] name = "click-plugins" @@ -300,7 +300,7 @@ python-versions = "*" click = ">=4.0" [package.extras] -dev = ["coveralls", "wheel", "pytest-cov", "pytest (>=3.6)"] +dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] [[package]] name = "colorama" @@ -333,7 +333,7 @@ optional = false python-versions = "*" [package.extras] -test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" @@ -372,12 +372,12 @@ python-versions = ">=3.6" cffi = ">=1.12" [package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] -docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx_rtd_theme"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] sdist = ["setuptools_rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] [[package]] name = "cwl-upgrader" @@ -456,7 +456,7 @@ python-versions = ">=3.6" ordered-set = ">=4.1.0,<4.2.0" [package.extras] -cli = ["click (==8.0.3)", "pyyaml (==5.4.1)", "toml (==0.10.2)", "clevercsv (==0.7.1)"] +cli = ["clevercsv (==0.7.1)", "click (==8.0.3)", "pyyaml (==5.4.1)", "toml (==0.10.2)"] [[package]] name = "deepmerge" @@ -467,7 +467,7 @@ optional = false python-versions = "*" [[package]] -name = "deprecated" +name = "Deprecated" version = "1.2.13" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." category = "main" @@ -478,7 +478,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" wrapt = ">=1.10,<2" [package.extras] -dev = ["pytest-cov", "pytest", "PyTest-Cov (<2.6)", "PyTest (<5)", "zipp (<2)", "sphinxcontrib-websupport (<2)", "configparser (<5)", "importlib-resources (<4)", "importlib-metadata (<3)", "sphinx (<2)", "bump2version (<1)", "tox"] +dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] [[package]] name = "dill" @@ -505,8 +505,8 @@ requests = ">=2.14.2,<2.18.0 || >2.18.0" websocket-client = ">=0.32.0" [package.extras] -tls = ["idna (>=2.0.0)", "cryptography (>=3.4.7)", "pyOpenSSL (>=17.5.0)"] ssh = ["paramiko (>=2.4.2)"] +tls = ["cryptography (>=3.4.7)", "idna (>=2.0.0)", "pyOpenSSL (>=17.5.0)"] [[package]] name = "docutils" @@ -518,7 +518,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "dunamai" -version = "1.13.0" +version = "1.13.1" description = "Dynamic version generation" category = "main" optional = true @@ -530,7 +530,7 @@ packaging = ">=20.9" [[package]] name = "enlighten" -version = "1.10.2" +version = "1.11.1" description = "Enlighten Progress Bar" category = "main" optional = true @@ -575,8 +575,8 @@ six = ">=1.12" sortedcontainers = "*" [package.extras] -lua = ["lupa"] aioredis = ["aioredis"] +lua = ["lupa"] [[package]] name = "filelock" @@ -621,8 +621,8 @@ toml = "*" urllib3 = "*" [package.extras] -dev = ["wemake-python-styleguide", "typing-extensions", "pytest", "pylint", "pep8-naming", "pandas-vet", "mccabe", "isort", "flake8-variables-names", "flake8-use-fstring", "flake8-todo", "flake8-tidy-imports", "flake8-string-format", "flake8-strict", "flake8-sql", "flake8-spellcheck", "flake8-scrapy", "flake8-rst-docstrings", "flake8-requirements", "flake8-quotes", "flake8-pytest-style", "flake8-pytest", "flake8-pyi", "flake8-printf-formatting", "flake8-print", "flake8-pie", "flake8-pep3101", "flake8-mypy", "flake8-mutable", "flake8-mock", "flake8-logging-format", "flake8-isort", "flake8-import-order", "flake8-future-import", "flake8-functions", "flake8-fixme", "flake8-expression-complexity", "flake8-executable", "flake8-eradicate", "flake8-docstrings", "flake8-django", "flake8-debugger", "flake8-comprehensions", "flake8-commas", "flake8-cognitive-complexity", "flake8-coding", "flake8-builtins", "flake8-bugbear", "flake8-broken-line", "flake8-black", "flake8-bandit", "flake8-annotations-complexity", "flake8-alfred", "flake8-absolute-import", "flake8-aaa", "flake8-2020", "dlint"] -docs = ["sphinx", "recommonmark", "pygments-github-lexers", "alabaster"] +dev = ["dlint", "flake8-2020", "flake8-aaa", "flake8-absolute-import", "flake8-alfred", "flake8-annotations-complexity", "flake8-bandit", "flake8-black", "flake8-broken-line", "flake8-bugbear", "flake8-builtins", "flake8-coding", "flake8-cognitive-complexity", "flake8-commas", "flake8-comprehensions", "flake8-debugger", "flake8-django", "flake8-docstrings", "flake8-eradicate", "flake8-executable", "flake8-expression-complexity", "flake8-fixme", "flake8-functions", "flake8-future-import", "flake8-import-order", "flake8-isort", "flake8-logging-format", "flake8-mock", "flake8-mutable", "flake8-mypy", "flake8-pep3101", "flake8-pie", "flake8-print", "flake8-printf-formatting", "flake8-pyi", "flake8-pytest", "flake8-pytest-style", "flake8-quotes", "flake8-requirements", "flake8-rst-docstrings", "flake8-scrapy", "flake8-spellcheck", "flake8-sql", "flake8-strict", "flake8-string-format", "flake8-tidy-imports", "flake8-todo", "flake8-use-fstring", "flake8-variables-names", "isort[pyproject]", "mccabe", "pandas-vet", "pep8-naming", "pylint", "pytest", "typing-extensions", "wemake-python-styleguide"] +docs = ["alabaster", "pygments-github-lexers", "recommonmark", "sphinx"] [[package]] name = "flaky" @@ -633,7 +633,7 @@ optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] -name = "flask" +name = "Flask" version = "2.1.1" description = "A simple framework for building complex web applications." category = "main" @@ -648,8 +648,8 @@ Jinja2 = ">=3.0" Werkzeug = ">=2.0" [package.extras] -dotenv = ["python-dotenv"] async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] [[package]] name = "frozendict" @@ -679,7 +679,7 @@ python-versions = ">=3.6" smmap = ">=3.0.1,<6" [[package]] -name = "gitpython" +name = "GitPython" version = "3.1.27" description = "GitPython is a python library used to interact with Git repositories" category = "main" @@ -702,7 +702,7 @@ python-versions = "*" pyparsing = "*" [package.extras] -full = ["ply", "numpy"] +full = ["numpy", "ply"] [[package]] name = "gunicorn" @@ -745,7 +745,7 @@ python-versions = ">=3.7" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [package.extras] -tests = ["pytest-cov", "pytest", "freezegun"] +tests = ["freezegun", "pytest", "pytest-cov"] [[package]] name = "idna" @@ -776,9 +776,9 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl-flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "importlib-resources" @@ -792,8 +792,8 @@ python-versions = ">=3.7" zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "iniconfig" @@ -804,7 +804,7 @@ optional = true python-versions = "*" [[package]] -name = "inject" +name = "Inject" version = "4.3.1" description = "Python dependency injection framework" category = "main" @@ -831,10 +831,10 @@ optional = true python-versions = ">=3.6.1,<4.0" [package.extras] -plugins = ["setuptools"] colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +plugins = ["setuptools"] requirements_deprecated_finder = ["pip-api", "pipreqs"] -pipfile_deprecated_finder = ["requirementslib", "pipreqs"] [[package]] name = "itsdangerous" @@ -845,7 +845,7 @@ optional = true python-versions = ">=3.7" [[package]] -name = "jinja2" +name = "Jinja2" version = "3.1.2" description = "A very fast and expressive template engine." category = "main" @@ -904,11 +904,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" [package.extras] cssselect = ["cssselect (>=0.7)"] html5 = ["html5lib"] -htmlsoup = ["beautifulsoup4"] +htmlsoup = ["BeautifulSoup4"] source = ["Cython (>=0.29.7)"] [[package]] -name = "markupsafe" +name = "MarkupSafe" version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" @@ -927,11 +927,27 @@ python-versions = ">=3.7" packaging = ">=17.0" [package.extras] -dev = ["pytest", "pytz", "simplejson", "mypy (==0.971)", "flake8 (==5.0.4)", "flake8-bugbear (==22.8.22)", "pre-commit (>=2.4,<3.0)", "tox"] -docs = ["sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.9)"] -lint = ["mypy (==0.971)", "flake8 (==5.0.4)", "flake8-bugbear (==22.8.22)", "pre-commit (>=2.4,<3.0)"] +dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.8.22)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.8.22)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"] tests = ["pytest", "pytz", "simplejson"] +[[package]] +name = "marshmallow-oneofschema" +version = "3.0.1" +description = "marshmallow multiplexing schema" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +marshmallow = ">=3.0.0,<4.0.0" + +[package.extras] +dev = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mock", "pre-commit (>=2.7,<3.0)", "pytest", "tox"] +lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "pre-commit (>=2.7,<3.0)"] +tests = ["mock", "pytest"] + [[package]] name = "mccabe" version = "0.6.1" @@ -966,11 +982,11 @@ python-versions = ">=3.7" [[package]] name = "mypy" -version = "0.971" +version = "0.982" description = "Optional static typing for Python" category = "main" optional = true -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] mypy-extensions = ">=0.4.3" @@ -1000,11 +1016,11 @@ optional = false python-versions = ">=3.7" [package.extras] -default = ["numpy (>=1.19)", "scipy (>=1.5,!=1.6.1)", "matplotlib (>=3.3)", "pandas (>=1.1)"] +default = ["matplotlib (>=3.3)", "numpy (>=1.19)", "pandas (>=1.1)", "scipy (>=1.5,!=1.6.1)"] developer = ["black (==21.5b1)", "pre-commit (>=2.12)"] -doc = ["sphinx (>=4.0,<5.0)", "pydata-sphinx-theme (>=0.6,<1.0)", "sphinx-gallery (>=0.9,<1.0)", "numpydoc (>=1.1)", "pillow (>=8.2)", "nb2plots (>=0.6)", "texext (>=0.6.6)"] -extra = ["lxml (>=4.5)", "pygraphviz (>=1.7)", "pydot (>=1.4.1)"] -test = ["pytest (>=6.2)", "pytest-cov (>=2.12)", "codecov (>=2.1)"] +doc = ["nb2plots (>=0.6)", "numpydoc (>=1.1)", "pillow (>=8.2)", "pydata-sphinx-theme (>=0.6,<1.0)", "sphinx (>=4.0,<5.0)", "sphinx-gallery (>=0.9,<1.0)", "texext (>=0.6.6)"] +extra = ["lxml (>=4.5)", "pydot (>=1.4.1)", "pygraphviz (>=1.7)"] +test = ["codecov (>=2.1)", "pytest (>=6.2)", "pytest-cov (>=2.12)"] [[package]] name = "numpy" @@ -1023,7 +1039,7 @@ optional = false python-versions = ">=3.7" [package.extras] -dev = ["pytest", "black", "mypy"] +dev = ["black", "mypy", "pytest"] [[package]] name = "owlrl" @@ -1084,8 +1100,8 @@ cffi = {version = "*", markers = "platform_python_implementation == \"CPython\"" "zope.interface" = "*" [package.extras] -docs = ["sphinx", "repoze-sphinx-autointerface"] -test = ["zope-testrunner", "manuel"] +docs = ["Sphinx", "repoze.sphinx.autointerface"] +test = ["manuel", "zope.testrunner"] [[package]] name = "pexpect" @@ -1099,7 +1115,7 @@ python-versions = "*" ptyprocess = ">=0.5" [[package]] -name = "pillow" +name = "Pillow" version = "9.1.1" description = "Python Imaging Library (Fork)" category = "main" @@ -1132,8 +1148,8 @@ optional = true python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] -test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] [[package]] name = "pluggy" @@ -1147,8 +1163,8 @@ python-versions = ">=3.6" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] -testing = ["pytest-benchmark", "pytest"] -dev = ["tox", "pre-commit"] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "poetry-dynamic-versioning" @@ -1165,7 +1181,7 @@ tomlkit = ">=0.4" [[package]] name = "poetry-lock-package" -version = "0.4.4" +version = "0.4.5" description = "Poetry lock package generator" category = "dev" optional = false @@ -1189,7 +1205,7 @@ pywin32 = {version = ">=226", markers = "platform_system == \"Windows\""} [package.extras] docs = ["sphinx (>=1.7.1)"] redis = ["redis"] -tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "sphinx (>=3.0.3)", "pytest-mypy (>=0.8.0)", "redis"] +tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "redis", "sphinx (>=3.0.3)"] [[package]] name = "prefixed" @@ -1241,7 +1257,7 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -test = ["ipaddress", "mock", "enum34", "pywin32", "wmi"] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] [[package]] name = "ptyprocess" @@ -1331,7 +1347,7 @@ optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] -name = "pygments" +name = "Pygments" version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" @@ -1342,7 +1358,7 @@ python-versions = ">=3.6" plugins = ["importlib-metadata"] [[package]] -name = "pyjwt" +name = "PyJWT" version = "2.4.0" description = "JSON Web Token implementation in Python" category = "main" @@ -1351,12 +1367,12 @@ python-versions = ">=3.6" [package.extras] crypto = ["cryptography (>=3.3.1)"] -dev = ["sphinx", "sphinx-rtd-theme", "zope-interface", "cryptography (>=3.3.1)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"] -docs = ["sphinx", "sphinx-rtd-theme", "zope-interface"] -tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.3.1)", "mypy", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] -name = "pyld" +name = "PyLD" version = "2.0.3" description = "Python implementation of the JSON-LD API" category = "main" @@ -1375,7 +1391,7 @@ frozendict = ["frozendict"] requests = ["requests"] [[package]] -name = "pyopenssl" +name = "pyOpenSSL" version = "22.0.0" description = "Python wrapper module around the OpenSSL library" category = "main" @@ -1386,8 +1402,8 @@ python-versions = ">=3.6" cryptography = ">=35.0" [package.extras] -test = ["pytest (>=3.0.1)", "pretend", "flaky"] -docs = ["sphinx-rtd-theme", "sphinx"] +docs = ["sphinx", "sphinx-rtd-theme"] +test = ["flaky", "pretend", "pytest (>=3.0.1)"] [[package]] name = "pyparsing" @@ -1398,7 +1414,7 @@ optional = false python-versions = ">=3.6.8" [package.extras] -diagrams = ["railroad-diagrams", "jinja2"] +diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyreadline" @@ -1505,7 +1521,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "pytest-flake8" @@ -1543,7 +1559,7 @@ python-versions = ">=3.7" pytest = ">=5.0" [package.extras] -dev = ["pre-commit", "tox", "pytest-asyncio"] +dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "pytest-pep8" @@ -1648,7 +1664,7 @@ yaml = ["PyYaml (>=5.2)"] [[package]] name = "pytz" -version = "2022.2.1" +version = "2022.4" description = "World timezone definitions, modern and historical" category = "main" optional = true @@ -1663,7 +1679,7 @@ optional = false python-versions = "*" [[package]] -name = "pyyaml" +name = "PyYAML" version = "6.0" description = "YAML parser and emitter for Python" category = "main" @@ -1672,7 +1688,7 @@ python-versions = ">=3.6" [[package]] name = "pyzmq" -version = "24.0.0" +version = "24.0.1" description = "Python bindings for 0MQ" category = "main" optional = true @@ -1715,8 +1731,8 @@ importlib-metadata = {version = ">=1.0", markers = "python_version < \"3.8\""} packaging = ">=20.4" [package.extras] -ocsp = ["requests (>=2.26.0)", "pyopenssl (==20.0.1)", "cryptography (>=36.0.1)"] hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] [[package]] name = "renku-sphinx-theme" @@ -1731,10 +1747,10 @@ Sphinx = ">=1.6.3,<5.0.0" sphinx-rtd-theme = ">=0.5.0,<0.6" [package.extras] -tests = ["pydocstyle (>=1.0.0)", "isort (>=4.2.2)", "check-manifest (>=0.25)"] -docs = ["sphinx-rtd-theme (>=0.5.0,<0.6)", "Sphinx (>=1.6.3,<5.0.0)"] +all = ["Sphinx (>=1.6.3,<5.0.0)", "check-manifest (>=0.25)", "compile-scss (>=1.0.1,<1.1.0)", "isort (>=4.2.2)", "pydocstyle (>=1.0.0)", "sphinx-rtd-theme (>=0.5.0,<0.6)"] dev = ["compile-scss (>=1.0.1,<1.1.0)"] -all = ["pydocstyle (>=1.0.0)", "isort (>=4.2.2)", "check-manifest (>=0.25)", "compile-scss (>=1.0.1,<1.1.0)", "sphinx-rtd-theme (>=0.5.0,<0.6)", "Sphinx (>=1.6.3,<5.0.0)"] +docs = ["Sphinx (>=1.6.3,<5.0.0)", "sphinx-rtd-theme (>=0.5.0,<0.6)"] +tests = ["check-manifest (>=0.25)", "isort (>=4.2.2)", "pydocstyle (>=1.0.0)"] [[package]] name = "requests" @@ -1756,11 +1772,11 @@ use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-toolbelt" -version = "0.9.1" +version = "0.10.0" description = "A utility belt for advanced users of python-requests" category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] requests = ">=2.0.1,<3.0.0" @@ -1779,7 +1795,7 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""} urllib3 = ">=1.25.10" [package.extras] -tests = ["pytest (>=7.0.0)", "coverage (>=6.0.0)", "pytest-cov", "pytest-asyncio", "pytest-localserver", "flake8", "types-mock", "types-requests", "mypy"] +tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-localserver", "types-mock", "types-requests"] [[package]] name = "rich" @@ -1823,7 +1839,7 @@ python-dateutil = "*" rq = ">=0.13" [[package]] -name = "ruamel-yaml" +name = "ruamel.yaml" version = "0.17.21" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" category = "main" @@ -1838,7 +1854,7 @@ docs = ["ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] [[package]] -name = "ruamel-yaml-clib" +name = "ruamel.yaml.clib" version = "0.2.6" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" category = "main" @@ -1866,7 +1882,7 @@ requests = ">=1.0" setuptools = "*" [package.extras] -docs = ["sphinx (>=2.2)", "sphinx-rtd-theme", "pytest (<8)", "sphinx-autoapi", "sphinx-autodoc-typehints", "sphinxcontrib-autoprogram", "typed-ast"] +docs = ["pytest (<8)", "sphinx (>=2.2)", "sphinx-autoapi", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-autoprogram", "typed-ast"] pycodegen = ["black"] [[package]] @@ -1891,11 +1907,11 @@ celery = ["celery (>=3)"] chalice = ["chalice (>=1.16.0)"] django = ["django (>=1.8)"] falcon = ["falcon (>=1.4)"] -flask = ["flask (>=0.11)", "blinker (>=1.1)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)"] httpx = ["httpx (>=0.16.0)"] -pure_eval = ["pure-eval", "executing", "asttokens"] +pure_eval = ["asttokens", "executing", "pure-eval"] pyspark = ["pyspark (>=2.4.4)"] -quart = ["quart (>=0.16.1)", "blinker (>=1.1)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] rq = ["rq (>=0.6)"] sanic = ["sanic (>=0.8)"] sqlalchemy = ["sqlalchemy (>=1.2)"] @@ -1903,16 +1919,16 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "65.3.0" +version = "65.4.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)", "sphinx-notfound-page (==0.8.3)", "sphinx-hoverxref (<2)", "pygments-github-lexers (==0.0.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-reredirects", "sphinxcontrib-towncrier", "furo"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "flake8 (<5)", "pytest-enabler (>=1.3)", "pytest-perf", "mock", "flake8-2020", "virtualenv (>=13.0.0)", "wheel", "pip (>=19.1)", "jaraco.envs (>=2.2)", "pytest-xdist", "jaraco.path (>=3.2.0)", "build", "filelock (>=3.4.0)", "pip-run (>=8.8)", "ini2toml[lite] (>=0.9)", "tomli-w (>=1.0.0)", "pytest-black (>=0.3.7)", "pytest-cov", "pytest-mypy (>=0.9.1)"] -testing-integration = ["pytest", "pytest-xdist", "pytest-enabler", "virtualenv (>=13.0.0)", "tomli", "wheel", "jaraco.path (>=3.2.0)", "jaraco.envs (>=2.2)", "build", "filelock (>=3.4.0)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shellescape" @@ -1963,7 +1979,7 @@ optional = true python-versions = "*" [[package]] -name = "sphinx" +name = "Sphinx" version = "4.5.0" description = "Python documentation generator" category = "main" @@ -1991,8 +2007,8 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "docutils-stubs", "types-typed-ast", "types-requests"] -test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] [[package]] name = "sphinx-click" @@ -2037,7 +2053,7 @@ sphinx = ">=2,<5" [package.extras] code_style = ["pre-commit (==2.13.0)"] -testing = ["coverage", "pytest (>=3.6,<4)", "pytest-cov", "pytest-regressions", "pygments", "sphinx-testing", "bs4", "rinohtype"] +testing = ["bs4", "coverage", "pygments", "pytest (>=3.6,<4)", "pytest-cov", "pytest-regressions", "rinohtype", "sphinx-testing"] [[package]] name = "sphinxcontrib-applehelp" @@ -2048,8 +2064,8 @@ optional = true python-versions = ">=3.5" [package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-devhelp" @@ -2060,8 +2076,8 @@ optional = true python-versions = ">=3.5" [package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-htmlhelp" @@ -2072,8 +2088,8 @@ optional = true python-versions = ">=3.6" [package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["html5lib", "pytest"] -lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-jsmath" @@ -2084,7 +2100,7 @@ optional = true python-versions = ">=3.5" [package.extras] -test = ["mypy", "flake8", "pytest"] +test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" @@ -2095,8 +2111,8 @@ optional = true python-versions = ">=3.5" [package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-serializinghtml" @@ -2107,12 +2123,12 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-spelling" -version = "7.6.0" +version = "7.6.1" description = "Sphinx spelling extension" category = "main" optional = true @@ -2124,7 +2140,7 @@ PyEnchant = ">=3.1.1" Sphinx = ">=3.0.0" [package.extras] -test = ["pytest", "pytest-cov", "coverage (>=4.0,!=4.4)"] +test = ["coverage (>=4.0,!=4.4)", "pytest", "pytest-cov"] [[package]] name = "tabulate" @@ -2166,15 +2182,15 @@ requests = ">=2,<3" typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] -all = ["boto (>=2.48.0,<3)", "boto3 (>=1.20.46,<2)", "boto3-stubs[iam,s3,sdb] (==1.24.0)", "mypy-boto3-s3 (==1.24.0)", "mypy-boto3-sdb (==1.24.0)", "mypy-boto3-iam (==1.24.0)", "cwltool (==3.1.20220628170238)", "galaxy-tool-util", "ruamel.yaml (>=0.15,<=0.17.21)", "ruamel.yaml.clib (>=0.2.6)", "networkx (>=2,<2.8.5)", "pynacl (>=1.4.0,<2)", "apache-libcloud (>=2.2.1,<3)", "google-cloud-storage (>=1.6.0,<2)", "kubernetes (>=12.0.1,<22)", "idna (>=2)", "pymesos (>=0.3.15,<0.4)", "wdlparse (==0.1.0)", "connexion[swagger-ui] (>=2.10.0,<3)", "flask (>=2.0,<3)", "werkzeug (>=2.0,<3)", "flask-cors (==3.0.10)", "gunicorn (==20.1.0)", "celery (>=5.1.0,<6)", "wes-service (>=4.0.0,<5)", "ruamel.yaml (>=0.15,<0.17.22)"] -aws = ["boto (>=2.48.0,<3)", "boto3 (>=1.20.46,<2)", "boto3-stubs[iam,s3,sdb] (==1.24.0)", "mypy-boto3-s3 (==1.24.0)", "mypy-boto3-sdb (==1.24.0)", "mypy-boto3-iam (==1.24.0)"] -cwl = ["cwltool (==3.1.20220628170238)", "galaxy-tool-util", "ruamel.yaml (>=0.15,<=0.17.21)", "ruamel.yaml.clib (>=0.2.6)", "networkx (>=2,<2.8.5)"] +all = ["apache-libcloud (>=2.2.1,<3)", "boto (>=2.48.0,<3)", "boto3 (>=1.20.46,<2)", "boto3-stubs[iam,s3,sdb] (==1.24.0)", "celery (>=5.1.0,<6)", "connexion[swagger-ui] (>=2.10.0,<3)", "cwltool (==3.1.20220628170238)", "flask (>=2.0,<3)", "flask-cors (==3.0.10)", "galaxy-tool-util", "google-cloud-storage (>=1.6.0,<2)", "gunicorn (==20.1.0)", "idna (>=2)", "kubernetes (>=12.0.1,<22)", "mypy-boto3-iam (==1.24.0)", "mypy-boto3-s3 (==1.24.0)", "mypy-boto3-sdb (==1.24.0)", "networkx (>=2,<2.8.5)", "pymesos (>=0.3.15,<0.4)", "pynacl (>=1.4.0,<2)", "ruamel.yaml (>=0.15,<0.17.22)", "ruamel.yaml (>=0.15,<=0.17.21)", "ruamel.yaml.clib (>=0.2.6)", "wdlparse (==0.1.0)", "werkzeug (>=2.0,<3)", "wes-service (>=4.0.0,<5)"] +aws = ["boto (>=2.48.0,<3)", "boto3 (>=1.20.46,<2)", "boto3-stubs[iam,s3,sdb] (==1.24.0)", "mypy-boto3-iam (==1.24.0)", "mypy-boto3-s3 (==1.24.0)", "mypy-boto3-sdb (==1.24.0)"] +cwl = ["cwltool (==3.1.20220628170238)", "galaxy-tool-util", "networkx (>=2,<2.8.5)", "ruamel.yaml (>=0.15,<=0.17.21)", "ruamel.yaml.clib (>=0.2.6)"] encryption = ["pynacl (>=1.4.0,<2)"] google = ["apache-libcloud (>=2.2.1,<3)", "google-cloud-storage (>=1.6.0,<2)"] htcondor = ["htcondor (>=8.6.0,<9)"] -kubernetes = ["kubernetes (>=12.0.1,<22)", "idna (>=2)"] +kubernetes = ["idna (>=2)", "kubernetes (>=12.0.1,<22)"] mesos = ["pymesos (>=0.3.15,<0.4)"] -server = ["connexion[swagger-ui] (>=2.10.0,<3)", "flask (>=2.0,<3)", "werkzeug (>=2.0,<3)", "flask-cors (==3.0.10)", "gunicorn (==20.1.0)", "celery (>=5.1.0,<6)", "wes-service (>=4.0.0,<5)", "ruamel.yaml (>=0.15,<0.17.22)"] +server = ["celery (>=5.1.0,<6)", "connexion[swagger-ui] (>=2.10.0,<3)", "flask (>=2.0,<3)", "flask-cors (==3.0.10)", "gunicorn (==20.1.0)", "ruamel.yaml (>=0.15,<0.17.22)", "werkzeug (>=2.0,<3)", "wes-service (>=4.0.0,<5)"] wdl = ["wdlparse (==0.1.0)"] [[package]] @@ -2195,7 +2211,7 @@ python-versions = ">=3.7" [[package]] name = "tomlkit" -version = "0.11.4" +version = "0.11.5" description = "Style preserving TOML library" category = "main" optional = true @@ -2237,9 +2253,9 @@ python-versions = "*" "zope.interface" = "*" [package.extras] -docs = ["sphinx", "repoze-sphinx-autointerface"] +docs = ["Sphinx", "repoze.sphinx.autointerface"] test = ["mock"] -testing = ["nose", "coverage", "mock"] +testing = ["coverage", "mock", "nose"] [[package]] name = "typed-ast" @@ -2258,8 +2274,8 @@ optional = true python-versions = "*" [[package]] -name = "types-pyyaml" -version = "6.0.11" +name = "types-PyYAML" +version = "6.0.12" description = "Typing stubs for PyYAML" category = "main" optional = true @@ -2291,7 +2307,7 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "4.3.0" +version = "4.4.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false @@ -2306,8 +2322,8 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" [package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] @@ -2352,12 +2368,12 @@ optional = false python-versions = ">=3.7" [package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] test = ["websockets"] -optional = ["wsaccel", "python-socks"] -docs = ["sphinx-rtd-theme (>=0.5)", "Sphinx (>=3.4)"] [[package]] -name = "werkzeug" +name = "Werkzeug" version = "2.2.2" description = "The comprehensive WSGI web application library." category = "main" @@ -2415,7 +2431,7 @@ python-versions = ">=3.6.2,<4.0.0" termcolor = ">=1.1.0,<2.0.0" [[package]] -name = "zc-lockfile" +name = "zc.lockfile" version = "2.0" description = "Basic inter-process locks" category = "main" @@ -2426,10 +2442,10 @@ python-versions = "*" setuptools = "*" [package.extras] -test = ["zope-testing"] +test = ["zope.testing"] [[package]] -name = "zc-relation" +name = "zc.relation" version = "1.1.post2" description = "Index intransitive and transitive n-ary relationships." category = "main" @@ -2447,7 +2463,7 @@ ZODB3 = ">=3.8dev" test = ["zc.relationship (>=2.0c1)"] [[package]] -name = "zconfig" +name = "ZConfig" version = "3.6.0" description = "Structured Configuration Library" category = "main" @@ -2456,7 +2472,7 @@ python-versions = "*" [package.extras] docs = ["sphinxcontrib-programoutput"] -test = ["docutils", "manuel", "zope-exceptions", "zope-testrunner"] +test = ["docutils", "manuel", "zope.exceptions", "zope.testrunner"] [[package]] name = "zdaemon" @@ -2471,10 +2487,10 @@ setuptools = "*" ZConfig = "*" [package.extras] -test = ["zope-testing", "zope-testrunner", "manuel", "mock", "zc-customdoctests"] +test = ["manuel", "mock", "zc.customdoctests", "zope.testing", "zope.testrunner"] [[package]] -name = "zeo" +name = "ZEO" version = "5.3.0" description = "ZEO - Single-server client-server database server for ZODB" category = "main" @@ -2492,25 +2508,25 @@ ZODB = ">=5.1.1" "zope.interface" = "*" [package.extras] -docs = ["sphinx", "repoze-sphinx-autointerface", "sphinx-rtd-theme"] +docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx-rtd-theme"] msgpack = ["msgpack-python"] -test = ["ZODB (>=5.5.1)", "zope-testing", "manuel", "random2", "mock", "msgpack (<1)", "zope-testrunner"] +test = ["ZODB (>=5.5.1)", "manuel", "mock", "msgpack (<1)", "random2", "zope.testing", "zope.testrunner"] uvloop = ["uvloop (>=0.5.1)"] [[package]] name = "zipp" -version = "3.8.1" +version = "3.9.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" [package.extras] -testing = ["pytest-mypy (>=0.9.1)", "pytest-black (>=0.3.7)", "func-timeout", "jaraco-itertools", "pytest-enabler (>=1.3)", "pytest-cov", "pytest-flake8", "pytest-checkdocs (>=2.4)", "pytest (>=6)"] -docs = ["jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "jaraco.packaging (>=9)", "sphinx"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] -name = "zodb" +name = "ZODB" version = "5.6.0" description = "ZODB, a Python object-oriented database" category = "main" @@ -2528,10 +2544,10 @@ zodbpickle = ">=1.0.1" "zope.interface" = "*" [package.extras] -test = ["manuel", "zope-testing", "zope.testrunner (>=4.4.6)", "mock"] +test = ["manuel", "mock", "zope.testing", "zope.testrunner (>=4.4.6)"] [[package]] -name = "zodb3" +name = "ZODB3" version = "3.11.0" description = "ZODB3 - Meta release for ZODB, persistent, BTrees and ZEO" category = "main" @@ -2546,7 +2562,7 @@ ZEO = ">=4.0.0dev" ZODB = ">=4.0.0dev" [package.extras] -test = ["zeo", "zodb", "btrees", "persistent"] +test = ["BTrees[test]", "ZEO[test]", "ZODB[test]", "persistent[test]"] [[package]] name = "zodbpickle" @@ -2560,11 +2576,11 @@ python-versions = "*" setuptools = "*" [package.extras] -test = ["zope-testrunner"] +test = ["zope.testrunner"] [[package]] -name = "zope-interface" -version = "5.4.0" +name = "zope.interface" +version = "5.5.0" description = "Interfaces for Python" category = "main" optional = false @@ -2574,12 +2590,12 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" setuptools = "*" [package.extras] -docs = ["sphinx", "repoze-sphinx-autointerface"] -test = ["coverage (>=5.0.3)", "zope-event", "zope-testing"] -testing = ["coverage (>=5.0.3)", "zope-event", "zope-testing"] +docs = ["Sphinx", "repoze.sphinx.autointerface"] +test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] +testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [[package]] -name = "zope-testing" +name = "zope.testing" version = "4.10" description = "Zope testing helpers" category = "main" @@ -2590,8 +2606,8 @@ python-versions = "*" setuptools = "*" [package.extras] -docs = ["sphinx", "repoze-sphinx-autointerface", "zope-exceptions", "zope-interface", "mock"] -test = ["zope-testrunner"] +docs = ["mock", "repoze.sphinx.autointerface", "sphinx", "zope.exceptions", "zope.interface"] +test = ["zope.testrunner"] [[package]] name = "zstandard" @@ -2617,7 +2633,7 @@ toil = ["toil"] [metadata] lock-version = "1.1" python-versions = "^3.7.1" -content-hash = "42d13bab00640cf6b36cbc83bab361a03fb97efe700e001da367d4f4fa2ea3e2" +content-hash = "27599bc13dc0b8c68d86ae6efa060b24ad0e0f845edfdc8132bf1a5c288de3ca" [metadata.files] addict = [ @@ -2652,7 +2668,7 @@ attrs = [ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, ] -babel = [ +Babel = [ {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, ] @@ -2693,7 +2709,7 @@ blinker = [ {file = "blinker-1.5-py2.py3-none-any.whl", hash = "sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36"}, {file = "blinker-1.5.tar.gz", hash = "sha256:923e5e2f69c155f2cc42dafbbd70e16e3fde24d2d4aa2ab72fbe386238892462"}, ] -btrees = [ +BTrees = [ {file = "BTrees-4.10.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:8e3b74c7f160add6c87c6eb1281a150aca0f8ae6601946c4a66cf44e15886935"}, {file = "BTrees-4.10.1-cp27-cp27m-win32.whl", hash = "sha256:cf9d3c49b5eb7e6a8cba6a206506edd955d4af0a43cbcf6ed4e182c6b194ed70"}, {file = "BTrees-4.10.1-cp27-cp27m-win_amd64.whl", hash = "sha256:43162fc5a255167c117b7caa2c47545491117b34eaaa1f2e6e42157d24d6c13d"}, @@ -2731,21 +2747,21 @@ btrees = [ {file = "BTrees-4.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:99352cc0d45b37d7b285964af4a4472adf2a3ad6f9cdef0a4684b467880ba9e7"}, {file = "BTrees-4.10.1.tar.gz", hash = "sha256:40e3d416b3b031a8c86334507d121b69d9c9de907e112344de526b5451d903c5"}, ] -cachecontrol = [ - {file = "CacheControl-0.12.12-py2.py3-none-any.whl", hash = "sha256:5986a16897cd296bdb7b86b42e0403c4ca30b9875f0e0c7c8e509b50cff115b7"}, - {file = "CacheControl-0.12.12.tar.gz", hash = "sha256:9c2e5208ea76ebd9921176569743ddf6d7f3bb4188dbf61806f0f8fc48ecad38"}, +CacheControl = [ + {file = "CacheControl-0.12.11-py2.py3-none-any.whl", hash = "sha256:2c75d6a8938cb1933c75c50184549ad42728a27e9f6b92fd677c3151aa72555b"}, + {file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"}, ] cachetools = [ {file = "cachetools-5.2.0-py3-none-any.whl", hash = "sha256:f9f17d2aec496a9aa6b76f53e3b614c965223c061982d434d160f930c698a9db"}, {file = "cachetools-5.2.0.tar.gz", hash = "sha256:6a94c6402995a99c3970cc7e4884bb60b4a8639938157eeed436098bf9831757"}, ] calamus = [ - {file = "calamus-0.3.14-py3-none-any.whl", hash = "sha256:d614c4b489f12933363abc2c46d45a58b1a53946f22ce1417047b7c9f33f45e4"}, - {file = "calamus-0.3.14.tar.gz", hash = "sha256:114b6e4461561aab7c8f44c52ea988f0718ae505242c613487727c3724d68fe3"}, + {file = "calamus-0.4.1-py3-none-any.whl", hash = "sha256:f46dad6376e6d708cafa76642b99127db20ba09ee46a37fd268afd17e925f541"}, + {file = "calamus-0.4.1.tar.gz", hash = "sha256:a3b2c7ec870fea7de28b2fda414ce0c12362aac5596e66a697716ec19f745346"}, ] certifi = [ - {file = "certifi-2022.9.14-py3-none-any.whl", hash = "sha256:e232343de1ab72c2aa521b625c80f699e356830fd0e2c620b465b304b17b0516"}, - {file = "certifi-2022.9.14.tar.gz", hash = "sha256:36973885b9542e6bd01dea287b2b4b3b21236307c56324fcc3f1160f2d655ed5"}, + {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"}, + {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"}, ] cffi = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, @@ -2826,8 +2842,8 @@ click = [ {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] click-option-group = [ - {file = "click-option-group-0.5.3.tar.gz", hash = "sha256:a6e924f3c46b657feb5b72679f7e930f8e5b224b766ab35c91ae4019b4e0615e"}, - {file = "click_option_group-0.5.3-py3-none-any.whl", hash = "sha256:9653a2297357335d7325a1827e71ac1245d91c97d959346a7decabd4a52d5354"}, + {file = "click-option-group-0.5.4.tar.gz", hash = "sha256:d4b8d808a1998f0f277ebe13c33d64863a5b2a619b1e54f67bc6e3723d91b910"}, + {file = "click_option_group-0.5.4-py3-none-any.whl", hash = "sha256:0976f0dd15ab5f9f2ee0823f4b83c5a84f8748668c0c37a334739b287711940d"}, ] click-plugins = [ {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, @@ -2945,7 +2961,7 @@ deepmerge = [ {file = "deepmerge-1.0.1-py3-none-any.whl", hash = "sha256:f851fff957697cb8f4580b465acf5c2d35841695306ff0abb9cb9c273ad76112"}, {file = "deepmerge-1.0.1.tar.gz", hash = "sha256:4b44779ed3d2fb791bb181fc2683423496fea428abb7af37feb23286de7f0a1a"}, ] -deprecated = [ +Deprecated = [ {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, ] @@ -2962,12 +2978,12 @@ docutils = [ {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, ] dunamai = [ - {file = "dunamai-1.13.0-py3-none-any.whl", hash = "sha256:9bc19b8c53c62f922f2835e210f2295089669ab18222601cef44eb8c3a51929e"}, - {file = "dunamai-1.13.0.tar.gz", hash = "sha256:5bf9e5baaa673b692a9613e4961b18b6d2d9743412f7bbcf4a39c84a979f47e2"}, + {file = "dunamai-1.13.1-py3-none-any.whl", hash = "sha256:f23d31fd3e7df1c16f018f4f0c408df7feda8cba9516f7c9822a29fa3ed665cd"}, + {file = "dunamai-1.13.1.tar.gz", hash = "sha256:49597bdf653bdacdeb51ec6e0f1d4d2327309376fc83e6f1d42af6e29600515f"}, ] enlighten = [ - {file = "enlighten-1.10.2-py2.py3-none-any.whl", hash = "sha256:b237fe562b320bf9f1d4bb76d0c98e0daf914372a76ab87c35cd02f57aa9d8c1"}, - {file = "enlighten-1.10.2.tar.gz", hash = "sha256:7a5b83cd0f4d095e59d80c648ebb5f7ffca0cd8bcf7ae6639828ee1ad000632a"}, + {file = "enlighten-1.11.1-py2.py3-none-any.whl", hash = "sha256:e825eb534ca80778bb7d46e5581527b2a6fae559b6cf09e290a7952c6e11961e"}, + {file = "enlighten-1.11.1.tar.gz", hash = "sha256:57abd98a3d3f83484ef9f91f9255f4d23c8b3097ecdb647c7b9b0049d600b7f8"}, ] entrypoints = [ {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, @@ -2997,7 +3013,7 @@ flaky = [ {file = "flaky-3.7.0-py2.py3-none-any.whl", hash = "sha256:d6eda73cab5ae7364504b7c44670f70abed9e75f77dd116352f662817592ec9c"}, {file = "flaky-3.7.0.tar.gz", hash = "sha256:3ad100780721a1911f57a165809b7ea265a7863305acb66708220820caf8aa0d"}, ] -flask = [ +Flask = [ {file = "Flask-2.1.1-py3-none-any.whl", hash = "sha256:8a4cf32d904cf5621db9f0c9fbcd7efabf3003f22a04e4d0ce790c7137ec5264"}, {file = "Flask-2.1.1.tar.gz", hash = "sha256:a8c9bd3e558ec99646d177a9739c41df1ded0629480b4c8d2975412f3c9519c8"}, ] @@ -3027,7 +3043,7 @@ gitdb = [ {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] -gitpython = [ +GitPython = [ {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, ] @@ -3066,7 +3082,7 @@ iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] -inject = [ +Inject = [ {file = "Inject-4.3.1.tar.gz", hash = "sha256:7f996f2c9ed2082af776ddf6b528d97036898ac63040385feb1d12286a73c3cc"}, ] isodate = [ @@ -3081,7 +3097,7 @@ itsdangerous = [ {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, ] -jinja2 = [ +Jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] @@ -3272,7 +3288,7 @@ lxml = [ {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9"}, {file = "lxml-4.9.1.tar.gz", hash = "sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"}, ] -markupsafe = [ +MarkupSafe = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, @@ -3318,6 +3334,10 @@ marshmallow = [ {file = "marshmallow-3.17.1-py3-none-any.whl", hash = "sha256:1172ce82765bf26c24a3f9299ed6dbeeca4d213f638eaa39a37772656d7ce408"}, {file = "marshmallow-3.17.1.tar.gz", hash = "sha256:48e2d88d4ab431ad5a17c25556d9da529ea6e966876f2a38d274082e270287f0"}, ] +marshmallow-oneofschema = [ + {file = "marshmallow-oneofschema-3.0.1.tar.gz", hash = "sha256:62cd2099b29188c92493c2940ee79d1bf2f2619a71721664e5a98ec2faa58237"}, + {file = "marshmallow_oneofschema-3.0.1-py2.py3-none-any.whl", hash = "sha256:bd29410a9f2f7457a2b428286e2a80ef76b8ddc3701527dc1f935a88914b02f2"}, +] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, @@ -3442,29 +3462,30 @@ multidict = [ {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, ] mypy = [ - {file = "mypy-0.971-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"}, - {file = "mypy-0.971-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5"}, - {file = "mypy-0.971-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3"}, - {file = "mypy-0.971-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655"}, - {file = "mypy-0.971-cp310-cp310-win_amd64.whl", hash = "sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103"}, - {file = "mypy-0.971-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca"}, - {file = "mypy-0.971-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417"}, - {file = "mypy-0.971-cp36-cp36m-win_amd64.whl", hash = "sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09"}, - {file = "mypy-0.971-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8"}, - {file = "mypy-0.971-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0"}, - {file = "mypy-0.971-cp37-cp37m-win_amd64.whl", hash = "sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2"}, - {file = "mypy-0.971-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27"}, - {file = "mypy-0.971-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856"}, - {file = "mypy-0.971-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71"}, - {file = "mypy-0.971-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27"}, - {file = "mypy-0.971-cp38-cp38-win_amd64.whl", hash = "sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58"}, - {file = "mypy-0.971-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6"}, - {file = "mypy-0.971-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe"}, - {file = "mypy-0.971-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9"}, - {file = "mypy-0.971-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf"}, - {file = "mypy-0.971-cp39-cp39-win_amd64.whl", hash = "sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0"}, - {file = "mypy-0.971-py3-none-any.whl", hash = "sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9"}, - {file = "mypy-0.971.tar.gz", hash = "sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56"}, + {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, + {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, + {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, + {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"}, + {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"}, + {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"}, + {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"}, + {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"}, + {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"}, + {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"}, + {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"}, + {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"}, + {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"}, + {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"}, + {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"}, + {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"}, + {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"}, + {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"}, + {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"}, + {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"}, + {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"}, + {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"}, + {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, + {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, @@ -3571,7 +3592,7 @@ pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, ] -pillow = [ +Pillow = [ {file = "Pillow-9.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:42dfefbef90eb67c10c45a73a9bc1599d4dac920f7dfcbf4ec6b80cb620757fe"}, {file = "Pillow-9.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffde4c6fabb52891d81606411cbfaf77756e3b561b566efd270b3ed3791fde4e"}, {file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c857532c719fb30fafabd2371ce9b7031812ff3889d75273827633bca0c4602"}, @@ -3628,8 +3649,8 @@ poetry-dynamic-versioning = [ {file = "poetry_dynamic_versioning-0.17.1-py3-none-any.whl", hash = "sha256:647d41eb554496eb657c7745ab2321b77d1f9c7cf52f7deb6ff674736fbd8ea1"}, ] poetry-lock-package = [ - {file = "poetry-lock-package-0.4.4.tar.gz", hash = "sha256:32b8d3472409a84c5056c4532152854dd08cb644f5e1b2507678fdfb044812a1"}, - {file = "poetry_lock_package-0.4.4-py3-none-any.whl", hash = "sha256:b2106e14dbe4ab98ca67be8374c2adcd3871991d483f46adb3ef4e143476917d"}, + {file = "poetry-lock-package-0.4.5.tar.gz", hash = "sha256:a74a4916cc53ebbfd3b3575b73d0e93bac74b75ff2d74f92a24e7a8938fc20f2"}, + {file = "poetry_lock_package-0.4.5-py3-none-any.whl", hash = "sha256:5b8ae966ec8c760a69d38bde6887501d594b467e00902dd341328cd8079ccaee"}, ] portalocker = [ {file = "portalocker-2.4.0-py2.py3-none-any.whl", hash = "sha256:b092f48e1e30a234ab3dd1cfd44f2f235e8a41f4e310e463fc8d6798d1c3c235"}, @@ -3718,18 +3739,18 @@ pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] -pygments = [ +Pygments = [ {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] -pyjwt = [ +PyJWT = [ {file = "PyJWT-2.4.0-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"}, {file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"}, ] -pyld = [ +PyLD = [ {file = "PyLD-2.0.3.tar.gz", hash = "sha256:287445f888c3a332ccbd20a14844c66c2fcbaeab3c99acd506a0788e2ebb2f82"}, ] -pyopenssl = [ +pyOpenSSL = [ {file = "pyOpenSSL-22.0.0-py2.py3-none-any.whl", hash = "sha256:ea252b38c87425b64116f808355e8da644ef9b07e429398bfece610f893ee2e0"}, {file = "pyOpenSSL-22.0.0.tar.gz", hash = "sha256:660b1b1425aac4a1bea1d94168a85d99f0b3144c869dd4390d27629d0087f1bf"}, ] @@ -3815,8 +3836,8 @@ python-gitlab = [ {file = "python_gitlab-3.8.1-py3-none-any.whl", hash = "sha256:6d10de90f4fcb95ea92e301528d2442ac5094da9b7137fa1294614369679e791"}, ] pytz = [ - {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"}, - {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"}, + {file = "pytz-2022.4-py2.py3-none-any.whl", hash = "sha256:2c0784747071402c6e99f0bafdb7da0fa22645f06554c7ae06bf6358897e9c91"}, + {file = "pytz-2022.4.tar.gz", hash = "sha256:48ce799d83b6f8aab2020e369b627446696619e79645419610b9facd909b3174"}, ] pywin32 = [ {file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"}, @@ -3832,7 +3853,7 @@ pywin32 = [ {file = "pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"}, {file = "pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"}, ] -pyyaml = [ +PyYAML = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, @@ -3840,13 +3861,6 @@ pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, @@ -3875,80 +3889,80 @@ pyyaml = [ {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] pyzmq = [ - {file = "pyzmq-24.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:38e9ff2918d50a588e56aa80dae0373ef9f67512fc5691f95db2f59edabc083e"}, - {file = "pyzmq-24.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5439bef77fd3818c20e1bf5657836e105e4e48e1a7996e24ebb55402a681934e"}, - {file = "pyzmq-24.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8698db54fd49af74333190fb154448dcfc67a382aa2b2d784ffe981b7cf421ec"}, - {file = "pyzmq-24.0.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e24d7bda7a32ff35d0c914a52dd920a408f73d7e4b93d6755d7c67e819a8cd8c"}, - {file = "pyzmq-24.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00a8a4f83259b91b238244c999a33e0a77c0427d496902fb75fdf1601e4c9d3d"}, - {file = "pyzmq-24.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4985a82958d67eafd3f8c9c215c3da8f633592024f771420477f22f011846235"}, - {file = "pyzmq-24.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0775b65e79cccfca2b017e80ffe6dbd224b035a47245c4140b08e93996425942"}, - {file = "pyzmq-24.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e5a398955b1cfdd85dd699f2390661b7bbe9edcbadd70a444c79c69e6c31c91"}, - {file = "pyzmq-24.0.0-cp310-cp310-win32.whl", hash = "sha256:1bdec8988cad1f9a8453b4d00fd11598a91604cd9b205640e98b2f22e0435921"}, - {file = "pyzmq-24.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:eadf1d3841c2155b68ef49147253fd4ac1447a972d01c08248114edc4d3ba9d5"}, - {file = "pyzmq-24.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:99bb8cff279f7d1f516919d82b35ed0796c53ce7da7dca191fabfa4c53f47740"}, - {file = "pyzmq-24.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:452c3d5bfbaf96f32ef20673e6ba238355891884009f0c87e0f97a985293ef42"}, - {file = "pyzmq-24.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65b532c95a4cde95bb4787b5545321ed5624fa8d7391bce17c4e2a0717b97bb7"}, - {file = "pyzmq-24.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9cd5c7449f297a1b53a4803413db907a8cad1178435e2879c1b92816f2bbe56"}, - {file = "pyzmq-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f81f4065ff8ccd207204129463fd587b25c9f593128176a717dbabc03af9b233"}, - {file = "pyzmq-24.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b5107313c7f28b0e074da7a9d8c0132cb8dc32fdd3b5a4c6a224d30e50d6324"}, - {file = "pyzmq-24.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f200cdca2fc842749a3f263ccf9e4b50e732ad14f53b60faf68ef656b75c32a"}, - {file = "pyzmq-24.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ae33cb195304ac16996184b115f9e27eb9f0b14062e97fbd1d446e3e4a594ff0"}, - {file = "pyzmq-24.0.0-cp311-cp311-win32.whl", hash = "sha256:62ee069fe338d0b057b81e752dad2b9b6b206ba8570a878dbbe8b93b7b99ebb1"}, - {file = "pyzmq-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:e9e3fa94fa1e58763a7b824b8e0015d93c9fdd8e449d0218d13d01172e3d1539"}, - {file = "pyzmq-24.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b94a3453a18bb914b2cac1ac38c09f980a3c86a8cd0bb744dd6bd03ab8ff958a"}, - {file = "pyzmq-24.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e66d3237a7e8a11cdb8b470c77a6fb43f49fe9407936a2c9ac468ba2ba0982e"}, - {file = "pyzmq-24.0.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:44c1dc858b76d2ab28f5ea040dd5e816a71624a8cf38d4ca3208006fd2a9375e"}, - {file = "pyzmq-24.0.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:761016baa6ca720677ce01d453801e41db2d0e53cf052ed00ba8c2e6cae4d2cd"}, - {file = "pyzmq-24.0.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:ef4bd725c06d6ee9e502419ceeb5dfaccb6bbe36f359fd0366b90a56b6dae647"}, - {file = "pyzmq-24.0.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:40cdb50e82393253d340b6a357474588eb01cfd90b06231d5bfbc14490490b1b"}, - {file = "pyzmq-24.0.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:eb3b8acb5dc33ec812d79f35b85fddc43d8f75b65c00c635ee3c0b527e11c8ea"}, - {file = "pyzmq-24.0.0-cp36-cp36m-win32.whl", hash = "sha256:8988209d5efae9b5c9297fb48d153e2528384c1afe2c9fd8eeb474cd6e765199"}, - {file = "pyzmq-24.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52d881c33f8db5ffcb0aabc14cc71098453f4700511195cebca846000b44b080"}, - {file = "pyzmq-24.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:844040d44cc4320cdffb258fe03768ff0b37e710d56a70dd1f6c2902738f1e28"}, - {file = "pyzmq-24.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f05524f7cfa4f7398259a428fbb22ec4c3f0665c6a303a0d6afdd98457b19af3"}, - {file = "pyzmq-24.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ff4e510a9509d36359c7af4684e73489cdd53c781dd4bc9b07dc808fab56cc48"}, - {file = "pyzmq-24.0.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dbb871b22acec52c1b046ef6aa3d16f83618c704772f126a49e7446a0c885278"}, - {file = "pyzmq-24.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f1ddc43cceb500e4a6495250d9d34cac93e6d9e89a46f0e34fcefcc3caf66ef3"}, - {file = "pyzmq-24.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a5cdbede23aae133e50f786adc4a2cacf60bddde649e3dc122c32368daa2c007"}, - {file = "pyzmq-24.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ee24b94b5ae22af9148e597f512fae8383908ca07d3b7f99b349679fede4d7d3"}, - {file = "pyzmq-24.0.0-cp37-cp37m-win32.whl", hash = "sha256:c37c0046d04c0fdd99a9a31d6a9ce7d703cca3b7fdde5738603503dfba58a25f"}, - {file = "pyzmq-24.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b1ef471c62c3d0681cfbaa8bbaf96f22e20cafd391ecad8a43317f6b1598478e"}, - {file = "pyzmq-24.0.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:dfde6624d3d99d9a67235b60ae13be1a6ffce2f1de3cd2be9900f011d5d6a6a6"}, - {file = "pyzmq-24.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fc2c363f68bbb9fea6b8137c432c6df9d7c8c76b01549c4410c506dac9e30663"}, - {file = "pyzmq-24.0.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6814a6add1b7cb76e3fdfd961ce4c48c1f0a29e82bdb3d060a669b85bc6db454"}, - {file = "pyzmq-24.0.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1a53c6030c4f45859af9a75cfc0d8b551b8924f9b2503397c69d0fa2f62d2370"}, - {file = "pyzmq-24.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f328d28d0a1ea43d7030e1999ced9db252ba4ef2531af3e9bfc135cca77b8324"}, - {file = "pyzmq-24.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8a43105683a66de78489b30037b97c9ce5f821f57035f6944f633bbd4baadd15"}, - {file = "pyzmq-24.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3823e5e613a61948b2e6b85fd91f872772717d24cd1df871836665d4c56d6b34"}, - {file = "pyzmq-24.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1c23568e1581f637b1a1e1fd15dcd5e9165332c94bf8101c562e7c50640d673e"}, - {file = "pyzmq-24.0.0-cp38-cp38-win32.whl", hash = "sha256:8a93abd67a46c6b91f28a7513b9f8b9a5432fd573c3d6444c083e07209bf96e4"}, - {file = "pyzmq-24.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:8361c90701fc6ff5f16c81c969563c6915402fbecb2ddc9c5063fec0238e5e52"}, - {file = "pyzmq-24.0.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:4ec8847ab93200a94fd3e88e2824a6bba9a46d28161f1bf0be24f2237c40c291"}, - {file = "pyzmq-24.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f969214a9ebf1175a8aba863d6f1220174130188686d4ed475d138a240e60c1c"}, - {file = "pyzmq-24.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a07fb73ae006a5b565d19232e5a6592fd7c93e57e67c2e592bf0b21f1676767"}, - {file = "pyzmq-24.0.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1b978e1318311eb69523ed60f084b752f52f27ffea4ce2f60deab4d8a4cca6de"}, - {file = "pyzmq-24.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f355d23a420a6b39241bbef2c803839b01d52d680d89aac39460505e57b2cd03"}, - {file = "pyzmq-24.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5c1c2d7ebe991d8e36365ccd6b47297b6b96393ad453cad990901c21924add30"}, - {file = "pyzmq-24.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0128c7b723984e31c1b0df5bc532715defd13bf64d8d9eddd7207d093759ae4"}, - {file = "pyzmq-24.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ed10f5a942a2903a722d63806b7a9d2e0a966c038100dc763483d8fbe8ea074"}, - {file = "pyzmq-24.0.0-cp39-cp39-win32.whl", hash = "sha256:201e4d5733cecfd469d9ceee57500a0f365f85d6f14dd524105e2a0be8cd93c1"}, - {file = "pyzmq-24.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:460f0ff945d4b46c2d568941be33cf08954fca1e3239cf6a6ee03b1371de8f64"}, - {file = "pyzmq-24.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:534d13940b2476e4bacb54558c7b9b62fb275c2839e06267597a3e4b2f291196"}, - {file = "pyzmq-24.0.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ebdb43e947291c5cb80ef6c7d525f64bc4ed685de43f855ba0cf2b0fd8052e3a"}, - {file = "pyzmq-24.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ce8c61297a751c67062d11e44352e9379da03a90d95aa352395d3b1e53e9f20a"}, - {file = "pyzmq-24.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adbf2bb11a616210735d6a112186d378f7934be3f2935e6d9dd760c110840c3a"}, - {file = "pyzmq-24.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0584420cbd2dac77f81bdc4b9da2635a54300563d4632433b08cb1f505341ef0"}, - {file = "pyzmq-24.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8ea1861512c7f32d0c585119a2caea6665eec6df429abf5810826b0df9587de1"}, - {file = "pyzmq-24.0.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:be67e7f48fce8dbefd602f779c7382c874a1a1a3d08f375366c4d28baaa0bfd4"}, - {file = "pyzmq-24.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ff6c9fdbcfb285e28fe35eaf5ba39644cbc65343aa41844217c2b5a99abbdd7e"}, - {file = "pyzmq-24.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6736703c7581d18e3959c1d786035c620def2f096e762aefaf08cfa39844d1e"}, - {file = "pyzmq-24.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a5b9471e5e507f51f4c0acabec60a7ae2ea218ac6134a8e5ec5674845347a63a"}, - {file = "pyzmq-24.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:dbece43299705eca217808759f4322c7cab41db2ba3ad8d7261ee2b17abe6488"}, - {file = "pyzmq-24.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:025a0815d36ccd54cf002feb9cbe0e37c2253eca305ee4dc72ccdb4a814eefb4"}, - {file = "pyzmq-24.0.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc21a74f337298840f59f21a12fbf6ec1de798cd69d6b331ef9ed88ac8cb18f0"}, - {file = "pyzmq-24.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6477a083f8a1b54893ad24bc15f94dd0684b02320c69d2a69dcf36f70e73cb"}, - {file = "pyzmq-24.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aa6d0dfa94ce89d050dca0104389e10c537715bf10e5b0bfe5ece79f17f1719e"}, - {file = "pyzmq-24.0.0.tar.gz", hash = "sha256:13b008bd142c9f6079ad75a30504eef2291502e9eac90e722b16fcf9ce856147"}, + {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:28b119ba97129d3001673a697b7cce47fe6de1f7255d104c2f01108a5179a066"}, + {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bcbebd369493d68162cddb74a9c1fcebd139dfbb7ddb23d8f8e43e6c87bac3a6"}, + {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae61446166983c663cee42c852ed63899e43e484abf080089f771df4b9d272ef"}, + {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f7ac99b15270db8d53f28c3c7b968612993a90a5cf359da354efe96f5372b4"}, + {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca7c3956b03b7663fac4d150f5e6d4f6f38b2462c1e9afd83bcf7019f17913"}, + {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8c78bfe20d4c890cb5580a3b9290f700c570e167d4cdcc55feec07030297a5e3"}, + {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:48f721f070726cd2a6e44f3c33f8ee4b24188e4b816e6dd8ba542c8c3bb5b246"}, + {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afe1f3bc486d0ce40abb0a0c9adb39aed3bbac36ebdc596487b0cceba55c21c1"}, + {file = "pyzmq-24.0.1-cp310-cp310-win32.whl", hash = "sha256:3e6192dbcefaaa52ed81be88525a54a445f4b4fe2fffcae7fe40ebb58bd06bfd"}, + {file = "pyzmq-24.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:86de64468cad9c6d269f32a6390e210ca5ada568c7a55de8e681ca3b897bb340"}, + {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:838812c65ed5f7c2bd11f7b098d2e5d01685a3f6d1f82849423b570bae698c00"}, + {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dfb992dbcd88d8254471760879d48fb20836d91baa90f181c957122f9592b3dc"}, + {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7abddb2bd5489d30ffeb4b93a428130886c171b4d355ccd226e83254fcb6b9ef"}, + {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94010bd61bc168c103a5b3b0f56ed3b616688192db7cd5b1d626e49f28ff51b3"}, + {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8242543c522d84d033fe79be04cb559b80d7eb98ad81b137ff7e0a9020f00ace"}, + {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ccb94342d13e3bf3ffa6e62f95b5e3f0bc6bfa94558cb37f4b3d09d6feb536ff"}, + {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6640f83df0ae4ae1104d4c62b77e9ef39be85ebe53f636388707d532bee2b7b8"}, + {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a180dbd5ea5d47c2d3b716d5c19cc3fb162d1c8db93b21a1295d69585bfddac1"}, + {file = "pyzmq-24.0.1-cp311-cp311-win32.whl", hash = "sha256:624321120f7e60336be8ec74a172ae7fba5c3ed5bf787cc85f7e9986c9e0ebc2"}, + {file = "pyzmq-24.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:1724117bae69e091309ffb8255412c4651d3f6355560d9af312d547f6c5bc8b8"}, + {file = "pyzmq-24.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:15975747462ec49fdc863af906bab87c43b2491403ab37a6d88410635786b0f4"}, + {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b947e264f0e77d30dcbccbb00f49f900b204b922eb0c3a9f0afd61aaa1cedc3d"}, + {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ec91f1bad66f3ee8c6deb65fa1fe418e8ad803efedd69c35f3b5502f43bd1dc"}, + {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:db03704b3506455d86ec72c3358a779e9b1d07b61220dfb43702b7b668edcd0d"}, + {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e7e66b4e403c2836ac74f26c4b65d8ac0ca1eef41dfcac2d013b7482befaad83"}, + {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7a23ccc1083c260fa9685c93e3b170baba45aeed4b524deb3f426b0c40c11639"}, + {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fa0ae3275ef706c0309556061185dd0e4c4cd3b7d6f67ae617e4e677c7a41e2e"}, + {file = "pyzmq-24.0.1-cp36-cp36m-win32.whl", hash = "sha256:f01de4ec083daebf210531e2cca3bdb1608dbbbe00a9723e261d92087a1f6ebc"}, + {file = "pyzmq-24.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:de4217b9eb8b541cf2b7fde4401ce9d9a411cc0af85d410f9d6f4333f43640be"}, + {file = "pyzmq-24.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:78068e8678ca023594e4a0ab558905c1033b2d3e806a0ad9e3094e231e115a33"}, + {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77c2713faf25a953c69cf0f723d1b7dd83827b0834e6c41e3fb3bbc6765914a1"}, + {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bb4af15f305056e95ca1bd086239b9ebc6ad55e9f49076d27d80027f72752f6"}, + {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0f14cffd32e9c4c73da66db97853a6aeceaac34acdc0fae9e5bbc9370281864c"}, + {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0108358dab8c6b27ff6b985c2af4b12665c1bc659648284153ee501000f5c107"}, + {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d66689e840e75221b0b290b0befa86f059fb35e1ee6443bce51516d4d61b6b99"}, + {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ae08ac90aa8fa14caafc7a6251bd218bf6dac518b7bff09caaa5e781119ba3f2"}, + {file = "pyzmq-24.0.1-cp37-cp37m-win32.whl", hash = "sha256:8421aa8c9b45ea608c205db9e1c0c855c7e54d0e9c2c2f337ce024f6843cab3b"}, + {file = "pyzmq-24.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54d8b9c5e288362ec8595c1d98666d36f2070fd0c2f76e2b3c60fbad9bd76227"}, + {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:acbd0a6d61cc954b9f535daaa9ec26b0a60a0d4353c5f7c1438ebc88a359a47e"}, + {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:47b11a729d61a47df56346283a4a800fa379ae6a85870d5a2e1e4956c828eedc"}, + {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abe6eb10122f0d746a0d510c2039ae8edb27bc9af29f6d1b05a66cc2401353ff"}, + {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:07bec1a1b22dacf718f2c0e71b49600bb6a31a88f06527dfd0b5aababe3fa3f7"}, + {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d945a85b70da97ae86113faf9f1b9294efe66bd4a5d6f82f2676d567338b66"}, + {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1b7928bb7580736ffac5baf814097be342ba08d3cfdfb48e52773ec959572287"}, + {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b946da90dc2799bcafa682692c1d2139b2a96ec3c24fa9fc6f5b0da782675330"}, + {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c8840f064b1fb377cffd3efeaad2b190c14d4c8da02316dae07571252d20b31f"}, + {file = "pyzmq-24.0.1-cp38-cp38-win32.whl", hash = "sha256:4854f9edc5208f63f0841c0c667260ae8d6846cfa233c479e29fdc85d42ebd58"}, + {file = "pyzmq-24.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:42d4f97b9795a7aafa152a36fe2ad44549b83a743fd3e77011136def512e6c2a"}, + {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:52afb0ac962963fff30cf1be775bc51ae083ef4c1e354266ab20e5382057dd62"}, + {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bad8210ad4df68c44ff3685cca3cda448ee46e20d13edcff8909eba6ec01ca4"}, + {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dabf1a05318d95b1537fd61d9330ef4313ea1216eea128a17615038859da3b3b"}, + {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5bd3d7dfd9cd058eb68d9a905dec854f86649f64d4ddf21f3ec289341386c44b"}, + {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8012bce6836d3f20a6c9599f81dfa945f433dab4dbd0c4917a6fb1f998ab33d"}, + {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c31805d2c8ade9b11feca4674eee2b9cce1fec3e8ddb7bbdd961a09dc76a80ea"}, + {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3104f4b084ad5d9c0cb87445cc8cfd96bba710bef4a66c2674910127044df209"}, + {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:df0841f94928f8af9c7a1f0aaaffba1fb74607af023a152f59379c01c53aee58"}, + {file = "pyzmq-24.0.1-cp39-cp39-win32.whl", hash = "sha256:a435ef8a3bd95c8a2d316d6e0ff70d0db524f6037411652803e118871d703333"}, + {file = "pyzmq-24.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:2032d9cb994ce3b4cba2b8dfae08c7e25bc14ba484c770d4d3be33c27de8c45b"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bb5635c851eef3a7a54becde6da99485eecf7d068bd885ac8e6d173c4ecd68b0"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:83ea1a398f192957cb986d9206ce229efe0ee75e3c6635baff53ddf39bd718d5"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:941fab0073f0a54dc33d1a0460cb04e0d85893cb0c5e1476c785000f8b359409"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e8f482c44ccb5884bf3f638f29bea0f8dc68c97e38b2061769c4cb697f6140d"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:613010b5d17906c4367609e6f52e9a2595e35d5cc27d36ff3f1b6fa6e954d944"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:65c94410b5a8355cfcf12fd600a313efee46ce96a09e911ea92cf2acf6708804"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:20e7eeb1166087db636c06cae04a1ef59298627f56fb17da10528ab52a14c87f"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2712aee7b3834ace51738c15d9ee152cc5a98dc7d57dd93300461b792ab7b43"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7c280185c4da99e0cc06c63bdf91f5b0b71deb70d8717f0ab870a43e376db8"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:858375573c9225cc8e5b49bfac846a77b696b8d5e815711b8d4ba3141e6e8879"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:80093b595921eed1a2cead546a683b9e2ae7f4a4592bb2ab22f70d30174f003a"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f3f3154fde2b1ff3aa7b4f9326347ebc89c8ef425ca1db8f665175e6d3bd42f"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb756147314430bee5d10919b8493c0ccb109ddb7f5dfd2fcd7441266a25b75"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e706bac34e9f50779cb8c39f10b53a4d15aebb97235643d3112ac20bd577b4"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:687700f8371643916a1d2c61f3fdaa630407dd205c38afff936545d7b7466066"}, + {file = "pyzmq-24.0.1.tar.gz", hash = "sha256:216f5d7dbb67166759e59b0479bca82b8acf9bed6015b526b8eb10143fb08e77"}, ] rdflib = [ {file = "rdflib-6.1.1-py3-none-any.whl", hash = "sha256:fc81cef513cd552d471f2926141396b633207109d0154c8e77926222c70367fe"}, @@ -3967,8 +3981,8 @@ requests = [ {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] requests-toolbelt = [ - {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, - {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, + {file = "requests-toolbelt-0.10.0.tar.gz", hash = "sha256:f695d6207931200b46c8ef6addbc8a921fb5d77cc4cd209c2e7d39293fcd2b30"}, + {file = "requests_toolbelt-0.10.0-py2.py3-none-any.whl", hash = "sha256:64c6b8c51b515d123f9f708a29743f44eb70c4479440641ed2df8c4dea56d985"}, ] responses = [ {file = "responses-0.21.0-py3-none-any.whl", hash = "sha256:2dcc863ba63963c0c3d9ee3fa9507cbe36b7d7b0fccb4f0bdfd9e96c539b1487"}, @@ -3986,12 +4000,13 @@ rq-scheduler = [ {file = "rq-scheduler-0.11.0.tar.gz", hash = "sha256:db79bb56cdbc4f7ffdd8bd659e389e91aa0db9c1abf002dc46f5dd6f0dbd2910"}, {file = "rq_scheduler-0.11.0-py2.py3-none-any.whl", hash = "sha256:da94e9b6badf112995ff38fe16192e4f4c43c412b3c9614684ed8c8f7ca517d2"}, ] -ruamel-yaml = [ +"ruamel.yaml" = [ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, ] -ruamel-yaml-clib = [ +"ruamel.yaml.clib" = [ {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:066f886bc90cc2ce44df8b5f7acfc6a7e2b2e672713f027136464492b0c34d7c"}, {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"}, {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"}, {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"}, @@ -4001,30 +4016,52 @@ ruamel-yaml-clib = [ {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"}, {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"}, {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d3c620a54748a3d4cf0bcfe623e388407c8e85a4b06b8188e126302bcab93ea8"}, {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"}, {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"}, {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"}, {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:210c8fcfeff90514b7133010bf14e3bad652c8efde6b20e00c43854bf94fa5a6"}, {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"}, {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"}, {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"}, {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:61bc5e5ca632d95925907c569daa559ea194a4d16084ba86084be98ab1cec1c6"}, {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"}, {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"}, {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"}, {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1b4139a6ffbca8ef60fdaf9b33dec05143ba746a6f0ae0f9d11d38239211d335"}, {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"}, {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"}, {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, ] -schema-salad = [] +schema-salad = [ + {file = "schema-salad-8.3.20220801194920.tar.gz", hash = "sha256:82f7c3bbe15351e4dbe2e2f2a8330240c5da4604f4a54f6c12ed15af0ce85917"}, + {file = "schema_salad-8.3.20220801194920-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a87ab75e586cf729ad3434a068cae05cc15cf1c56371651b343f763099c97f6"}, + {file = "schema_salad-8.3.20220801194920-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:aaee209a13682f6f804bbf374e2f37b0e9f717cbd8a4a5eadf534e2a39cabdb5"}, + {file = "schema_salad-8.3.20220801194920-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f71e7a79d199b0ede21a181280fc8b613167d170fb1681a2f824c2db8e665bb"}, + {file = "schema_salad-8.3.20220801194920-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c585d0a8510aa928a293cc0361202bd279a361fe29fbc6a18f71f05a5bc22709"}, + {file = "schema_salad-8.3.20220801194920-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ef1fd242ead116f002dddd6e116902e6521f8ba152e29a32cc15bb72abe78cd6"}, + {file = "schema_salad-8.3.20220801194920-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b66f719df48b4c72f4df8a834892fbf15449280365ac78a92acb65e51bc804e"}, + {file = "schema_salad-8.3.20220801194920-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4b6f1d18c98e4646c7358163181dfb628ac73ee4b5d59b4569d5baf5748a5c2"}, + {file = "schema_salad-8.3.20220801194920-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:58165f049c459d2f31e634fe3b50ac21dc9ecd58d83280375523dc79e9f2811c"}, + {file = "schema_salad-8.3.20220801194920-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6af681973789bd81293e61910d11d9b892875137ed36d5d2f49aa495a8821eb"}, + {file = "schema_salad-8.3.20220801194920-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30773a32b4d34eb0d5c05e2433864f239837bf20ca07da6d3a08b6beeeacca6a"}, + {file = "schema_salad-8.3.20220801194920-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:298b18712c17146b3148b66f2fcad20b7862c5ce2c8d4f890e05b13f7098e740"}, + {file = "schema_salad-8.3.20220801194920-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a61ec22b5c22bbd54d843a4ad56c39bf1eb9186bf12b6066ca0c766d1f5f44e4"}, + {file = "schema_salad-8.3.20220801194920-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477b808c1475e5d16c569595083a63a721c43e539b0d832ca2f927df642ec769"}, + {file = "schema_salad-8.3.20220801194920-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:74777bcb3c141080e2e46d86608982d565ceb187419ab55c0a1fa30e6185c712"}, + {file = "schema_salad-8.3.20220801194920-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e547af5ed207a07fc9b3c8e96889c76b050448c69e39ea50d8ed20cca73ef6a2"}, + {file = "schema_salad-8.3.20220801194920-py3-none-any.whl", hash = "sha256:ef820daad4c25c639abfd56b99628d891ce6fa9d69e45abd46a469ce31f50a42"}, +] sentry-sdk = [ {file = "sentry-sdk-1.5.11.tar.gz", hash = "sha256:6c01d9d0b65935fd275adc120194737d1df317dce811e642cbf0394d0d37a007"}, {file = "sentry_sdk-1.5.11-py2.py3-none-any.whl", hash = "sha256:c17179183cac614e900cbd048dab03f49a48e2820182ec686c25e7ce46f8548f"}, ] setuptools = [ - {file = "setuptools-65.3.0-py3-none-any.whl", hash = "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82"}, - {file = "setuptools-65.3.0.tar.gz", hash = "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"}, + {file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"}, + {file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"}, ] shellescape = [ {file = "shellescape-3.8.1-py2.py3-none-any.whl", hash = "sha256:f17127e390fa3f9aaa80c69c16ea73615fd9b5318fd8309c1dca6168ae7d85bf"}, @@ -4050,7 +4087,7 @@ sortedcontainers = [ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] -sphinx = [ +Sphinx = [ {file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"}, {file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"}, ] @@ -4091,8 +4128,8 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] sphinxcontrib-spelling = [ - {file = "sphinxcontrib-spelling-7.6.0.tar.gz", hash = "sha256:292cd7e1f73a763451693b4d48c9bded151084f6a91e5337733e9fa8715d20ec"}, - {file = "sphinxcontrib_spelling-7.6.0-py3-none-any.whl", hash = "sha256:6c1313618412511109f7b76029fbd60df5aa4acf67a2dc9cd1b1016d15e882ff"}, + {file = "sphinxcontrib-spelling-7.6.1.tar.gz", hash = "sha256:444857579dd61914f3970ad1046c74bf6758136f7e144b46ca9c28108870f508"}, + {file = "sphinxcontrib_spelling-7.6.1-py3-none-any.whl", hash = "sha256:14e3ea21f551228f36c0349463552d2d88c05ffffcfe7cdd513b01aa3407f561"}, ] tabulate = [ {file = "tabulate-0.8.10-py3-none-any.whl", hash = "sha256:0ba055423dbaa164b9e456abe7920c5e8ed33fcc16f6d1b2f2d152c8e1e8b4fc"}, @@ -4114,8 +4151,8 @@ tomli = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] tomlkit = [ - {file = "tomlkit-0.11.4-py3-none-any.whl", hash = "sha256:25d4e2e446c453be6360c67ddfb88838cfc42026322770ba13d1fbd403a93a5c"}, - {file = "tomlkit-0.11.4.tar.gz", hash = "sha256:3235a9010fae54323e727c3ac06fb720752fe6635b3426e379daec60fbd44a83"}, + {file = "tomlkit-0.11.5-py3-none-any.whl", hash = "sha256:f2ef9da9cef846ee027947dc99a45d6b68a63b0ebc21944649505bf2e8bc5fe7"}, + {file = "tomlkit-0.11.5.tar.gz", hash = "sha256:571854ebbb5eac89abcb4a2e47d7ea27b89bf29e09c35395da6f03dd4ae23d1c"}, ] tornado = [ {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, @@ -4168,9 +4205,9 @@ types-python-dateutil = [ {file = "types-python-dateutil-2.8.19.tar.gz", hash = "sha256:bfd3eb39c7253aea4ba23b10f69b017d30b013662bb4be4ab48b20bbd763f309"}, {file = "types_python_dateutil-2.8.19-py3-none-any.whl", hash = "sha256:6284df1e4783d8fc6e587f0317a81333856b872a6669a282f8a325342bce7fa8"}, ] -types-pyyaml = [ - {file = "types-PyYAML-6.0.11.tar.gz", hash = "sha256:7f7da2fd11e9bc1e5e9eb3ea1be84f4849747017a59fc2eee0ea34ed1147c2e0"}, - {file = "types_PyYAML-6.0.11-py3-none-any.whl", hash = "sha256:8f890028123607379c63550179ddaec4517dc751f4c527a52bb61934bf495989"}, +types-PyYAML = [ + {file = "types-PyYAML-6.0.12.tar.gz", hash = "sha256:f6f350418125872f3f0409d96a62a5a5ceb45231af5cc07ee0034ec48a3c82fa"}, + {file = "types_PyYAML-6.0.12-py3-none-any.whl", hash = "sha256:29228db9f82df4f1b7febee06bbfb601677882e98a3da98132e31c6874163e15"}, ] types-redis = [ {file = "types-redis-4.0.6.tar.gz", hash = "sha256:3d870147794303077598343125727ee24a1a8961dca1a6870013152f2ba2a81b"}, @@ -4185,8 +4222,8 @@ types-tabulate = [ {file = "types_tabulate-0.8.9-py3-none-any.whl", hash = "sha256:7971ed0cd40454eb18d82c01e2f18bcd09ca23cc9eb901c62d2b04e5d1f57f84"}, ] typing-extensions = [ - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] urllib3 = [ {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, @@ -4207,7 +4244,7 @@ websocket-client = [ {file = "websocket-client-1.4.1.tar.gz", hash = "sha256:f9611eb65c8241a67fb373bef040b3cf8ad377a9f6546a12b620b6511e8ea9ef"}, {file = "websocket_client-1.4.1-py3-none-any.whl", hash = "sha256:398909eb7e261f44b8f4bd474785b6ec5f5b499d4953342fe9755e01ef624090"}, ] -werkzeug = [ +Werkzeug = [ {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, ] @@ -4280,39 +4317,99 @@ wrapt = [ yagup = [ {file = "yagup-0.1.1.tar.gz", hash = "sha256:247bdeec4dd2b5bac7f33f42551f3cc248aee1f87f7831274a71501c902a9673"}, ] -yarl = [] +yarl = [ + {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"}, + {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"}, + {file = "yarl-1.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9de955d98e02fab288c7718662afb33aab64212ecb368c5dc866d9a57bf48880"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ec362167e2c9fd178f82f252b6d97669d7245695dc057ee182118042026da40"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20df6ff4089bc86e4a66e3b1380460f864df3dd9dccaf88d6b3385d24405893b"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5999c4662631cb798496535afbd837a102859568adc67d75d2045e31ec3ac497"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed19b74e81b10b592084a5ad1e70f845f0aacb57577018d31de064e71ffa267a"}, + {file = "yarl-1.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e4808f996ca39a6463f45182e2af2fae55e2560be586d447ce8016f389f626f"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2d800b9c2eaf0684c08be5f50e52bfa2aa920e7163c2ea43f4f431e829b4f0fd"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6628d750041550c5d9da50bb40b5cf28a2e63b9388bac10fedd4f19236ef4957"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f5af52738e225fcc526ae64071b7e5342abe03f42e0e8918227b38c9aa711e28"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:76577f13333b4fe345c3704811ac7509b31499132ff0181f25ee26619de2c843"}, + {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c03f456522d1ec815893d85fccb5def01ffaa74c1b16ff30f8aaa03eb21e453"}, + {file = "yarl-1.8.1-cp310-cp310-win32.whl", hash = "sha256:ea30a42dc94d42f2ba4d0f7c0ffb4f4f9baa1b23045910c0c32df9c9902cb272"}, + {file = "yarl-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:9130ddf1ae9978abe63808b6b60a897e41fccb834408cde79522feb37fb72fb0"}, + {file = "yarl-1.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0ab5a138211c1c366404d912824bdcf5545ccba5b3ff52c42c4af4cbdc2c5035"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0fb2cb4204ddb456a8e32381f9a90000429489a25f64e817e6ff94879d432fc"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85cba594433915d5c9a0d14b24cfba0339f57a2fff203a5d4fd070e593307d0b"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ca7e596c55bd675432b11320b4eacc62310c2145d6801a1f8e9ad160685a231"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f77539733e0ec2475ddcd4e26777d08996f8cd55d2aef82ec4d3896687abda"}, + {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29e256649f42771829974e742061c3501cc50cf16e63f91ed8d1bf98242e5507"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7fce6cbc6c170ede0221cc8c91b285f7f3c8b9fe28283b51885ff621bbe0f8ee"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:59ddd85a1214862ce7c7c66457f05543b6a275b70a65de366030d56159a979f0"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:12768232751689c1a89b0376a96a32bc7633c08da45ad985d0c49ede691f5c0d"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:b19255dde4b4f4c32e012038f2c169bb72e7f081552bea4641cab4d88bc409dd"}, + {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6c8148e0b52bf9535c40c48faebb00cb294ee577ca069d21bd5c48d302a83780"}, + {file = "yarl-1.8.1-cp37-cp37m-win32.whl", hash = "sha256:de839c3a1826a909fdbfe05f6fe2167c4ab033f1133757b5936efe2f84904c07"}, + {file = "yarl-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:dd032e8422a52e5a4860e062eb84ac94ea08861d334a4bcaf142a63ce8ad4802"}, + {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:19cd801d6f983918a3f3a39f3a45b553c015c5aac92ccd1fac619bd74beece4a"}, + {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6347f1a58e658b97b0a0d1ff7658a03cb79bdbda0331603bed24dd7054a6dea1"}, + {file = "yarl-1.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c0da7e44d0c9108d8b98469338705e07f4bb7dab96dbd8fa4e91b337db42548"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5587bba41399854703212b87071c6d8638fa6e61656385875f8c6dff92b2e461"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31a9a04ecccd6b03e2b0e12e82131f1488dea5555a13a4d32f064e22a6003cfe"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205904cffd69ae972a1707a1bd3ea7cded594b1d773a0ce66714edf17833cdae"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea513a25976d21733bff523e0ca836ef1679630ef4ad22d46987d04b372d57fc"}, + {file = "yarl-1.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0b51530877d3ad7a8d47b2fff0c8df3b8f3b8deddf057379ba50b13df2a5eae"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d2b8f245dad9e331540c350285910b20dd913dc86d4ee410c11d48523c4fd546"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ab2a60d57ca88e1d4ca34a10e9fb4ab2ac5ad315543351de3a612bbb0560bead"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:449c957ffc6bc2309e1fbe67ab7d2c1efca89d3f4912baeb8ead207bb3cc1cd4"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a165442348c211b5dea67c0206fc61366212d7082ba8118c8c5c1c853ea4d82e"}, + {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b3ded839a5c5608eec8b6f9ae9a62cb22cd037ea97c627f38ae0841a48f09eae"}, + {file = "yarl-1.8.1-cp38-cp38-win32.whl", hash = "sha256:c1445a0c562ed561d06d8cbc5c8916c6008a31c60bc3655cdd2de1d3bf5174a0"}, + {file = "yarl-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:56c11efb0a89700987d05597b08a1efcd78d74c52febe530126785e1b1a285f4"}, + {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e80ed5a9939ceb6fda42811542f31c8602be336b1fb977bccb012e83da7e4936"}, + {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6afb336e23a793cd3b6476c30f030a0d4c7539cd81649683b5e0c1b0ab0bf350"}, + {file = "yarl-1.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c322cbaa4ed78a8aac89b2174a6df398faf50e5fc12c4c191c40c59d5e28357"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fae37373155f5ef9b403ab48af5136ae9851151f7aacd9926251ab26b953118b"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5395da939ffa959974577eff2cbfc24b004a2fb6c346918f39966a5786874e54"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:076eede537ab978b605f41db79a56cad2e7efeea2aa6e0fa8f05a26c24a034fb"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1a50e461615747dd93c099f297c1994d472b0f4d2db8a64e55b1edf704ec1c"}, + {file = "yarl-1.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7de89c8456525650ffa2bb56a3eee6af891e98f498babd43ae307bd42dca98f6"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a88510731cd8d4befaba5fbd734a7dd914de5ab8132a5b3dde0bbd6c9476c64"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2d93a049d29df172f48bcb09acf9226318e712ce67374f893b460b42cc1380ae"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:21ac44b763e0eec15746a3d440f5e09ad2ecc8b5f6dcd3ea8cb4773d6d4703e3"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d0272228fabe78ce00a3365ffffd6f643f57a91043e119c289aaba202f4095b0"}, + {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99449cd5366fe4608e7226c6cae80873296dfa0cde45d9b498fefa1de315a09e"}, + {file = "yarl-1.8.1-cp39-cp39-win32.whl", hash = "sha256:8b0af1cf36b93cee99a31a545fe91d08223e64390c5ecc5e94c39511832a4bb6"}, + {file = "yarl-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:de49d77e968de6626ba7ef4472323f9d2e5a56c1d85b7c0e2a190b2173d3b9be"}, + {file = "yarl-1.8.1.tar.gz", hash = "sha256:af887845b8c2e060eb5605ff72b6f2dd2aab7a761379373fd89d314f4752abbf"}, +] yaspin = [ {file = "yaspin-2.1.0-py3-none-any.whl", hash = "sha256:d574cbfaf0a349df466c91f7f81b22460ae5ebb15ecb8bf9411d6049923aee8d"}, {file = "yaspin-2.1.0.tar.gz", hash = "sha256:c8d34eca9fda3f4dfbe59f57f3cf0f3641af3eefbf1544fbeb9b3bacf82c580a"}, ] -zc-lockfile = [ +"zc.lockfile" = [ {file = "zc.lockfile-2.0-py2.py3-none-any.whl", hash = "sha256:cc33599b549f0c8a248cb72f3bf32d77712de1ff7ee8814312eb6456b42c015f"}, {file = "zc.lockfile-2.0.tar.gz", hash = "sha256:307ad78227e48be260e64896ec8886edc7eae22d8ec53e4d528ab5537a83203b"}, ] -zc-relation = [ +"zc.relation" = [ {file = "zc.relation-1.1.post2-py2.py3-none-any.whl", hash = "sha256:57218b4d18e1842eba33b96b53a13004fa2e187bc849887c3150be299e6fc261"}, {file = "zc.relation-1.1.post2.tar.gz", hash = "sha256:b1aeb9e97ead0206a210e7f1fe32ae60f43630109e46e2ff40e99294fb892add"}, ] -zconfig = [ +ZConfig = [ {file = "ZConfig-3.6.0-py2.py3-none-any.whl", hash = "sha256:d36cebe34b49fb2a9123472c8a22dbbf25837f33d31300d391403810cb1557d7"}, {file = "ZConfig-3.6.0.tar.gz", hash = "sha256:a28e95a0ae335795747eccad35b2cb708f37d44c7f325e2acb201e98330b16e5"}, ] zdaemon = [ {file = "zdaemon-4.3.tar.gz", hash = "sha256:f249fc6885646d165d7d6b228a7b71f5170fc7117de9e0688271f8fb97840f72"}, ] -zeo = [ +ZEO = [ {file = "ZEO-5.3.0-py2.py3-none-any.whl", hash = "sha256:cec59227cb87d938b000a875fa0049433f7a3cc2f0452b95876c890f485584b6"}, {file = "ZEO-5.3.0.tar.gz", hash = "sha256:ffc54334f8384d6faf1693dce823c0a5fcd429fa015c47d2d49d1bd689997157"}, ] zipp = [ - {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, - {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, + {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"}, + {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"}, ] -zodb = [ +ZODB = [ {file = "ZODB-5.6.0-py2.py3-none-any.whl", hash = "sha256:b34fe2515c4f18cf247989b891254a64a0835c7d80a450f461efe8fd843e18d0"}, {file = "ZODB-5.6.0.tar.gz", hash = "sha256:4654fb543393533291cfbf8adfb4ea2658ff385871d4b926d725508142cb07fe"}, ] -zodb3 = [ +ZODB3 = [ {file = "ZODB3-3.11.0.tar.gz", hash = "sha256:b5767028e732c619f45c27189dd001e14ec155d7984807991fce751b35b4fcb0"}, ] zodbpickle = [ @@ -4354,60 +4451,46 @@ zodbpickle = [ {file = "zodbpickle-2.4-cp39-cp39-win_amd64.whl", hash = "sha256:38c92b824f43f4cbca4b0e6ed9cd7b352d5b56a55b31324fc037e72f0d7b213b"}, {file = "zodbpickle-2.4.tar.gz", hash = "sha256:bd6cc920f2833ba6d35b3bf1c3269e9210ebfc01ecd7f1768c22f63aaa0753ad"}, ] -zope-interface = [ - {file = "zope.interface-5.4.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:7df1e1c05304f26faa49fa752a8c690126cf98b40b91d54e6e9cc3b7d6ffe8b7"}, - {file = "zope.interface-5.4.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2c98384b254b37ce50eddd55db8d381a5c53b4c10ee66e1e7fe749824f894021"}, - {file = "zope.interface-5.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:08f9636e99a9d5410181ba0729e0408d3d8748026ea938f3b970a0249daa8192"}, - {file = "zope.interface-5.4.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ea1d73b7c9dcbc5080bb8aaffb776f1c68e807767069b9ccdd06f27a161914a"}, - {file = "zope.interface-5.4.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:273f158fabc5ea33cbc936da0ab3d4ba80ede5351babc4f577d768e057651531"}, - {file = "zope.interface-5.4.0-cp27-cp27m-win32.whl", hash = "sha256:a1e6e96217a0f72e2b8629e271e1b280c6fa3fe6e59fa8f6701bec14e3354325"}, - {file = "zope.interface-5.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:877473e675fdcc113c138813a5dd440da0769a2d81f4d86614e5d62b69497155"}, - {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263"}, - {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:b0297b1e05fd128d26cc2460c810d42e205d16d76799526dfa8c8ccd50e74959"}, - {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:af310ec8335016b5e52cae60cda4a4f2a60a788cbb949a4fbea13d441aa5a09e"}, - {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:9a9845c4c6bb56e508651f005c4aeb0404e518c6f000d5a1123ab077ab769f5c"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0b465ae0962d49c68aa9733ba92a001b2a0933c317780435f00be7ecb959c702"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:5dd9ca406499444f4c8299f803d4a14edf7890ecc595c8b1c7115c2342cadc5f"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:469e2407e0fe9880ac690a3666f03eb4c3c444411a5a5fddfdabc5d184a79f05"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:52de7fc6c21b419078008f697fd4103dbc763288b1406b4562554bd47514c004"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:3dd4952748521205697bc2802e4afac5ed4b02909bb799ba1fe239f77fd4e117"}, - {file = "zope.interface-5.4.0-cp35-cp35m-win32.whl", hash = "sha256:dd93ea5c0c7f3e25335ab7d22a507b1dc43976e1345508f845efc573d3d779d8"}, - {file = "zope.interface-5.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:3748fac0d0f6a304e674955ab1365d515993b3a0a865e16a11ec9d86fb307f63"}, - {file = "zope.interface-5.4.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:66c0061c91b3b9cf542131148ef7ecbecb2690d48d1612ec386de9d36766058f"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d0c1bc2fa9a7285719e5678584f6b92572a5b639d0e471bb8d4b650a1a910920"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2876246527c91e101184f63ccd1d716ec9c46519cc5f3d5375a3351c46467c46"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:334701327f37c47fa628fc8b8d28c7d7730ce7daaf4bda1efb741679c2b087fc"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:71aace0c42d53abe6fc7f726c5d3b60d90f3c5c055a447950ad6ea9cec2e37d9"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5bb3489b4558e49ad2c5118137cfeaf59434f9737fa9c5deefc72d22c23822e2"}, - {file = "zope.interface-5.4.0-cp36-cp36m-win32.whl", hash = "sha256:1c0e316c9add0db48a5b703833881351444398b04111188069a26a61cfb4df78"}, - {file = "zope.interface-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f0c02cbb9691b7c91d5009108f975f8ffeab5dff8f26d62e21c493060eff2a1"}, - {file = "zope.interface-5.4.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:7d97a4306898b05404a0dcdc32d9709b7d8832c0c542b861d9a826301719794e"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:867a5ad16892bf20e6c4ea2aab1971f45645ff3102ad29bd84c86027fa99997b"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5f931a1c21dfa7a9c573ec1f50a31135ccce84e32507c54e1ea404894c5eb96f"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:194d0bcb1374ac3e1e023961610dc8f2c78a0f5f634d0c737691e215569e640d"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8270252effc60b9642b423189a2fe90eb6b59e87cbee54549db3f5562ff8d1b8"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:15e7d1f7a6ee16572e21e3576d2012b2778cbacf75eb4b7400be37455f5ca8bf"}, - {file = "zope.interface-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:8892f89999ffd992208754851e5a052f6b5db70a1e3f7d54b17c5211e37a98c7"}, - {file = "zope.interface-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2e5a26f16503be6c826abca904e45f1a44ff275fdb7e9d1b75c10671c26f8b94"}, - {file = "zope.interface-5.4.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0f91b5b948686659a8e28b728ff5e74b1be6bf40cb04704453617e5f1e945ef3"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:4de4bc9b6d35c5af65b454d3e9bc98c50eb3960d5a3762c9438df57427134b8e"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf68f4b2b6683e52bec69273562df15af352e5ed25d1b6641e7efddc5951d1a7"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:63b82bb63de7c821428d513607e84c6d97d58afd1fe2eb645030bdc185440120"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:db1fa631737dab9fa0b37f3979d8d2631e348c3b4e8325d6873c2541d0ae5a48"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4"}, - {file = "zope.interface-5.4.0-cp38-cp38-win32.whl", hash = "sha256:a9506a7e80bcf6eacfff7f804c0ad5350c8c95b9010e4356a4b36f5322f09abb"}, - {file = "zope.interface-5.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:3c02411a3b62668200910090a0dff17c0b25aaa36145082a5a6adf08fa281e54"}, - {file = "zope.interface-5.4.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:0cee5187b60ed26d56eb2960136288ce91bcf61e2a9405660d271d1f122a69a4"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a8156e6a7f5e2a0ff0c5b21d6bcb45145efece1909efcbbbf48c56f8da68221d"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:205e40ccde0f37496904572035deea747390a8b7dc65146d30b96e2dd1359a83"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3f24df7124c323fceb53ff6168da70dbfbae1442b4f3da439cd441681f54fe25"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:5208ebd5152e040640518a77827bdfcc73773a15a33d6644015b763b9c9febc1"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:17776ecd3a1fdd2b2cd5373e5ef8b307162f581c693575ec62e7c5399d80794c"}, - {file = "zope.interface-5.4.0-cp39-cp39-win32.whl", hash = "sha256:d4d9d6c1a455d4babd320203b918ccc7fcbefe308615c521062bc2ba1aa4d26e"}, - {file = "zope.interface-5.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:0cba8477e300d64a11a9789ed40ee8932b59f9ee05f85276dbb4b59acee5dd09"}, - {file = "zope.interface-5.4.0.tar.gz", hash = "sha256:5dba5f530fec3f0988d83b78cc591b58c0b6eb8431a85edd1569a0539a8a5a0e"}, -] -zope-testing = [ +"zope.interface" = [ + {file = "zope.interface-5.5.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:2cb3003941f5f4fa577479ac6d5db2b940acb600096dd9ea9bf07007f5cab46f"}, + {file = "zope.interface-5.5.0-cp27-cp27m-win32.whl", hash = "sha256:8c791f4c203ccdbcda588ea4c8a6e4353e10435ea48ddd3d8734a26fe9714cba"}, + {file = "zope.interface-5.5.0-cp27-cp27m-win_amd64.whl", hash = "sha256:3eedf3d04179774d750e8bb4463e6da350956a50ed44d7b86098e452d7ec385e"}, + {file = "zope.interface-5.5.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:58a66c2020a347973168a4a9d64317bac52f9fdfd3e6b80b252be30da881a64e"}, + {file = "zope.interface-5.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da7912ae76e1df6a1fb841b619110b1be4c86dfb36699d7fd2f177105cdea885"}, + {file = "zope.interface-5.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:423c074e404f13e6fa07f4454f47fdbb38d358be22945bc812b94289d9142374"}, + {file = "zope.interface-5.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7bdcec93f152e0e1942102537eed7b166d6661ae57835b20a52a2a3d6a3e1bf3"}, + {file = "zope.interface-5.5.0-cp310-cp310-win32.whl", hash = "sha256:03f5ae315db0d0de668125d983e2a819a554f3fdb2d53b7e934e3eb3c3c7375d"}, + {file = "zope.interface-5.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:8b9f153208d74ccfa25449a0c6cb756ab792ce0dc99d9d771d935f039b38740c"}, + {file = "zope.interface-5.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeac590cce44e68ee8ad0b8ecf4d7bf15801f102d564ca1b0eb1f12f584ee656"}, + {file = "zope.interface-5.5.0-cp35-cp35m-win32.whl", hash = "sha256:7d9ec1e6694af39b687045712a8ad14ddcb568670d5eb1b66b48b98b9312afba"}, + {file = "zope.interface-5.5.0-cp35-cp35m-win_amd64.whl", hash = "sha256:d18fb0f6c8169d26044128a2e7d3c39377a8a151c564e87b875d379dbafd3930"}, + {file = "zope.interface-5.5.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0eb2b3e84f48dd9cfc8621c80fba905d7e228615c67f76c7df7c716065669bb6"}, + {file = "zope.interface-5.5.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df6593e150d13cfcce69b0aec5df7bc248cb91e4258a7374c129bb6d56b4e5ca"}, + {file = "zope.interface-5.5.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9dc4493aa3d87591e3d2bf1453e25b98038c839ca8e499df3d7106631b66fe83"}, + {file = "zope.interface-5.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5c6023ae7defd052cf76986ce77922177b0c2f3913bea31b5b28fbdf6cb7099e"}, + {file = "zope.interface-5.5.0-cp36-cp36m-win32.whl", hash = "sha256:a69c28d85bb7cf557751a5214cb3f657b2b035c8c96d71080c1253b75b79b69b"}, + {file = "zope.interface-5.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:85dd6dd9aaae7a176948d8bb62e20e2968588fd787c29c5d0d964ab475168d3d"}, + {file = "zope.interface-5.5.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:970661ece2029915b8f7f70892e88404340fbdefd64728380cad41c8dce14ff4"}, + {file = "zope.interface-5.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e3495bb0cdcea212154e558082c256f11b18031f05193ae2fb85d048848db14"}, + {file = "zope.interface-5.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3f68404edb1a4fb6aa8a94675521ca26c83ebbdbb90e894f749ae0dc4ca98418"}, + {file = "zope.interface-5.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:740f3c1b44380658777669bcc42f650f5348e53797f2cee0d93dc9b0f9d7cc69"}, + {file = "zope.interface-5.5.0-cp37-cp37m-win32.whl", hash = "sha256:006f8dd81fae28027fc28ada214855166712bf4f0bfbc5a8788f9b70982b9437"}, + {file = "zope.interface-5.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:43490ad65d4c64e45a30e51a2beb7a6b63e1ff395302ad22392224eb618476d6"}, + {file = "zope.interface-5.5.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f70726b60009433111fe9928f5d89cbb18962411d33c45fb19eb81b9bbd26fcd"}, + {file = "zope.interface-5.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa614d049667bed1c737435c609c0956c5dc0dbafdc1145ee7935e4658582cb"}, + {file = "zope.interface-5.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:58a975f89e4584d0223ab813c5ba4787064c68feef4b30d600f5e01de90ae9ce"}, + {file = "zope.interface-5.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:37ec9ade9902f412cc7e7a32d71f79dec3035bad9bd0170226252eed88763c48"}, + {file = "zope.interface-5.5.0-cp38-cp38-win32.whl", hash = "sha256:be11fce0e6af6c0e8d93c10ef17b25aa7c4acb7ec644bff2596c0d639c49e20f"}, + {file = "zope.interface-5.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:cbbf83914b9a883ab324f728de869f4e406e0cbcd92df7e0a88decf6f9ab7d5a"}, + {file = "zope.interface-5.5.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:26c1456520fdcafecc5765bec4783eeafd2e893eabc636908f50ee31fe5c738c"}, + {file = "zope.interface-5.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47ff078734a1030c48103422a99e71a7662d20258c00306546441adf689416f7"}, + {file = "zope.interface-5.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:687cab7f9ae18d2c146f315d0ca81e5ffe89a139b88277afa70d52f632515854"}, + {file = "zope.interface-5.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d80f6236b57a95eb19d5e47eb68d0296119e1eff6deaa2971ab8abe3af918420"}, + {file = "zope.interface-5.5.0-cp39-cp39-win32.whl", hash = "sha256:9cdc4e898d3b1547d018829fd4a9f403e52e51bba24be0fbfa37f3174e1ef797"}, + {file = "zope.interface-5.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:6566b3d2657e7609cd8751bcb1eab1202b1692a7af223035a5887d64bb3a2f3b"}, + {file = "zope.interface-5.5.0.tar.gz", hash = "sha256:700ebf9662cf8df70e2f0cb4988e078c53f65ee3eefd5c9d80cf988c4175c8e3"}, +] +"zope.testing" = [ {file = "zope.testing-4.10-py2.py3-none-any.whl", hash = "sha256:aa32eeb971aa5e50aaa1657d97ff97669e4c1054db0dc92ee6338361f2864be4"}, {file = "zope.testing-4.10.tar.gz", hash = "sha256:3b6e5906caddd148c23fe958e6aae3fadc8a08a8a53f747d9791c2d8135ee56e"}, ] diff --git a/pyproject.toml b/pyproject.toml index cd4f3465cc..a574dea953 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,6 +84,7 @@ inject = "<4.4.0,>=4.3.0" isort = { version = "<5.10.2,>=5.3.2", optional = true } jinja2 = { version = ">=2.11.3,<3.1.3" } marshmallow = { version = ">=3.14.0,<3.18.0", optional = true } +marshmallow-oneofschema = { version=">=3.0.1,<4.0.0", optional = true } mypy = {version = ">=0.942,<1.0", optional = true} networkx = "<2.7,>=2.6.0" numpy = ">=1.20.0,<1.22.0" @@ -161,6 +162,7 @@ service = [ "flask", "gunicorn", "marshmallow", + "marshmallow-oneofschema", "pillow", "ptvsd", "python-dotenv", @@ -218,6 +220,7 @@ all = [ "gunicorn", "isort", "marshmallow", + "marshmallow-oneofschema", "mypy", "pexpect", "pillow", @@ -361,6 +364,7 @@ module = [ "humanize", "lazy_object_proxy", "lockfile", + "marshmallow_oneofschema", "networkx.*", "pathspec", "patoolib.*", diff --git a/renku/command/run.py b/renku/command/run.py index 1d61e2dd76..c333deeb56 100644 --- a/renku/command/run.py +++ b/renku/command/run.py @@ -20,6 +20,7 @@ import os import sys from subprocess import call +from typing import cast import click @@ -32,10 +33,12 @@ from renku.core.interface.plan_gateway import IPlanGateway from renku.core.storage import check_external_storage, pull_paths_from_storage from renku.core.util.datetime8601 import local_now +from renku.core.util.git import get_git_user from renku.core.util.urls import get_slug from renku.core.workflow.plan_factory import PlanFactory from renku.domain_model.project_context import project_context from renku.domain_model.provenance.activity import Activity +from renku.domain_model.provenance.agent import Person def run_command(): @@ -56,6 +59,7 @@ def _run_command( no_output_detection, success_codes, command_line, + creators, activity_gateway: IActivityGateway, plan_gateway: IPlanGateway, ) -> PlanViewModel: @@ -197,7 +201,10 @@ def parse_explicit_definition(entries, type): if return_code not in (success_codes or {0}): raise errors.InvalidSuccessCode(return_code, success_codes=success_codes) - plan = tool.to_plan(name=name, description=description, keywords=keyword) + if not creators: + creators = [cast(Person, get_git_user(project_context.repository))] + + plan = tool.to_plan(name=name, description=description, keywords=keyword, creators=creators) activity = Activity.from_plan( plan=plan, repository=project_context.repository, diff --git a/renku/command/schema/composite_plan.py b/renku/command/schema/composite_plan.py index f80c7d4f08..5d1951e526 100644 --- a/renku/command/schema/composite_plan.py +++ b/renku/command/schema/composite_plan.py @@ -19,6 +19,7 @@ from marshmallow import EXCLUDE +from renku.command.schema.agent import PersonSchema from renku.command.schema.calamus import JsonLDSchema, Nested, fields, prov, renku, schema from renku.command.schema.parameter import ParameterLinkSchema, ParameterMappingSchema from renku.command.schema.plan import PlanSchema @@ -37,6 +38,7 @@ class Meta: description = fields.String(schema.description, load_default=None) id = fields.Id() + creators = Nested(schema.creator, PersonSchema, many=True) mappings = Nested(renku.hasMappings, [ParameterMappingSchema], many=True, load_default=None) date_created = fields.DateTime(schema.dateCreated, format="iso") invalidated_at = fields.DateTime(prov.invalidatedAtTime, format="iso") diff --git a/renku/command/schema/plan.py b/renku/command/schema/plan.py index b8b766637b..331a68d8b5 100644 --- a/renku/command/schema/plan.py +++ b/renku/command/schema/plan.py @@ -21,6 +21,7 @@ import marshmallow +from renku.command.schema.agent import PersonSchema from renku.command.schema.annotation import AnnotationSchema from renku.command.schema.calamus import JsonLDSchema, Nested, fields, oa, prov, renku, schema from renku.command.schema.parameter import CommandInputSchema, CommandOutputSchema, CommandParameterSchema @@ -41,6 +42,7 @@ class Meta: command = fields.String(renku.command, load_default=None) description = fields.String(schema.description, load_default=None) + creators = Nested(schema.creator, PersonSchema, many=True) id = fields.Id() inputs = Nested(renku.hasInputs, CommandInputSchema, many=True, load_default=None) date_created = fields.DateTime(schema.dateCreated, format="iso") diff --git a/renku/command/view_model/agent.py b/renku/command/view_model/agent.py new file mode 100644 index 0000000000..19a763bf31 --- /dev/null +++ b/renku/command/view_model/agent.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2017-2022 - Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Agent view model.""" + + +from typing import Optional + +from renku.domain_model.provenance.agent import Person + + +class PersonViewModel: + """View model for ``Person``.""" + + def __init__(self, name: str, email: str, affiliation: Optional[str]) -> None: + self.name = name + self.email = email + self.affiliation = affiliation + + @classmethod + def from_person(cls, person: Person): + """Create view model from ``Person``. + + Args: + person(Person): The person to convert. + Returns: + View model for person + """ + return cls(name=person.name, email=person.email, affiliation=person.affiliation) + + def __str__(self) -> str: + email = affiliation = "" + + if self.email: + email = f" <{self.email}>" + + if self.affiliation: + affiliation = f" [{self.affiliation}]" + + return f"{self.name}{email}{affiliation}" diff --git a/renku/command/view_model/composite_plan.py b/renku/command/view_model/composite_plan.py index b6e4c635a9..96b2d6d0a2 100644 --- a/renku/command/view_model/composite_plan.py +++ b/renku/command/view_model/composite_plan.py @@ -17,54 +17,129 @@ # limitations under the License. """CompositePlan view model.""" -from typing import List, Optional +import json +from datetime import datetime, timedelta +from typing import Dict, List, NamedTuple, Optional +from renku.command.view_model.agent import PersonViewModel +from renku.core.errors import ParameterNotFoundError +from renku.core.workflow.plan import get_latest_plan from renku.domain_model.workflow.composite_plan import CompositePlan -from renku.domain_model.workflow.parameter import ParameterLink, ParameterMapping +from renku.domain_model.workflow.parameter import ( + CommandInput, + CommandOutput, + CommandParameter, + CommandParameterBase, + ParameterLink, + ParameterMapping, +) from renku.domain_model.workflow.plan import AbstractPlan +ParameterReference = NamedTuple("ParameterReference", [("id", str), ("plan_id", str), ("type", str), ("name", str)]) + +parameter_type_mapping: Dict[type, str] = { + CommandInput: "Input", + CommandOutput: "Output", + CommandParameter: "Parameter", + ParameterMapping: "Mapping", +} + + +def _parameter_id_to_plan_id(parameter: CommandParameterBase, parent_plan: AbstractPlan, latest: bool = False): + """Extract plan id from a parameter id.""" + plan_path = parent_plan.get_parameter_path(parameter) + + if plan_path is None or len(plan_path) < 1: + raise ParameterNotFoundError(parameter.name, parent_plan.name) + + containing_plan = plan_path[-1] + + if latest: + containing_plan = get_latest_plan(containing_plan) + return containing_plan.id + + +def _parameter_to_type_string(parameter: CommandParameterBase): + """Get a type string for a parameter.""" + return parameter_type_mapping[type(parameter)] + class ParameterMappingViewModel: """View model for ``ParameterMapping``.""" - def __init__(self, name: str, default_value: str, maps_to: List[str], description: Optional[str] = None): + def __init__( + self, + name: str, + default_value: str, + maps_to: List[str], + targets: List[ParameterReference], + description: Optional[str] = None, + plan_id: Optional[str] = None, + ): self.name = name self.default_value = default_value self.maps_to = maps_to + self.targets = targets self.description = description + self.plan_id = plan_id + self.type = "Mapping" @classmethod - def from_mapping(cls, mapping: ParameterMapping): + def from_mapping(cls, mapping: ParameterMapping, plan: AbstractPlan, latest: bool = False): """Create view model from ``ParameterMapping``. Args: mapping(ParameterMapping): Mapping to create view model from. + plan(AbstractPlan): Parent plan. + latest(bool): Whether to get latest plan data. Returns: View model of mapping. """ + targets = [ + ParameterReference( + t.id, _parameter_id_to_plan_id(t, plan, latest=latest), _parameter_to_type_string(t), t.name + ) + for t in mapping.mapped_parameters + ] return cls( name=mapping.name, default_value=str(mapping.default_value), - maps_to=[m.name for m in mapping.mapped_parameters], + maps_to=[t.name for t in mapping.mapped_parameters], + targets=targets, description=mapping.description, + plan_id=plan.id, ) class ParameterLinkViewModel: """View model for ``ParameterLink``.""" - def __init__(self, source: str, sinks: List[str]): + def __init__( + self, + id: str, + plan_id: str, + source: str, + sinks: List[str], + source_entry: Optional[ParameterReference] = None, + sink_entries: Optional[List[ParameterReference]] = None, + ): + self.id = id + self.plan_id = plan_id self.source = source self.sinks = sinks + self.source_entry = source_entry + self.sink_entries = sink_entries + self.type = "Link" @classmethod - def from_link(cls, link: ParameterLink, plan: AbstractPlan): + def from_link(cls, link: ParameterLink, plan: AbstractPlan, latest: bool = False): """Create view model from ``ParameterLink``. Args: link(ParameterLink): Link to get view model from. plan(AbstractPlan): Parent plan. + latest(bool): Whether to get latest plan data. Returns: View model for link. @@ -73,14 +148,37 @@ def from_link(cls, link: ParameterLink, plan: AbstractPlan): source_path.append(link.source) source_path = ".".join(p.name for p in source_path[1:]) + source_entry = ParameterReference( + link.source.id, + _parameter_id_to_plan_id(link.source, plan, latest=latest), + _parameter_to_type_string(link.source), + link.source.name, + ) + sinks = [] + sink_entries = [] for sink in link.sinks: sink_path = plan.get_parameter_path(sink) sink_path.append(sink) sink_path = ".".join(p.name for p in sink_path[1:]) sinks.append(sink_path) - return cls(source=source_path, sinks=sinks) + sink_entries.append( + ParameterReference( + sink.id, + _parameter_id_to_plan_id(sink, plan, latest=latest), + _parameter_to_type_string(sink), + sink.name, + ) + ) + return cls( + id=link.id, + plan_id=plan.id, + source=source_path, + sinks=sinks, + source_entry=source_entry, + sink_entries=sink_entries, + ) class StepViewModel: @@ -98,25 +196,42 @@ def __init__( self, id: str, name: str, + created: datetime, mappings: List[ParameterMappingViewModel], links: List[ParameterLinkViewModel], steps: List[StepViewModel], + keywords: List[str], description: Optional[str] = None, + creators: Optional[List[PersonViewModel]] = None, + annotations: Optional[str] = None, + touches_existing_files: Optional[bool] = None, + latest: Optional[str] = None, + duration: Optional[timedelta] = None, ): self.id = id self.name = name self.description = description + self.created = created self.mappings = mappings self.links = links self.steps = steps - self.full_command = "" + self.creators = creators + self.keywords = keywords + self.annotations = annotations + self.touches_existing_files = touches_existing_files + self.latest = latest + self.type = "CompositePlan" + + if duration is not None: + self.duration = duration.seconds @classmethod - def from_composite_plan(cls, plan: CompositePlan): + def from_composite_plan(cls, plan: CompositePlan, latest: bool = False): """Create view model from ``Plan``. Args: plan(CompositePlan): Composite Plan to get view model from. + latest(bool): Whether to get latest plan data. Returns: View model of composite Plan. @@ -125,7 +240,16 @@ def from_composite_plan(cls, plan: CompositePlan): id=plan.id, name=plan.name, description=plan.description, - mappings=[ParameterMappingViewModel.from_mapping(mapping) for mapping in plan.mappings], - links=[ParameterLinkViewModel.from_link(link, plan) for link in plan.links], - steps=[StepViewModel(id=s.id, name=s.name) for s in plan.plans], + created=plan.date_created, + mappings=[ParameterMappingViewModel.from_mapping(mapping, plan, latest) for mapping in plan.mappings], + links=[ParameterLinkViewModel.from_link(link, plan, latest) for link in plan.links], + steps=[StepViewModel(id=s.id, name=s.name) for s in getattr(plan, "newest_plans", plan.plans)], + creators=[PersonViewModel.from_person(p) for p in plan.creators] if plan.creators else None, + keywords=plan.keywords, + annotations=json.dumps([{"id": a.id, "body": a.body, "source": a.source} for a in plan.annotations]) + if plan.annotations + else None, + latest=getattr(plan, "latest", None), + touches_existing_files=getattr(plan, "touches_existing_files", False), + duration=getattr(plan, "duration", None), ) diff --git a/renku/command/view_model/plan.py b/renku/command/view_model/plan.py index 2504319dee..3562126e79 100644 --- a/renku/command/view_model/plan.py +++ b/renku/command/view_model/plan.py @@ -20,8 +20,11 @@ from __future__ import annotations import json -from typing import List, Optional, Union +from datetime import datetime, timedelta +from typing import List, Optional, Union, cast +from renku.command.view_model.agent import PersonViewModel +from renku.domain_model.project_context import project_context from renku.domain_model.workflow.composite_plan import CompositePlan from renku.domain_model.workflow.parameter import CommandInput, CommandOutput, CommandParameter from renku.domain_model.workflow.plan import AbstractPlan, Plan @@ -40,6 +43,8 @@ def __init__( position: Optional[str] = None, prefix: Optional[str] = None, mapped_to: Optional[str] = None, + plan_id: Optional[str] = None, + encoding_format: Optional[str] = None, ): self.name = name self.description = description @@ -47,9 +52,13 @@ def __init__( self.position = position self.prefix = prefix self.mapped_to = mapped_to + self.plan_id = plan_id + self.type = "Input" + self.exists = (project_context.path / self.default_value).exists() + self.encoding_format = encoding_format @classmethod - def from_input(cls, input: CommandInput): + def from_input(cls, input: CommandInput, plan_id: Optional[str] = None): """Create view model from ``CommandInput``. Args: @@ -62,9 +71,11 @@ def from_input(cls, input: CommandInput): name=input.name, description=input.description, default_value=str(input.default_value), - position=str(input.position), + position=str(input.position) if input.position is not None else None, prefix=input.prefix, mapped_to=input.mapped_to.stream_type if input.mapped_to else None, + plan_id=plan_id, + encoding_format=",".join(input.encoding_format) if input.encoding_format else None, ) @@ -75,20 +86,28 @@ def __init__( self, name: str, default_value: str, + create_folder: bool, description: Optional[str] = None, position: Optional[str] = None, prefix: Optional[str] = None, mapped_to: Optional[str] = None, + plan_id: Optional[str] = None, + encoding_format: Optional[str] = None, ): self.name = name self.description = description + self.create_folder = create_folder self.default_value = default_value self.position = position self.prefix = prefix self.mapped_to = mapped_to + self.plan_id = plan_id + self.type = "Output" + self.exists = (project_context.path / self.default_value).exists() + self.encoding_format = encoding_format @classmethod - def from_output(cls, output: CommandOutput): + def from_output(cls, output: CommandOutput, plan_id: Optional[str] = None): """Create view model from ``CommandOutput``. Args: @@ -100,10 +119,13 @@ def from_output(cls, output: CommandOutput): return cls( name=output.name, description=output.description, + create_folder=output.create_folder, default_value=str(output.default_value), - position=str(output.position), + position=str(output.position) if output.position is not None else None, prefix=output.prefix, mapped_to=output.mapped_to.stream_type if output.mapped_to else None, + plan_id=plan_id, + encoding_format=",".join(output.encoding_format) if output.encoding_format else None, ) @@ -117,15 +139,18 @@ def __init__( description: Optional[str] = None, position: Optional[str] = None, prefix: Optional[str] = None, + plan_id: Optional[str] = None, ): self.name = name self.description = description self.default_value = default_value self.position = position self.prefix = prefix + self.plan_id = plan_id + self.type = "Parameter" @classmethod - def from_parameter(cls, parameter: CommandParameter): + def from_parameter(cls, parameter: CommandParameter, plan_id: Optional[str] = None): """Create view model from ``CommandParameter``. Args: @@ -138,8 +163,9 @@ def from_parameter(cls, parameter: CommandParameter): name=parameter.name, description=parameter.description, default_value=str(parameter.default_value), - position=str(parameter.position), + position=str(parameter.position) if parameter.position is not None else None, prefix=parameter.prefix, + plan_id=plan_id, ) @@ -150,23 +176,44 @@ def __init__( self, id: str, name: str, + created: datetime, + command: str, full_command: str, inputs: List[CommandInputViewModel], outputs: List[CommandOutputViewModel], parameters: List[CommandParameterViewModel], + keywords: List[str], description: Optional[str] = None, success_codes: Optional[str] = None, annotations: Optional[str] = None, + creators: Optional[List[PersonViewModel]] = None, + touches_existing_files: Optional[bool] = None, + last_executed: Optional[datetime] = None, + number_of_executions: Optional[int] = None, + latest: Optional[str] = None, + duration: Optional[timedelta] = None, ): self.id = id self.name = name self.description = description + self.created = created + self.command = command self.full_command = full_command self.success_codes = success_codes self.inputs = inputs self.outputs = outputs self.parameters = parameters self.annotations = annotations + self.creators = creators + self.keywords = keywords + self.touches_existing_files = touches_existing_files + self.last_executed = last_executed + self.number_of_executions = number_of_executions + self.latest = latest + self.type = "Plan" + + if duration is not None: + self.duration = duration.seconds @classmethod def from_plan(cls, plan: Plan): @@ -182,26 +229,37 @@ def from_plan(cls, plan: Plan): id=plan.id, name=plan.name, description=plan.description, + created=plan.date_created, full_command=" ".join(plan.to_argv(with_streams=True)), + command=plan.command, success_codes=", ".join(str(c) for c in plan.success_codes), - inputs=[CommandInputViewModel.from_input(input) for input in plan.inputs], - outputs=[CommandOutputViewModel.from_output(output) for output in plan.outputs], - parameters=[CommandParameterViewModel.from_parameter(param) for param in plan.parameters], + inputs=[CommandInputViewModel.from_input(input, plan.id) for input in plan.inputs], + outputs=[CommandOutputViewModel.from_output(output, plan.id) for output in plan.outputs], + parameters=[CommandParameterViewModel.from_parameter(param, plan.id) for param in plan.parameters], annotations=json.dumps([{"id": a.id, "body": a.body, "source": a.source} for a in plan.annotations]) if plan.annotations else None, + creators=[PersonViewModel.from_person(p) for p in plan.creators] if plan.creators else None, + keywords=plan.keywords, + touches_existing_files=getattr(plan, "touches_existing_files", False), + latest=getattr(plan, "latest", None), + last_executed=getattr(plan, "last_executed", None), + number_of_executions=getattr(plan, "number_of_executions", None), + duration=getattr(plan, "duration", None), ) -def plan_view(workflow: AbstractPlan) -> Union[CompositePlanViewModel, PlanViewModel]: +def plan_view(workflow: AbstractPlan, latest: bool = False) -> Union[CompositePlanViewModel, PlanViewModel]: """Convert an ``CompositePlan`` or ``Plan`` to a ``ViewModel``. Args: workflow(AbstractPlan): Plan to convert. + latest(bool): Whether to get latest plan data. Returns: View model for converted Plan. """ + if isinstance(workflow, CompositePlan): return CompositePlanViewModel.from_composite_plan(workflow) - return PlanViewModel.from_plan(workflow) + return PlanViewModel.from_plan(cast(Plan, workflow)) diff --git a/renku/core/errors.py b/renku/core/errors.py index 90cda5c223..011876b484 100644 --- a/renku/core/errors.py +++ b/renku/core/errors.py @@ -573,13 +573,22 @@ def __init__(self, mapping: str, workflow: str): class ChildWorkflowNotFoundError(RenkuException): - """Raised when a parameter reference cannot be resolved to a parameter.""" + """Raised when a child could not be found on a composite workflow.""" def __init__(self, child: str, workflow: str): """Embed exception and build a custom message.""" super().__init__(f"Cannot find child step '{child}' on workflow {workflow}") +class WorkflowNotFoundError(RenkuException): + """Raised when a workflow could not be found.""" + + def __init__(self, name_or_id: str): + """Embed exception and build a custom message.""" + self.name_or_id = name_or_id + super().__init__(f"The specified workflow '{name_or_id}' cannot be found.") + + class ParameterLinkError(RenkuException): """Raised when a parameter link cannot be created.""" diff --git a/renku/core/workflow/plan.py b/renku/core/workflow/plan.py index a04d0ae1ec..d8ebbf938b 100644 --- a/renku/core/workflow/plan.py +++ b/renku/core/workflow/plan.py @@ -18,33 +18,47 @@ """Plan management.""" import itertools -from datetime import datetime +from datetime import datetime, timedelta from pathlib import Path -from typing import Dict, Generator, List, Optional, Union, cast +from typing import Dict, Generator, List, Optional, Set, Tuple, Union, cast, overload from renku.command.command_builder import inject from renku.command.format.workflow import WORKFLOW_FORMATS from renku.command.view_model.activity_graph import ActivityGraphViewModel -from renku.command.view_model.composite_plan import CompositePlanViewModel -from renku.command.view_model.plan import plan_view from renku.core import errors from renku.core.interface.activity_gateway import IActivityGateway from renku.core.interface.plan_gateway import IPlanGateway from renku.core.interface.project_gateway import IProjectGateway from renku.core.util import communication from renku.core.util.datetime8601 import local_now +from renku.core.util.git import get_git_user from renku.core.util.os import are_paths_related, get_relative_paths, safe_read_yaml +from renku.core.util.util import NO_VALUE, NoValueType from renku.core.workflow.concrete_execution_graph import ExecutionGraph from renku.core.workflow.value_resolution import CompositePlanValueResolver, ValueResolver from renku.domain_model.project_context import project_context from renku.domain_model.provenance.activity import Activity +from renku.domain_model.provenance.agent import Person from renku.domain_model.provenance.annotation import Annotation from renku.domain_model.workflow.composite_plan import CompositePlan from renku.domain_model.workflow.plan import AbstractPlan, Plan +from renku.infrastructure.immutable import DynamicProxy + + +@overload +def get_latest_plan(plan: None = ..., plan_gateway: IPlanGateway = ...) -> None: # noqa: D103 + ... + + +@overload +def get_latest_plan(plan: AbstractPlan, plan_gateway: IPlanGateway = ...) -> AbstractPlan: # noqa: D103 + ... @inject.autoparams() -def get_latest_plan(plan: Optional[AbstractPlan], plan_gateway: IPlanGateway) -> Optional[AbstractPlan]: +def get_latest_plan( + plan: Optional[AbstractPlan], plan_gateway: IPlanGateway +) -> Union[Optional[AbstractPlan], AbstractPlan]: """Return the latest version of a given plan in its derivative chain.""" if plan is None: return None @@ -100,6 +114,8 @@ def list_workflows(plan_gateway: IPlanGateway, format: str, columns: List[str]): Returns: List of workflows formatted by ``format``. """ + from renku.command.view_model.plan import plan_view + workflows = plan_gateway.get_newest_plans_by_names() if format not in WORKFLOW_FORMATS: @@ -111,22 +127,61 @@ def list_workflows(plan_gateway: IPlanGateway, format: str, columns: List[str]): return WORKFLOW_FORMATS[format](list(map(lambda x: plan_view(x), workflows.values())), columns=columns) -@inject.autoparams() -def show_workflow(name_or_id: str, plan_gateway: IPlanGateway): +@inject.autoparams("plan_gateway", "activity_gateway") +def show_workflow( + name_or_id: str, plan_gateway: IPlanGateway, activity_gateway: IActivityGateway, with_metadata: bool = False +): """Show the details of a workflow. Args: name_or_id(str): Name or id of the Plan to show. plan_gateway(IPlanGateway): The injected Plan gateway. + activity_gateway(IActivityGateway): The injected Activity gateway. + with_metadata(bool): Whether to get additional calculated metadata for the plan. Returns: Details of the Plan. """ - workflow = plan_gateway.get_by_name_or_id(name_or_id) + from renku.command.view_model.plan import plan_view + + workflow = cast(Union[Plan, CompositePlan], plan_gateway.get_by_name_or_id(name_or_id)) if is_plan_removed(workflow): - raise errors.ParameterError(f"The specified workflow '{name_or_id}' cannot be found.") + raise errors.WorkflowNotFoundError(name_or_id) + + if with_metadata: + activities = activity_gateway.get_all_activities() + activity_map = _reverse_activity_plan_map(activities) + plan_chain = list(get_derivative_chain(workflow)) + relevant_activities = [a for p in plan_chain for a in activity_map.get(p.id, [])] + touches_files_cache: Dict[str, bool] = {} + duration_cache: Dict[str, Optional[timedelta]] = {} + touches_existing_files = _check_workflow_touches_existing_files(workflow, touches_files_cache, activity_map) + + if isinstance(workflow, Plan): + + num_executions = 0 + last_execution = None + + for activity in relevant_activities: + num_executions += 1 + + if not last_execution or last_execution < activity.ended_at_time: + last_execution = activity.ended_at_time + + workflow = cast(Plan, DynamicProxy(workflow)) + workflow.number_of_executions = num_executions + workflow.last_executed = last_execution + workflow.touches_existing_files = touches_existing_files + workflow.latest = plan_chain[0].id + workflow.duration = _get_plan_duration(workflow, duration_cache, activity_map) + else: + workflow = cast(CompositePlan, DynamicProxy(workflow)) + workflow.touches_existing_files = touches_existing_files + workflow.latest = plan_chain[0].id + workflow.duration = _get_plan_duration(workflow, duration_cache, activity_map) + workflow.newest_plans = [get_latest_plan(p) for p in workflow.plans] - return plan_view(workflow) + return plan_view(cast(AbstractPlan, workflow), latest=with_metadata) @inject.autoparams("plan_gateway") @@ -183,6 +238,8 @@ def edit_workflow( map_params: List[str], rename_params: List[str], describe_params: List[str], + creators: Union[List[Person], NoValueType], + keywords: Union[List[str], NoValueType], plan_gateway: IPlanGateway, custom_metadata: Optional[Dict] = None, ): @@ -196,12 +253,15 @@ def edit_workflow( map_params(List[str]): New mappings for Plan. rename_params(List[str]): New names for parameters. describe_params(List[str]): New descriptions for parameters. + creators(Union[List[Person], NoValueType]): Creators of the workflow. + keywords(Union[List[str], NoValueType]): New keywords for the workflow. plan_gateway(IPlanGateway): Injected plan gateway. custom_metadata(Dict, optional): Custom JSON-LD metadata (Default value = None). Returns: Details of the modified Plan. """ + from renku.command.view_model.plan import plan_view derived_from = plan_gateway.get_by_name_or_id(name) @@ -216,17 +276,29 @@ def edit_workflow( and not rename_params and not describe_params and not custom_metadata + and creators == NO_VALUE + and keywords == NO_VALUE ): # NOTE: Nothing to do return plan_view(derived_from) - workflow = derived_from.derive() + git_creator = cast(Person, get_git_user(project_context.repository)) + workflow = derived_from.derive(creator=git_creator) if new_name: workflow.name = new_name if description: workflow.description = description + if creators != NO_VALUE: + workflow.creators = cast(List[Person], creators) + + if all(c.email != git_creator.email for c in workflow.creators): + workflow.creators.append(git_creator) + + if keywords != NO_VALUE: + workflow.keywords = cast(List[str], keywords) + if isinstance(workflow, Plan): if custom_metadata: existing_metadata = [a for a in workflow.annotations if a.source != "renku"] @@ -281,6 +353,7 @@ def compose_workflow( steps: List[str], sources: List[str], sinks: List[str], + creators: Optional[List[Person]], activity_gateway: IActivityGateway, plan_gateway: IPlanGateway, project_gateway: IProjectGateway, @@ -302,6 +375,7 @@ def compose_workflow( steps(List[str]): Child steps to include. sources(List[str]): Starting files when automatically detecting child Plans. sinks(List[str]): Ending files when automatically detecting child Plans. + creators(Optional[List[Person]]): Creator(s) of the composite plan. activity_gateway(IActivityGateway): Injected activity gateway. plan_gateway(IPlanGateway): Injected plan gateway. project_gateway(IProjectGateway): Injected project gateway. @@ -309,6 +383,7 @@ def compose_workflow( Returns: The newly created ``CompositePlan``. """ + from renku.command.view_model.composite_plan import CompositePlanViewModel from renku.core.workflow.activity import get_activities_until_paths, sort_activities if plan_gateway.get_by_name(name): @@ -354,12 +429,16 @@ def compose_workflow( child_workflows.append(child_workflow) plan_activities.append((i, activity.plan_with_values)) + if not creators: + creators = [cast(Person, get_git_user(project_context.repository))] + plan = CompositePlan( description=description, id=CompositePlan.generate_id(), keywords=keywords, name=name, plans=child_workflows, + creators=creators, project_id=project_gateway.get_project().id, ) @@ -638,3 +717,141 @@ def get_composite_plans_by_child(plan: AbstractPlan, plan_gateway: IPlanGateway) composites_containing_child = [c for c in composites if {p.id for p in c.plans}.intersection(derivatives)] return composites_containing_child + + +@inject.autoparams("activity_gateway", "plan_gateway") +def get_plans_with_metadata(activity_gateway: IActivityGateway, plan_gateway: IPlanGateway) -> List[AbstractPlan]: + """Get all plans in the project with additional metadata. + + Adds information about last execution, number of executions and whether the plan was used to create files + currently existing in the project. + """ + + all_activities = activity_gateway.get_all_activities() + activity_map = _reverse_activity_plan_map(list(all_activities)) + latest_plan_chains: Set[Tuple[AbstractPlan]] = set( + cast(Tuple[AbstractPlan], tuple(get_derivative_chain(p))) + for p in plan_gateway.get_newest_plans_by_names().values() + ) + + result: Dict[str, Union[Plan, CompositePlan]] = {} + touches_file_cache: Dict[str, bool] = {} + duration_cache: Dict[str, Optional[timedelta]] = {} + + # check which plans where involved in using/creating existing files + for plan_chain in latest_plan_chains: + latest_plan = cast(Union[Plan, CompositePlan], DynamicProxy(plan_chain[0])) + latest_plan.touches_existing_files = False + latest_plan.number_of_executions = 0 + latest_plan.created = latest_plan.date_created + latest_plan.last_executed = None + latest_plan.children = [] + duration = _get_plan_duration(latest_plan, duration_cache, activity_map) + + if duration is not None: + latest_plan.duration = duration.seconds + + if isinstance(plan_chain[0], Plan): + for activity in activity_map.get(latest_plan.id, []): + if not latest_plan.last_executed or latest_plan.last_executed < activity.ended_at_time: + latest_plan.last_executed = activity.ended_at_time + + latest_plan.number_of_executions += 1 + + latest_plan.touches_existing_files = _check_workflow_touches_existing_files( + latest_plan, touches_file_cache, activity_map + ) + latest_plan.type = "Plan" + else: + latest_plan.number_of_executions = None + latest_plan.type = "CompositePlan" + latest_plan.children = [get_latest_plan(p).id for p in latest_plan.plans] + latest_plan.touches_existing_files = _check_workflow_touches_existing_files( + latest_plan, touches_file_cache, activity_map + ) + + result[latest_plan.id] = latest_plan + + return list(result.values()) # type: ignore + + +def _reverse_activity_plan_map(activities: List[Activity], latest: bool = True) -> Dict[str, Set[Activity]]: + """Create a map from plan id to relevant activities.""" + result: Dict[str, Set[Activity]] = {} + + for activity in activities: + plan = activity.association.plan + + if latest: + plan = get_latest_plan(plan) + + if plan.id not in result: + result[plan.id] = {activity} + else: + result[plan.id].add(activity) + + return result + + +def _check_workflow_touches_existing_files( + workflow: Union[Plan, CompositePlan], + cache: Dict[str, bool], + activity_map: Dict[str, Set[Activity]], + latest: bool = True, +) -> bool: + """Check if a workflow or one of its children touches existing files.""" + if latest: + workflow = get_latest_plan(workflow) + + if workflow.id in cache: + return cache[workflow.id] + + if isinstance(workflow, Plan): + for activity in activity_map.get(workflow.id, []): + for output in activity.generations: + if (project_context.path / output.entity.path).exists(): + return cache.setdefault(workflow.id, True) + else: + for child in workflow.plans: + if _check_workflow_touches_existing_files(child, cache, activity_map): + return cache.setdefault(workflow.id, True) + + return cache.setdefault(workflow.id, False) + + +def _get_plan_duration( + workflow: Union[Plan, CompositePlan], + cache: Dict[str, Optional[timedelta]], + activity_map: Dict[str, Set[Activity]], + latest: bool = True, +) -> Optional[timedelta]: + if latest: + workflow = get_latest_plan(workflow) + + if workflow.id in cache: + return cache[workflow.id] + + if isinstance(workflow, Plan): + times = [] + for activity in activity_map.get(workflow.id, []): + times.append(activity.ended_at_time - activity.started_at_time) + + if not times: + return cache.setdefault(workflow.id, None) + + return cache.setdefault(workflow.id, sum(times, timedelta(0)) / len(times)) + + else: + total = timedelta(0) + found = False + for child in workflow.plans: + child_time = _get_plan_duration(child, cache, activity_map) + + if child_time is not None: + total += child_time + found = True + + if found: + return cache.setdefault(workflow.id, total) + + return cache.setdefault(workflow.id, None) diff --git a/renku/core/workflow/plan_factory.py b/renku/core/workflow/plan_factory.py index a973ee5d59..d5e02329d8 100644 --- a/renku/core/workflow/plan_factory.py +++ b/renku/core/workflow/plan_factory.py @@ -41,6 +41,7 @@ from renku.core.workflow.types import PATH_OBJECTS, Directory, File from renku.domain_model.datastructures import DirectoryTree from renku.domain_model.project_context import project_context +from renku.domain_model.provenance.agent import Person from renku.domain_model.workflow.parameter import ( DIRECTORY_MIME_TYPE, CommandInput, @@ -699,6 +700,7 @@ def to_plan( name: Optional[str] = None, description: Optional[str] = None, keywords: Optional[List[str]] = None, + creators: Optional[List[Person]] = None, ) -> Plan: """Return an instance of ``Plan`` based on this factory.""" plan = Plan( @@ -712,6 +714,7 @@ def to_plan( parameters=self.parameters, project_id=project_gateway.get_project().id, success_codes=self.success_codes, + creators=creators, ) pm = get_plugin_manager() diff --git a/renku/domain_model/workflow/composite_plan.py b/renku/domain_model/workflow/composite_plan.py index 22c7f2eef6..c3040110d2 100644 --- a/renku/domain_model/workflow/composite_plan.py +++ b/renku/domain_model/workflow/composite_plan.py @@ -25,6 +25,8 @@ from renku.core import errors from renku.core.util.datetime8601 import local_now +from renku.domain_model.provenance.agent import Person +from renku.domain_model.provenance.annotation import Annotation from renku.domain_model.workflow.parameter import ( CommandInput, CommandOutput, @@ -39,6 +41,8 @@ class CompositePlan(AbstractPlan): """A plan containing child plans.""" + annotations: List[Annotation] = list() + def __init__( self, *, @@ -53,6 +57,8 @@ def __init__( name: str, plans: List[Union["CompositePlan", Plan]], project_id: Optional[str] = None, + annotations: Optional[List[Annotation]] = None, + creators: Optional[List[Person]] = None, ): super().__init__( derived_from=derived_from, @@ -63,7 +69,9 @@ def __init__( keywords=keywords, name=name, project_id=project_id, + creators=creators, ) + self.annotations: List[Annotation] = annotations or [] self.plans: List[Union["CompositePlan", Plan]] = plans self.mappings: List[ParameterMapping] = mappings or [] @@ -365,7 +373,7 @@ def resolve_direct_reference(self, reference: str) -> CommandParameterBase: def _get_default_name(self) -> str: return uuid4().hex[:MAX_GENERATED_NAME_LENGTH] - def derive(self) -> "CompositePlan": + def derive(self, creator: Optional[Person] = None) -> "CompositePlan": """Create a new ``CompositePlan`` that is derived from self.""" derived = copy.copy(self) derived.derived_from = self.id @@ -374,6 +382,10 @@ def derive(self) -> "CompositePlan": derived.mappings = self.mappings.copy() derived.links = self.links.copy() derived.assign_new_id() + + if creator and hasattr(creator, "email") and not any(c for c in self.creators if c.email == creator.email): + self.creators.append(creator) + return derived def is_derivation(self) -> bool: diff --git a/renku/domain_model/workflow/plan.py b/renku/domain_model/workflow/plan.py index c1a2eeeb9c..338a925fe8 100644 --- a/renku/domain_model/workflow/plan.py +++ b/renku/domain_model/workflow/plan.py @@ -30,6 +30,7 @@ from renku.core import errors from renku.core.util.datetime8601 import local_now +from renku.domain_model.provenance.agent import Person from renku.domain_model.provenance.annotation import Annotation from renku.domain_model.workflow.parameter import CommandInput, CommandOutput, CommandParameter, CommandParameterBase from renku.infrastructure.database import Persistent @@ -40,6 +41,8 @@ class AbstractPlan(Persistent, ABC): """Abstract base class for all plans.""" + creators: List[Person] = list() + def __init__( self, *, @@ -51,6 +54,7 @@ def __init__( name: Optional[str] = None, project_id: Optional[str] = None, derived_from: Optional[str] = None, + creators: Optional[List[Person]] = None, ): self.description: Optional[str] = description self.id: str = id @@ -58,6 +62,9 @@ def __init__( self.invalidated_at: Optional[datetime] = invalidated_at self.keywords: List[str] = keywords or [] + if creators: + self.creators = creators + self.project_id: Optional[str] = project_id self.derived_from: Optional[str] = derived_from @@ -132,7 +139,7 @@ def find_parameter_workflow(self, parameter: CommandParameterBase) -> Optional[" """Return the workflow a parameter belongs to.""" raise NotImplementedError() - def derive(self) -> "AbstractPlan": + def derive(self, creator: Optional[Person] = None) -> "AbstractPlan": """Create a new ``AbstractPlan`` that is derived from self.""" raise NotImplementedError() @@ -173,6 +180,7 @@ def __init__( outputs: Optional[List[CommandOutput]] = None, success_codes: Optional[List[int]] = None, annotations: Optional[List[Annotation]] = None, + creators: Optional[List[Person]] = None, ): self.command: str = command self.inputs: List[CommandInput] = inputs or [] @@ -189,6 +197,7 @@ def __init__( name=name, project_id=project_id, derived_from=derived_from, + creators=creators, ) # NOTE: Validate plan @@ -299,7 +308,7 @@ def assign_new_id(self): for a in self.parameters: a.id = a.id.replace(current_uuid, new_uuid) - def derive(self) -> "Plan": + def derive(self, creator: Optional[Person] = None) -> "Plan": """Create a new ``Plan`` that is derived from self.""" derived = copy.copy(self) derived.derived_from = self.id @@ -310,6 +319,10 @@ def derive(self) -> "Plan": derived.outputs = self.outputs.copy() derived.success_codes = self.success_codes.copy() derived.assign_new_id() + + if creator and hasattr(creator, "email") and not any(c for c in self.creators if c.email == creator.email): + self.creators.append(creator) + return derived def is_derivation(self) -> bool: diff --git a/renku/infrastructure/gateway/plan_gateway.py b/renku/infrastructure/gateway/plan_gateway.py index 86f232365d..e9fd6c352a 100644 --- a/renku/infrastructure/gateway/plan_gateway.py +++ b/renku/infrastructure/gateway/plan_gateway.py @@ -41,7 +41,7 @@ def get_by_name_or_id(self, name_or_id: str) -> AbstractPlan: workflow = self.get_by_id(name_or_id) or self.get_by_name(name_or_id) if not workflow: - raise errors.ParameterError(f'The specified workflow "{name_or_id}" cannot be found.') + raise errors.WorkflowNotFoundError(name_or_id) return workflow def list_by_name(self, starts_with: str, ends_with: str = None) -> List[str]: diff --git a/renku/infrastructure/immutable.py b/renku/infrastructure/immutable.py index 4530f1b88a..0323340674 100644 --- a/renku/infrastructure/immutable.py +++ b/renku/infrastructure/immutable.py @@ -114,6 +114,13 @@ def __init__(self, subject, update=True): def __getattr__(self, name): return getattr(self._subject, name) + def __getattribute__(self, name: str): + if name == "__class__": + # NOTE: Makes isinstance() checks work with proxies + return object.__getattribute__(self._subject, "__class__") + + return object.__getattribute__(self, name) + def __setattr__(self, name, value): if name == "_subject" or not hasattr(self._subject, name): super().__setattr__(name, value) diff --git a/renku/ui/cli/dataset.py b/renku/ui/cli/dataset.py index 58ac3421d0..527307b1bf 100644 --- a/renku/ui/cli/dataset.py +++ b/renku/ui/cli/dataset.py @@ -651,7 +651,7 @@ def create(name, title, description, creators, metadata, keyword, storage, datad default=[NO_VALUE], multiple=True, type=click.UNPROCESSED, - help="Creator's name, email, and affiliation. " "Accepted format is 'Forename Surname [affiliation]'.", + help="Creator's name, email, and affiliation. Accepted format is 'Forename Surname [affiliation]'.", ) @click.option( "-m", diff --git a/renku/ui/cli/run.py b/renku/ui/cli/run.py index 72766f50bf..c519f44a4b 100644 --- a/renku/ui/cli/run.py +++ b/renku/ui/cli/run.py @@ -307,6 +307,14 @@ @click.option("--isolation", is_flag=True, default=False, help="Invoke the given command in isolation.") @click.argument("command_line", nargs=-1, required=True, type=click.UNPROCESSED) @click.option("--verbose", is_flag=True, default=False, help="Print generated plan after the execution.") +@click.option( + "--creator", + "creators", + default=None, + multiple=True, + type=click.UNPROCESSED, + help="Creator's name, email, and affiliation. Accepted format is 'Forename Surname [affiliation]'.", +) def run( name, description, @@ -321,9 +329,11 @@ def run( isolation, command_line, verbose, + creators, ): """Tracking work on a specific problem.""" from renku.command.run import run_command + from renku.core.util.metadata import construct_creators from renku.ui.cli.utils.terminal import print_plan communicator = ClickCallback() @@ -332,6 +342,9 @@ def run( if isolation: command = command.with_git_isolation() + if creators: + creators, _ = construct_creators(creators) + result = ( command.with_communicator(communicator) .build() @@ -347,6 +360,7 @@ def run( no_output_detection=no_output_detection, success_codes=success_codes, command_line=command_line, + creators=creators, ) ) diff --git a/renku/ui/cli/utils/terminal.py b/renku/ui/cli/utils/terminal.py index 243dff509a..8122e1f21f 100644 --- a/renku/ui/cli/utils/terminal.py +++ b/renku/ui/cli/utils/terminal.py @@ -28,6 +28,7 @@ from renku.ui.cli.utils import color if TYPE_CHECKING: + from renku.command.view_model.composite_plan import CompositePlanViewModel from renku.command.view_model.plan import PlanViewModel style_header = functools.partial(click.style, bold=True, fg=color.YELLOW) @@ -56,12 +57,34 @@ def show_text_with_pager(text: str) -> None: click.echo(text) -def print_markdown(text: str): +def print_markdown(text: str, err: bool = False): """Print markdown text to console.""" from rich.console import Console from rich.markdown import Markdown - Console().print(Markdown(text)) + Console(stderr=err).print(Markdown(text)) + + +def print_key_value(key, value, print_empty: bool = True, err: bool = False): + """Print a key value pair.""" + if print_empty or value: + click.echo(style_key(key) + style_value(value), err=err) + + +def print_key(key, err: bool = False): + """Print a key.""" + click.echo(style_key(key), err=err) + + +def print_value(value, err: bool = False): + """Print a value.""" + click.echo(style_value(value), err=err) + + +def print_description(description, err: bool = False): + """Print a description.""" + if description: + click.echo(f"\t\t{description}", err=err) def print_plan(plan: "PlanViewModel", err: bool = False): @@ -71,56 +94,89 @@ def print_plan(plan: "PlanViewModel", err: bool = False): plan(PlanViewModel): The plan to print. err(bool,optional): Print to ``stderr`` (Default value = False). """ + print_key_value("Id: ", plan.id, err=err) + print_key_value("Name: ", plan.name, err=err) - def print_key_value(key, value, print_empty: bool = True): - if print_empty or value: - click.echo(style_key(key) + style_value(value), err=err) - - def print_key(key): - click.echo(style_key(key), err=err) - - def print_value(value): - click.echo(style_value(value), err=err) - - def print_description(description): - if description: - click.echo(f"\t\t{description}", err=err) + if plan.description: + print_markdown(plan.description, err=err) - print_key_value("Id: ", plan.id) - print_key_value("Name: ", plan.name) + if plan.creators: + print_key_value("Creators: ", ", ".join(str(c) for c in plan.creators), err=err) - if plan.description: - print_markdown(plan.description) + if plan.keywords: + print_key_value("Keywords: ", ", ".join(k for k in plan.keywords), err=err) - print_key_value("Command: ", plan.full_command) - print_key_value("Success Codes: ", plan.success_codes) + print_key_value("Command: ", plan.full_command, err=err) + print_key_value("Success Codes: ", plan.success_codes, err=err) if plan.annotations: - print_key_value("Annotations:\n", plan.annotations) + print_key_value("Annotations:\n", plan.annotations, err=err) if plan.inputs: - print_key("Inputs:") + print_key("Inputs:", err=err) for run_input in plan.inputs: - print_value(f"\t- {run_input.name}:") - print_description(run_input.description) - print_key_value("\t\tDefault Value: ", run_input.default_value) - print_key_value("\t\tPosition: ", run_input.position, print_empty=False) - print_key_value("\t\tPrefix: ", run_input.prefix, print_empty=False) + print_value(f"\t- {run_input.name}:", err=err) + print_description(run_input.description, err=err) + print_key_value("\t\tDefault Value: ", run_input.default_value, err=err) + print_key_value("\t\tPosition: ", run_input.position, print_empty=False, err=err) + print_key_value("\t\tPrefix: ", run_input.prefix, print_empty=False, err=err) if plan.outputs: print_key("Outputs:") for run_output in plan.outputs: - print_value(f"\t- {run_output.name}:") - print_description(run_output.description) - print_key_value("\t\tDefault Value: ", run_output.default_value) - print_key_value("\t\tPosition: ", run_output.position, print_empty=False) - print_key_value("\t\tPrefix: ", run_output.prefix, print_empty=False) + print_value(f"\t- {run_output.name}:", err=err) + print_description(run_output.description, err=err) + print_key_value("\t\tDefault Value: ", run_output.default_value, err=err) + print_key_value("\t\tPosition: ", run_output.position, print_empty=False, err=err) + print_key_value("\t\tPrefix: ", run_output.prefix, print_empty=False, err=err) if plan.parameters: - print_key("Parameters:") + print_key("Parameters:", err=err) for run_parameter in plan.parameters: - print_value(f"\t- {run_parameter.name}:") - print_description(run_parameter.description) - print_key_value("\t\tDefault Value: ", run_parameter.default_value) - print_key_value("\t\tPosition: ", run_parameter.position, print_empty=False) - print_key_value("\t\tPrefix: ", run_parameter.prefix, print_empty=False) + print_value(f"\t- {run_parameter.name}:", err=err) + print_description(run_parameter.description, err=err) + print_key_value("\t\tDefault Value: ", run_parameter.default_value, err=err) + print_key_value("\t\tPosition: ", run_parameter.position, print_empty=False, err=err) + print_key_value("\t\tPrefix: ", run_parameter.prefix, print_empty=False, err=err) + + +def print_composite_plan(composite_plan: "CompositePlanViewModel"): + """Print a CompositePlan to stdout.""" + + print_key_value("Id: ", composite_plan.id) + print_key_value("Name: ", composite_plan.name) + + if composite_plan.creators: + print_key_value("Creators: ", ", ".join(str(c) for c in composite_plan.creators)) + + if composite_plan.keywords: + print_key_value("Keywords: ", ", ".join(k for k in composite_plan.keywords)) + + if composite_plan.description: + print_markdown(composite_plan.description) + + print_key("Steps:") + for step in composite_plan.steps: + print_value(f"\t- {step.name}:") + print_key_value("\t\tId: ", f"{step.id}") + + if composite_plan.mappings: + print_key("Mappings:") + for mapping in composite_plan.mappings: + print_value(f"\t- {mapping.name}:") + + if mapping.description: + print_description(mapping.description) + + print_key_value("\t\tDefault Value: ", mapping.default_value) + print_key("\t\tMaps to: ") + for maps_to in mapping.maps_to: + print_value(f"\t\t\t{maps_to}") + + if composite_plan.links: + print_key("Links: ") + for link in composite_plan.links: + print_key_value("\t- From: ", link.source) + print_key("\t\t To: ") + for sink in link.sinks: + print_value(f"\t\t\t- {sink}") diff --git a/renku/ui/cli/workflow.py b/renku/ui/cli/workflow.py index cd93cd0170..2f019bf5ab 100644 --- a/renku/ui/cli/workflow.py +++ b/renku/ui/cli/workflow.py @@ -722,7 +722,6 @@ import shutil import sys from pathlib import Path -from typing import TYPE_CHECKING import click from lazy_object_proxy import Proxy @@ -732,13 +731,11 @@ from renku.command.util import ERROR from renku.command.view_model.activity_graph import ACTIVITY_GRAPH_COLUMNS from renku.core import errors +from renku.core.util.util import NO_VALUE from renku.ui.cli.utils.callback import ClickCallback from renku.ui.cli.utils.plugins import available_workflow_providers, supported_formats from renku.ui.cli.utils.terminal import show_text_with_pager -if TYPE_CHECKING: - from renku.command.view_model.composite_plan import CompositePlanViewModel - def _complete_workflows(ctx, param, incomplete): from renku.command.workflow import search_workflows_command @@ -750,46 +747,6 @@ def _complete_workflows(ctx, param, incomplete): return [] -def _print_composite_plan(composite_plan: "CompositePlanViewModel"): - """Print a CompositePlan to stdout.""" - from renku.ui.cli.utils.terminal import print_markdown - - click.echo(click.style("Id: ", bold=True, fg=color.MAGENTA) + click.style(composite_plan.id, bold=True)) - click.echo(click.style("Name: ", bold=True, fg=color.MAGENTA) + click.style(composite_plan.name, bold=True)) - - if composite_plan.description: - print_markdown(composite_plan.description) - - click.echo(click.style("Steps: ", bold=True, fg=color.MAGENTA)) - for step in composite_plan.steps: - click.echo(click.style(f"\t- {step.name}:", bold=True)) - click.echo(click.style("\t\tId: ", bold=True, fg=color.MAGENTA) + click.style(f"{step.id}", bold=True)) - - if composite_plan.mappings: - click.echo(click.style("Mappings: ", bold=True, fg=color.MAGENTA)) - for mapping in composite_plan.mappings: - click.echo(click.style(f"\t- {mapping.name}:", bold=True)) - - if mapping.description: - click.echo(click.style(f"\t\t{mapping.description}")) - - click.echo( - click.style("\t\tDefault Value: ", bold=True, fg=color.MAGENTA) - + click.style(mapping.default_value, bold=True) - ) - click.echo(click.style("\tMaps to: ", bold=True, fg=color.MAGENTA)) - for maps_to in mapping.maps_to: - click.echo(click.style(f"\t\t{maps_to}", bold=True)) - - if composite_plan.links: - click.echo(click.style("Links: ", bold=True, fg=color.MAGENTA)) - for link in composite_plan.links: - click.echo(click.style("\t- From: ", bold=True, fg=color.MAGENTA) + click.style(link.source, bold=True)) - click.echo(click.style("\t\t To: ", bold=True, fg=color.MAGENTA)) - for sink in link.sinks: - click.echo(click.style(f"\t\t- {sink}", bold=True)) - - @click.group() def workflow(): """Workflow commands.""" @@ -823,7 +780,7 @@ def show(name_or_id): """Show details for workflow .""" from renku.command.view_model.plan import PlanViewModel from renku.command.workflow import show_workflow_command - from renku.ui.cli.utils.terminal import print_plan + from renku.ui.cli.utils.terminal import print_composite_plan, print_plan plan = show_workflow_command().build().execute(name_or_id=name_or_id).output @@ -831,7 +788,7 @@ def show(name_or_id): if isinstance(plan, PlanViewModel): print_plan(plan) else: - _print_composite_plan(plan) + print_composite_plan(plan) else: click.secho(ERROR + f"Workflow '{name_or_id}' not found.") @@ -874,6 +831,14 @@ def remove(name, force): multiple=True, help="End a composite plan at this file as output.", ) +@click.option( + "--creator", + "creators", + default=None, + multiple=True, + type=click.UNPROCESSED, + help="Creator's name, email, and affiliation. Accepted format is 'Forename Surname [affiliation]'.", +) @click.argument("name", required=True) @click.argument("steps", nargs=-1, type=click.UNPROCESSED, shell_complete=_complete_workflows) def compose( @@ -890,11 +855,14 @@ def compose( keyword, sources, sinks, + creators, name, steps, ): """Create a composite workflow consisting of multiple steps.""" from renku.command.workflow import compose_workflow_command + from renku.core.util.metadata import construct_creators + from renku.ui.cli.utils.terminal import print_composite_plan if (sources or sinks) and steps: click.secho(ERROR + "--from/--to cannot be used at the same time as passing run/step names.") @@ -903,6 +871,9 @@ def compose( click.secho(ERROR + "Either --from/--to passing run/step names is required.") exit(1) + if creators: + creators, _ = construct_creators(creators) + if map_all: map_inputs = map_outputs = map_params = True @@ -924,11 +895,12 @@ def compose( steps=steps, sources=sources, sinks=sinks, + creators=creators, ) ) if not result.error: - _print_composite_plan(result.output) + print_composite_plan(result.output) @workflow.command() @@ -973,11 +945,49 @@ def compose( type=click.Path(exists=True, dir_okay=False), help="Custom metadata to be associated with the workflow.", ) -def edit(workflow_name, name, description, set_params, map_params, rename_params, describe_params, metadata): +@click.option( + "--creator", + "creators", + default=[NO_VALUE], + multiple=True, + type=click.UNPROCESSED, + help="Creator's name, email, and affiliation. Accepted format is 'Forename Surname [affiliation]'.", +) +@click.option( + "--keyword", + "keywords", + default=[NO_VALUE], + type=click.UNPROCESSED, + multiple=True, + help="List of keywords for the workflow.", +) +def edit( + workflow_name, + name, + description, + set_params, + map_params, + rename_params, + describe_params, + metadata, + creators, + keywords, +): """Edit workflow details.""" from renku.command.view_model.plan import PlanViewModel from renku.command.workflow import edit_workflow_command - from renku.ui.cli.utils.terminal import print_plan + from renku.core.util.metadata import construct_creators + from renku.ui.cli.utils.terminal import print_composite_plan, print_plan + + if list(creators) == [NO_VALUE]: + creators = NO_VALUE + + keywords = list(keywords) + if keywords == [NO_VALUE]: + keywords = NO_VALUE + + if creators and creators is not NO_VALUE: + creators, _ = construct_creators(creators, ignore_email=True) custom_metadata = None @@ -995,6 +1005,8 @@ def edit(workflow_name, name, description, set_params, map_params, rename_params map_params=map_params, rename_params=rename_params, describe_params=describe_params, + creators=creators, + keywords=keywords, custom_metadata=custom_metadata, ) ) @@ -1003,7 +1015,7 @@ def edit(workflow_name, name, description, set_params, map_params, rename_params if isinstance(plan, PlanViewModel): print_plan(plan) else: - _print_composite_plan(plan) + print_composite_plan(plan) @workflow.command() @@ -1286,7 +1298,7 @@ def iterate(name_or_id, mappings, mapping_path, dry_run, provider, config, skip_ """Execute a workflow by iterating through a range of provided parameters.""" from renku.command.view_model.plan import PlanViewModel from renku.command.workflow import iterate_workflow_command, show_workflow_command - from renku.ui.cli.utils.terminal import print_plan + from renku.ui.cli.utils.terminal import print_composite_plan, print_plan if len(mappings) == 0 and mapping_path is None: raise errors.UsageError("No mapping has been given for the iteration!") @@ -1297,7 +1309,7 @@ def iterate(name_or_id, mappings, mapping_path, dry_run, provider, config, skip_ if isinstance(plan, PlanViewModel): print_plan(plan) else: - _print_composite_plan(plan) + print_composite_plan(plan) communicator = ClickCallback() iterate_workflow_command(skip_metadata_update=skip_metadata_update).with_communicator(communicator).build().execute( diff --git a/renku/ui/service/controllers/workflow_plans_list.py b/renku/ui/service/controllers/workflow_plans_list.py new file mode 100644 index 0000000000..ba81a77932 --- /dev/null +++ b/renku/ui/service/controllers/workflow_plans_list.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2020 - Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Renku service plans list controller.""" + +from renku.command.command_builder.command import Command +from renku.core.workflow.plan import get_plans_with_metadata +from renku.ui.service.controllers.api.abstract import ServiceCtrl +from renku.ui.service.controllers.api.mixins import RenkuOperationMixin +from renku.ui.service.serializers.workflows import WorkflowPlansListRequest, WorkflowPlansListResponseRPC +from renku.ui.service.views import result_response + + +class WorkflowPlansListCtrl(ServiceCtrl, RenkuOperationMixin): + """Controller for plans list endpoint.""" + + REQUEST_SERIALIZER = WorkflowPlansListRequest() + RESPONSE_SERIALIZER = WorkflowPlansListResponseRPC() + + def __init__(self, cache, user_data, request_data): + """Construct a plans list controller.""" + self.ctx = WorkflowPlansListCtrl.REQUEST_SERIALIZER.load(request_data) + super(WorkflowPlansListCtrl, self).__init__(cache, user_data, request_data) + + @property + def context(self): + """Controller operation context.""" + return self.ctx + + def renku_op(self): + """Renku operation for the controller.""" + plan_list_command = Command().command(get_plans_with_metadata).with_database().require_migration() + result = plan_list_command.build().execute() + return result.output + + def to_response(self): + """Execute controller flow and serialize to service response.""" + self.ctx["plans"] = self.execute_op() + return result_response(WorkflowPlansListCtrl.RESPONSE_SERIALIZER, self.ctx) diff --git a/renku/ui/service/controllers/workflow_plans_show.py b/renku/ui/service/controllers/workflow_plans_show.py new file mode 100644 index 0000000000..50a97a2dcc --- /dev/null +++ b/renku/ui/service/controllers/workflow_plans_show.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2020 - Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Renku service plans show controller.""" + +from renku.command.workflow import show_workflow_command +from renku.ui.service.controllers.api.abstract import ServiceCtrl +from renku.ui.service.controllers.api.mixins import RenkuOperationMixin +from renku.ui.service.serializers.workflows import WorkflowPlansShowRequest, WorkflowPlansShowResponseRPC +from renku.ui.service.views import result_response + + +class WorkflowPlansShowCtrl(ServiceCtrl, RenkuOperationMixin): + """Controller for plan show endpoint.""" + + REQUEST_SERIALIZER = WorkflowPlansShowRequest() + RESPONSE_SERIALIZER = WorkflowPlansShowResponseRPC() + + def __init__(self, cache, user_data, request_data): + """Construct a workflow plan show controller.""" + self.ctx = WorkflowPlansShowCtrl.REQUEST_SERIALIZER.load(request_data) + super(WorkflowPlansShowCtrl, self).__init__(cache, user_data, request_data) + + @property + def context(self): + """Controller operation context.""" + return self.ctx + + def renku_op(self): + """Renku operation for the controller.""" + result = show_workflow_command().build().execute(name_or_id=self.ctx["plan_id"], with_metadata=True) + return result.output + + def to_response(self): + """Execute controller flow and serialize to service response.""" + result = self.execute_op() + return result_response(WorkflowPlansShowCtrl.RESPONSE_SERIALIZER, result) diff --git a/renku/ui/service/entrypoint.py b/renku/ui/service/entrypoint.py index d881450281..e7d4b80c09 100644 --- a/renku/ui/service/entrypoint.py +++ b/renku/ui/service/entrypoint.py @@ -50,6 +50,7 @@ from renku.ui.service.views.project import project_blueprint from renku.ui.service.views.templates import templates_blueprint from renku.ui.service.views.version import version_blueprint +from renku.ui.service.views.workflow_plans import workflow_plans_blueprint logging.basicConfig(level=os.getenv("SERVICE_LOG_LEVEL", "WARNING")) @@ -142,6 +143,7 @@ def exceptions(e): def build_routes(app): """Register routes to given app instance.""" + app.register_blueprint(workflow_plans_blueprint) app.register_blueprint(cache_blueprint) app.register_blueprint(config_blueprint) app.register_blueprint(dataset_blueprint) diff --git a/renku/ui/service/errors.py b/renku/ui/service/errors.py index 7f80a3be7d..76604aea87 100644 --- a/renku/ui/service/errors.py +++ b/renku/ui/service/errors.py @@ -700,7 +700,7 @@ class IntermittentSettingExistsError(ServiceError): and one tries to work on content already deleted from another one. """ - code = SVC_ERROR_INTERMITTENT + 111 + code = SVC_ERROR_INTERMITTENT + 112 userMessage = "There was an error with the setting '{setting_name}'. Please refresh the page and try again." devMessage = "Unexpected error on setting '{setting_name}', possibly caused by concurrent actions." @@ -747,6 +747,25 @@ def __init__(self, exception=None): super().__init__(exception=exception) +class IntermittentWorkflowNotFound(ServiceError): + """An operation failed because a workflow could not be found. + + It may be a synchronization error happening when two or more concurrent operations overlap + and one tries to read content not yet created. + """ + + code = SVC_ERROR_INTERMITTENT + 150 + userMessage = "The workflow '{name_or_id}' could not be found. Check that the name/id is correct and try again." + devMessage = "Unexpected error on workflow '{name_or_id}', possibly caused by concurrent actions." + + def __init__(self, exception=None, name_or_id=ERROR_NOT_AVAILABLE): + super().__init__( + userMessage=self.userMessage.format(name_or_id=name_or_id), + devMessage=self.devMessage.format(name_or_id=name_or_id), + exception=exception, + ) + + class IntermittentTimeoutError(ServiceError): """An operation timed out.""" diff --git a/renku/ui/service/serializers/workflows.py b/renku/ui/service/serializers/workflows.py new file mode 100644 index 0000000000..2162415c12 --- /dev/null +++ b/renku/ui/service/serializers/workflows.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2020 - Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Renku service workflow serializers.""" +from marshmallow import Schema, fields +from marshmallow_oneofschema import OneOfSchema + +from renku.domain_model.dataset import DatasetCreatorsJson +from renku.ui.service.serializers.common import LocalRepositorySchema, RemoteRepositorySchema +from renku.ui.service.serializers.rpc import JsonRPCResponse + + +class WorkflowPlansListRequest(LocalRepositorySchema, RemoteRepositorySchema): + """Request schema for plan list view.""" + + +class AbstractPlanResponse(Schema): + """Base schema for abstract plan responses.""" + + id = fields.String(required=True) + name = fields.String(required=True) + description = fields.String() + type = fields.String() + created = fields.DateTime() + creators = fields.List(fields.Nested(DatasetCreatorsJson)) + keywords = fields.List(fields.String()) + touches_existing_files = fields.Boolean() + duration = fields.Integer(dump_default=None) + + +class WorflowPlanEntryResponse(AbstractPlanResponse): + """Serialize a plan to a response object.""" + + last_executed = fields.DateTime() + number_of_executions = fields.Integer() + children = fields.List(fields.String) + + +class WorkflowPlansListResponse(Schema): + """Response schema for plan list view.""" + + plans = fields.List(fields.Nested(WorflowPlanEntryResponse), required=True) + + +class WorkflowPlansListResponseRPC(JsonRPCResponse): + """RPC response schema for plan list view.""" + + result = fields.Nested(WorkflowPlansListResponse) + + +class WorkflowPlansShowRequest(LocalRepositorySchema, RemoteRepositorySchema): + """Request schema for plan show view.""" + + plan_id = fields.String(required=True) + + +class AnnotationSchema(Schema): + """Custom metadata annotation schema.""" + + id = fields.String(required=True) + source = fields.String() + body = fields.Raw() + + +class ParameterBaseSchema(Schema): + """Base schema for parameters.""" + + id = fields.String(required=True) + plan_id = fields.String(required=True) + name = fields.String(required=True) + type = fields.String() + description = fields.String(allow_none=True) + default_value = fields.String() + prefix = fields.String(allow_none=True) + position = fields.Integer(allow_none=True) + + +class InputSchema(ParameterBaseSchema): + """Schema for plan input.""" + + mapped_to = fields.String(allow_none=True) + encoding_format = fields.String(allow_none=True) + exists = fields.Boolean() + + +class OutputSchema(ParameterBaseSchema): + """Schema for plan input.""" + + mapped_to = fields.String(allow_none=True) + encoding_format = fields.String(allow_none=True) + create_folder = fields.Boolean() + exists = fields.Boolean() + last_touched_by_this_plan = fields.Boolean() + + +class ParameterSchema(ParameterBaseSchema): + """Schema for a plan parameter.""" + + pass + + +class PlanDetailsResponse(AbstractPlanResponse): + """Schema for Plan details.""" + + last_executed = fields.DateTime() + number_of_executions = fields.Integer() + full_command = fields.String() + command = fields.String() + inputs = fields.List(fields.Nested(InputSchema)) + outputs = fields.List(fields.Nested(OutputSchema)) + parameters = fields.List(fields.Nested(ParameterSchema)) + success_codes = fields.List(fields.Integer()) + latest = fields.String(dump_default=None) + annotations = fields.List(fields.Nested(AnnotationSchema)) + + +class PlanReferenceSchema(Schema): + """Schema for a plan reference.""" + + id = fields.String(required=True) + name = fields.String(required=True) + description = fields.String(allow_none=True) + + +class ParameterTargetSchema(Schema): + """Schema for a mapping target.""" + + id = fields.String(required=True) + plan_id = fields.String(required=True) + name = fields.String(required=True) + type = fields.String() + + +class MappingSchema(Schema): + """Schema for a plan mapping.""" + + id = fields.String(required=True) + plan_id = fields.String(required=True) + name = fields.String(required=True) + type = fields.String() + description = fields.String(allow_none=True) + default_value = fields.String() + targets = fields.List(fields.Nested(ParameterTargetSchema)) + + +class LinkSchema(Schema): + """Schema for a parameter link.""" + + id = fields.String(required=True) + plan_id = fields.String(required=True) + type = fields.String() + source_entry = fields.Nested(ParameterTargetSchema, data_key="source") + sink_entries = fields.List(fields.Nested(ParameterTargetSchema), data_key="sinks") + + +class CompositePlanDetailsResponse(AbstractPlanResponse): + """Schema for Plan details.""" + + steps = fields.List(fields.Nested(PlanReferenceSchema), data_key="plans") + mappings = fields.List(fields.Nested(MappingSchema)) + links = fields.List(fields.Nested(LinkSchema)) + latest = fields.String(dump_default=None) + annotations = fields.List(fields.Nested(AnnotationSchema)) + + +class PlanSuperSchema(OneOfSchema): + """Combined schema for Plan and CompositePlan.""" + + type_schemas = {"Plan": PlanDetailsResponse, "CompositePlan": CompositePlanDetailsResponse} + + def get_obj_type(self, obj): + """Get type from object.""" + return str(obj.type) + + +class WorkflowPlansShowResponseRPC(JsonRPCResponse): + """Response schema for plan show view.""" + + result = fields.Nested(PlanSuperSchema) diff --git a/renku/ui/service/views/api_versions.py b/renku/ui/service/views/api_versions.py index ec845a98f6..f71b3e1b68 100644 --- a/renku/ui/service/views/api_versions.py +++ b/renku/ui/service/views/api_versions.py @@ -61,11 +61,12 @@ def add_url_rule( V1_0 = ApiVersion("1.0") V1_1 = ApiVersion("1.1") V1_2 = ApiVersion("1.2") -V1_3 = ApiVersion("1.3", is_base_version=True) +V1_3 = ApiVersion("1.3") +V1_4 = ApiVersion("1.4", is_base_version=True) -VERSIONS_FROM_V1_1 = [V1_1, V1_2, V1_3] +VERSIONS_FROM_V1_1 = [V1_1, V1_2, V1_3, V1_4] VERSIONS_FROM_V1_0 = [V1_0] + VERSIONS_FROM_V1_1 ALL_VERSIONS = [V0_9] + VERSIONS_FROM_V1_0 MINIMUM_VERSION = V0_9 -MAXIMUM_VERSION = V1_3 +MAXIMUM_VERSION = V1_4 diff --git a/renku/ui/service/views/error_handlers.py b/renku/ui/service/views/error_handlers.py index a7118e8afd..8e3c313e1e 100644 --- a/renku/ui/service/views/error_handlers.py +++ b/renku/ui/service/views/error_handlers.py @@ -42,6 +42,7 @@ TemplateMissingReferenceError, TemplateUpdateError, UninitializedProject, + WorkflowNotFoundError, ) from renku.ui.service.errors import ( IntermittentAuthenticationError, @@ -52,6 +53,7 @@ IntermittentRedisError, IntermittentSettingExistsError, IntermittentTimeoutError, + IntermittentWorkflowNotFound, ProgramGitError, ProgramGraphCorruptError, ProgramInternalError, @@ -367,6 +369,22 @@ def decorated_function(*args, **kwargs): return decorated_function +def handle_workflow_errors(f): + """Wrapper which handles workflow errors.""" + # noqa + @wraps(f) + def decorated_function(*args, **kwargs): + """Represents decorated function.""" + try: + return f(*args, **kwargs) + except WorkflowNotFoundError as e: + raise IntermittentWorkflowNotFound(e, name_or_id=e.name_or_id) + except RenkuException: + raise + + return decorated_function + + def handle_datasets_unlink_errors(f): """Wrapper which handles datasets unlink errors.""" # noqa diff --git a/renku/ui/service/views/workflow_plans.py b/renku/ui/service/views/workflow_plans.py new file mode 100644 index 0000000000..b4dc1104e5 --- /dev/null +++ b/renku/ui/service/views/workflow_plans.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2020 - Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Renku service workflow plans view.""" +from flask import request + +from renku.ui.service.config import SERVICE_PREFIX +from renku.ui.service.controllers.workflow_plans_list import WorkflowPlansListCtrl +from renku.ui.service.controllers.workflow_plans_show import WorkflowPlansShowCtrl +from renku.ui.service.views.api_versions import V1_4, VersionedBlueprint +from renku.ui.service.views.decorators import optional_identity, requires_cache +from renku.ui.service.views.error_handlers import handle_common_except, handle_workflow_errors + +PLAN_BLUEPRINT_TAG = "workflow plans" +workflow_plans_blueprint = VersionedBlueprint(PLAN_BLUEPRINT_TAG, __name__, url_prefix=SERVICE_PREFIX) + + +@workflow_plans_blueprint.route( + "/workflow_plans.list", methods=["GET"], provide_automatic_options=False, versions=[V1_4] +) +@handle_common_except +@handle_workflow_errors +@requires_cache +@optional_identity +def list_plans_view(user_data, cache): + """ + List all plans in a project. + + --- + get: + description: List all plans in a project. + parameters: + - in: query + schema: WorkflowPlansListRequest + responses: + 200: + description: Listing of all plans in a project. + content: + application/json: + schema: WorkflowPlansListResponseRPC + tags: + - workflow plans + """ + return WorkflowPlansListCtrl(cache, user_data, dict(request.args)).to_response() + + +@workflow_plans_blueprint.route( + "/workflow_plans.show", methods=["GET"], provide_automatic_options=False, versions=[V1_4] +) +@handle_common_except +@handle_workflow_errors +@requires_cache +@optional_identity +def show_plan_view(user_data, cache): + """ + Show details of a plan. + + --- + get: + description: Show details of a plan. + parameters: + - in: query + schema: WorkflowPlansShowRequest + responses: + 200: + description: The details of the plan. + content: + application/json: + schema: + WorkflowPlansShowResponseRPC + tags: + - workflow plans + """ + return WorkflowPlansShowCtrl(cache, user_data, dict(request.args)).to_response() diff --git a/tests/cli/test_run.py b/tests/cli/test_run.py index 5b2d26cc0b..185dc8861e 100644 --- a/tests/cli/test_run.py +++ b/tests/cli/test_run.py @@ -114,6 +114,8 @@ def test_run_metadata(renku_cli, runner, project, with_injection): plan = plan_gateway.get_by_id(plan.id) assert "run-1" == plan.name assert "first run" == plan.description + assert 1 == len(plan.creators) + assert "Renku Bot " == plan.creators[0].full_identity assert {"key1", "key2"} == set(plan.keywords) result = runner.invoke(cli, ["graph", "export", "--format", "json-ld", "--strict"]) @@ -163,6 +165,34 @@ def test_generated_run_name(runner, project, command, name, with_injection): assert name == plan.name[:-5] +def test_run_creator(runner, project, with_injection): + """Test generated run name.""" + result = runner.invoke( + cli, + [ + "run", + "--no-output", + "--creator", + "John Doe ", + "--creator", + "Jane Doe ", + "echo", + "-n", + "value", + ], + ) + + assert 0 == result.exit_code, format_result_exception(result) + with with_injection(): + plan_gateway = PlanGateway() + plan = plan_gateway.get_all_plans()[0] + assert plan + assert 2 == len(plan.creators) + names = [c.full_identity for c in plan.creators] + assert "John Doe " in names + assert "Jane Doe " in names + + def test_run_invalid_name(runner, project): """Test run with invalid name.""" result = runner.invoke(cli, ["run", "--name", "invalid name", "touch", "foo"]) diff --git a/tests/cli/test_workflow.py b/tests/cli/test_workflow.py index 6108a68d08..3aec1e37d2 100644 --- a/tests/cli/test_workflow.py +++ b/tests/cli/test_workflow.py @@ -368,7 +368,7 @@ def test_workflow_remove_command(runner, project): assert 2 == result.exit_code, format_result_exception(result) result = runner.invoke(cli, ["workflow", "show", workflow_name]) - assert 2 == result.exit_code, format_result_exception(result) + assert 1 == result.exit_code, format_result_exception(result) def test_workflow_remove_with_composite_command(runner, project): @@ -444,6 +444,16 @@ def _get_plan_id(output): first_plan = database["plans"][_get_plan_id(result.stdout)] assert first_plan.description == "Test workflow" + cmd = ["workflow", "edit", workflow_name, "--keyword", "bio", "--keyword", "informatics"] + result = runner.invoke(cli, cmd) + assert 0 == result.exit_code, format_result_exception(result) + + database = Database.from_path(project.database_path) + first_plan = database["plans"][_get_plan_id(result.stdout)] + assert 2 == len(first_plan.keywords) + assert "bio" in first_plan.keywords + assert "informatics" in first_plan.keywords + # edit parameter cmd = ["workflow", "edit", workflow_name, "--rename-param", "param1=param2"] result = runner.invoke(cli, cmd) diff --git a/tests/core/commands/test_graph.py b/tests/core/commands/test_graph.py index 98879e599d..9c882fc5e9 100644 --- a/tests/core/commands/test_graph.py +++ b/tests/core/commands/test_graph.py @@ -170,6 +170,7 @@ def input_objects(request, dataset_model, activity_model, plan_model): "http://www.w3.org/ns/prov#Plan", "https://swissdatasciencecenter.github.io/renku-ontology#Plan", ], + "http://schema.org/creator": [{"@id": "mailto:john.doe@example.com"}], "http://schema.org/dateCreated": [{"@value": "2022-07-12T16:29:14+02:00"}], "http://schema.org/keywords": [], "http://schema.org/name": [{"@value": "my-plan"}], @@ -207,6 +208,7 @@ def input_objects(request, dataset_model, activity_model, plan_model): "http://www.w3.org/ns/prov#Plan", "https://swissdatasciencecenter.github.io/renku-ontology#Plan", ], + "http://schema.org/creator": [{"@id": "mailto:john.doe@example.com"}], "http://schema.org/dateCreated": [{"@value": "2022-07-12T16:29:14+02:00"}], "http://schema.org/keywords": [], "http://schema.org/name": [{"@value": "my-plan"}], @@ -222,6 +224,12 @@ def input_objects(request, dataset_model, activity_model, plan_model): {"@id": "/plans/7f8bcaa36ef844528b88230343503163"} ], }, + { + "@id": "mailto:john.doe@example.com", + "@type": ["http://schema.org/Person", "http://www.w3.org/ns/prov#Person"], + "http://schema.org/email": [{"@value": "john.doe@example.com"}], + "http://schema.org/name": [{"@value": "John Doe"}], + }, ], ), ], @@ -279,6 +287,14 @@ def test_graph_export_full(): ) ] + plan = Plan( + id="/plans/abcdefg123456", + command="echo", + name="echo", + date_created=datetime.fromisoformat("2022-07-12T16:29:14+02:00"), + creators=[Person(email="test@example.com", name="John Doe")], + ) + activity_gateway = MagicMock(spec=IActivityGateway) activity_gateway.get_all_activities.return_value = [ MagicMock( @@ -287,12 +303,7 @@ def test_graph_export_full(): association=Association( id="/activities/abcdefg123456/association", agent=Person(email="test@example.com", name="John Doe"), - plan=Plan( - id="/plans/abcdefg123456", - command="echo", - name="echo", - date_created=datetime.fromisoformat("2022-07-12T16:29:14+02:00"), - ), + plan=plan, ), ) ] @@ -300,18 +311,13 @@ def test_graph_export_full(): plan_gateway = MagicMock(spec=IPlanGateway) plan_gateway.get_all_plans.return_value = [ CompositePlan( - id="/plans/abcdefg123456", + id="/plans/composite1", name="composite", date_created=datetime.fromisoformat("2022-07-12T16:29:14+02:00"), - plans=[ - Plan( - id="/plans/abcdefg123456", - command="echo", - name="echo", - date_created=datetime.fromisoformat("2022-07-12T16:29:14+02:00"), - ) - ], - ) + creators=[Person(email="test@example.com", name="John Doe")], + plans=[plan], + ), + plan, ] project_gateway = MagicMock(spec=IProjectGateway) @@ -324,7 +330,7 @@ def test_graph_export_full(): plan_gateway=plan_gateway, ) - assert result == [ + expected_output = [ { "@id": "/activities/abcdefg123456", "@type": ["http://www.w3.org/ns/prov#Activity"], @@ -344,6 +350,7 @@ def test_graph_export_full(): "http://www.w3.org/ns/prov#Plan", "https://swissdatasciencecenter.github.io/renku-ontology#Plan", ], + "http://schema.org/creator": [{"@id": "mailto:test@example.com"}], "http://schema.org/dateCreated": [{"@value": "2022-07-12T16:29:14+02:00"}], "http://schema.org/keywords": [], "http://schema.org/name": [{"@value": "echo"}], @@ -353,12 +360,37 @@ def test_graph_export_full(): "https://swissdatasciencecenter.github.io/renku-ontology#hasOutputs": [], "https://swissdatasciencecenter.github.io/renku-ontology#successCodes": [], }, + { + "@id": "/plans/composite1", + "@type": [ + "http://schema.org/Action", + "http://schema.org/CreativeWork", + "http://www.w3.org/ns/prov#Plan", + "https://swissdatasciencecenter.github.io/renku-ontology#CompositePlan", + ], + "http://schema.org/creator": [{"@id": "mailto:test@example.com"}], + "http://schema.org/dateCreated": [{"@value": "2022-07-12T16:29:14+02:00"}], + "http://schema.org/keywords": [], + "http://schema.org/name": [{"@value": "composite"}], + "https://swissdatasciencecenter.github.io/renku-ontology#hasMappings": [], + "https://swissdatasciencecenter.github.io/renku-ontology#hasSubprocess": [{"@id": "/plans/abcdefg123456"}], + "https://swissdatasciencecenter.github.io/renku-ontology#workflowLinks": [], + }, + { + "@id": "/projects/my-project", + "https://swissdatasciencecenter.github.io/renku-ontology#hasPlan": [ + {"@id": "/plans/composite1"}, + {"@id": "/plans/abcdefg123456"}, + ], + }, { "@id": "/projects/my-project", "https://swissdatasciencecenter.github.io/renku-ontology#hasActivity": [ {"@id": "/activities/abcdefg123456"} ], - "https://swissdatasciencecenter.github.io/renku-ontology#hasPlan": [{"@id": "/plans/abcdefg123456"}], + "https://swissdatasciencecenter.github.io/renku-ontology#hasPlan": [ + {"@id": "/plans/abcdefg123456"}, + ], }, { "@id": "mailto:test@example.com", @@ -429,6 +461,7 @@ def test_graph_export_full(): "https://swissdatasciencecenter.github.io/renku-ontology#hasDataset": [{"@id": "/datasets/0000000aaaaaaa"}], }, ] + assert not DeepDiff(result, expected_output, ignore_order=True, exclude_regex_paths=r"root.*\['@id'\]") @pytest.mark.parametrize( diff --git a/tests/core/fixtures/core_serialization.py b/tests/core/fixtures/core_serialization.py index 6d3f353751..bfc9ea159e 100644 --- a/tests/core/fixtures/core_serialization.py +++ b/tests/core/fixtures/core_serialization.py @@ -31,12 +31,3 @@ def dataset_metadata(): data = yaml.load(file_path.read_text(), Loader=NoDatesSafeLoader) yield data - - -@pytest.fixture -def dataset_metadata_before_calamus(): - """Return dataset metadata fixture.""" - from renku.core.util.yaml import NoDatesSafeLoader - - path = Path(__file__).parent / ".." / ".." / "data" / "dataset-v0.10.4-before-calamus.yml" - yield yaml.load(path.read_text(), Loader=NoDatesSafeLoader) diff --git a/tests/core/models/test_template.py b/tests/core/models/test_template.py index fd40419d1f..81b85b095d 100644 --- a/tests/core/models/test_template.py +++ b/tests/core/models/test_template.py @@ -17,7 +17,6 @@ # limitations under the License. """Template tests.""" -import shutil import textwrap import pytest @@ -26,22 +25,6 @@ from renku.core.util.metadata import read_renku_version_from_dockerfile from renku.domain_model.template import TemplateMetadata, TemplateParameter, TemplatesManifest -try: - import importlib_resources -except ImportError: - import importlib.resources as importlib_resources # type: ignore - - -@pytest.fixture() -def dummy_source(tmp_path): - """Provide a dummy (renku) source for a template.""" - ref = importlib_resources.files("renku") / "templates" - with importlib_resources.as_file(ref) as embedded_templates: - shutil.copytree(str(embedded_templates), str(tmp_path)) - - yield tmp_path - - TEMPLATE_METADATA = {"__name__": "my-project", "__project_description__": "My Project", "__renku_version__": "42.0.0"} diff --git a/tests/data/dataset-v0.10.4-before-calamus.yml b/tests/data/dataset-v0.10.4-before-calamus.yml deleted file mode 100644 index bc5e437d17..0000000000 --- a/tests/data/dataset-v0.10.4-before-calamus.yml +++ /dev/null @@ -1,595 +0,0 @@ -'@context': - '@version': 1.1 - _id: '@id' - _label: rdfs:label - _project: - '@context': - '@version': 1.1 - _id: '@id' - created: schema:dateCreated - creator: - '@context': - '@version': 1.1 - _id: '@id' - affiliation: schema:affiliation - alternate_name: schema:alternateName - email: schema:email - label: rdfs:label - name: schema:name - prov: http://www.w3.org/ns/prov# - rdfs: http://www.w3.org/2000/01/rdf-schema# - schema: http://schema.org/ - '@id': schema:creator - name: schema:name - prov: http://www.w3.org/ns/prov# - schema: http://schema.org/ - updated: schema:dateUpdated - version: schema:schemaVersion - '@id': schema:isPartOf - created: schema:dateCreated - creator: - '@context': - '@version': 1.1 - _id: '@id' - affiliation: schema:affiliation - alternate_name: schema:alternateName - email: schema:email - label: rdfs:label - name: schema:name - prov: http://www.w3.org/ns/prov# - rdfs: http://www.w3.org/2000/01/rdf-schema# - schema: http://schema.org/ - '@id': schema:creator - date_published: schema:datePublished - description: schema:description - files: - '@context': - '@version': 1.1 - _id: '@id' - _label: rdfs:label - _project: - '@context': - '@version': 1.1 - _id: '@id' - created: schema:dateCreated - creator: - '@context': - '@version': 1.1 - _id: '@id' - affiliation: schema:affiliation - alternate_name: schema:alternateName - email: schema:email - label: rdfs:label - name: schema:name - prov: http://www.w3.org/ns/prov# - rdfs: http://www.w3.org/2000/01/rdf-schema# - schema: http://schema.org/ - '@id': schema:creator - name: schema:name - prov: http://www.w3.org/ns/prov# - schema: http://schema.org/ - updated: schema:dateUpdated - version: schema:schemaVersion - '@id': schema:isPartOf - added: schema:dateCreated - based_on: schema:isBasedOn - creator: - '@context': - '@version': 1.1 - _id: '@id' - affiliation: schema:affiliation - alternate_name: schema:alternateName - email: schema:email - label: rdfs:label - name: schema:name - prov: http://www.w3.org/ns/prov# - rdfs: http://www.w3.org/2000/01/rdf-schema# - schema: http://schema.org/ - '@id': schema:creator - external: renku:external - name: schema:name - path: prov:atLocation - prov: http://www.w3.org/ns/prov# - rdfs: http://www.w3.org/2000/01/rdf-schema# - renku: https://swissdatasciencecenter.github.io/renku-ontology# - schema: http://schema.org/ - url: schema:url - wfprov: http://purl.org/wf4ever/wfprov# - '@id': schema:hasPart - identifier: schema:identifier - in_language: - '@context': - '@version': 1.1 - alternate_name: schema:alternateName - name: schema:name - schema: http://schema.org/ - '@id': schema:inLanguage - keywords: schema:keywords - license: schema:license - name: schema:name - path: prov:atLocation - prov: http://www.w3.org/ns/prov# - rdfs: http://www.w3.org/2000/01/rdf-schema# - same_as: - '@context': - '@version': 1.1 - _id: '@id' - schema: http://schema.org/ - url: schema:url - '@id': schema:sameAs - schema: http://schema.org/ - short_name: schema:alternateName - tags: - '@context': - '@version': 1.1 - _id: '@id' - commit: schema:location - created: schema:startDate - dataset: schema:about - description: schema:description - name: schema:name - schema: http://schema.org/ - '@id': schema:subjectOf - url: schema:url - version: schema:version - wfprov: http://purl.org/wf4ever/wfprov# -'@type': -- prov:Entity -- schema:Dataset -- wfprov:Artifact -_id: https://localhost/datasets/51db02ad-3cba-47e2-84d0-5ee5914bd654 -_label: 51db02ad-3cba-47e2-84d0-5ee5914bd654 -_project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' -created: '2020-06-15T08:34:03.607590+00:00' -creator: -- '@type': - - prov:Person - - schema:Person - _id: https://orcid.org/0000-0002-9528-9470 - affiliation: Harvard University - alternate_name: null - email: null - label: Durbin, Philip - name: Durbin, Philip -date_published: - '@type': schema:Date - '@value': '2019-07-03' -description: The tabular file contains information on known Harvard repositories on - GitHub, such as the number of stars, programming language, day last updated, number - of open issues, size, number of forks, repository URL, create date, and description. - Each repository has a corresponding JSON file that was retrieved using the GitHub - API with code and a list of repositories available from https://github.com/IQSS/open-source-at-harvard. -files: -- '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://localhost/blob/3ae788bcaa8eb6056e7d9855bf070f20b094b238/data/dataverse/IQSS-DataTaggingLibrary.json - _label: data/dataverse/IQSS-DataTaggingLibrary.json@3ae788bcaa8eb6056e7d9855bf070f20b094b238 - _project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' - added: '2020-06-15T08:37:04.556710+00:00' - based_on: null - creator: - - '@type': - - prov:Person - - schema:Person - _id: https://orcid.org/0000-0002-9528-9470 - affiliation: Harvard University - alternate_name: null - email: null - label: Durbin, Philip - name: Durbin, Philip - external: false - name: IQSS-DataTaggingLibrary.json - path: data/dataverse/IQSS-DataTaggingLibrary.json - url: https://dataverse.harvard.edu/api/access/datafile/3371444 -- '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://localhost/blob/3ae788bcaa8eb6056e7d9855bf070f20b094b238/data/dataverse/IQSS-dataverse.json - _label: data/dataverse/IQSS-dataverse.json@3ae788bcaa8eb6056e7d9855bf070f20b094b238 - _project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' - added: '2020-06-15T08:37:04.560380+00:00' - based_on: null - creator: - - '@type': - - prov:Person - - schema:Person - _id: https://orcid.org/0000-0002-9528-9470 - affiliation: Harvard University - alternate_name: null - email: null - label: Durbin, Philip - name: Durbin, Philip - external: false - name: IQSS-dataverse.json - path: data/dataverse/IQSS-dataverse.json - url: https://dataverse.harvard.edu/api/access/datafile/3371530 -- '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://localhost/blob/3ae788bcaa8eb6056e7d9855bf070f20b094b238/data/dataverse/IQSS-redmine2github.json - _label: data/dataverse/IQSS-redmine2github.json@3ae788bcaa8eb6056e7d9855bf070f20b094b238 - _project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' - added: '2020-06-15T08:37:04.564089+00:00' - based_on: null - creator: - - '@type': - - prov:Person - - schema:Person - _id: https://orcid.org/0000-0002-9528-9470 - affiliation: Harvard University - alternate_name: null - email: null - label: Durbin, Philip - name: Durbin, Philip - external: false - name: IQSS-redmine2github.json - path: data/dataverse/IQSS-redmine2github.json - url: https://dataverse.harvard.edu/api/access/datafile/3371465 -- '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://localhost/blob/3ae788bcaa8eb6056e7d9855bf070f20b094b238/data/dataverse/IQSS-TwoRavens.json - _label: data/dataverse/IQSS-TwoRavens.json@3ae788bcaa8eb6056e7d9855bf070f20b094b238 - _project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' - added: '2020-06-15T08:37:04.567837+00:00' - based_on: null - creator: - - '@type': - - prov:Person - - schema:Person - _id: https://orcid.org/0000-0002-9528-9470 - affiliation: Harvard University - alternate_name: null - email: null - label: Durbin, Philip - name: Durbin, Philip - external: false - name: IQSS-TwoRavens.json - path: data/dataverse/IQSS-TwoRavens.json - url: https://dataverse.harvard.edu/api/access/datafile/3371449 -- '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://localhost/blob/3ae788bcaa8eb6056e7d9855bf070f20b094b238/data/dataverse/IQSS-UNF.json - _label: data/dataverse/IQSS-UNF.json@3ae788bcaa8eb6056e7d9855bf070f20b094b238 - _project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' - added: '2020-06-15T08:37:04.571573+00:00' - based_on: null - creator: - - '@type': - - prov:Person - - schema:Person - _id: https://orcid.org/0000-0002-9528-9470 - affiliation: Harvard University - alternate_name: null - email: null - label: Durbin, Philip - name: Durbin, Philip - external: false - name: IQSS-UNF.json - path: data/dataverse/IQSS-UNF.json - url: https://dataverse.harvard.edu/api/access/datafile/3371500 -- '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://localhost/blob/3ae788bcaa8eb6056e7d9855bf070f20b094b238/data/dataverse/IQSS-Zelig.json - _label: data/dataverse/IQSS-Zelig.json@3ae788bcaa8eb6056e7d9855bf070f20b094b238 - _project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' - added: '2020-06-15T08:37:04.575493+00:00' - based_on: null - creator: - - '@type': - - prov:Person - - schema:Person - _id: https://orcid.org/0000-0002-9528-9470 - affiliation: Harvard University - alternate_name: null - email: null - label: Durbin, Philip - name: Durbin, Philip - external: false - name: IQSS-Zelig.json - path: data/dataverse/IQSS-Zelig.json - url: https://dataverse.harvard.edu/api/access/datafile/3371485 -- '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://localhost/blob/8bc8a0a180e08822a3960e63d4cfeb25e70822c5/data/dataverse/external/data.txt - _label: data/dataverse/external/data.txt@8bc8a0a180e08822a3960e63d4cfeb25e70822c5 - _project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' - added: '2020-06-15T08:38:01.859607+00:00' - based_on: null - creator: - - '@type': - - prov:Person - - schema:Person - _id: https://orcid.org/0000-0002-9528-9470 - affiliation: Harvard University - alternate_name: null - email: null - label: Durbin, Philip - name: Durbin, Philip - external: true - name: data.txt - path: data/dataverse/external/data.txt - url: file://../../../../tmp/data.txt -- '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://localhost/blob/d6afeba51089db197c87a5971a057ac96eaedd1b/data/dataverse/local/result.csv - _label: data/dataverse/local/result.csv@d6afeba51089db197c87a5971a057ac96eaedd1b - _project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' - added: '2020-06-15T08:38:08.788012+00:00' - based_on: null - creator: - - '@type': - - prov:Person - - schema:Person - _id: https://orcid.org/0000-0002-9528-9470 - affiliation: Harvard University - alternate_name: null - email: null - label: Durbin, Philip - name: Durbin, Philip - external: false - name: result.csv - path: data/dataverse/local/result.csv - url: file://../../../../tmp/result.csv -- '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://localhost/blob/0a51ab0f49e59a8bfea40411dbde7a47832bcc74/data/dataverse/git/index.ipynb - _label: data/dataverse/git/index.ipynb@0a51ab0f49e59a8bfea40411dbde7a47832bcc74 - _project: - '@type': - - prov:Location - - schema:Project - _id: https://localhost/projects/mohammad.alisafaee/tmp - created: '2020-06-15T08:33:50.672147+00:00' - creator: - '@type': - - prov:Person - - schema:Person - _id: mailto:mohammad.alisafaee@epfl.ch - affiliation: null - alternate_name: null - email: mohammad.alisafaee@epfl.ch - label: Mohammad Alisafaee - name: Mohammad Alisafaee - name: tmp - updated: '2020-06-15T08:33:50.696822+00:00' - version: '4' - added: '2020-06-15T09:20:09.754978+00:00' - based_on: - '@type': - - prov:Entity - - schema:DigitalDocument - - wfprov:Artifact - _id: https://github.com/blob/f98325d81c700f4b86ee05c2154e94d43ca068b8/notebooks/index.ipynb - _label: notebooks/index.ipynb@f98325d81c700f4b86ee05c2154e94d43ca068b8 - _project: null - added: '2020-06-15T09:20:08.715577+00:00' - based_on: null - creator: - - '@type': - - prov:Person - - schema:Person - _id: mailto:cramakri@ethz.ch - affiliation: null - alternate_name: null - email: cramakri@ethz.ch - label: Chandrasekhar Ramakrishnan - name: Chandrasekhar Ramakrishnan - external: false - name: index.ipynb - path: notebooks/index.ipynb - url: https://github.com/SwissDataScienceCenter/r10e-ds-py.git - creator: - - '@type': - - prov:Person - - schema:Person - _id: mailto:cramakri@ethz.ch - affiliation: null - alternate_name: null - email: cramakri@ethz.ch - label: Chandrasekhar Ramakrishnan - name: Chandrasekhar Ramakrishnan - external: false - name: index.ipynb - path: data/dataverse/git/index.ipynb - url: https://github.com/SwissDataScienceCenter/r10e-ds-py.git -identifier: 51db02ad-3cba-47e2-84d0-5ee5914bd654 -in_language: null -keywords: [] -license: - '@type': schema:Dataset - url: https://creativecommons.org/publicdomain/zero/1.0/ -name: Open Source at Harvard -path: .renku/datasets/51db02ad-3cba-47e2-84d0-5ee5914bd654 -same_as: - '@type': schema:URL - _id: _:URL@//doi.org/10.7910/DVN/TJCLKP - url: https://doi.org/10.7910/DVN/TJCLKP -short_name: dataverse -tags: -- '@type': schema:PublicationEvent - _id: _:3@3ae788bcaa8eb6056e7d9855bf070f20b094b238 - commit: 3ae788bcaa8eb6056e7d9855bf070f20b094b238 - created: '2020-06-15T08:37:05.662814+00:00' - dataset: Open Source at Harvard - description: Tag 3 created by renku import - name: '3' -url: https://localhost/datasets/51db02ad-3cba-47e2-84d0-5ee5914bd654 -version: '3' diff --git a/tests/fixtures/config.py b/tests/fixtures/config.py index 6bfb1d5976..9dcdc1b7f2 100644 --- a/tests/fixtures/config.py +++ b/tests/fixtures/config.py @@ -37,7 +37,9 @@ IT_REMOTE_NO_COMMITS_REPO_URL = os.getenv( "IT_REMOTE_NO_COMMITS_REPO_URL", "https://gitlab.dev.renku.ch/renku-python-integration-tests/core-it-no-commits" ) -IT_GIT_ACCESS_TOKEN = os.getenv("IT_OAUTH_GIT_TOKEN") +IT_WORKFLOW_REPO_URL = os.getenv( + "IT_WORKFLOW_REPO_URL", "https://dev.renku.ch/gitlab/renku-python-integration-tests/core-it-workflows" +) @pytest.fixture(scope="session") diff --git a/tests/fixtures/domain_models.py b/tests/fixtures/domain_models.py index e87059cd51..6b8bbade8d 100644 --- a/tests/fixtures/domain_models.py +++ b/tests/fixtures/domain_models.py @@ -101,6 +101,7 @@ def _create_project(namespace="john.doe", name="my-project", email="john.doe@exa @pytest.fixture() def plan_model(): """Plan object.""" + from renku.domain_model.provenance.agent import Person from renku.domain_model.workflow.plan import Plan def _create_plan(name="my-plan", command="echo", identifier="7f8bcaa36ef844528b88230343503163"): @@ -109,6 +110,13 @@ def _create_plan(name="my-plan", command="echo", identifier="7f8bcaa36ef844528b8 name=name, command=command, date_created=datetime.fromisoformat("2022-07-12T16:29:14+02:00"), + creators=[ + Person( + id=Person.generate_id(email="john.doe@example.com", full_identity="john.doe"), + email="john.doe@example.com", + name="John Doe", + ) + ], ) yield _create_plan diff --git a/tests/service/fixtures/service_client.py b/tests/service/fixtures/service_client.py index 6db00ccb76..3aca15e2d0 100644 --- a/tests/service/fixtures/service_client.py +++ b/tests/service/fixtures/service_client.py @@ -153,29 +153,6 @@ def view_user_data(identity_headers): return user_data -@pytest.fixture(scope="module") -def authentication_headers_raw(): - """Get authentication headers without renku user identification.""" - headers = { - "Content-Type": "application/json", - "Authorization": "Bearer {0}".format(os.getenv("IT_OAUTH_GIT_TOKEN")), - } - - return headers - - -@pytest.fixture(scope="module") -def authentication_headers(authentication_headers_raw): - """Get authentication headers.""" - identification = { - "Renku-User-Id": "b4b4de0eda0f471ab82702bd5c367fa7", - "Renku-User-FullName": "Just Sam", - "Renku-User-Email": "contact@renkulab.io", - } - - return {**authentication_headers_raw, **identification} - - @pytest.fixture def svc_client_with_user(svc_client_cache): """Service client with a predefined user.""" diff --git a/tests/service/fixtures/service_integration.py b/tests/service/fixtures/service_integration.py index 4f768b3659..6020f37fef 100644 --- a/tests/service/fixtures/service_integration.py +++ b/tests/service/fixtures/service_integration.py @@ -91,16 +91,28 @@ def integration_repo(headers, project_id, url_components) -> Generator[Repositor @pytest.fixture() -def integration_lifecycle(svc_client, mock_redis, identity_headers, it_remote_repo_url): +def integration_lifecycle( + svc_client, mock_redis, identity_headers, it_remote_repo_url, it_protected_repo_url, it_workflow_repo_url, request +): """Setup and teardown steps for integration tests.""" from renku.domain_model.git import GitURL - url_components = GitURL.parse(it_remote_repo_url) + marker = request.node.get_closest_marker("remote_repo") - payload = {"git_url": it_remote_repo_url, "depth": -1} + if marker is None or marker.args[0] == "default": + remote_repo = it_remote_repo_url + elif marker.args[0] == "protected": + remote_repo = it_protected_repo_url + elif marker.args[0] == "workflow": + remote_repo = it_workflow_repo_url + else: + raise ValueError(f"Couldn't get remote repo for marker {marker.args[0]}") - response = svc_client.post("/cache.project_clone", data=json.dumps(payload), headers=identity_headers) + url_components = GitURL.parse(remote_repo) + + payload = {"git_url": remote_repo, "depth": -1} + response = svc_client.post("/cache.project_clone", data=json.dumps(payload), headers=identity_headers) assert response assert {"result"} == set(response.json.keys()) @@ -159,32 +171,6 @@ def svc_client_with_repo(svc_client_setup): yield svc_client, deepcopy(headers), project_id, url_components -@pytest.fixture -def svc_protected_repo(svc_client, identity_headers, it_protected_repo_url): - """Service client with migrated remote protected repository.""" - from renku.domain_model.git import GitURL - - payload = { - "git_url": it_protected_repo_url, - "depth": 0, - } - - response = svc_client.post("/cache.project_clone", data=json.dumps(payload), headers=identity_headers) - - data = { - "project_id": response.json["result"]["project_id"], - "skip_template_update": True, - "skip_docker_update": True, - } - svc_client.post("/cache.migrate", data=json.dumps(data), headers=identity_headers) - - url_components = GitURL.parse(it_protected_repo_url) - - with integration_repo(identity_headers, response.json["result"]["project_id"], url_components) as repo: - with _mock_cache_sync(repo): - yield svc_client, identity_headers, payload, response - - @pytest.fixture def svc_protected_old_repo(svc_synced_client, it_protected_repo_url): """Service client with remote protected repository.""" diff --git a/tests/service/fixtures/service_projects.py b/tests/service/fixtures/service_projects.py index a5e3b81bfb..d9a278c572 100644 --- a/tests/service/fixtures/service_projects.py +++ b/tests/service/fixtures/service_projects.py @@ -49,14 +49,6 @@ def project_metadata(project) -> Generator[Tuple["RenkuProject", Dict[str, Any]] yield project, metadata -@pytest.fixture(scope="module") -def it_git_access_token(): - """Returns a git access token for a testing run.""" - from tests.fixtures.config import IT_GIT_ACCESS_TOKEN - - return IT_GIT_ACCESS_TOKEN - - @pytest.fixture(scope="module") def it_remote_repo_url(): """Returns a remote path to integration test repository.""" @@ -124,3 +116,11 @@ def it_protected_repo_url(): from tests.fixtures.config import IT_PROTECTED_REMOTE_REPO_URL return IT_PROTECTED_REMOTE_REPO_URL + + +@pytest.fixture(scope="module") +def it_workflow_repo_url(): + """Returns a repository url containing workflows.""" + from tests.fixtures.config import IT_WORKFLOW_REPO_URL + + return IT_WORKFLOW_REPO_URL diff --git a/tests/service/views/test_dataset_views.py b/tests/service/views/test_dataset_views.py index d1deb3fddb..5f571d6b65 100644 --- a/tests/service/views/test_dataset_views.py +++ b/tests/service/views/test_dataset_views.py @@ -1482,18 +1482,16 @@ def test_remote_edit_view(svc_client, it_remote_repo_url, identity_headers): assert response.json["result"]["job_id"] +@pytest.mark.remote_repo("protected") @pytest.mark.integration @pytest.mark.service @retry_failed -def test_protected_branch(svc_protected_repo): +def test_protected_branch(svc_client_with_repo): """Test adding a file to protected branch.""" - svc_client, headers, payload, response = svc_protected_repo - - assert_rpc_response(response) - assert {"result"} == set(response.json.keys()) + svc_client, headers, project_id, _ = svc_client_with_repo payload = { - "project_id": response.json["result"]["project_id"], + "project_id": project_id, "name": uuid.uuid4().hex, } response = svc_client.post("/datasets.create", data=json.dumps(payload), headers=headers) diff --git a/tests/service/views/test_workflow_plan_views.py b/tests/service/views/test_workflow_plan_views.py new file mode 100644 index 0000000000..782cf00340 --- /dev/null +++ b/tests/service/views/test_workflow_plan_views.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2019-2022 - Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Renku service dataset view tests.""" + +import pytest + +from tests.utils import assert_rpc_response, retry_failed + + +@pytest.mark.remote_repo("workflow") +@pytest.mark.service +@pytest.mark.integration +@retry_failed +def test_list_workflow_plans_view(svc_client_with_repo): + """Check listing of plans.""" + svc_client, headers, project_id, _ = svc_client_with_repo + + params = { + "project_id": project_id, + } + + response = svc_client.get("/workflow_plans.list", query_string=params, headers=headers) + + assert_rpc_response(response) + assert {"plans"} == set(response.json["result"].keys()) + assert 0 != len(response.json["result"]["plans"]) + assert { + "children", + "created", + "creators", + "description", + "id", + "keywords", + "name", + "number_of_executions", + "touches_existing_files", + "type", + "last_executed", + "duration", + } == set(response.json["result"]["plans"][0].keys()) + + reexecuted = next(p for p in response.json["result"]["plans"] if p["name"] == "some-step") + assert 2 == reexecuted["number_of_executions"] + assert reexecuted["touches_existing_files"] + assert "2022-10-04T13:05:44+02:00" == reexecuted["last_executed"] + + non_recent = next(p for p in response.json["result"]["plans"] if p["name"] == "deleted-outputs") + assert not non_recent["touches_existing_files"] + assert 2 == non_recent["number_of_executions"] + + composite = next(p for p in response.json["result"]["plans"] if p["name"] == "composite1") + + assert not composite["number_of_executions"] + assert composite["touches_existing_files"] + assert not composite["last_executed"] + + +@pytest.mark.parametrize( + "plan_id,expected_fields,executions,touches_files,latest", + [ + ( + "/plans/6943ee7f620c4f2d8f75b657e9d9e765", + { + "annotations", + "creators", + "created", + "description", + "id", + "mappings", + "keywords", + "latest", + "name", + "links", + "plans", + "touches_existing_files", + "type", + "duration", + }, + None, + True, + "/plans/6943ee7f620c4f2d8f75b657e9d9e765", + ), + ( + "/plans/56b3149fc21e43bea9b73b887934e084", + { + "annotations", + "creators", + "created", + "description", + "full_command", + "command", + "id", + "inputs", + "keywords", + "latest", + "name", + "outputs", + "parameters", + "success_codes", + "touches_existing_files", + "number_of_executions", + "last_executed", + "type", + "duration", + }, + 1, + True, + "/plans/56b3149fc21e43bea9b73b887934e084", + ), + ( + "/plans/7c4e51b1ac7143c287b5b0001d843310", + { + "annotations", + "creators", + "created", + "description", + "full_command", + "command", + "id", + "inputs", + "keywords", + "latest", + "name", + "outputs", + "parameters", + "success_codes", + "touches_existing_files", + "number_of_executions", + "last_executed", + "type", + "duration", + }, + 2, + False, + "/plans/6fee5bb01de449f6bc39d7e7cd23f4c2", + ), + ], +) +@pytest.mark.remote_repo("workflow") +@pytest.mark.service +@pytest.mark.integration +@retry_failed +def test_show_workflow_plans_view(plan_id, expected_fields, executions, touches_files, latest, svc_client_with_repo): + """Check showing of plans.""" + svc_client, headers, project_id, _ = svc_client_with_repo + + params = {"project_id": project_id, "plan_id": plan_id} + + response = svc_client.get("/workflow_plans.show", query_string=params, headers=headers) + + assert_rpc_response(response) + assert expected_fields == set(response.json["result"].keys()) + + if executions is not None: + assert executions == response.json["result"]["number_of_executions"] + assert response.json["result"]["last_executed"] + else: + assert "number_of_executions" not in response.json["result"] + + assert touches_files == response.json["result"]["touches_existing_files"] + assert latest == response.json["result"]["latest"]