From 632d0a6586fbf1e1b197285887b543af00753ad5 Mon Sep 17 00:00:00 2001 From: Ian Date: Tue, 11 Jun 2024 12:19:03 -0700 Subject: [PATCH 1/9] feat(server): support jobs without `rights` field --- poetry.lock | 154 ++++++++++++++++++------------------------------- pyproject.toml | 4 +- 2 files changed, 58 insertions(+), 100 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3de016f4f..67b6e9df6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -98,7 +98,6 @@ files = [ [package.dependencies] aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" @@ -187,17 +186,6 @@ types-python-dateutil = ">=2.8.10" doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - [[package]] name = "attrs" version = "23.2.0" @@ -602,20 +590,6 @@ typing-extensions = ">=3.7.4.3" all = ["sphinx (>=3.4.0)", "sphinx-jinja2-compat (>=0.1.1)", "sphinx-toolbox (>=2.16.0)"] sphinx = ["sphinx (>=3.4.0)", "sphinx-jinja2-compat (>=0.1.1)", "sphinx-toolbox (>=2.16.0)"] -[[package]] -name = "exceptiongroup" -version = "1.2.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, -] - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "faker" version = "25.3.0" @@ -1155,57 +1129,57 @@ files = [ [[package]] name = "orjson" -version = "3.10.3" +version = "3.10.4" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"}, - {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"}, - {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"}, - {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"}, - {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"}, - {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"}, - {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"}, - {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"}, - {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"}, - {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"}, - {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"}, - {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"}, - {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"}, - {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"}, - {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"}, - {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"}, - {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"}, - {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"}, - {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"}, - {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"}, - {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"}, - {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"}, - {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"}, - {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"}, - {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"}, - {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"}, + {file = "orjson-3.10.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:afca963f19ca60c7aedadea9979f769139127288dd58ccf3f7c5e8e6dc62cabf"}, + {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b112eff36ba7ccc7a9d6b87e17b9d6bde4312d05e3ddf66bf5662481dee846"}, + {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02b192eaba048b1039eca9a0cef67863bd5623042f5c441889a9957121d97e14"}, + {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:827c3d0e4fc44242c82bfdb1a773235b8c0575afee99a9fa9a8ce920c14e440f"}, + {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca8ec09724f10ec209244caeb1f9f428b6bb03f2eda9ed5e2c4dd7f2b7fabd44"}, + {file = "orjson-3.10.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8eaa5d531a8fde11993cbcb27e9acf7d9c457ba301adccb7fa3a021bfecab46c"}, + {file = "orjson-3.10.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e112aa7fc4ea67367ec5e86c39a6bb6c5719eddc8f999087b1759e765ddaf2d4"}, + {file = "orjson-3.10.4-cp310-none-win32.whl", hash = "sha256:1538844fb88446c42da3889f8c4ecce95a630b5a5ba18ecdfe5aea596f4dff21"}, + {file = "orjson-3.10.4-cp310-none-win_amd64.whl", hash = "sha256:de02811903a2e434127fba5389c3cc90f689542339a6e52e691ab7f693407b5a"}, + {file = "orjson-3.10.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:358afaec75de7237dfea08e6b1b25d226e33a1e3b6dc154fc99eb697f24a1ffa"}, + {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb4e292c3198ab3d93e5f877301d2746be4ca0ba2d9c513da5e10eb90e19ff52"}, + {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c39e57cf6323a39238490092985d5d198a7da4a3be013cc891a33fef13a536e"}, + {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f86df433fc01361ff9270ad27455ce1ad43cd05e46de7152ca6adb405a16b2f6"}, + {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c9966276a2c97e93e6cbe8286537f88b2a071827514f0d9d47a0aefa77db458"}, + {file = "orjson-3.10.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c499a14155a1f5a1e16e0cd31f6cf6f93965ac60a0822bc8340e7e2d3dac1108"}, + {file = "orjson-3.10.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3087023ce904a327c29487eb7e1f2c060070e8dbb9a3991b8e7952a9c6e62f38"}, + {file = "orjson-3.10.4-cp311-none-win32.whl", hash = "sha256:f965893244fe348b59e5ce560693e6dd03368d577ce26849b5d261ce31c70101"}, + {file = "orjson-3.10.4-cp311-none-win_amd64.whl", hash = "sha256:c212f06fad6aa6ce85d5665e91a83b866579f29441a47d3865c57329c0857357"}, + {file = "orjson-3.10.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d0965a8b0131959833ca8a65af60285995d57ced0de2fd8f16fc03235975d238"}, + {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27b64695d9f2aef3ae15a0522e370ec95c946aaea7f2c97a1582a62b3bdd9169"}, + {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:867d882ddee6a20be4c8b03ae3d2b0333894d53ad632d32bd9b8123649577171"}, + {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0667458f8a8ceb6dee5c08fec0b46195f92c474cbbec71dca2a6b7fd5b67b8d"}, + {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3eac9befc4eaec1d1ff3bba6210576be4945332dde194525601c5ddb5c060d3"}, + {file = "orjson-3.10.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4343245443552eae240a33047a6d1bcac7a754ad4b1c57318173c54d7efb9aea"}, + {file = "orjson-3.10.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30153e269eea43e98918d4d462a36a7065031d9246407dfff2579a4e457515c1"}, + {file = "orjson-3.10.4-cp312-none-win32.whl", hash = "sha256:1a7d092ee043abf3db19c2183115e80676495c9911843fdb3ebd48ca7b73079e"}, + {file = "orjson-3.10.4-cp312-none-win_amd64.whl", hash = "sha256:07a2adbeb8b9efe6d68fc557685954a1f19d9e33f5cc018ae1a89e96647c1b65"}, + {file = "orjson-3.10.4-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f5a746f3d908bce1a1e347b9ca89864047533bdfab5a450066a0315f6566527b"}, + {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:465b4a8a3e459f8d304c19071b4badaa9b267c59207a005a7dd9dfe13d3a423f"}, + {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35858d260728c434a3d91b60685ab32418318567e8902039837e1c2af2719e0b"}, + {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a5ba090d40c4460312dd69c232b38c2ff67a823185cfe667e841c9dd5c06841"}, + {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dde86755d064664e62e3612a166c28298aa8dfd35a991553faa58855ae739cc"}, + {file = "orjson-3.10.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:020a9e9001cfec85c156ef3b185ff758b62ef986cefdb8384c4579facd5ce126"}, + {file = "orjson-3.10.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3bf8e6e3388a2e83a86466c912387e0f0a765494c65caa7e865f99969b76ba0d"}, + {file = "orjson-3.10.4-cp38-none-win32.whl", hash = "sha256:c5a1cca6a4a3129db3da68a25dc0a459a62ae58e284e363b35ab304202d9ba9e"}, + {file = "orjson-3.10.4-cp38-none-win_amd64.whl", hash = "sha256:ecd97d98d7bee3e3d51d0b51c92c457f05db4993329eea7c69764f9820e27eb3"}, + {file = "orjson-3.10.4-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:71362daa330a2fc85553a1469185ac448547392a8f83d34e67779f8df3a52743"}, + {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d24b59d1fecb0fd080c177306118a143f7322335309640c55ed9580d2044e363"}, + {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e906670aea5a605b083ebb58d575c35e88cf880fa372f7cedaac3d51e98ff164"}, + {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ce32ed4bc4d632268e4978e595fe5ea07e026b751482b4a0feec48f66a90abc"}, + {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dcd34286246e0c5edd0e230d1da2daab2c1b465fcb6bac85b8d44057229d40a"}, + {file = "orjson-3.10.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c45d4b8c403e50beedb1d006a8916d9910ed56bceaf2035dc253618b44d0a161"}, + {file = "orjson-3.10.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:aaed3253041b5002a4f5bfdf6f7b5cce657d974472b0699a469d439beba40381"}, + {file = "orjson-3.10.4-cp39-none-win32.whl", hash = "sha256:9a4f41b7dbf7896f8dbf559b9b43dcd99e31e0d49ac1b59d74f52ce51ab10eb9"}, + {file = "orjson-3.10.4-cp39-none-win_amd64.whl", hash = "sha256:6c4eb7d867ed91cb61e6514cb4f457aa01d7b0fd663089df60a69f3d38b69d4c"}, + {file = "orjson-3.10.4.tar.gz", hash = "sha256:c912ed25b787c73fe994a5decd81c3f3b256599b8a87d410d799d5d52013af2a"}, ] [[package]] @@ -1451,11 +1425,9 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -1554,9 +1526,6 @@ files = [ {file = "redis-5.0.4.tar.gz", hash = "sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61"}, ] -[package.dependencies] -async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} - [package.extras] hiredis = ["hiredis (>=1.0.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] @@ -1687,13 +1656,13 @@ files = [ [[package]] name = "sentry-sdk" -version = "2.3.1" +version = "2.5.1" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" files = [ - {file = "sentry_sdk-2.3.1-py2.py3-none-any.whl", hash = "sha256:c5aeb095ba226391d337dd42a6f9470d86c9fc236ecc71cfc7cd1942b45010c6"}, - {file = "sentry_sdk-2.3.1.tar.gz", hash = "sha256:139a71a19f5e9eb5d3623942491ce03cf8ebc14ea2e39ba3e6fe79560d8a5b1f"}, + {file = "sentry_sdk-2.5.1-py2.py3-none-any.whl", hash = "sha256:1f87acdce4a43a523ae5aa21a3fc37522d73ebd9ec04b1dbf01aa3d173852def"}, + {file = "sentry_sdk-2.5.1.tar.gz", hash = "sha256:fbc40a78a8a9c6675133031116144f0d0940376fa6e4e1acd5624c90b0aaf58b"}, ] [package.dependencies] @@ -2075,17 +2044,6 @@ files = [ [package.extras] widechars = ["wcwidth"] -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - [[package]] name = "tornado" version = "6.4" @@ -2147,13 +2105,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtool-core" -version = "12.3.1" +version = "14.0.0" description = "Core utilities for Virtool." optional = false -python-versions = "<3.13,>3.9" +python-versions = "<3.13,>=3.12" files = [ - {file = "virtool_core-12.3.1-py3-none-any.whl", hash = "sha256:680f6b7cbe26edf1b8a97d7655827774b623f0454a60bbc4eebed424be915a09"}, - {file = "virtool_core-12.3.1.tar.gz", hash = "sha256:0bc9a3b21d54fb4187bd55abc68f791d41174cc9310a550afc23d4a3a9ebb6b6"}, + {file = "virtool_core-14.0.0-py3-none-any.whl", hash = "sha256:b3d76c7bddd91cb82b0de41ab9e3e5c9b85dabbf51c44363744619a1672f7955"}, + {file = "virtool_core-14.0.0.tar.gz", hash = "sha256:1e618f07e1239d868e8da98d18cf0d710516eff59e65f1ba37ff0d175992a69f"}, ] [package.dependencies] @@ -2285,5 +2243,5 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" -python-versions = "^3.10,<3.13" -content-hash = "087a52101981c86d4f671da03ab6a3ca3604029b4d29b72434f327e7aeec4987" +python-versions = "~3.12" +content-hash = "c62e5f027b967c435dd1721280c29bea0af1a20baa198106c0be569f6c7b0f08" diff --git a/pyproject.toml b/pyproject.toml index fa20edd63..4e5fed6ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.10,<3.13" +python = "~3.12" aiofiles = "^0.7.0" aiohttp = "^3.8.1" biopython = "^1.81" @@ -38,7 +38,7 @@ orjson = "^3.9.9" pydantic-factories = "^1.17.3" pyfixtures = "^1.0.0" sentry-sdk = "^2.3.1" -virtool-core = "^12.0.0" +virtool-core = "^14.0.0" [tool.poetry.scripts] run-workflow = "virtool_workflow.cli:cli_main" From 31c40b3c54a44ebacc083453bf5baf964ed5800a Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 12 Jun 2024 09:06:50 -0700 Subject: [PATCH 2/9] fix: use python 3.12.3 --- .github/workflows/ci.yml | 8 ++++---- Dockerfile | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f16d87a5a..9b0ff6a72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,14 +15,14 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 - name: Commitlint uses: wagoid/commitlint-github-action@v5 test: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Compose Up run: docker-compose up -d - name: Test @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Poetry uses: snok/install-poetry@v1 - name: Poetry Build @@ -49,7 +49,7 @@ jobs: - test steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup NodeJS uses: actions/setup-node@v4 with: diff --git a/Dockerfile b/Dockerfile index bfb587a75..bcdc48b90 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12-bookworm +FROM python:3.12.3-bookworm WORKDIR /test COPY --from=ghcr.io/virtool/workflow-tools:2.0.1 /opt/fastqc /opt/fastqc COPY --from=ghcr.io/virtool/workflow-tools:2.0.1 /opt/hmmer /opt/hmmer From f33c97053f7107c736ce91197acaf0c78fb57bc7 Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 12 Jun 2024 09:07:33 -0700 Subject: [PATCH 3/9] ci: fix checkout action version --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b0ff6a72..2b55d433f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,14 +15,14 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v4 - name: Commitlint uses: wagoid/commitlint-github-action@v5 test: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v4 - name: Compose Up run: docker-compose up -d - name: Test @@ -49,7 +49,7 @@ jobs: - test steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v4 - name: Setup NodeJS uses: actions/setup-node@v4 with: From ce0515c8f05c4615f84839f9a4786410ddf88815 Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 12 Jun 2024 09:08:01 -0700 Subject: [PATCH 4/9] ci: fix another action version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b55d433f..ccc685504 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v4 - name: Install Poetry uses: snok/install-poetry@v1 - name: Poetry Build From 54bfa473de2068357aeb71577c2b487941d00798 Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 12 Jun 2024 09:09:40 -0700 Subject: [PATCH 5/9] ci: pin more python versions --- .github/workflows/ci.yml | 4 ++++ .github/workflows/publish.yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccc685504..396052054 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.12.3 - name: Install Poetry uses: snok/install-poetry@v1 - name: Poetry Build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 92d87d0ce..e4ff91745 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.12.3' - name: Checkout uses: actions/checkout@v4 - name: Install Poetry From 30c5d9011817527ad2fef3cf3b703e90231fcc59 Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 12 Jun 2024 09:19:32 -0700 Subject: [PATCH 6/9] test: remove job rights in testing --- tests/data/__snapshots__/test_jobs.ambr | 12 +++++------- virtool_workflow/errors.py | 13 ++----------- virtool_workflow/pytest_plugin/data.py | 1 - 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/tests/data/__snapshots__/test_jobs.ambr b/tests/data/__snapshots__/test_jobs.ambr index 365f78d46..c20e2af53 100644 --- a/tests/data/__snapshots__/test_jobs.ambr +++ b/tests/data/__snapshots__/test_jobs.ambr @@ -13,23 +13,21 @@ 'pinged_at': datetime.datetime(2020, 1, 1, 1, 1, 1), }), 'progress': 6380, - 'rights': dict({ - }), 'stage': 'iPhGgaTklZTpXCJkWJYU', 'state': , 'status': list([ dict({ 'error': dict({ 'details': list([ - 'VCqTDwYzyKtSiEMpnbXp', + 'vhVGiBXJqoyUfQBjWTjz', ]), 'traceback': list([ - 'WhbIXmchuyDmybzRLozZ', + 'ACAdPsEbpDIRnZLXrjSy', ]), - 'type': 'ASTUagoKFMbsJQgaCJfJ', + 'type': 'OqopAJsorWvViTxsrhYk', }), - 'progress': 3132, - 'stage': 'aOwDKGzVuuSkpRdRKwoO', + 'progress': 5931, + 'stage': 'WyJLLiOEIvGOHLbiwHlJ', 'state': , 'step_description': None, 'step_name': None, diff --git a/virtool_workflow/errors.py b/virtool_workflow/errors.py index 840a4745a..e21859ad3 100644 --- a/virtool_workflow/errors.py +++ b/virtool_workflow/errors.py @@ -5,11 +5,6 @@ class IllegalJobArguments(ValueError): """The `job.args` dict is in an illegal state.""" - -class InsufficientJobRights(Exception): - ... - - class JobAlreadyAcquired(Exception): def __init__(self, job_id: str): super(JobAlreadyAcquired, self).__init__( @@ -17,15 +12,13 @@ def __init__(self, job_id: str): ) -class JobAlreadyFinalized(Exception): - ... +class JobAlreadyFinalized(Exception): ... class JobsAPIError(Exception): """A base exception for errors due to HTTP errors from the jobs API.""" - class JobsAPIBadRequest(JobsAPIError): """A ``400 Bad Request`` response from the jobs API.""" @@ -52,9 +45,7 @@ class MissingJobArgument(ValueError): """The `job.args` dict is missing a required key for some funcionality.""" - -class NotFound(KeyError): - ... +class NotFound(KeyError): ... class SubprocessFailed(SubprocessError): diff --git a/virtool_workflow/pytest_plugin/data.py b/virtool_workflow/pytest_plugin/data.py index d528250b2..9f94c2db0 100644 --- a/virtool_workflow/pytest_plugin/data.py +++ b/virtool_workflow/pytest_plugin/data.py @@ -116,7 +116,6 @@ class SubtractionFactory(ModelFactory): "test": True, } job.ping = JobPing(pinged_at=static_datetime) - job.rights = {} """A finalized sample to be used for testing analyses.""" sample = SampleFactory.build() From aa0638286445a045cd4696ec2e122c00c20d377d Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 12 Jun 2024 09:29:17 -0700 Subject: [PATCH 7/9] style: clean up errors module --- virtool_workflow/errors.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/virtool_workflow/errors.py b/virtool_workflow/errors.py index e21859ad3..86df0aa8a 100644 --- a/virtool_workflow/errors.py +++ b/virtool_workflow/errors.py @@ -1,10 +1,6 @@ from subprocess import SubprocessError -class IllegalJobArguments(ValueError): - """The `job.args` dict is in an illegal state.""" - - class JobAlreadyAcquired(Exception): def __init__(self, job_id: str): super(JobAlreadyAcquired, self).__init__( @@ -12,9 +8,6 @@ def __init__(self, job_id: str): ) -class JobAlreadyFinalized(Exception): ... - - class JobsAPIError(Exception): """A base exception for errors due to HTTP errors from the jobs API.""" @@ -45,8 +38,5 @@ class MissingJobArgument(ValueError): """The `job.args` dict is missing a required key for some funcionality.""" -class NotFound(KeyError): ... - - class SubprocessFailed(SubprocessError): """Subprocess exited with non-zero status during a workflow.""" From 336c30afe4a8599196e788a45deb12303f66beea Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 12 Jun 2024 09:56:47 -0700 Subject: [PATCH 8/9] style: fix some ruff issues --- pyproject.toml | 6 +++ tests/data/test_analyses.py | 14 +++--- tests/data/test_indexes.py | 22 ++++----- tests/data/test_samples.py | 54 ++++++++++++---------- tests/data/test_subtractions.py | 39 ++++++++-------- tests/test_subprocess.py | 6 +-- virtool_workflow/api/acquire.py | 8 +++- virtool_workflow/api/utils.py | 12 ++--- virtool_workflow/data/indexes.py | 21 +++++---- virtool_workflow/data/samples.py | 22 ++++++--- virtool_workflow/data/subtractions.py | 19 +++++--- virtool_workflow/decorators.py | 12 +++-- virtool_workflow/errors.py | 40 ++++++++++++---- virtool_workflow/files.py | 22 +++++++-- virtool_workflow/hooks.py | 5 +- virtool_workflow/runtime/run_subprocess.py | 5 +- 16 files changed, 189 insertions(+), 118 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4e5fed6ea..d3c1b8676 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,8 +71,14 @@ exclude = [ ".ruff_cache", "__pypackages__", ] +target-version = "py312" [tool.ruff.lint] +ignore = [ + "ANN101", + "D203", + "D213" +] select = ["ALL"] [build-system] diff --git a/tests/data/test_analyses.py b/tests/data/test_analyses.py index af4ae49d8..a0c1a8c89 100644 --- a/tests/data/test_analyses.py +++ b/tests/data/test_analyses.py @@ -4,13 +4,12 @@ from pyfixtures import FixtureScope from virtool_workflow.data.analyses import WFAnalysis -from virtool_workflow.errors import JobsAPIConflict, JobsAPINotFound +from virtool_workflow.errors import JobsAPIConflictError, JobsAPINotFoundError from virtool_workflow.pytest_plugin.data import Data async def test_ok(data: Data, scope: FixtureScope): - """Test that the analysis fixture returns an Analysis object with the expected values. - """ + """Test that the analysis fixture returns an Analysis object with the expected values.""" data.job.args["analysis_id"] = data.analysis.id analysis = await scope.instantiate_by_key("analysis") @@ -22,12 +21,15 @@ async def test_not_found(data: Data, scope: FixtureScope): """Test that JobsAPINotFound is raised if the analysis does not exist.""" data.job.args["analysis_id"] = "not_found" - with pytest.raises(JobsAPINotFound) as err: + with pytest.raises(JobsAPINotFoundError) as err: await scope.instantiate_by_key("analysis") async def test_upload_file( - captured_uploads_path: Path, data: Data, scope: FixtureScope, work_path: Path, + captured_uploads_path: Path, + data: Data, + scope: FixtureScope, + work_path: Path, ): """Test that the ``Analysis`` object returned by the fixture can be used to upload an analysis file. @@ -67,7 +69,7 @@ async def test_delete_finalized(data: Data, scope: FixtureScope): analysis: WFAnalysis = await scope.instantiate_by_key("analysis") - with pytest.raises(JobsAPIConflict) as err: + with pytest.raises(JobsAPIConflictError) as err: await analysis.delete() assert "Analysis is finalized" in str(err) diff --git a/tests/data/test_indexes.py b/tests/data/test_indexes.py index f4751d9e7..c67a3eb5f 100644 --- a/tests/data/test_indexes.py +++ b/tests/data/test_indexes.py @@ -4,9 +4,9 @@ from pyfixtures import FixtureScope from syrupy import SnapshotAssertion -from virtool_workflow.pytest_plugin.data import Data from virtool_workflow.data.indexes import WFIndex, WFNewIndex -from virtool_workflow.errors import JobsAPINotFound, JobsAPIConflict +from virtool_workflow.errors import JobsAPIConflictError, JobsAPINotFoundError +from virtool_workflow.pytest_plugin.data import Data class TestIndex: @@ -18,8 +18,7 @@ async def test_ok( snapshot: SnapshotAssertion, work_path: Path, ): - """ - Test that the index fixture instantiates, contains the expected data, and + """Test that the index fixture instantiates, contains the expected data, and downloads the index files to the work path. """ data.job.args["analysis_id"] = data.analysis.id @@ -67,14 +66,12 @@ async def test_ok( work_path / "test.fa", ) - assert open(work_path / "test.fa", "r").read() == snapshot(name="fasta") + assert open(work_path / "test.fa").read() == snapshot(name="fasta") class TestNewIndex: async def test_ok(self, data: Data, scope: FixtureScope, work_path: Path): - """ - Test that the ``new_index`` fixture instantiates and contains the expected data. - """ + """Test that the ``new_index`` fixture instantiates and contains the expected data.""" data.job.args["index_id"] = data.new_index.id new_index: WFNewIndex = await scope.instantiate_by_key("new_index") @@ -129,14 +126,17 @@ async def test_upload_and_finalize( } async def test_upload_invalid_filename( - self, data: Data, example_path: Path, scope: FixtureScope + self, + data: Data, + example_path: Path, + scope: FixtureScope, ): """Test that an invalid filename raises an error.""" data.job.args["index_id"] = data.new_index.id new_index: WFNewIndex = await scope.instantiate_by_key("new_index") - with pytest.raises(JobsAPINotFound) as err: + with pytest.raises(JobsAPINotFoundError) as err: await new_index.upload( example_path / "hmms/annotations.json.gz", "unknown", @@ -164,7 +164,7 @@ async def test_finalize_incomplete( "unknown", ) - with pytest.raises(JobsAPIConflict) as err: + with pytest.raises(JobsAPIConflictError) as err: await new_index.finalize() assert ( diff --git a/tests/data/test_samples.py b/tests/data/test_samples.py index d283c510f..7bc05b264 100644 --- a/tests/data/test_samples.py +++ b/tests/data/test_samples.py @@ -5,9 +5,9 @@ from pyfixtures import FixtureScope from virtool_core.models.samples import Quality +from virtool_workflow.data.samples import WFNewSample, WFSample +from virtool_workflow.errors import JobsAPIConflictError, JobsAPINotFoundError from virtool_workflow.pytest_plugin.data import Data -from virtool_workflow.data.samples import WFSample, WFNewSample -from virtool_workflow.errors import JobsAPINotFound, JobsAPIConflict QUALITY = { "bases": [ @@ -38,8 +38,7 @@ class TestSample: - """ - Tests for the sample fixture that provides sample data for analyses. + """Tests for the sample fixture that provides sample data for analyses. See ``TestNewSample`` for tests of the ``new_sample`` fixture, that is used for sample creation workflows. @@ -54,8 +53,7 @@ async def test_ok( scope: FixtureScope, work_path: Path, ): - """ - Test that the sample fixture instantiates, contains the expected data, and + """Test that the sample fixture instantiates, contains the expected data, and downloads the sample files to the work path. """ data.job.args["sample_id"] = data.sample.id @@ -73,7 +71,8 @@ async def test_ok( assert path == work_path / "reads" / file_name with open(path, "rb") as f1, open( - example_path / "sample" / file_name, "rb" + example_path / "sample" / file_name, + "rb", ) as f2: assert f1.read() == f2.read() @@ -82,7 +81,7 @@ async def test_ok( async def test_not_found(self, data: Data, scope: FixtureScope): data.job.args["sample_id"] = "not_found" - with pytest.raises(JobsAPINotFound) as err: + with pytest.raises(JobsAPINotFoundError) as err: await scope.instantiate_by_key("sample") assert "Sample not found" in str(err) @@ -90,7 +89,11 @@ async def test_not_found(self, data: Data, scope: FixtureScope): class TestNewSample: async def test_ok( - self, data: Data, example_path: Path, scope: FixtureScope, work_path: Path + self, + data: Data, + example_path: Path, + scope: FixtureScope, + work_path: Path, ): data.job.args.update( { @@ -107,7 +110,7 @@ async def test_ok( }, ], "sample_id": data.new_sample.id, - } + }, ) data.job.args["sample_id"] = data.new_sample.id @@ -131,7 +134,8 @@ async def test_ok( for file_name in ("reads_1.fq.gz", "reads_2.fq.gz"): with open(work_path / "uploads" / file_name, "rb") as f1, open( - example_path / "sample" / file_name, "rb" + example_path / "sample" / file_name, + "rb", ) as f2: assert f1.read() == f2.read() @@ -152,7 +156,7 @@ async def test_finalize(self, data: Data, scope: FixtureScope): }, ], "sample_id": data.new_sample.id, - } + }, ) new_sample = await scope.instantiate_by_key("new_sample") @@ -166,8 +170,7 @@ async def test_finalize(self, data: Data, scope: FixtureScope): assert data.new_sample.quality == Quality(**QUALITY) async def test_already_finalized(self, data: Data, scope: FixtureScope): - """ - Test that the finalize method raises an error if the sample is already + """Test that the finalize method raises an error if the sample is already finalized. """ @@ -190,7 +193,7 @@ class QualityFactory(ModelFactory): }, ], "sample_id": data.new_sample.id, - } + }, ) data.new_sample.ready = True @@ -198,12 +201,11 @@ class QualityFactory(ModelFactory): new_sample = await scope.instantiate_by_key("new_sample") - with pytest.raises(JobsAPIConflict): + with pytest.raises(JobsAPIConflictError): await new_sample.finalize(QUALITY) async def test_delete(self, data: Data, scope: FixtureScope): """Test that the delete method deletes the sample.""" - data.job.args.update( { "files": [ @@ -219,7 +221,7 @@ async def test_delete(self, data: Data, scope: FixtureScope): }, ], "sample_id": data.new_sample.id, - } + }, ) new_sample: WFNewSample = await scope.instantiate_by_key("new_sample") @@ -230,7 +232,6 @@ async def test_delete(self, data: Data, scope: FixtureScope): async def test_delete_not_found(self, data: Data, scope: FixtureScope): """Test that the delete method raises an error if the sample does not exist.""" - data.job.args.update( { "files": [ @@ -246,14 +247,14 @@ async def test_delete_not_found(self, data: Data, scope: FixtureScope): }, ], "sample_id": data.new_sample.id, - } + }, ) new_sample: WFNewSample = await scope.instantiate_by_key("new_sample") data.new_sample = None - with pytest.raises(JobsAPINotFound): + with pytest.raises(JobsAPINotFoundError): await new_sample.delete() async def test_delete_finalized(self, data: Data, scope: FixtureScope): @@ -273,20 +274,23 @@ async def test_delete_finalized(self, data: Data, scope: FixtureScope): }, ], "sample_id": data.new_sample.id, - } + }, ) new_sample: WFNewSample = await scope.instantiate_by_key("new_sample") data.new_sample.ready = True - with pytest.raises(JobsAPIConflict) as err: + with pytest.raises(JobsAPIConflictError) as err: await new_sample.delete() assert "Sample already finalized" in str(err) async def test_upload( - self, captured_uploads_path: Path, data: Data, scope: FixtureScope + self, + captured_uploads_path: Path, + data: Data, + scope: FixtureScope, ): """Test that reads can be uploaded to unfinalized samples.""" data.job.args.update( @@ -304,7 +308,7 @@ async def test_upload( }, ], "sample_id": data.new_sample.id, - } + }, ) new_sample: WFNewSample = await scope.instantiate_by_key("new_sample") diff --git a/tests/data/test_subtractions.py b/tests/data/test_subtractions.py index 3018b7355..8bd4d0451 100644 --- a/tests/data/test_subtractions.py +++ b/tests/data/test_subtractions.py @@ -5,13 +5,13 @@ from pyfixtures import FixtureScope from virtool_core.models.job import Job -from virtool_workflow.pytest_plugin.utils import SUBTRACTION_FILENAMES +from virtool_workflow.data.subtractions import WFNewSubtraction, WFSubtraction +from virtool_workflow.errors import JobsAPIConflictError from virtool_workflow.pytest_plugin.data import Data -from virtool_workflow.data.subtractions import WFSubtraction, WFNewSubtraction -from virtool_workflow.errors import JobsAPIConflict +from virtool_workflow.pytest_plugin.utils import SUBTRACTION_FILENAMES -@pytest.fixture +@pytest.fixture() def _new_subtraction_job(data: Data): """A job for creating a new subtraction.""" data.job.args = { @@ -25,14 +25,13 @@ def _new_subtraction_job(data: Data): class TestSubtractions: async def test_ok(self, example_path: Path, data: Data, scope: FixtureScope): - """ - Test that the subtractions fixture matches the expected data and writes the + """Test that the subtractions fixture matches the expected data and writes the subtraction data files to the work path. """ data.job.args["analysis_id"] = data.analysis.id subtractions: list[WFSubtraction] = await scope.instantiate_by_key( - "subtractions" + "subtractions", ) assert len(subtractions) == 1 @@ -54,13 +53,11 @@ async def test_ok( example_path: Path, scope: FixtureScope, ): - """ - Test that the new_subtraction fixture matches the expected data and writes the + """Test that the new_subtraction fixture matches the expected data and writes the subtraction data files to the work path. """ - new_subtraction: WFNewSubtraction = await scope.instantiate_by_key( - "new_subtraction" + "new_subtraction", ) assert new_subtraction.id == data.new_subtraction.id @@ -80,7 +77,7 @@ async def test_upload_and_finalize( scope: FixtureScope, ): new_subtraction: WFNewSubtraction = await scope.instantiate_by_key( - "new_subtraction" + "new_subtraction", ) for filename in SUBTRACTION_FILENAMES: @@ -101,11 +98,9 @@ async def test_already_finalized( example_path: Path, scope: FixtureScope, ): - """ - Test that an exception is raised when a subtraction is finalized a second time. - """ + """Test that an exception is raised when a subtraction is finalized a second time.""" new_subtraction: WFNewSubtraction = await scope.instantiate_by_key( - "new_subtraction" + "new_subtraction", ) for filename in SUBTRACTION_FILENAMES: @@ -113,18 +108,22 @@ async def test_already_finalized( data.new_subtraction.ready = True - with pytest.raises(JobsAPIConflict) as err: + with pytest.raises(JobsAPIConflictError) as err: await new_subtraction.finalize( - {"a": 0.2, "t": 0.2, "c": 0.2, "g": 0.4}, 100 + {"a": 0.2, "t": 0.2, "c": 0.2, "g": 0.4}, + 100, ) assert "Subtraction already finalized" in str(err.value) async def test_delete( - self, _new_subtraction_job: Job, data: Data, scope: FixtureScope + self, + _new_subtraction_job: Job, + data: Data, + scope: FixtureScope, ): new_subtraction: WFNewSubtraction = await scope.instantiate_by_key( - "new_subtraction" + "new_subtraction", ) await new_subtraction.delete() diff --git a/tests/test_subprocess.py b/tests/test_subprocess.py index 24ac6e1cf..6d333cc97 100644 --- a/tests/test_subprocess.py +++ b/tests/test_subprocess.py @@ -9,7 +9,7 @@ from virtool_core.redis import Redis from virtool_workflow import RunSubprocess, Workflow -from virtool_workflow.errors import SubprocessFailed +from virtool_workflow.errors import SubprocessFailedError from virtool_workflow.pytest_plugin.data import Data from virtool_workflow.runtime.redis import CANCELLATION_CHANNEL from virtool_workflow.runtime.run import start_runtime @@ -61,7 +61,7 @@ async def test_stderr_is_handled(bash: Path, run_subprocess: RunSubprocess): async def stderr_handler(line): lines.append(line) - with pytest.raises(SubprocessFailed): + with pytest.raises(SubprocessFailedError): await run_subprocess(["bash", "/foo/bar"], stderr_handler=stderr_handler) assert lines == [b"bash: /foo/bar: No such file or directory\n"] @@ -71,7 +71,7 @@ async def test_subprocess_failed(run_subprocess: RunSubprocess): """Test that a ``SubprocessFailed`` error is raised when a command fails and is not raised when it succeeds. """ - with pytest.raises(SubprocessFailed): + with pytest.raises(SubprocessFailedError): await run_subprocess(["ls", "-doesnotexist"]) await run_subprocess(["ls"]) diff --git a/virtool_workflow/api/acquire.py b/virtool_workflow/api/acquire.py index 5d3d1592a..a6f202ebf 100644 --- a/virtool_workflow/api/acquire.py +++ b/virtool_workflow/api/acquire.py @@ -4,7 +4,11 @@ from structlog import get_logger from virtool_core.models.job import JobAcquired -from virtool_workflow.errors import JobAlreadyAcquired, JobsAPIError, JobsAPIServerError +from virtool_workflow.errors import ( + JobAlreadyAcquiredError, + JobsAPIError, + JobsAPIServerError, +) logger = get_logger("api") @@ -39,7 +43,7 @@ async def acquire_job_by_id( if resp.status == 400: if "already acquired" in await resp.text(): - raise JobAlreadyAcquired(await resp.json()) + raise JobAlreadyAcquiredError(await resp.json()) logger.critical( "unexpected api error during job acquisition", diff --git a/virtool_workflow/api/utils.py b/virtool_workflow/api/utils.py index 878be0f3c..b5e6ffb63 100644 --- a/virtool_workflow/api/utils.py +++ b/virtool_workflow/api/utils.py @@ -10,10 +10,10 @@ from structlog import get_logger from virtool_workflow.errors import ( - JobsAPIBadRequest, - JobsAPIConflict, + JobsAPIBadRequestError, + JobsAPIConflictError, JobsAPIForbidden, - JobsAPINotFound, + JobsAPINotFoundError, JobsAPIServerError, ) @@ -80,10 +80,10 @@ async def raise_exception_by_status_code(resp: ClientResponse): :raise JobsAPIServerError: the response status code is 500 """ status_exception_map = { - 400: JobsAPIBadRequest, + 400: JobsAPIBadRequestError, 403: JobsAPIForbidden, - 404: JobsAPINotFound, - 409: JobsAPIConflict, + 404: JobsAPINotFoundError, + 409: JobsAPIConflictError, 500: JobsAPIServerError, } diff --git a/virtool_workflow/data/indexes.py b/virtool_workflow/data/indexes.py index 034413dc9..a87c04404 100644 --- a/virtool_workflow/data/indexes.py +++ b/virtool_workflow/data/indexes.py @@ -13,7 +13,7 @@ from virtool_core.utils import decompress_file from virtool_workflow.api.client import APIClient -from virtool_workflow.errors import MissingJobArgument +from virtool_workflow.errors import MissingJobArgumentError from virtool_workflow.files import VirtoolFileFormat logger = get_logger("api") @@ -43,9 +43,7 @@ class WFIndex: @property def bowtie_path(self) -> Path: - """The path to the Bowtie2 index prefix for the Virtool index. - - """ + """The path to the Bowtie2 index prefix for the Virtool index.""" return self.path / "reference" @property @@ -151,7 +149,10 @@ async def finalize(self): await self._api.patch_json(f"/indexes/{self.id}", {}) async def upload( - self, path: Path, fmt: VirtoolFileFormat = "fasta", name: str | None = None, + self, + path: Path, + fmt: VirtoolFileFormat = "fasta", + name: str | None = None, ): """Upload a file to associate with the index being built. @@ -272,14 +273,16 @@ async def index( @fixture async def new_index( - _api: APIClient, job: Job, proc: int, work_path: Path, + _api: APIClient, + job: Job, + proc: int, + work_path: Path, ) -> WFNewIndex: - """The :class:`.WFNewIndex` for an index being created by the current job. - """ + """The :class:`.WFNewIndex` for an index being created by the current job.""" try: id_ = job.args["index_id"] except KeyError: - raise MissingJobArgument("Missing jobs args key 'index_id'") + raise MissingJobArgumentError("Missing jobs args key 'index_id'") log = logger.bind(resource="new_index", id=id_, job_id=job.id) log.info("loading index") diff --git a/virtool_workflow/data/samples.py b/virtool_workflow/data/samples.py index bbe2f6e8c..642e0ce6f 100644 --- a/virtool_workflow/data/samples.py +++ b/virtool_workflow/data/samples.py @@ -12,7 +12,7 @@ from virtool_workflow.analysis.utils import ReadPaths from virtool_workflow.api.client import APIClient from virtool_workflow.data.uploads import WFUploads -from virtool_workflow.errors import JobsAPINotFound +from virtool_workflow.errors import JobsAPINotFoundError from virtool_workflow.files import VirtoolFileFormat logger = get_logger("api") @@ -83,7 +83,10 @@ class WFNewSample: @fixture async def sample( - _api: APIClient, job: Job, uploads: WFUploads, work_path: Path, + _api: APIClient, + job: Job, + uploads: WFUploads, + work_path: Path, ) -> WFSample: """The sample associated with the current job.""" id_ = job.args["sample_id"] @@ -92,8 +95,8 @@ async def sample( try: sample_json = await _api.get_json(base_url_path) - except JobsAPINotFound: - raise JobsAPINotFound("Sample not found") + except JobsAPINotFoundError: + raise JobsAPINotFoundError("Sample not found") sample = Sample(**sample_json) @@ -101,7 +104,8 @@ async def sample( await asyncio.to_thread(reads_path.mkdir, exist_ok=True, parents=True) await _api.get_file( - f"{base_url_path}/reads/reads_1.fq.gz", reads_path / "reads_1.fq.gz", + f"{base_url_path}/reads/reads_1.fq.gz", + reads_path / "reads_1.fq.gz", ) if sample.paired: @@ -110,7 +114,8 @@ async def sample( reads_path / "reads_2.fq.gz", ) await _api.get_file( - f"{base_url_path}/reads/reads_2.fq.gz", reads_path / "reads_2.fq.gz", + f"{base_url_path}/reads/reads_2.fq.gz", + reads_path / "reads_2.fq.gz", ) else: read_paths = (reads_path / "reads_1.fq.gz",) @@ -127,7 +132,10 @@ async def sample( @fixture async def new_sample( - _api: APIClient, job: Job, uploads: WFUploads, work_path: Path, + _api: APIClient, + job: Job, + uploads: WFUploads, + work_path: Path, ) -> WFNewSample: """The sample associated with the current job.""" id_ = job.args["sample_id"] diff --git a/virtool_workflow/data/subtractions.py b/virtool_workflow/data/subtractions.py index 28fef9050..9cc4290db 100644 --- a/virtool_workflow/data/subtractions.py +++ b/virtool_workflow/data/subtractions.py @@ -15,7 +15,7 @@ from virtool_workflow.api.client import APIClient from virtool_workflow.data.analyses import WFAnalysis from virtool_workflow.data.uploads import WFUploads -from virtool_workflow.errors import MissingJobArgument +from virtool_workflow.errors import MissingJobArgumentError logger = get_logger("api") @@ -118,7 +118,9 @@ def fasta_path(self) -> Path: @fixture async def subtractions( - _api: APIClient, analysis: WFAnalysis, work_path: Path, + _api: APIClient, + analysis: WFAnalysis, + work_path: Path, ) -> list[WFSubtraction]: """The subtractions to be used for the current analysis job.""" subtraction_work_path = work_path / "subtractions" @@ -158,7 +160,10 @@ async def subtractions( @fixture async def new_subtraction( - _api: APIClient, job: Job, uploads: WFUploads, work_path: Path, + _api: APIClient, + job: Job, + uploads: WFUploads, + work_path: Path, ) -> WFNewSubtraction: """A new subtraction that will be created during the current job. @@ -167,12 +172,12 @@ async def new_subtraction( try: id_ = job.args["subtraction_id"] except KeyError: - raise MissingJobArgument("subtraction_id") + raise MissingJobArgumentError("subtraction_id") try: upload_id = job.args["files"][0]["id"] except KeyError: - raise MissingJobArgument("files") + raise MissingJobArgumentError("files") subtraction_json = await _api.get_json(f"/subtractions/{id_}") subtraction_ = Subtraction(**subtraction_json) @@ -221,7 +226,9 @@ async def upload(path: Path): log.info("Uploading subtraction file") await _api.put_file( - f"/subtractions/{subtraction_.id}/files/{filename}", path, "unknown", + f"/subtractions/{subtraction_.id}/files/{filename}", + path, + "unknown", ) log.info("Finished uploading subtraction file") diff --git a/virtool_workflow/decorators.py b/virtool_workflow/decorators.py index d4d7f7bc4..7f879557a 100644 --- a/virtool_workflow/decorators.py +++ b/virtool_workflow/decorators.py @@ -1,11 +1,13 @@ """Create Workflows by decorating module scope functions.""" + +from collections.abc import Callable from types import ModuleType -from typing import Callable +from virtool_workflow.errors import WorkflowStepsError from virtool_workflow.workflow import Workflow -def step(func: Callable = None, *, name: str | None = None) -> Callable: +def step(func: Callable | None = None, *, name: str | None = None) -> Callable: """Mark a function as a workflow step function. :param func: the workflow step function @@ -16,7 +18,7 @@ def step(func: Callable = None, *, name: str | None = None) -> Callable: return lambda _f: step(_f, name=name) func.__workflow_marker__ = "step" - func.__workflow_step_props__ = dict(name=name) + func.__workflow_step_props__ = {"name": name} return func @@ -39,7 +41,7 @@ def collect(module: ModuleType) -> Workflow: if marked.__workflow_marker__ == "step": workflow.step(marked, **marked.__workflow_step_props__) - if len(workflow.steps) == 0: - raise ValueError(f"No workflow steps could be found in {module}") + if not workflow.steps: + raise WorkflowStepsError(str(module)) return workflow diff --git a/virtool_workflow/errors.py b/virtool_workflow/errors.py index 86df0aa8a..58c32eb8c 100644 --- a/virtool_workflow/errors.py +++ b/virtool_workflow/errors.py @@ -1,9 +1,14 @@ +"""Custom exceptions for ``virtool_workflow``.""" + from subprocess import SubprocessError -class JobAlreadyAcquired(Exception): - def __init__(self, job_id: str): - super(JobAlreadyAcquired, self).__init__( +class JobAlreadyAcquiredError(Exception): + """Raised when an attempt is made to reacquire a job.""" + + def __init__(self, job_id: str) -> None: + """Initialize the exception with a message containing the job ID.""" + super().__init__( f"Job {job_id} is has already been acquired.", ) @@ -12,31 +17,46 @@ class JobsAPIError(Exception): """A base exception for errors due to HTTP errors from the jobs API.""" -class JobsAPIBadRequest(JobsAPIError): - """A ``400 Bad Request`` response from the jobs API.""" +class JobsAPIBadRequestError(JobsAPIError): + """A ``400 Bad Request`` response was received from the jobs API.""" status = 400 -class JobsAPIForbidden(JobsAPIError): +class JobsAPIForbiddenError(JobsAPIError): + """A ``403 Forbidden`` response was received from the jobs API.""" + status = 403 -class JobsAPINotFound(JobsAPIError): +class JobsAPINotFoundError(JobsAPIError): + """A ``404 Not Found`` response was received from the jobs API.""" + status = 404 -class JobsAPIConflict(JobsAPIError): +class JobsAPIConflictError(JobsAPIError): + """A ``409 Conflict`` response was received from the jobs API.""" + status = 409 class JobsAPIServerError(JobsAPIError): + """A ``500 Internal Server Error`` response was received from the jobs API.""" + status = 500 -class MissingJobArgument(ValueError): +class MissingJobArgumentError(ValueError): """The `job.args` dict is missing a required key for some funcionality.""" -class SubprocessFailed(SubprocessError): +class WorkflowStepsError(Exception): + """Raised when no workflow steps are found in a module.""" + + def __init__(self, module: str) -> None: + super().__init__(f"No workflow steps could be found in {module}") + + +class SubprocessFailedError(SubprocessError): """Subprocess exited with non-zero status during a workflow.""" diff --git a/virtool_workflow/files.py b/virtool_workflow/files.py index e28104d84..900e53b61 100644 --- a/virtool_workflow/files.py +++ b/virtool_workflow/files.py @@ -1,5 +1,7 @@ +"""Dataclasses for describing files uploaded to the Virtool server.""" + +import datetime from dataclasses import dataclass -from datetime import datetime from typing import Literal VirtoolFileFormat = Literal[ @@ -12,13 +14,27 @@ "json", "unknown", ] +"""A literal type hint for the format of a :class:`.VirtoolFile`.""" @dataclass class VirtoolFile: + """A description of a file uploaded to the Virtool server.""" + id: int + """The unique ID for the file.""" + name: str + """The name of the file.""" + size: int + """The size of the file in bytes.""" + format: VirtoolFileFormat - name_on_disk: str = None - uploaded_at: datetime = None + """The format of the file.""" + + name_on_disk: str | None = None + """The actual name of the file on disk.""" + + uploaded_at: datetime.datetime | None = None + """When the file was uploaded.""" diff --git a/virtool_workflow/hooks.py b/virtool_workflow/hooks.py index b9d462387..6927434cf 100644 --- a/virtool_workflow/hooks.py +++ b/virtool_workflow/hooks.py @@ -1,5 +1,4 @@ -"""Hooks provide a way to do things when events happen during the workflow lifecycle. -""" +"""Hooks do things when events happen during the workflow lifecycle.""" from virtool_workflow.runtime.hook import Hook @@ -124,7 +123,7 @@ async def do_something_on_finish(): ] -def cleanup_builtin_status_hooks(): +def cleanup_builtin_status_hooks() -> None: """Clear callbacks for built-in status hooks. This prevents carryover of hooks between tests. Carryover won't be encountered in diff --git a/virtool_workflow/runtime/run_subprocess.py b/virtool_workflow/runtime/run_subprocess.py index a5519000f..fafa57c63 100644 --- a/virtool_workflow/runtime/run_subprocess.py +++ b/virtool_workflow/runtime/run_subprocess.py @@ -1,4 +1,5 @@ """Code for running and managing subprocesses.""" + import asyncio from asyncio.subprocess import Process from pathlib import Path @@ -8,7 +9,7 @@ from structlog import get_logger from virtool_core.utils import timestamp -from virtool_workflow.errors import SubprocessFailed +from virtool_workflow.errors import SubprocessFailedError logger = get_logger("subprocess") @@ -146,7 +147,7 @@ async def _stderr_handler(line): # Exit code 15 indicates that the process was terminated. This is expected # when the workflow fails for some other reason, hence not an exception if process.returncode not in [0, 15, -15]: - raise SubprocessFailed( + raise SubprocessFailedError( f"{command[0]} failed with exit code {process.returncode}\n" f"arguments: {command}\n", ) From 6e992703f60f59261f75be75cc4fd462bcf56794 Mon Sep 17 00:00:00 2001 From: Ian Date: Wed, 12 Jun 2024 10:00:52 -0700 Subject: [PATCH 9/9] test: fix failing test --- virtool_workflow/api/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/virtool_workflow/api/utils.py b/virtool_workflow/api/utils.py index b5e6ffb63..b10ca7823 100644 --- a/virtool_workflow/api/utils.py +++ b/virtool_workflow/api/utils.py @@ -12,7 +12,7 @@ from virtool_workflow.errors import ( JobsAPIBadRequestError, JobsAPIConflictError, - JobsAPIForbidden, + JobsAPIForbiddenError, JobsAPINotFoundError, JobsAPIServerError, ) @@ -81,7 +81,7 @@ async def raise_exception_by_status_code(resp: ClientResponse): """ status_exception_map = { 400: JobsAPIBadRequestError, - 403: JobsAPIForbidden, + 403: JobsAPIForbiddenError, 404: JobsAPINotFoundError, 409: JobsAPIConflictError, 500: JobsAPIServerError,