diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 0e0c7e0..50f7f65 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/vscode/devcontainers/python:3.9-bullseye +FROM mcr.microsoft.com/devcontainers/python:1-3.12-bullseye ENV PYTHONUNBUFFERED 1 @@ -7,23 +7,13 @@ ARG USER_UID=1000 ARG USER_GID=$USER_UID RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then groupmod --gid $USER_GID vscode && usermod --uid $USER_UID --gid $USER_GID vscode; fi -# Copy install script -COPY ./dev_install /bin +# [Option] Install Node.js +ARG INSTALL_NODE="true" +ARG NODE_VERSION="lts/*" +RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi -# Install secondary Python version -USER vscode -RUN <> ~/.profile -echo "$INIT_PYENV" >> ~/.bashrc -. ~/.profile -pyenv install --skip-existing 3.12 -pyenv global system 3.12 -EOF +# Copy install and launcher script to bin: +COPY ./dev_install /bin +COPY ./dev_launcher /bin CMD ["sleep", "infinity"] diff --git a/.devcontainer/dev_launcher b/.devcontainer/dev_launcher new file mode 100755 index 0000000..dc4f4f6 --- /dev/null +++ b/.devcontainer/dev_launcher @@ -0,0 +1,4 @@ +#!/bin/bash + +echo "The dev_launcher for this project is not implemented." >&2 +exit 1 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9fcc9b4..b9b990e 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,5 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.177.0/containers/python-3-postgres -// Update the VARIANT arg in docker-compose.yml to pick a Python version: 3, 3.12, 3.11, 3.10, 3.9 { "name": "${localWorkspaceFolderBasename}", "dockerComposeFile": "docker-compose.yml", @@ -23,7 +22,7 @@ "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "editor.codeActionsOnSave": { - "source.organizeImports": true + "source.organizeImports": "explicit" }, "editor.formatOnSave": true, "editor.renderWhitespace": "all", diff --git a/.github/workflows/check_pyproject.yaml b/.github/workflows/check_pyproject.yaml index 4600d0a..0c7abdc 100644 --- a/.github/workflows/check_pyproject.yaml +++ b/.github/workflows/check_pyproject.yaml @@ -15,9 +15,7 @@ jobs: - name: Common steps id: common - uses: ghga-de/gh-action-common@v5 - with: - python-version: '3.12' + uses: ghga-de/gh-action-common@v6 - name: Check pyproject.toml id: check-pyproject diff --git a/.github/workflows/static_code_analysis.yaml b/.github/workflows/static_code_analysis.yaml index 12134ed..052dfdc 100644 --- a/.github/workflows/static_code_analysis.yaml +++ b/.github/workflows/static_code_analysis.yaml @@ -15,7 +15,7 @@ jobs: - name: Common steps id: common - uses: ghga-de/gh-action-common@v5 + uses: ghga-de/gh-action-common@v6 - name: Run pre-commit uses: pre-commit/action@v3.0.1 @@ -36,4 +36,4 @@ jobs: - name: Check license header and file id: license-checker run: | - ./scripts/license_checker.py + ./scripts/check_license.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index ad29f93..56a9c19 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -1,17 +1,13 @@ -name: Tests +name: Run test suite and measure coverage on: push jobs: tests: - name: Tests + name: Run test suite runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.9', '3.10', '3.11', '3.12'] - steps: - name: Checkout repository id: checkout @@ -19,34 +15,20 @@ jobs: - name: Common steps id: common - uses: ghga-de/gh-action-common@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install tox plugin - id: install-tox-gh-actions - run: | - pip install --disable-pip-version-check "tox-gh-actions>=3.2,<4" + uses: ghga-de/gh-action-common@v6 - - name: Run tests with Python ${{ matrix.python-version }} + - name: Run tests id: pytest - if: ${{ matrix.python-version != '3.12' }} - run: | - export ${{ steps.common.outputs.CONFIG_YAML_ENV_VAR_NAME }}="${{ steps.common.outputs.CONFIG_YAML }}" - - tox - - - name: Run tests with Python ${{ matrix.python-version }} measuring coverage - id: pytest-coverage - if: ${{ matrix.python-version == '3.12' }} run: | export ${{ steps.common.outputs.CONFIG_YAML_ENV_VAR_NAME }}="${{ steps.common.outputs.CONFIG_YAML }}" - tox -- --cov="${{ steps.common.outputs.PACKAGE_NAME }}" --cov-report=xml + pytest \ + --cov="${{ steps.common.outputs.PACKAGE_NAME }}" \ + --cov-report=xml \ + tests - name: Upload coverage to coveralls id: coveralls - if: ${{ matrix.python-version == '3.12' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 98c7d9a..4ac77e2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,7 +45,7 @@ repos: - id: no-commit-to-branch args: [--branch, dev, --branch, int, --branch, main] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.1 + rev: v0.4.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/.pyproject_generation/pyproject_custom.toml b/.pyproject_generation/pyproject_custom.toml index 38aeb17..6903989 100644 --- a/.pyproject_generation/pyproject_custom.toml +++ b/.pyproject_generation/pyproject_custom.toml @@ -4,7 +4,7 @@ name = "metldata" version = "1.0.0" description = "metldata - A framework for handling metadata based on ETL, CQRS, and event sourcing." dependencies = [ - "schemapack<=1.0.0, <2.0.0" + "schemapack == 2.0.0-alpha.3" ] [project.urls] diff --git a/.pyproject_generation/pyproject_template.toml b/.pyproject_generation/pyproject_template.toml index e2d5958..07a11ff 100644 --- a/.pyproject_generation/pyproject_template.toml +++ b/.pyproject_generation/pyproject_template.toml @@ -7,14 +7,12 @@ readme = "README.md" authors = [ { name = "German Human Genome Phenome Archive (GHGA)", email = "contact@ghga.de" }, ] -requires-python = ">=3.9" +requires-python = ">=3.12" license = { text = "Apache 2.0" } classifiers = [ "Development Status :: 1 - Planning", "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", "License :: OSI Approved :: Apache Software License", "Topic :: Internet :: WWW/HTTP :: HTTP Servers", @@ -35,7 +33,7 @@ exclude = [ ] line-length = 88 src = ["src", "tests", "examples", "scripts"] -target-version = "py39" +target-version = "py312" [tool.ruff.lint] fixable = [ @@ -60,6 +58,7 @@ ignore = [ "D107", # missing docstring in __init__ "D206", # indent-with-spaces (for formatter) "D300", # triple-single-quotes (for formatter) + "UP040", # type statement (not yet supported by mypy) ] select = [ "C90", # McCabe Complexity @@ -105,7 +104,7 @@ asyncio_mode = "strict" [tool.coverage.paths] source = [ - "src", - "/workspace/src", - "**/lib/python*/site-packages", + "src", + "/workspace/src", + "**/lib/python*/site-packages", ] diff --git a/.template/deprecated_files.txt b/.template/deprecated_files.txt index df962ef..152ea26 100644 --- a/.template/deprecated_files.txt +++ b/.template/deprecated_files.txt @@ -13,6 +13,7 @@ .github/workflows/cd.yaml scripts/check_mandatory_and_static_files.py +scripts/license_checker.py scripts/update_static_files.py docs diff --git a/.template/mandatory_files_ignore.txt b/.template/mandatory_files_ignore.txt index 4ac1451..156fcd6 100644 --- a/.template/mandatory_files_ignore.txt +++ b/.template/mandatory_files_ignore.txt @@ -1,7 +1,6 @@ # Optional list of files which are actually mandatory in the template # but are allowed to be removed in the current repository -.devcontainer/dev_launcher .github/workflows/cd.yaml .github/workflows/dev_cd.yaml diff --git a/.template/static_files.txt b/.template/static_files.txt index 46e46bf..1cf9790 100644 --- a/.template/static_files.txt +++ b/.template/static_files.txt @@ -15,10 +15,12 @@ scripts/script_utils/__init__.py scripts/script_utils/cli.py +scripts/script_utils/deps.py +scripts/script_utils/lock_deps.py scripts/__init__.py scripts/update_all.py -scripts/license_checker.py +scripts/check_license.py scripts/get_package_name.py scripts/update_config_docs.py scripts/update_template_files.py diff --git a/.template/static_files_ignore.txt b/.template/static_files_ignore.txt index 51d8b28..4549038 100644 --- a/.template/static_files_ignore.txt +++ b/.template/static_files_ignore.txt @@ -5,7 +5,6 @@ .github/workflows/check_openapi_spec.yaml .github/workflows/check_readme.yaml .github/workflows/cd.yaml -.github/workflows/tests.yaml .github/workflows/ci_release.yaml .github/workflows/ci_workflow_dispatch.yaml @@ -16,7 +15,5 @@ scripts/update_all.py example_data/README.md -.devcontainer/Dockerfile -.devcontainer/dev_install .readme_generation/README.md .readme_generation/readme_template.md diff --git a/lock/requirements-dev-template.in b/lock/requirements-dev-template.in index 36459d7..da84663 100644 --- a/lock/requirements-dev-template.in +++ b/lock/requirements-dev-template.in @@ -1,17 +1,17 @@ # common requirements for development and testing of services -pytest>=8.1 -pytest-asyncio>=0.23 +pytest>=8.2 +pytest-asyncio>=0.23.6 pytest-cov>=5 snakeviz>=2.2 logot>=1.3 pre-commit>=3.7 -mypy>=1.9 +mypy>=1.10 mypy-extensions>=1.0 -ruff>=0.3 +ruff>=0.4 click>=8.1 typer>=0.12 @@ -27,7 +27,6 @@ jsonschema2md>=1.1 setuptools>=69.5 # required since switch to pyproject.toml and pip-tools -tomli>=2.0.1 tomli_w>=1.0 -uv>=0.1.31 +uv>=0.1.39 diff --git a/lock/requirements-dev.txt b/lock/requirements-dev.txt index f67b995..c92d891 100644 --- a/lock/requirements-dev.txt +++ b/lock/requirements-dev.txt @@ -189,22 +189,12 @@ distlib==0.3.8 \ --hash=sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 \ --hash=sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64 # via virtualenv -exceptiongroup==1.2.1 \ - --hash=sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad \ - --hash=sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16 - # via - # anyio - # pytest -filelock==3.13.4 \ - --hash=sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f \ - --hash=sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4 +filelock==3.14.0 \ + --hash=sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f \ + --hash=sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a # via # tox # virtualenv -gprof2dot==2022.7.29 \ - --hash=sha256:45b4d298bd36608fccf9511c3fd88a773f7a1abc04d6cd39445b11ba43133ec5 \ - --hash=sha256:f165b3851d3c52ee4915eb1bd6cca571e5759823c2cd0f71a79bda93c2dc85d6 - # via pytest-profiling h11==0.14.0 \ --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 @@ -415,14 +405,13 @@ pyproject-api==1.6.1 \ --hash=sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538 \ --hash=sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675 # via tox -pytest==8.1.1 \ - --hash=sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7 \ - --hash=sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044 +pytest==8.2.0 \ + --hash=sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233 \ + --hash=sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f # via # pytest-asyncio # pytest-cov # pytest-httpx - # pytest-profiling pytest-asyncio==0.23.6 \ --hash=sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a \ --hash=sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f @@ -432,9 +421,6 @@ pytest-cov==5.0.0 \ pytest-httpx==0.30.0 \ --hash=sha256:6d47849691faf11d2532565d0c8e0e02b9f4ee730da31687feae315581d7520c \ --hash=sha256:755b8edca87c974dd4f3605c374fda11db84631de3d163b99c0df5807023a19a -pytest-profiling==1.7.0 \ - --hash=sha256:93938f147662225d2b8bd5af89587b979652426a8a6ffd7e73ec4a23e24b7f29 \ - --hash=sha256:999cc9ac94f2e528e3f5d43465da277429984a1c237ae9818f8cfd0b06acb019 python-dotenv==1.0.1 \ --hash=sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca \ --hash=sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a @@ -494,7 +480,6 @@ pyyaml==6.0.1 \ # via # jsonschema2md # pre-commit - # schemapack referencing==0.35.0 \ --hash=sha256:191e936b0c696d0af17ad7430a3dc68e88bc11be6514f4757dc890f04ab05889 \ --hash=sha256:8080727b30e364e5783152903672df9b6b091c926a146a759080b62ca3126cd6 @@ -611,27 +596,83 @@ rpds-py==0.18.0 \ # via # jsonschema # referencing -ruff==0.4.1 \ - --hash=sha256:0926cefb57fc5fced629603fbd1a23d458b25418681d96823992ba975f050c2b \ - --hash=sha256:1c859f294f8633889e7d77de228b203eb0e9a03071b72b5989d89a0cf98ee262 \ - --hash=sha256:2c6e37f2e3cd74496a74af9a4fa67b547ab3ca137688c484749189bf3a686ceb \ - --hash=sha256:2d9ef6231e3fbdc0b8c72404a1a0c46fd0dcea84efca83beb4681c318ea6a953 \ - --hash=sha256:6e68d248ed688b9d69fd4d18737edcbb79c98b251bba5a2b031ce2470224bdf9 \ - --hash=sha256:9485f54a7189e6f7433e0058cf8581bee45c31a25cd69009d2a040d1bd4bfaef \ - --hash=sha256:a1eaf03d87e6a7cd5e661d36d8c6e874693cb9bc3049d110bc9a97b350680c43 \ - --hash=sha256:b34510141e393519a47f2d7b8216fec747ea1f2c81e85f076e9f2910588d4b64 \ - --hash=sha256:b90506f3d6d1f41f43f9b7b5ff845aeefabed6d2494307bc7b178360a8805252 \ - --hash=sha256:b92f03b4aa9fa23e1799b40f15f8b95cdc418782a567d6c43def65e1bbb7f1cf \ - --hash=sha256:baa27d9d72a94574d250f42b7640b3bd2edc4c58ac8ac2778a8c82374bb27984 \ - --hash=sha256:c7d391e5936af5c9e252743d767c564670dc3889aff460d35c518ee76e4b26d7 \ - --hash=sha256:d2921ac03ce1383e360e8a95442ffb0d757a6a7ddd9a5be68561a671e0e5807e \ - --hash=sha256:d592116cdbb65f8b1b7e2a2b48297eb865f6bdc20641879aa9d7b9c11d86db79 \ - --hash=sha256:eec8d185fe193ad053eda3a6be23069e0c8ba8c5d20bc5ace6e3b9e37d246d3f \ - --hash=sha256:efd703a5975ac1998c2cc5e9494e13b28f31e66c616b0a76e206de2562e0843c \ - --hash=sha256:f1ee41580bff1a651339eb3337c20c12f4037f6110a36ae4a2d864c52e5ef954 -schemapack==1.0.0 \ - --hash=sha256:2f62d7bac01c7d703f5bea095020a4911b7b8329547d47acb1bc0f53a9b42bdb \ - --hash=sha256:3146b19b4b512732b52976c1f435bae6fc88aa0a1ed065021f037f8dd1f51b9d +ruamel-yaml==0.18.6 \ + --hash=sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636 \ + --hash=sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b + # via schemapack +ruamel-yaml-clib==0.2.8 \ + --hash=sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d \ + --hash=sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001 \ + --hash=sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462 \ + --hash=sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9 \ + --hash=sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe \ + --hash=sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b \ + --hash=sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b \ + --hash=sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615 \ + --hash=sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62 \ + --hash=sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15 \ + --hash=sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b \ + --hash=sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1 \ + --hash=sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9 \ + --hash=sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675 \ + --hash=sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899 \ + --hash=sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7 \ + --hash=sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7 \ + --hash=sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312 \ + --hash=sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa \ + --hash=sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91 \ + --hash=sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b \ + --hash=sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6 \ + --hash=sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3 \ + --hash=sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334 \ + --hash=sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5 \ + --hash=sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3 \ + --hash=sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe \ + --hash=sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c \ + --hash=sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed \ + --hash=sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337 \ + --hash=sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880 \ + --hash=sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f \ + --hash=sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d \ + --hash=sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248 \ + --hash=sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d \ + --hash=sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf \ + --hash=sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512 \ + --hash=sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069 \ + --hash=sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb \ + --hash=sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942 \ + --hash=sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d \ + --hash=sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31 \ + --hash=sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92 \ + --hash=sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5 \ + --hash=sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28 \ + --hash=sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d \ + --hash=sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1 \ + --hash=sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2 \ + --hash=sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875 \ + --hash=sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412 + # via ruamel-yaml +ruff==0.4.2 \ + --hash=sha256:0e2e06459042ac841ed510196c350ba35a9b24a643e23db60d79b2db92af0c2b \ + --hash=sha256:1f32cadf44c2020e75e0c56c3408ed1d32c024766bd41aedef92aa3ca28eef68 \ + --hash=sha256:22e306bf15e09af45ca812bc42fa59b628646fa7c26072555f278994890bc7ac \ + --hash=sha256:24016ed18db3dc9786af103ff49c03bdf408ea253f3cb9e3638f39ac9cf2d483 \ + --hash=sha256:33bcc160aee2520664bc0859cfeaebc84bb7323becff3f303b8f1f2d81cb4edc \ + --hash=sha256:3afabaf7ba8e9c485a14ad8f4122feff6b2b93cc53cd4dad2fd24ae35112d5c5 \ + --hash=sha256:5ec481661fb2fd88a5d6cf1f83403d388ec90f9daaa36e40e2c003de66751798 \ + --hash=sha256:652e4ba553e421a6dc2a6d4868bc3b3881311702633eb3672f9f244ded8908cd \ + --hash=sha256:6a2243f8f434e487c2a010c7252150b1fdf019035130f41b77626f5655c9ca22 \ + --hash=sha256:6ab165ef5d72392b4ebb85a8b0fbd321f69832a632e07a74794c0e598e7a8376 \ + --hash=sha256:7891ee376770ac094da3ad40c116258a381b86c7352552788377c6eb16d784fe \ + --hash=sha256:799eb468ea6bc54b95527143a4ceaf970d5aa3613050c6cff54c85fda3fde480 \ + --hash=sha256:82986bb77ad83a1719c90b9528a9dd663c9206f7c0ab69282af8223566a0c34e \ + --hash=sha256:8772130a063f3eebdf7095da00c0b9898bd1774c43b336272c3e98667d4fb8fa \ + --hash=sha256:8d14dc8953f8af7e003a485ef560bbefa5f8cc1ad994eebb5b12136049bbccc5 \ + --hash=sha256:cbd1e87c71bca14792948c4ccb51ee61c3296e164019d2d484f3eaa2d360dfaf \ + --hash=sha256:ec4ba9436a51527fb6931a8839af4c36a5481f8c19e8f5e42c2f7ad3a49f5069 +schemapack==2.0.0a3 \ + --hash=sha256:897c2659beb358a4256b95db94b9dfa1095b46698f46e4e3f2c50989e6c0210c \ + --hash=sha256:fc6c182cf0f27e362a3a6163bf1c070f79c758362e53420980fbce7f1927c1bf setuptools==69.5.1 \ --hash=sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987 \ --hash=sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32 @@ -640,10 +681,6 @@ shellingham==1.5.4 \ --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \ --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de # via typer -six==1.16.0 \ - --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ - --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 - # via pytest-profiling snakeviz==2.2.0 \ --hash=sha256:569e2d71c47f80a886aa6e70d6405cb6d30aa3520969ad956b06f824c5f02b8e \ --hash=sha256:7bfd00be7ae147eb4a170a471578e1cd3f41f803238958b6b8efcf2c698a6aa9 @@ -655,15 +692,6 @@ sniffio==1.3.1 \ # httpx stringcase==1.2.0 \ --hash=sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008 -tomli==2.0.1 \ - --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ - --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f - # via - # coverage - # mypy - # pyproject-api - # pytest - # tox tomli-w==1.0.0 \ --hash=sha256:9f2a07e8be30a0729e533ec968016807069991ae2fd921a78d42f429ae5f4463 \ --hash=sha256:f463434305e0336248cac9c2dc8076b707d8a12d019dd349f5c1e382dd1ae1b9 @@ -680,9 +708,9 @@ tornado==6.4 \ --hash=sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e \ --hash=sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2 # via snakeviz -tox==4.14.2 \ - --hash=sha256:0defb44f6dafd911b61788325741cc6b2e12ea71f987ac025ad4d649f1f1a104 \ - --hash=sha256:2900c4eb7b716af4a928a7fdc2ed248ad6575294ed7cfae2ea41203937422847 +tox==4.15.0 \ + --hash=sha256:300055f335d855b2ab1b12c5802de7f62a36d4fd53f30bd2835f6a201dda46ea \ + --hash=sha256:7a0beeef166fbe566f54f795b4906c31b428eddafc0102ac00d20998dd1933f6 typer==0.12.3 \ --hash=sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914 \ --hash=sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482 @@ -690,8 +718,6 @@ typing-extensions==4.11.0 \ --hash=sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0 \ --hash=sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a # via - # anyio - # logot # mypy # pydantic # pydantic-core @@ -700,27 +726,27 @@ urllib3==2.2.1 \ --hash=sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d \ --hash=sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19 # via requests -uv==0.1.38 \ - --hash=sha256:03242a734a572733f2b9a5dbb94517e918fe26fc01114b7c51d12296dfbb8f8b \ - --hash=sha256:067af2d986329db4fa3c7373017d49f0e16ddff23e483b7e5bc3a5a18ce08ea6 \ - --hash=sha256:0937ad16ae0e0b6bb6dd3c386f8fb33141ad08d1762eaacffb4d2b27fb466a17 \ - --hash=sha256:0e1d64ac437b0a14fbcec55b1c3f162fa24860711e0d855fcd9c672b149a122a \ - --hash=sha256:1be7aa46936c0351ccb1400ea95e5381b3f05fef772fa3b9f23af728cc175dea \ - --hash=sha256:309e73a3ec3a5a536a3efaf434270fc94b483069f1425765165c1c9d786c27fd \ - --hash=sha256:4251f9771d392d7badc1e5fb934b397b12ca00fef9d955207ade169cc1f7e872 \ - --hash=sha256:43772e7589f70e954b1ae29230e575ef9e4d8d769138a94dfa5ae7eaf1e26ac5 \ - --hash=sha256:4a6024256d38b77151e32876be9fcb99cf75df7a86b26e0161cc202bed558adf \ - --hash=sha256:5a98d6aacd4b57b7e00daf154919e7c9206fefdf40bd28cfb13efe0e0324d491 \ - --hash=sha256:8de6dbd8f348ee90af044f4cc7b6650521d25ba2d20a813c1e157a3f90069dd9 \ - --hash=sha256:9133e24db9bdd4f412eab69586d03294419825432a9a27ee1b510a4c01eb7b0b \ - --hash=sha256:92f65b6e4e5c8126501785af3629dc537d7c82caa56ac9336a86929c73d0e138 \ - --hash=sha256:afd85029923e712b6b2c45ddc1680c785392220876c766521e45778db3f71f8e \ - --hash=sha256:b0b15e51a0f8240969bc412ed0dd60cfe3f664b30173139ef263d71c596d631f \ - --hash=sha256:ea44c07605d1359a7d82bf42706dd86d341f15f4ca2e1f36e51626a7111c2ad5 \ - --hash=sha256:f87c9711493c53d32012a96b49c4d53aabdf7ed666cbf2c3fb55dd402a6b31a8 -virtualenv==20.26.0 \ - --hash=sha256:0846377ea76e818daaa3e00a4365c018bc3ac9760cbb3544de542885aad61fb3 \ - --hash=sha256:ec25a9671a5102c8d2657f62792a27b48f016664c6873f6beed3800008577210 +uv==0.1.39 \ + --hash=sha256:2333dd52e6734e0da6722bdd7b7257d0f8beeac89623c5cfc3888b4c56bc812e \ + --hash=sha256:2ae930189742536f8178617c4ec05cb10271cb3886f6039abd36ee6ab511b160 \ + --hash=sha256:2bda6686a9bb1370d7f53436d34f8ede0fa1b9877b5e152aedd9b22fc3cb33a9 \ + --hash=sha256:3330bd7ab8a6160d815fdc36f48479edf6db8b58d39d20959555095ea7eb63c5 \ + --hash=sha256:3365e0631a738a482d2379e565a230b135f7c5665394313829ccabf7c76c1362 \ + --hash=sha256:388018659e5d73fdeb8ce13c1d812391ec981bf446ab86fb9c0e3d227f727da2 \ + --hash=sha256:4c6ee1148f23aa5d6edf1a1106cc33c4aa57bdbfe8d4c5068c672105415d3b99 \ + --hash=sha256:6b2acc907f7a1735dd9ffeb20d8c7aeeb86b1e5ba0a999e09433ad7f2789dc78 \ + --hash=sha256:7848d703201e6867ae2c70d611e6ffd53d5e5adfc2c9abe89b6d021975e43e81 \ + --hash=sha256:7ee426e0c5fa048cc44f3ac78e476121ef4365bb8bc9199d3cbffc372a80e55d \ + --hash=sha256:88f5601ee957f9be2efc7a24d186f9d2641053806e107e0e42c5e522882c89e0 \ + --hash=sha256:93217578e68a431df235173e390ad7df090499367cd7f5c811520fd4ea3d5047 \ + --hash=sha256:c131dba5fe5079d9c5f06846649e35662901a9afd9b31de17714c63e042d91d2 \ + --hash=sha256:c20b9023dac12ee518de79c91df313be7abb052440cb78f8ffb20dea81d3289e \ + --hash=sha256:cd6d9629ab0e22ab2336b8d6363573ea5a7060ef82ff5d3e6da4b1b30522ef13 \ + --hash=sha256:ce911087f56edc97a5792c17f682ed7611fedead0ea117f56bb6f3942eb3e7b3 \ + --hash=sha256:fba96b3049aea5c1394cd360e5900e4af39829df48ed6fc55eba115c00c8195a +virtualenv==20.26.1 \ + --hash=sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b \ + --hash=sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75 # via # pre-commit # tox diff --git a/lock/requirements.txt b/lock/requirements.txt index 68b9ed1..abf1bc9 100644 --- a/lock/requirements.txt +++ b/lock/requirements.txt @@ -115,59 +115,6 @@ python-dotenv==1.0.1 \ --hash=sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca \ --hash=sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a # via pydantic-settings -pyyaml==6.0.1 \ - --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ - --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \ - --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \ - --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \ - --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \ - --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \ - --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \ - --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \ - --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \ - --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \ - --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \ - --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \ - --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \ - --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \ - --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \ - --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \ - --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \ - --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \ - --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \ - --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \ - --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \ - --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \ - --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \ - --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \ - --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \ - --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \ - --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \ - --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \ - --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \ - --hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \ - --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \ - --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \ - --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \ - --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \ - --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \ - --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \ - --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \ - --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \ - --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \ - --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \ - --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \ - --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \ - --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \ - --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \ - --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \ - --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \ - --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \ - --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \ - --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \ - --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \ - --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f - # via schemapack referencing==0.35.0 \ --hash=sha256:191e936b0c696d0af17ad7430a3dc68e88bc11be6514f4757dc890f04ab05889 \ --hash=sha256:8080727b30e364e5783152903672df9b6b091c926a146a759080b62ca3126cd6 @@ -277,9 +224,65 @@ rpds-py==0.18.0 \ # via # jsonschema # referencing -schemapack==1.0.0 \ - --hash=sha256:2f62d7bac01c7d703f5bea095020a4911b7b8329547d47acb1bc0f53a9b42bdb \ - --hash=sha256:3146b19b4b512732b52976c1f435bae6fc88aa0a1ed065021f037f8dd1f51b9d +ruamel-yaml==0.18.6 \ + --hash=sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636 \ + --hash=sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b + # via schemapack +ruamel-yaml-clib==0.2.8 \ + --hash=sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d \ + --hash=sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001 \ + --hash=sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462 \ + --hash=sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9 \ + --hash=sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe \ + --hash=sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b \ + --hash=sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b \ + --hash=sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615 \ + --hash=sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62 \ + --hash=sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15 \ + --hash=sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b \ + --hash=sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1 \ + --hash=sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9 \ + --hash=sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675 \ + --hash=sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899 \ + --hash=sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7 \ + --hash=sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7 \ + --hash=sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312 \ + --hash=sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa \ + --hash=sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91 \ + --hash=sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b \ + --hash=sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6 \ + --hash=sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3 \ + --hash=sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334 \ + --hash=sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5 \ + --hash=sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3 \ + --hash=sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe \ + --hash=sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c \ + --hash=sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed \ + --hash=sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337 \ + --hash=sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880 \ + --hash=sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f \ + --hash=sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d \ + --hash=sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248 \ + --hash=sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d \ + --hash=sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf \ + --hash=sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512 \ + --hash=sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069 \ + --hash=sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb \ + --hash=sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942 \ + --hash=sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d \ + --hash=sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31 \ + --hash=sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92 \ + --hash=sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5 \ + --hash=sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28 \ + --hash=sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d \ + --hash=sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1 \ + --hash=sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2 \ + --hash=sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875 \ + --hash=sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412 + # via ruamel-yaml +schemapack==2.0.0a3 \ + --hash=sha256:897c2659beb358a4256b95db94b9dfa1095b46698f46e4e3f2c50989e6c0210c \ + --hash=sha256:fc6c182cf0f27e362a3a6163bf1c070f79c758362e53420980fbce7f1927c1bf typing-extensions==4.11.0 \ --hash=sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0 \ --hash=sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a diff --git a/pyproject.toml b/pyproject.toml index 018ff08..40be2f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,13 +9,11 @@ readme = "README.md" authors = [ { name = "German Human Genome Phenome Archive (GHGA)", email = "contact@ghga.de" }, ] -requires-python = ">=3.9" +requires-python = ">=3.12" classifiers = [ "Development Status :: 1 - Planning", "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", "License :: OSI Approved :: Apache Software License", "Topic :: Internet :: WWW/HTTP :: HTTP Servers", @@ -26,7 +24,7 @@ name = "metldata" version = "1.0.0" description = "metldata - A framework for handling metadata based on ETL, CQRS, and event sourcing." dependencies = [ - "schemapack<=1.0.0, <2.0.0", + "schemapack == 2.0.0-alpha.3", ] [project.license] @@ -55,7 +53,7 @@ src = [ "examples", "scripts", ] -target-version = "py39" +target-version = "py312" [tool.ruff.lint] fixable = [ @@ -80,6 +78,7 @@ ignore = [ "D107", "D206", "D300", + "UP040", ] select = [ "C90", @@ -150,4 +149,22 @@ source = [ ] [tool.tox] -legacy_tox_ini = " [tox]\n env_list = py3{9,12}\n\n [gh-actions]\n python =\n 3.9: py39\n 3.10: py310\n 3.11: py311\n 3.12: py312\n\n [testenv]\n pass_env =\n TC_HOST\n DOCKER_HOST\n deps =\n --no-deps -r ./lock/requirements-dev.txt\n commands = pytest {posargs}\n" +legacy_tox_ini = """ + [tox] + env_list = py3{9,12} + + [gh-actions] + python = + 3.9: py39 + 3.10: py310 + 3.11: py311 + 3.12: py312 + + [testenv] + pass_env = + TC_HOST + DOCKER_HOST + deps = + --no-deps -r ./lock/requirements-dev.txt + commands = pytest {posargs} +""" diff --git a/scripts/license_checker.py b/scripts/check_license.py similarity index 89% rename from scripts/license_checker.py rename to scripts/check_license.py index 9d484db..37103d8 100755 --- a/scripts/license_checker.py +++ b/scripts/check_license.py @@ -24,7 +24,6 @@ import sys from datetime import date from pathlib import Path -from typing import Optional, Union # root directory of the package: ROOT_DIR = Path(__file__).parent.parent.resolve() @@ -34,45 +33,45 @@ # exclude files and dirs from license header check: EXCLUDE = [ + ".coveragerc", ".devcontainer", - "eggs", + ".editorconfig", ".eggs", - "build", - "dist", - "develop-eggs", - "lib", - "lib62", - "parts", - "sdist", - "wheels", - "pip-wheel-metadata", - ".coveragerc", ".git", ".github", ".flake8", ".gitignore", - ".pylintrc", - ".ruff.toml", - ".ruff_cache", - "example_config.yaml", - "config_schema.json", - "LICENSE", # is checked but not for the license header - ".pre-commit-config.yaml", - "docs", - ".vscode", ".mypy_cache", ".mypy.ini", + ".pylintrc", ".pytest_cache", - ".editorconfig", - ".tox", - "venv", - ".venv", + ".ruff.toml", + ".ruff_cache", ".template/.static_files.txt", ".template/.static_files_ignore.txt", ".template/.mandatory_files.txt", ".template/.mandatory_files_ignore.txt", ".template/.deprecated_files.txt", ".template/.deprecated_files_ignore.txt", + ".tox", + ".venv", + ".vscode", + "eggs", + "build", + "config_schema.json", + "dist", + "docs", + "develop-eggs", + "example_config.yaml", + "htmlcov", + "lib", + "lib62", + "parts", + "pip-wheel-metadata", + "sdist", + "venv", + "wheels", + "LICENSE", # is checked but not for the license header ] # exclude file by file ending from license header check: @@ -116,8 +115,8 @@ See the License for the specific language governing permissions and limitations under the License.""" -# A list of all chars that may be used to introduce a comment: -COMMENT_CHARS = ["#"] +# A list of strings that may be used to introduce a line comment: +LINE_COMMENTS = ["#"] AUTHOR = """Universität Tübingen, DKFZ, EMBL, and Universität zu Köln for the German Human Genome-Phenome Archive (GHGA)""" @@ -140,11 +139,11 @@ class GlobalCopyrightNotice: """ def __init__(self): - self._text: Optional[str] = None - self._n_lines: Optional[int] = None + self._text: str | None = None + self._n_lines: int | None = None @property - def text(self) -> Optional[str]: + def text(self) -> str | None: return self._text @text.setter @@ -167,7 +166,7 @@ def n_lines(self) -> int: class UnexpectedBinaryFileError(RuntimeError): """Thrown when trying to read a binary file.""" - def __init__(self, file_path: Union[str, Path]): + def __init__(self, file_path: str | Path): message = f"The file could not be read because it is binary: {str(file_path)}" super().__init__(message) @@ -181,13 +180,13 @@ def get_target_files( """Get target files that are not match the exclude conditions. Args: target_dir (pathlib.Path): The target dir to search. - exclude (List[str], optional): + exclude (list[str], optional): Overwrite default list of file/dir paths relative to the target dir that shall be excluded. - exclude_endings (List[str], optional): + exclude_endings (list[str], optional): Overwrite default list of file endings that shall be excluded. - exclude_pattern (List[str], optional): + exclude_pattern (list[str], optional): Overwrite default list of regex patterns match file path for exclusion. """ @@ -203,24 +202,25 @@ def get_target_files( file_ for file_ in all_files if not ( - any([file_.is_relative_to(excl) for excl in exclude_normalized]) - or any([str(file_).endswith(ending) for ending in exclude_endings]) - or any([re.match(pattern, str(file_)) for pattern in exclude_pattern]) + any(file_.is_relative_to(excl) for excl in exclude_normalized) + or any(str(file_).endswith(ending) for ending in exclude_endings) + or any(re.match(pattern, str(file_)) for pattern in exclude_pattern) ) ] return target_files -def normalized_line(line: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str: - norm_line = line.strip() +def normalized_line(line: str, line_comments: list[str] = LINE_COMMENTS) -> str: + line = line.strip() + for line_comment in line_comments: + line_without_comment = line.removeprefix(line_comment) + if line_without_comment != line: + line = line_without_comment.lstrip() + break + return line - for char in chars_to_trim: - norm_line = norm_line.strip(char) - return norm_line.strip("\n").strip("\t").strip() - - -def normalized_text(text: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str: +def normalized_text(text: str, line_comments: list[str] = LINE_COMMENTS) -> str: "Normalize a license header text." lines = text.split("\n") @@ -232,7 +232,7 @@ def normalized_text(text: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str: if stripped_line.startswith("#!"): continue - norm_line = normalized_line(stripped_line) + norm_line = normalized_line(stripped_line, line_comments=line_comments) # exclude empty lines: if norm_line == "": @@ -250,22 +250,17 @@ def format_copyright_template(copyright_template: str, author: str) -> str: return normalized_text(copyright_template.replace("{author}", author)) -def is_commented_line(line: str, comment_chars: list[str] = COMMENT_CHARS) -> bool: +def is_commented_line(line: str, line_comments: list[str] = LINE_COMMENTS) -> bool: """Checks whether a line is a comment.""" - line_stripped = line.strip() - for comment_char in comment_chars: - if line_stripped.startswith(comment_char): - return True - - return False + return line.lstrip().startswith(tuple(line_comments)) def is_empty_line(line: str) -> bool: """Checks whether a line is empty.""" - return line.strip("\n").strip("\t").strip() == "" + return not line.strip() -def get_header(file_path: Path, comment_chars: list[str] = COMMENT_CHARS): +def get_header(file_path: Path, line_comments: list[str] = LINE_COMMENTS): """Extracts the header from a file and normalizes it.""" header_lines: list[str] = [] @@ -273,7 +268,7 @@ def get_header(file_path: Path, comment_chars: list[str] = COMMENT_CHARS): with open(file_path) as file: for line in file: if is_commented_line( - line, comment_chars=comment_chars + line, line_comments=line_comments ) or is_empty_line(line): header_lines.append(line) else: @@ -283,7 +278,7 @@ def get_header(file_path: Path, comment_chars: list[str] = COMMENT_CHARS): # normalize the lines: header = "".join(header_lines) - return normalized_text(header, chars_to_trim=comment_chars) + return normalized_text(header, line_comments=line_comments) def validate_year_string(year_string: str, min_year: int = MIN_YEAR) -> bool: @@ -318,7 +313,7 @@ def check_copyright_notice( global_copyright: GlobalCopyrightNotice, copyright_template: str = COPYRIGHT_TEMPLATE, author: str = AUTHOR, - comment_chars: list[str] = COMMENT_CHARS, + line_comments: list[str] = LINE_COMMENTS, min_year: int = MIN_YEAR, ) -> bool: """Checks the specified copyright text against a template. @@ -386,7 +381,7 @@ def check_file_headers( exclude: list[str] = EXCLUDE, exclude_endings: list[str] = EXCLUDE_ENDINGS, exclude_pattern: list[str] = EXCLUDE_PATTERN, - comment_chars: list[str] = COMMENT_CHARS, + line_comments: list[str] = LINE_COMMENTS, min_year: int = MIN_YEAR, ) -> tuple[list[Path], list[Path]]: """Check files for presence of a license header and verify that @@ -407,13 +402,13 @@ def check_file_headers( The author that shall be included in the license header. It will replace any appearance of "{author}" in the license header. This defaults to an author info for GHGA. - exclude (List[str], optional): + exclude (list[str], optional): Overwrite default list of file/dir paths relative to the target dir that shall be excluded. - exclude_endings (List[str], optional): + exclude_endings (list[str], optional): Overwrite default list of file endings that shall be excluded. - exclude_pattern (List[str], optional): + exclude_pattern (list[str], optional): Overwrite default list of regex patterns match file path for exclusion. """ @@ -430,13 +425,13 @@ def check_file_headers( for target_file in target_files: try: - header = get_header(target_file, comment_chars=comment_chars) + header = get_header(target_file, line_comments=line_comments) if check_copyright_notice( copyright=header, global_copyright=global_copyright, copyright_template=copyright_template, author=author, - comment_chars=comment_chars, + line_comments=line_comments, min_year=min_year, ): passed_files.append(target_file) @@ -454,7 +449,7 @@ def check_license_file( global_copyright: GlobalCopyrightNotice, copyright_template: str = COPYRIGHT_TEMPLATE, author: str = AUTHOR, - comment_chars: list[str] = COMMENT_CHARS, + line_comments: list[str] = LINE_COMMENTS, min_year: int = MIN_YEAR, ) -> bool: """Currently only checks if the copyright notice in the @@ -496,7 +491,7 @@ def check_license_file( global_copyright=global_copyright, copyright_template=copyright_template, author=author, - comment_chars=comment_chars, + line_comments=line_comments, min_year=min_year, ) diff --git a/scripts/list_outdated_dependencies.py b/scripts/list_outdated_dependencies.py index cbdcb3a..894818c 100755 --- a/scripts/list_outdated_dependencies.py +++ b/scripts/list_outdated_dependencies.py @@ -139,12 +139,14 @@ def print_table( header_lengths = [len(header) for header in headers] # Find the maximum length of each column - col_widths = [max(len(str(cell)) for cell in col) for col in zip(*rows)] + col_widths = [ + max(len(str(cell)) for cell in col) for col in zip(*rows, strict=True) + ] # Create a row format based on the maximum column widths row_format = delimiter.join( f"{{:<{max(width, header_len)}}}" - for width, header_len in zip(col_widths, header_lengths) + for width, header_len in zip(col_widths, header_lengths, strict=True) ) print(" " + row_format.format(*headers)) diff --git a/scripts/script_utils/cli.py b/scripts/script_utils/cli.py index 0d77ce2..c7d6af4 100644 --- a/scripts/script_utils/cli.py +++ b/scripts/script_utils/cli.py @@ -26,6 +26,13 @@ def echo_success(message: str): typer.echo(styled_message) +def echo_warning(message: str): + """Print a warning message.""" + + styled_message = typer.style(text=message, fg=typer.colors.YELLOW) + typer.echo(styled_message) + + def echo_failure(message: str): """Print a failure message.""" diff --git a/scripts/script_utils/deps.py b/scripts/script_utils/deps.py index 2e058a0..4e2f8e7 100644 --- a/scripts/script_utils/deps.py +++ b/scripts/script_utils/deps.py @@ -15,12 +15,12 @@ # """Contains utils for working with dependencies, lock files, etc.""" +import tomllib from copy import deepcopy from pathlib import Path from typing import Any import stringcase -import tomli def exclude_from_dependency_list(*, package_name: str, dependencies: list) -> list: @@ -69,7 +69,7 @@ def remove_self_dependencies(pyproject: dict) -> dict: def get_modified_pyproject(pyproject_toml_path: Path) -> dict[str, Any]: """Get a copy of pyproject.toml with any self-referencing dependencies removed.""" with open(pyproject_toml_path, "rb") as pyproject_toml: - pyproject = tomli.load(pyproject_toml) + pyproject = tomllib.load(pyproject_toml) modified_pyproject = remove_self_dependencies(pyproject) return modified_pyproject diff --git a/scripts/script_utils/lock_deps.py b/scripts/script_utils/lock_deps.py index b5f83aa..f800d14 100644 --- a/scripts/script_utils/lock_deps.py +++ b/scripts/script_utils/lock_deps.py @@ -17,14 +17,13 @@ import re from pathlib import Path -from typing import Optional from packaging.requirements import Requirement def get_lock_file_deps( lock_file_path: Path, - exclude: Optional[set[str]] = None, + exclude: set[str] | None = None, ) -> list[Requirement]: """Inspect the lock file to get the dependencies. diff --git a/scripts/update_pyproject.py b/scripts/update_pyproject.py index 6f63cd3..a09c6f0 100755 --- a/scripts/update_pyproject.py +++ b/scripts/update_pyproject.py @@ -19,9 +19,9 @@ """A script to update the pyproject.toml.""" import sys +import tomllib from pathlib import Path -import tomli import tomli_w from script_utils import cli @@ -37,33 +37,33 @@ def read_template_pyproject() -> dict[str, object]: """Read the pyproject_template.toml.""" with open(PYPROJECT_TEMPLATE_PATH, "rb") as file: - return tomli.load(file) + return tomllib.load(file) def read_custom_pyproject() -> dict[str, object]: """Read the pyproject_custom.toml.""" with open(PYPROJECT_CUSTOM_PATH, "rb") as file: - return tomli.load(file) + return tomllib.load(file) def read_current_pyproject() -> dict[str, object]: """Read the current pyproject.toml.""" with open(PYPROJECT_TOML, "rb") as file: - return tomli.load(file) + return tomllib.load(file) def write_pyproject(pyproject: dict[str, object]) -> None: """Write the given pyproject dict into the pyproject.toml.""" with open(PYPROJECT_TOML, "wb") as file: - tomli_w.dump(pyproject, file) + tomli_w.dump(pyproject, file, multiline_strings=True) def merge_fields(*, source: dict[str, object], dest: dict[str, object]): """Merge fields existing in both custom and template pyproject definitions. If a given field is a dictionary, merge or assign depending on if it's found in dest. - If the field is anything else either assign the value or exit with a message if a - conflict exists. + If the field is anything else either assign the value or exit with an error message + if the values have different types. """ for field, value in source.items(): if isinstance(value, dict): @@ -72,11 +72,13 @@ def merge_fields(*, source: dict[str, object], dest: dict[str, object]): else: dest[field] = value else: - if field in dest and value != dest[field]: - cli.echo_failure(f"Conflicting values for '{field}'") - exit(1) - elif field not in dest: - dest[field] = value + if field in dest: + if type(value) == type(dest[field]): + cli.echo_warning(f"Overriding value for '{field}'...") + else: + cli.echo_failure(f"Conflicting types for '{field}'...") + sys.exit(1) + dest[field] = value def merge_pyprojects(inputs: list[dict[str, object]]) -> dict[str, object]: @@ -85,10 +87,10 @@ def merge_pyprojects(inputs: list[dict[str, object]]) -> dict[str, object]: for input in inputs[1:]: for field, value in input.items(): - if field not in pyproject: - pyproject[field] = value - else: + if field in pyproject: merge_fields(source=value, dest=pyproject[field]) # type: ignore + else: + pyproject[field] = value return pyproject diff --git a/src/metldata/builtin_transformations/delete_properties/model_transform.py b/src/metldata/builtin_transformations/delete_properties/model_transform.py index 2128112..0bfe657 100644 --- a/src/metldata/builtin_transformations/delete_properties/model_transform.py +++ b/src/metldata/builtin_transformations/delete_properties/model_transform.py @@ -22,7 +22,6 @@ ClassDefinition, SchemaPack, ) -from schemapack.utils import FrozenDict from metldata.transform.base import EvitableTransformationError @@ -64,6 +63,6 @@ def delete_properties( {**class_def.model_dump(), "content": content_schema} ) - return model.model_copy( - update={"classes": FrozenDict({**model.classes, **updated_class_defs})} - ) + model_dict = model.model_dump() + model_dict["classes"].update(updated_class_defs) + return SchemaPack.model_validate(model_dict) diff --git a/src/metldata/builtin_transformations/infer_relations/config.py b/src/metldata/builtin_transformations/infer_relations/config.py index a164a77..a7cee53 100644 --- a/src/metldata/builtin_transformations/infer_relations/config.py +++ b/src/metldata/builtin_transformations/infer_relations/config.py @@ -46,17 +46,14 @@ class RelationInferenceConfig(BaseSettings): "ClassA": { "class_d": { "path": "ClassA(class_b)>ClassB(class_d)>ClassD", - "cardinality": "many_to_many", }, "class_c": { "path": "ClassA(class_b)>ClassB<(class_c)ClassC", - "cardinality": "many_to_one", }, }, "ClassB": { "class_c": { "path": "ClassB<(class_c)ClassC", - "cardinality": "many_to_many", } }, } @@ -72,7 +69,6 @@ def inference_instructions(self) -> list[InferenceInstruction]: target=relation_details.path.target, path=relation_details.path, new_property=property_name, - allow_multiple=relation_details.allow_multiple, ) for source, slot_description in self.inferred_relations.items() for property_name, relation_details in slot_description.items() diff --git a/src/metldata/builtin_transformations/infer_relations/data_transform.py b/src/metldata/builtin_transformations/infer_relations/data_transform.py index d92c48f..f941d24 100644 --- a/src/metldata/builtin_transformations/infer_relations/data_transform.py +++ b/src/metldata/builtin_transformations/infer_relations/data_transform.py @@ -44,7 +44,8 @@ as defined in the inferred relation """ -from schemapack.spec.datapack import DataPack, Resource, ResourceId +from schemapack.spec.custom_types import ResourceId +from schemapack.spec.datapack import DataPack, Resource from metldata.builtin_transformations.infer_relations.path.path import ( RelationPath, @@ -93,12 +94,12 @@ def resolve_active_path_element( if not source_resource: raise EvitableTransformationError() - target_resource_ids = source_resource.relations.get(path_element.property, []) - return ( - set(target_resource_ids) - if isinstance(target_resource_ids, list) - else {target_resource_ids} - ) + target_resource_ids = source_resource.relations.get(path_element.property) + if target_resource_ids is None: + target_resource_ids = set() + elif isinstance(target_resource_ids, str): + target_resource_ids = {target_resource_ids} + return target_resource_ids def resolve_passive_path_element( @@ -132,10 +133,10 @@ def resolve_passive_path_element( target_resource_ids = set() for candidate_resource_id, candidate_resource in candidate_resources.items(): - relation = candidate_resource.relations.get(path_element.property, []) + relation = candidate_resource.relations.get(path_element.property, set()) if ( - isinstance(relation, list) and source_resource_id in relation + isinstance(relation, set) and source_resource_id in relation ) or source_resource_id == relation: target_resource_ids.add(candidate_resource_id) @@ -218,12 +219,11 @@ def add_inferred_relations( ) # transform into list (as references are stored as such) and make order # deterministic: - target_list = sorted(target_resource_ids) updated_host_resources[host_resource_id] = host_resource.model_copy( update={ "relations": { **host_resource.relations, - instruction.new_property: target_list, + instruction.new_property: target_resource_ids, } } ) diff --git a/src/metldata/builtin_transformations/infer_relations/model_transform.py b/src/metldata/builtin_transformations/infer_relations/model_transform.py index 6f87ca0..41c9efa 100644 --- a/src/metldata/builtin_transformations/infer_relations/model_transform.py +++ b/src/metldata/builtin_transformations/infer_relations/model_transform.py @@ -17,19 +17,105 @@ """Logic for transforming metadata models.""" from schemapack.spec.schemapack import ( - Cardinality, ClassDefinition, + MandatoryRelationSpec, + MultipleRelationSpec, Relation, SchemaPack, ) -from schemapack.utils import FrozenDict +from metldata.builtin_transformations.infer_relations.path.path import RelationPath +from metldata.builtin_transformations.infer_relations.path.path_elements import ( + RelationPathElement, + RelationPathElementType, +) from metldata.builtin_transformations.infer_relations.relations import ( InferenceInstruction, ) from metldata.transform.base import EvitableTransformationError +def get_relation(element: RelationPathElement, schema: SchemaPack) -> Relation: + """Get the relation object for a path element. + + Args: + element: The path element to get the relation for. + schema: The underlying schema. + + Returns: + The relation object. + """ + element_active = element.type_ == RelationPathElementType.ACTIVE + class_name = element.source if element_active else element.target + return schema.classes[class_name].relations[element.property] + + +def infer_mutiplicity_from_path( + path: RelationPath, schema: SchemaPack +) -> MultipleRelationSpec: + """Infer the multiplicity of an inferred relation based on the path. + + Args: + path: The path to infer the multiplicity for. + schema: The underlying schema. + + Returns: + The inferred multiplicity. + """ + origin = target = False + # Traverse the path and check for multiplicity + for element in path.elements: + relation = get_relation(element, schema) + # If any multiplicity is observed, toggle the origin / source flag depending on + # the orientation of the relation. + if element.type_ == RelationPathElementType.ACTIVE: + if relation.multiple.origin: + origin = True + if relation.multiple.target: + target = True + else: + if relation.multiple.origin: + target = True + if relation.multiple.target: + origin = True + if origin and target: + break + return MultipleRelationSpec(origin=origin, target=target) + + +def infer_mandatory_from_path( + path: RelationPath, schema: SchemaPack +) -> MandatoryRelationSpec: + """Infer the mandatory property of an inferred relation based on the path. + + Args: + path: The path to infer the mandatory property for. + schema: The underlying schema. + + Returns: + The inferred mandatory property. + """ + origin = target = True + # Traverse the path and check for mandatory + for element in path.elements: + relation = get_relation(element, schema) + # If either end is not mandatory, toggle the origin / source flag depending on + # the orientation of the relation. + if element.type_ == RelationPathElementType.ACTIVE: + if not relation.mandatory.origin: + origin = False + if not relation.mandatory.target: + target = False + else: + if not relation.mandatory.origin: + target = False + if not relation.mandatory.target: + origin = False + if not origin and not target: + break + return MandatoryRelationSpec(origin=origin, target=target) + + def add_inferred_relations( *, model: SchemaPack, instructions: list[InferenceInstruction] ) -> SchemaPack: @@ -53,12 +139,13 @@ def add_inferred_relations( if class_def is None: raise EvitableTransformationError() + mandatory = infer_mandatory_from_path(instruction.path, model) + multiple = infer_mutiplicity_from_path(instruction.path, model) new_relation = Relation.model_validate( { - "to": instruction.target, - "cardinality": Cardinality.MANY_TO_MANY - if instruction.allow_multiple - else Cardinality.ONE_TO_MANY, + "targetClass": instruction.target, + "mandatory": mandatory, + "multiple": multiple, } ) updated_class_defs[instruction.source] = ClassDefinition.model_validate( @@ -72,6 +159,6 @@ def add_inferred_relations( } ) - return model.model_copy( - update={"classes": FrozenDict({**model.classes, **updated_class_defs})} - ) + model_dict = model.model_dump() + model_dict["classes"].update(updated_class_defs) + return SchemaPack.model_validate(model_dict) diff --git a/src/metldata/builtin_transformations/infer_relations/path/path_str.py b/src/metldata/builtin_transformations/infer_relations/path/path_str.py index 0a1fc16..6d3e548 100644 --- a/src/metldata/builtin_transformations/infer_relations/path/path_str.py +++ b/src/metldata/builtin_transformations/infer_relations/path/path_str.py @@ -17,7 +17,6 @@ """Data models""" import re -from typing import Optional from metldata.builtin_transformations.infer_relations.path.path_elements import ( RelationPathElement, @@ -107,7 +106,7 @@ def get_target_class(*, path_str: str) -> str: return match.group(1) -def split_first_element(*, path_str: str) -> tuple[str, Optional[str]]: +def split_first_element(*, path_str: str) -> tuple[str, str | None]: """Return a tuple of the first element and the remaining path string. Thereby, the target class of the first element is set as the source class of the remaining path. diff --git a/src/metldata/builtin_transformations/infer_relations/relations.py b/src/metldata/builtin_transformations/infer_relations/relations.py index 4144122..962089b 100644 --- a/src/metldata/builtin_transformations/infer_relations/relations.py +++ b/src/metldata/builtin_transformations/infer_relations/relations.py @@ -34,15 +34,6 @@ class RelationDetails(BaseModel): "The path to reconstruct the new relation based on existing relations." ), ) - allow_multiple: bool = Field( - True, - description=( - "Whether multiple target resources to expect for this relation." - + " `True` corresponds to a `many_to_many` cardinality, `False` represents" - + " a `many_to_one` cardinality. `one_to_one` or `one_to_many`" - + " cardinalities are currently not possible for inferred relations." - ), - ) class InferenceInstruction(RelationDetails): diff --git a/src/metldata/custom_types.py b/src/metldata/custom_types.py index 5786f76..6ea1946 100644 --- a/src/metldata/custom_types.py +++ b/src/metldata/custom_types.py @@ -16,9 +16,7 @@ """A collection of custom types.""" -from typing import Any - -from typing_extensions import TypeAlias +from typing import Any, TypeAlias Json: TypeAlias = dict[str, Any] diff --git a/src/metldata/transform/base.py b/src/metldata/transform/base.py index 6117b94..bbdcfd1 100644 --- a/src/metldata/transform/base.py +++ b/src/metldata/transform/base.py @@ -18,9 +18,9 @@ from abc import ABC, abstractmethod from collections import defaultdict -from collections.abc import Generator +from collections.abc import Callable, Generator from graphlib import CycleError, TopologicalSorter -from typing import Callable, Generic, Optional, TypeVar +from typing import Generic, TypeAlias, TypeVar from pydantic import ( BaseModel, @@ -30,11 +30,10 @@ field_validator, model_validator, ) -from schemapack.integrate import integrate -from schemapack.isolate import isolate -from schemapack.spec.datapack import ClassName, DataPack, ResourceId +from schemapack import denormalize, isolate +from schemapack.spec.custom_types import ClassName, ResourceId +from schemapack.spec.datapack import DataPack from schemapack.spec.schemapack import SchemaPack -from typing_extensions import TypeAlias from metldata.custom_types import Json @@ -148,7 +147,7 @@ class WorkflowStepBase(BaseModel, ABC): model_config = ConfigDict(frozen=True) description: str = Field(..., description="A description of the step.") - input: Optional[str] = Field( + input: str | None = Field( ..., description=( "The name of the workflow step from which the output is used as input" @@ -286,6 +285,10 @@ class ArtifactResource(BaseModel): ..., description="A rooted datapack describing the resource and all its dependencies.", ) + schema: SchemaPack = Field( + ..., + description="A rooted schemapack describing the schema of the rooted datapack.", + ) integrated: Json = Field( ..., description="An integrated representation of the resource in JSON format.", @@ -313,19 +316,20 @@ def resource_iterator(self) -> Generator[ArtifactResource, None, None]: """ for class_name, resources in self.data.resources.items(): for resource_id in resources: - isolated_datapack = isolate( + rooted_schemapack, rooted_datapack = isolate( datapack=self.data, class_name=class_name, resource_id=resource_id, schemapack=self.model, ) - integrated_json = integrate( - datapack=isolated_datapack, - schemapack=self.model, + integrated_json = denormalize( + datapack=rooted_datapack, + schemapack=rooted_schemapack, ) yield ArtifactResource( class_name=class_name, resource_id=resource_id, - datapack=isolated_datapack, + datapack=rooted_datapack, + schema=rooted_schemapack, integrated=integrated_json, ) diff --git a/src/metldata/transform/handling.py b/src/metldata/transform/handling.py index edabe96..ecf5ca6 100644 --- a/src/metldata/transform/handling.py +++ b/src/metldata/transform/handling.py @@ -18,9 +18,9 @@ import schemapack.exceptions from pydantic import BaseModel, ConfigDict +from schemapack import SchemaPackValidator from schemapack.spec.datapack import DataPack from schemapack.spec.schemapack import SchemaPack -from schemapack.validation import SchemaPackValidator from metldata.transform.base import ( Config, diff --git a/tests/builtin_transformations/infer_relations/path/test_config.py b/tests/builtin_transformations/infer_relations/path/test_config.py index 051f293..803332a 100644 --- a/tests/builtin_transformations/infer_relations/path/test_config.py +++ b/tests/builtin_transformations/infer_relations/path/test_config.py @@ -33,17 +33,14 @@ def test_config(): "class_a": { "class_d": { "path": "class_a(class_b)>class_b(class_d)>class_d", - "allow_multiple": False, }, "class_c": { "path": "class_a(class_b)>class_b<(class_c)class_c", - "allow_multiple": True, }, }, "class_b": { "class_c": { "path": "class_b<(class_c)class_c", - "allow_multiple": True, } }, } @@ -53,21 +50,18 @@ def test_config(): target="class_d", path=RelationPath(path_str="class_a(class_b)>class_b(class_d)>class_d"), new_property="class_d", - allow_multiple=False, ), InferenceInstruction( source="class_a", target="class_c", path=RelationPath(path_str="class_a(class_b)>class_b<(class_c)class_c"), new_property="class_c", - allow_multiple=True, ), InferenceInstruction( source="class_b", target="class_c", path=RelationPath(path_str="class_b<(class_c)class_c"), new_property="class_c", - allow_multiple=True, ), ] diff --git a/tests/builtin_transformations/infer_relations/path/test_path_str.py b/tests/builtin_transformations/infer_relations/path/test_path_str.py index 46636fb..3cb06ee 100644 --- a/tests/builtin_transformations/infer_relations/path/test_path_str.py +++ b/tests/builtin_transformations/infer_relations/path/test_path_str.py @@ -17,7 +17,6 @@ """Test the path_str module.""" from contextlib import nullcontext -from typing import Optional import pytest @@ -132,7 +131,7 @@ def test_get_target_class(path_str: str, expected_target_class: str): ], ) def test_split_first_element( - path_str: str, expected_first_element: str, expected_remaining_path: Optional[str] + path_str: str, expected_first_element: str, expected_remaining_path: str | None ): """Test the split_first_element method.""" observed_first_element, observed_remaining_path = split_first_element( diff --git a/tests/fixtures/data.py b/tests/fixtures/data.py index 03e74d1..4f70c22 100644 --- a/tests/fixtures/data.py +++ b/tests/fixtures/data.py @@ -16,7 +16,7 @@ """Valid and invalid metadata examples using the minimal model.""" -from schemapack.load import load_datapack +from schemapack import load_datapack from schemapack.spec.datapack import DataPack from tests.fixtures.utils import BASE_DIR diff --git a/tests/fixtures/example_content_schemas/Dataset.schema.json b/tests/fixtures/example_content_schemas/Dataset.schema.json index 9358f83..b8eddbd 100644 --- a/tests/fixtures/example_content_schemas/Dataset.schema.json +++ b/tests/fixtures/example_content_schemas/Dataset.schema.json @@ -3,15 +3,9 @@ "additionalProperties": false, "description": "A dataset that is a collection of files.", "properties": { - "alias": { - "type": "string" - }, "dac_contact": { "type": "string" } }, - "required": [ - "alias" - ], "type": "object" } diff --git a/tests/fixtures/example_content_schemas/Experiment.schema.json b/tests/fixtures/example_content_schemas/Experiment.schema.json index fdb2de4..47a97c4 100644 --- a/tests/fixtures/example_content_schemas/Experiment.schema.json +++ b/tests/fixtures/example_content_schemas/Experiment.schema.json @@ -3,15 +3,9 @@ "additionalProperties": false, "description": "An experiment containing one or multiple samples.", "properties": { - "alias": { - "type": "string" - }, "description": { "type": "string" } }, - "required": [ - "alias" - ], "type": "object" } diff --git a/tests/fixtures/example_content_schemas/File.schema.json b/tests/fixtures/example_content_schemas/File.schema.json index 5ca385d..74b50c1 100644 --- a/tests/fixtures/example_content_schemas/File.schema.json +++ b/tests/fixtures/example_content_schemas/File.schema.json @@ -3,9 +3,6 @@ "additionalProperties": false, "description": "A file is an object that contains information generated from a process, either an Experiment or an Analysis.", "properties": { - "alias": { - "type": "string" - }, "checksum": { "type": "string" }, @@ -20,7 +17,6 @@ } }, "required": [ - "alias", "filename", "format", "checksum", diff --git a/tests/fixtures/example_content_schemas/Sample.schema.json b/tests/fixtures/example_content_schemas/Sample.schema.json index 54880cb..8656493 100644 --- a/tests/fixtures/example_content_schemas/Sample.schema.json +++ b/tests/fixtures/example_content_schemas/Sample.schema.json @@ -3,15 +3,9 @@ "additionalProperties": false, "description": "A sample used to generate files in the context of an experiment.", "properties": { - "alias": { - "type": "string" - }, "description": { "type": "string" } }, - "required": [ - "alias" - ], "type": "object" } diff --git a/tests/fixtures/example_data/advanced.datapack.yaml b/tests/fixtures/example_data/advanced.datapack.yaml index 9dddd42..1f11158 100644 --- a/tests/fixtures/example_data/advanced.datapack.yaml +++ b/tests/fixtures/example_data/advanced.datapack.yaml @@ -1,23 +1,20 @@ -datapack: 0.1.0 +datapack: 0.3.0 resources: File: file_a: content: - alias: file_a filename: file_a.fastq format: FASTQ checksum: 1a5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92b8 size: 12321 file_b: content: - alias: file_b filename: file_b.fastq format: FASTQ checksum: 2b5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92c9 size: 12314 file_c: content: - alias: file_c filename: file_c.fastq format: FASTQ checksum: a9c24870071da03f78515e6197048f3a2172e90e597e9250cd01a0cb8f0986ed @@ -25,7 +22,6 @@ resources: Dataset: dataset_1: content: - alias: dataset_1 dac_contact: dac@example.org relations: files: @@ -35,22 +31,19 @@ resources: Sample: sample_x: content: - alias: sample_x description: Some sample. relations: files: - file_a - file_b sample_y: - content: - alias: sample_y + content: {} relations: files: - file_c Experiment: experiment_i: - content: - alias: experiment_i + content: {} relations: samples: - sample_x diff --git a/tests/fixtures/example_data/invalid_minimal.datapack.yaml b/tests/fixtures/example_data/invalid_minimal.datapack.yaml index 14e3a2e..cf44f19 100644 --- a/tests/fixtures/example_data/invalid_minimal.datapack.yaml +++ b/tests/fixtures/example_data/invalid_minimal.datapack.yaml @@ -1,10 +1,9 @@ # Misses content property defined in the content schema: -datapack: 0.1.0 +datapack: 0.3.0 resources: File: example_file_a: content: - alias: example_file_a filename: example_file_a.fastq format: FASTQ checksum: 1a5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92b8 @@ -12,7 +11,6 @@ resources: Dataset: example_dataset: content: - alias: example_dataset dac_contact: dac@example.org relations: files: diff --git a/tests/fixtures/example_data/minimal.datapack.yaml b/tests/fixtures/example_data/minimal.datapack.yaml index 21cca89..722ff80 100644 --- a/tests/fixtures/example_data/minimal.datapack.yaml +++ b/tests/fixtures/example_data/minimal.datapack.yaml @@ -1,23 +1,20 @@ -datapack: 0.1.0 +datapack: 0.3.0 resources: File: example_file_a: content: - alias: example_file_a filename: example_file_a.fastq format: FASTQ checksum: 1a5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92b8 size: 12321 example_file_b: content: - alias: example_file_b filename: example_file_b.fastq format: FASTQ checksum: 2b5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92c9 size: 12314 example_file_c: content: - alias: example_file_c filename: example_file_c.fastq format: FASTQ checksum: a9c24870071da03f78515e6197048f3a2172e90e597e9250cd01a0cb8f0986ed @@ -25,7 +22,6 @@ resources: Dataset: example_dataset_1: content: - alias: example_dataset_1 dac_contact: dac@example.org relations: files: @@ -33,7 +29,6 @@ resources: - example_file_b example_dataset_2: content: - alias: example_dataset_2 dac_contact: dac@example.org relations: files: diff --git a/tests/fixtures/example_data/valid_minimal.datapack.yaml b/tests/fixtures/example_data/valid_minimal.datapack.yaml index 21cca89..722ff80 100644 --- a/tests/fixtures/example_data/valid_minimal.datapack.yaml +++ b/tests/fixtures/example_data/valid_minimal.datapack.yaml @@ -1,23 +1,20 @@ -datapack: 0.1.0 +datapack: 0.3.0 resources: File: example_file_a: content: - alias: example_file_a filename: example_file_a.fastq format: FASTQ checksum: 1a5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92b8 size: 12321 example_file_b: content: - alias: example_file_b filename: example_file_b.fastq format: FASTQ checksum: 2b5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92c9 size: 12314 example_file_c: content: - alias: example_file_c filename: example_file_c.fastq format: FASTQ checksum: a9c24870071da03f78515e6197048f3a2172e90e597e9250cd01a0cb8f0986ed @@ -25,7 +22,6 @@ resources: Dataset: example_dataset_1: content: - alias: example_dataset_1 dac_contact: dac@example.org relations: files: @@ -33,7 +29,6 @@ resources: - example_file_b example_dataset_2: content: - alias: example_dataset_2 dac_contact: dac@example.org relations: files: diff --git a/tests/fixtures/example_models/advanced.schemapack.yaml b/tests/fixtures/example_models/advanced.schemapack.yaml index e5493ca..29b38aa 100644 --- a/tests/fixtures/example_models/advanced.schemapack.yaml +++ b/tests/fixtures/example_models/advanced.schemapack.yaml @@ -1,31 +1,46 @@ # a more advanced schemapack: -schemapack: 0.1.0 +schemapack: 0.3.0 classes: File: id: - from_content: alias + propertyName: alias content: ../example_content_schemas/File.schema.json Dataset: id: - from_content: alias + propertyName: alias content: ../example_content_schemas/Dataset.schema.json relations: files: - to: File - cardinality: many_to_many + targetClass: File + multiple: + origin: true + target: true + mandatory: + origin: false + target: true Sample: id: - from_content: alias + propertyName: alias content: ../example_content_schemas/Sample.schema.json relations: files: - to: File - cardinality: one_to_many + targetClass: File + multiple: + origin: false + target: true + mandatory: + origin: false + target: true Experiment: id: - from_content: alias + propertyName: alias content: ../example_content_schemas/Experiment.schema.json relations: samples: - to: Sample - cardinality: one_to_many + targetClass: Sample + multiple: + origin: false + target: true + mandatory: + origin: true + target: true diff --git a/tests/fixtures/example_models/minimal.schemapack.yaml b/tests/fixtures/example_models/minimal.schemapack.yaml index c475c9e..6cf1654 100644 --- a/tests/fixtures/example_models/minimal.schemapack.yaml +++ b/tests/fixtures/example_models/minimal.schemapack.yaml @@ -1,15 +1,20 @@ # a simple schemapack: -schemapack: 0.1.0 +schemapack: 0.3.0 classes: File: id: - from_content: alias + propertyName: alias content: ../example_content_schemas/File.schema.json Dataset: id: - from_content: alias + propertyName: alias content: ../example_content_schemas/Dataset.schema.json relations: files: - to: File - cardinality: many_to_many + targetClass: File + multiple: + origin: true + target: true + mandatory: + origin: false + target: true diff --git a/tests/fixtures/example_transformations/delete_properties/multiple/transformed.datapack.yaml b/tests/fixtures/example_transformations/delete_properties/multiple/transformed.datapack.yaml index 462977a..6e363cd 100644 --- a/tests/fixtures/example_transformations/delete_properties/multiple/transformed.datapack.yaml +++ b/tests/fixtures/example_transformations/delete_properties/multiple/transformed.datapack.yaml @@ -1,25 +1,21 @@ -datapack: 0.1.0 +datapack: 0.3.0 resources: File: file_a: content: - alias: file_a format: FASTQ size: 12321 file_b: content: - alias: file_b format: FASTQ size: 12314 file_c: content: - alias: file_c format: FASTQ size: 12123 Dataset: dataset_1: content: - alias: dataset_1 dac_contact: dac@example.org relations: files: @@ -28,22 +24,19 @@ resources: - file_c Sample: sample_x: - content: - alias: sample_x + content: {} relations: files: - file_a - file_b sample_y: - content: - alias: sample_y + content: {} relations: files: - file_c Experiment: experiment_i: - content: - alias: experiment_i + content: {} relations: samples: - sample_x diff --git a/tests/fixtures/example_transformations/delete_properties/multiple/transformed.schemapack.yaml b/tests/fixtures/example_transformations/delete_properties/multiple/transformed.schemapack.yaml index 60ac427..d56e22d 100644 --- a/tests/fixtures/example_transformations/delete_properties/multiple/transformed.schemapack.yaml +++ b/tests/fixtures/example_transformations/delete_properties/multiple/transformed.schemapack.yaml @@ -1,17 +1,14 @@ # Transformed content schemas are embedded: -schemapack: 0.1.0 +schemapack: 0.3.0 classes: File: id: - from_content: alias + propertyName: alias content: { # <- "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "description": "A file is an object that contains information generated from a process, either an Experiment or an Analysis.", "properties": { - "alias": { - "type": "string" - }, "format": { "type": "string" }, @@ -20,7 +17,6 @@ classes: } }, "required": [ - "alias", "format", "size" ], @@ -28,38 +24,47 @@ classes: } Dataset: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Dataset.schema.json relations: files: - to: File - cardinality: many_to_many + targetClass: File + multiple: + origin: true + target: true + mandatory: + origin: false + target: true Sample: id: - from_content: alias + propertyName: alias content: { # <- "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "description": "A sample used to generate files in the context of an experiment.", "properties": { - "alias": { - "type": "string" - }, }, - "required": [ - "alias" - ], "type": "object" } relations: files: - to: File - cardinality: one_to_many + targetClass: File + multiple: + origin: false + target: true + mandatory: + origin: false + target: true Experiment: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Experiment.schema.json relations: samples: - to: Sample - cardinality: one_to_many + targetClass: Sample + multiple: + origin: false + target: true + mandatory: + origin: true + target: true diff --git a/tests/fixtures/example_transformations/infer_relations/active_relations/transformed.datapack.yaml b/tests/fixtures/example_transformations/infer_relations/active_relations/transformed.datapack.yaml index cdcd1c0..7a4c978 100644 --- a/tests/fixtures/example_transformations/infer_relations/active_relations/transformed.datapack.yaml +++ b/tests/fixtures/example_transformations/infer_relations/active_relations/transformed.datapack.yaml @@ -1,23 +1,20 @@ -datapack: 0.1.0 +datapack: 0.3.0 resources: File: file_a: content: - alias: file_a filename: file_a.fastq format: FASTQ checksum: 1a5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92b8 size: 12321 file_b: content: - alias: file_b filename: file_b.fastq format: FASTQ checksum: 2b5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92c9 size: 12314 file_c: content: - alias: file_c filename: file_c.fastq format: FASTQ checksum: a9c24870071da03f78515e6197048f3a2172e90e597e9250cd01a0cb8f0986ed @@ -25,7 +22,6 @@ resources: Dataset: dataset_1: content: - alias: dataset_1 dac_contact: dac@example.org relations: files: @@ -35,22 +31,19 @@ resources: Sample: sample_x: content: - alias: sample_x description: Some sample. relations: files: - file_a - file_b sample_y: - content: - alias: sample_y + content: {} relations: files: - file_c Experiment: experiment_i: - content: - alias: experiment_i + content: {} relations: samples: - sample_x diff --git a/tests/fixtures/example_transformations/infer_relations/active_relations/transformed.schemapack.yaml b/tests/fixtures/example_transformations/infer_relations/active_relations/transformed.schemapack.yaml index f963147..9a1c10c 100644 --- a/tests/fixtures/example_transformations/infer_relations/active_relations/transformed.schemapack.yaml +++ b/tests/fixtures/example_transformations/infer_relations/active_relations/transformed.schemapack.yaml @@ -1,33 +1,53 @@ -schemapack: 0.1.0 +schemapack: 0.3.0 classes: File: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/File.schema.json Dataset: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Dataset.schema.json relations: files: - to: File - cardinality: many_to_many + targetClass: File + multiple: + origin: true + target: true + mandatory: + origin: false + target: true Sample: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Sample.schema.json relations: files: - to: File - cardinality: one_to_many + targetClass: File + multiple: + origin: false + target: true + mandatory: + origin: false + target: true Experiment: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Experiment.schema.json relations: samples: - to: Sample - cardinality: one_to_many + targetClass: Sample + multiple: + origin: false + target: true + mandatory: + origin: true + target: true files: # <- - to: File - cardinality: many_to_many + targetClass: File + multiple: + origin: false + target: true + mandatory: + origin: false + target: true diff --git a/tests/fixtures/example_transformations/infer_relations/complex_relations/transformed.datapack.yaml b/tests/fixtures/example_transformations/infer_relations/complex_relations/transformed.datapack.yaml index 856c106..815c5e3 100644 --- a/tests/fixtures/example_transformations/infer_relations/complex_relations/transformed.datapack.yaml +++ b/tests/fixtures/example_transformations/infer_relations/complex_relations/transformed.datapack.yaml @@ -1,23 +1,20 @@ -datapack: 0.1.0 +datapack: 0.3.0 resources: File: file_a: content: - alias: file_a filename: file_a.fastq format: FASTQ checksum: 1a5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92b8 size: 12321 file_b: content: - alias: file_b filename: file_b.fastq format: FASTQ checksum: 2b5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92c9 size: 12314 file_c: content: - alias: file_c filename: file_c.fastq format: FASTQ checksum: a9c24870071da03f78515e6197048f3a2172e90e597e9250cd01a0cb8f0986ed @@ -25,7 +22,6 @@ resources: Dataset: dataset_1: content: - alias: dataset_1 dac_contact: dac@example.org relations: files: @@ -38,22 +34,19 @@ resources: Sample: sample_x: content: - alias: sample_x description: Some sample. relations: files: - file_a - file_b sample_y: - content: - alias: sample_y + content: {} relations: files: - file_c Experiment: experiment_i: - content: - alias: experiment_i + content: {} relations: samples: - sample_x diff --git a/tests/fixtures/example_transformations/infer_relations/complex_relations/transformed.schemapack.yaml b/tests/fixtures/example_transformations/infer_relations/complex_relations/transformed.schemapack.yaml index 9d3e1d7..deee69a 100644 --- a/tests/fixtures/example_transformations/infer_relations/complex_relations/transformed.schemapack.yaml +++ b/tests/fixtures/example_transformations/infer_relations/complex_relations/transformed.schemapack.yaml @@ -1,33 +1,53 @@ -schemapack: 0.1.0 +schemapack: 0.3.0 classes: File: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/File.schema.json Dataset: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Dataset.schema.json relations: files: - to: File - cardinality: many_to_many + targetClass: File + multiple: + origin: true + target: true + mandatory: + origin: false + target: true samples: # <- - to: Sample - cardinality: many_to_many + targetClass: Sample + multiple: + origin: true + target: true + mandatory: + origin: false + target: false Sample: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Sample.schema.json relations: files: - to: File - cardinality: one_to_many + targetClass: File + multiple: + origin: false + target: true + mandatory: + origin: false + target: true Experiment: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Experiment.schema.json relations: samples: - to: Sample - cardinality: one_to_many + targetClass: Sample + multiple: + origin: false + target: true + mandatory: + origin: true + target: true diff --git a/tests/fixtures/example_transformations/infer_relations/passive_relations/input.datapack.yaml b/tests/fixtures/example_transformations/infer_relations/passive_relations/input.datapack.yaml index 62d4543..e535736 100644 --- a/tests/fixtures/example_transformations/infer_relations/passive_relations/input.datapack.yaml +++ b/tests/fixtures/example_transformations/infer_relations/passive_relations/input.datapack.yaml @@ -1,23 +1,20 @@ -datapack: 0.1.0 +datapack: 0.3.0 resources: File: file_a: content: - alias: file_a filename: file_a.fastq format: FASTQ checksum: 1a5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92b8 size: 12321 file_b: content: - alias: file_b filename: file_b.fastq format: FASTQ checksum: 2b5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92c9 size: 12314 file_c: content: - alias: file_c filename: file_c.fastq format: FASTQ checksum: a9c24870071da03f78515e6197048f3a2172e90e597e9250cd01a0cb8f0986ed @@ -25,7 +22,6 @@ resources: Dataset: dataset_1: content: - alias: dataset_1 dac_contact: dac@example.org relations: files: @@ -33,7 +29,6 @@ resources: - file_b dataset_2: content: - alias: dataset_2 dac_contact: dac@example.org relations: files: diff --git a/tests/fixtures/example_transformations/infer_relations/passive_relations/transformed.datapack.yaml b/tests/fixtures/example_transformations/infer_relations/passive_relations/transformed.datapack.yaml index 9522b0b..d367f63 100644 --- a/tests/fixtures/example_transformations/infer_relations/passive_relations/transformed.datapack.yaml +++ b/tests/fixtures/example_transformations/infer_relations/passive_relations/transformed.datapack.yaml @@ -1,9 +1,8 @@ -datapack: 0.1.0 +datapack: 0.3.0 resources: File: file_a: content: - alias: file_a filename: file_a.fastq format: FASTQ checksum: 1a5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92b8 @@ -14,7 +13,6 @@ resources: - dataset_2 file_b: content: - alias: file_b filename: file_b.fastq format: FASTQ checksum: 2b5ac10ab42911dc0224172c118a326d9a4c03969112a2f3eb1ad971e96e92c9 @@ -24,7 +22,6 @@ resources: - dataset_1 file_c: content: - alias: file_c filename: file_c.fastq format: FASTQ checksum: a9c24870071da03f78515e6197048f3a2172e90e597e9250cd01a0cb8f0986ed @@ -34,7 +31,6 @@ resources: Dataset: dataset_1: content: - alias: dataset_1 dac_contact: dac@example.org relations: files: @@ -42,7 +38,6 @@ resources: - file_b dataset_2: content: - alias: dataset_2 dac_contact: dac@example.org relations: files: diff --git a/tests/fixtures/example_transformations/infer_relations/passive_relations/transformed.schemapack.yaml b/tests/fixtures/example_transformations/infer_relations/passive_relations/transformed.schemapack.yaml index 1209e54..87002ca 100644 --- a/tests/fixtures/example_transformations/infer_relations/passive_relations/transformed.schemapack.yaml +++ b/tests/fixtures/example_transformations/infer_relations/passive_relations/transformed.schemapack.yaml @@ -1,34 +1,54 @@ -schemapack: 0.1.0 +schemapack: 0.3.0 classes: File: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/File.schema.json relations: # <- datasets: - to: Dataset - cardinality: many_to_many + targetClass: Dataset + multiple: + origin: true + target: true + mandatory: + origin: true + target: false Dataset: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Dataset.schema.json relations: files: - to: File - cardinality: many_to_many + targetClass: File + multiple: + origin: true + target: true + mandatory: + origin: false + target: true Sample: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Sample.schema.json relations: files: - to: File - cardinality: one_to_many + targetClass: File + multiple: + origin: false + target: true + mandatory: + origin: false + target: true Experiment: id: - from_content: alias + propertyName: alias content: ../../../example_content_schemas/Experiment.schema.json relations: samples: - to: Sample - cardinality: one_to_many + targetClass: Sample + multiple: + origin: false + target: true + mandatory: + origin: true + target: true diff --git a/tests/fixtures/models.py b/tests/fixtures/models.py index f755376..643f596 100644 --- a/tests/fixtures/models.py +++ b/tests/fixtures/models.py @@ -16,7 +16,7 @@ """Example models.""" -from schemapack.load import load_schemapack +from schemapack import load_schemapack from schemapack.spec.schemapack import SchemaPack from tests.fixtures.utils import BASE_DIR diff --git a/tests/fixtures/transformations.py b/tests/fixtures/transformations.py index c194420..ae56376 100644 --- a/tests/fixtures/transformations.py +++ b/tests/fixtures/transformations.py @@ -19,7 +19,7 @@ from dataclasses import dataclass from pydantic import BaseModel -from schemapack.load import load_datapack, load_schemapack +from schemapack import load_datapack, load_schemapack from schemapack.spec.datapack import DataPack from schemapack.spec.schemapack import SchemaPack diff --git a/tests/transform/test_base.py b/tests/transform/test_base.py index fa3595d..42424a7 100644 --- a/tests/transform/test_base.py +++ b/tests/transform/test_base.py @@ -239,9 +239,8 @@ def test_workflow_artifact_resource_iterator(): observed_resources_by_class = defaultdict(set) for resource in resources: # check that the datapacks of all resources are rooted: - assert resource.datapack.root - assert resource.class_name == resource.datapack.root.class_name - assert resource.resource_id == resource.datapack.root.resource_id + assert resource.class_name == resource.schema.rootClass + assert resource.resource_id == resource.datapack.rootResource observed_resources_by_class[resource.class_name].add(resource.resource_id)