From 77eb160da56cdb35dbd61bb656b2749c95b80ce5 Mon Sep 17 00:00:00 2001 From: Daylin Morgan Date: Mon, 22 Jul 2024 15:17:58 -0500 Subject: [PATCH] lost the thread a bit on this library --- .gitignore | 1 + DESIGN.md | 60 ++++++ README.md | 4 +- hello.py | 6 + pdm.lock | 218 ---------------------- pyproject.toml | 22 +-- src/swydd/__init__.py | 385 ++++++++++++++++++++++++++++++++++----- tasks.py | 58 +++--- tests/fixtures/input.txt | 1 + tests/test_operators.py | 57 ++++++ tests/test_sub.py | 23 +++ uv.lock | 288 +++++++++++++++++++++++++++++ 12 files changed, 814 insertions(+), 309 deletions(-) create mode 100644 DESIGN.md create mode 100644 hello.py delete mode 100644 pdm.lock create mode 100644 tests/fixtures/input.txt create mode 100755 tests/test_operators.py create mode 100755 tests/test_sub.py create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore index f5dc123..4198ac7 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,4 @@ cython_debug/ #.idea/ docs/**/*.py +tests/products diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..d776fe1 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,60 @@ +# Swydd Design + +Goals: + +- Generic task runner +- Portable (single python module) +- library not an exe + +basic design: + +`tasks.py`: +```python +from swydd import taks, option, cli + +@task +@option("program","name of program to compile") +def build(program: str = "hello"): + """build a program""" + sub < f"gcc -o {program} {program}.c" + +cli() +``` + +```sh +./tasks.py build +./tasks.py build --program goodbye +``` + +## Ideas + +### Simple shell pipelines + +```python +@task +def pipe_commands() + """run pre-commit (and mypy)""" + p = Pipe(Exec("find . -name file"), Exec("grep 'pattern'")).get() + print(p.stdout) + +``` + +Made even simpler with operator overloading: + +```python +@task +def run_commands(): + stdout = get < (pipe | "find . -name file" | "grep 'pattern'") + print(stdout) +``` + + +## Internal CLI + +```sh +./tasks.py _ swydd-subcmd +# vs +./tasks.py _swydd-subcmd +# or +./tasks.py +swydd-subcmd # <- current favorite (use a none valid function name to possibly prevent overlap) +``` diff --git a/README.md b/README.md index 8bc5238..c4b468d 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,14 @@ # https://github.com/daylinmorgan/swydd?tab=readme-ov-file#automagic-snippet _src = (_i := __import__)("pathlib").Path(__file__).parent / "swydd/__init__.py" if not (_i("importlib.util").util.find_spec("sywdd") or _src.is_file()): - _i("sys").stderr.write(f"installing swydd to {_src}\n") - _r= _i("urllib.request").request.urlopen("https://swydd.dayl.in/swydd.py") + _r = _i("urllib.request").request.urlopen("https://swydd.dayl.in/swydd.py") _src.parent.mkdir(exist_ok=True) _src.write_text(_r.read().decode("utf-8")) ``` ## Alternatives +- [task.mk](https://gh.dayl.in/task.mk) - [make](https://www.gnu.org/software/make/) - [just](https://just.systems) - [task](https://taskfile.dev) diff --git a/hello.py b/hello.py new file mode 100644 index 0000000..a1a6df7 --- /dev/null +++ b/hello.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from swydd!") + + +if __name__ == "__main__": + main() diff --git a/pdm.lock b/pdm.lock deleted file mode 100644 index 1d5fc9c..0000000 --- a/pdm.lock +++ /dev/null @@ -1,218 +0,0 @@ -# This file is @generated by PDM. -# It is not intended for manual editing. - -[metadata] -groups = ["default", "dev"] -strategy = ["cross_platform", "inherit_metadata"] -lock_version = "4.4.2" -content_hash = "sha256:da8ee1c2b79c1ab05ece67350e0399fa72da13cab2946354eaa21b722e90210b" - -[[package]] -name = "cfgv" -version = "3.4.0" -requires_python = ">=3.8" -summary = "Validate configuration and produce human readable error messages." -groups = ["dev"] -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "distlib" -version = "0.3.8" -summary = "Distribution utilities" -groups = ["dev"] -files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, -] - -[[package]] -name = "filelock" -version = "3.15.4" -requires_python = ">=3.8" -summary = "A platform independent file lock." -groups = ["dev"] -files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, -] - -[[package]] -name = "identify" -version = "2.6.0" -requires_python = ">=3.8" -summary = "File identification library for Python" -groups = ["dev"] -files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, -] - -[[package]] -name = "mypy" -version = "1.11.0" -requires_python = ">=3.8" -summary = "Optional static typing for Python" -groups = ["dev"] -dependencies = [ - "mypy-extensions>=1.0.0", - "tomli>=1.1.0; python_version < \"3.11\"", - "typing-extensions>=4.6.0", -] -files = [ - {file = "mypy-1.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229"}, - {file = "mypy-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287"}, - {file = "mypy-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6"}, - {file = "mypy-1.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be"}, - {file = "mypy-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00"}, - {file = "mypy-1.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb"}, - {file = "mypy-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1"}, - {file = "mypy-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3"}, - {file = "mypy-1.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d"}, - {file = "mypy-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a"}, - {file = "mypy-1.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20"}, - {file = "mypy-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba"}, - {file = "mypy-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd"}, - {file = "mypy-1.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d"}, - {file = "mypy-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2"}, - {file = "mypy-1.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095"}, - {file = "mypy-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe"}, - {file = "mypy-1.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c"}, - {file = "mypy-1.11.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13"}, - {file = "mypy-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac"}, - {file = "mypy-1.11.0-py3-none-any.whl", hash = "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace"}, - {file = "mypy-1.11.0.tar.gz", hash = "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538"}, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -requires_python = ">=3.5" -summary = "Type system extensions for programs checked with the mypy type checker." -groups = ["dev"] -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "nodeenv" -version = "1.9.1" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Node.js virtual environment builder" -groups = ["dev"] -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, -] - -[[package]] -name = "platformdirs" -version = "4.2.2" -requires_python = ">=3.8" -summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -groups = ["dev"] -files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, -] - -[[package]] -name = "pre-commit" -version = "3.7.1" -requires_python = ">=3.9" -summary = "A framework for managing and maintaining multi-language pre-commit hooks." -groups = ["dev"] -dependencies = [ - "cfgv>=2.0.0", - "identify>=1.0.0", - "nodeenv>=0.11.1", - "pyyaml>=5.1", - "virtualenv>=20.10.0", -] -files = [ - {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"}, - {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.1" -requires_python = ">=3.6" -summary = "YAML parser and emitter for Python" -groups = ["dev"] -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -requires_python = ">=3.7" -summary = "A lil' TOML parser" -groups = ["dev"] -marker = "python_version < \"3.11\"" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -requires_python = ">=3.8" -summary = "Backported and Experimental Type Hints for Python 3.8+" -groups = ["dev"] -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "virtualenv" -version = "20.26.3" -requires_python = ">=3.7" -summary = "Virtual Python Environment builder" -groups = ["dev"] -dependencies = [ - "distlib<1,>=0.3.7", - "filelock<4,>=3.12.2", - "platformdirs<5,>=3.9.1", -] -files = [ - {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, - {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, -] diff --git a/pyproject.toml b/pyproject.toml index 509a17d..a907692 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,30 +10,30 @@ readme = "README.md" license = {text = "MIT"} dynamic = ["version"] -[tool.pdm] -distribution = true - -[tool.pdm.version] -source = "file" -path = "src/swydd/__init__.py" - -[tool.pdm.dev-dependencies] -dev = [ +[tool.uv] +dev-dependencies = [ "pre-commit>=3.6.2", "mypy>=1.8.0", + "pytest>=8.3.2", ] [tool.ruff] select = ["E","F","I"] ignore = ["E402"] +[tool.pyright] +reportUnusedExpression = false + [tool.mypy] check_untyped_defs = true disallow_untyped_defs = true warn_unused_configs = true +[tool.pdm.version] +source = "file" +path = "src/swydd/__init__.py" + + [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" - - diff --git a/src/swydd/__init__.py b/src/swydd/__init__.py index 3ae3934..84012aa 100644 --- a/src/swydd/__init__.py +++ b/src/swydd/__init__.py @@ -2,6 +2,7 @@ import argparse import inspect import os import shlex +import shutil import subprocess import sys from argparse import ( @@ -13,11 +14,13 @@ from argparse import ( from functools import wraps from inspect import Parameter from pathlib import Path +from subprocess import PIPE, CompletedProcess, Popen from typing import Any, Callable, Dict, List, Optional, Tuple __version__ = "0.1.0" +# TODO: make this ouput wider help when necessary class SubcommandHelpFormatter(RawDescriptionHelpFormatter): """custom help formatter to remove bracketed list of subparsers""" @@ -152,25 +155,85 @@ def define_env(key: str, value: str) -> None: ctx._env.update({key: value}) -class Exec: +class SwyddSubResult: def __init__( - self, cmd: str, shell: bool = False, output: bool = False, **kwargs: Any + self, + code: int, + stdout: None | str, + stderr: None | str, + process: CompletedProcess | Popen, ) -> None: - self.shell = shell - self.cmd = cmd - if shell: - self._cmd = cmd - else: - self._cmd = shlex.split(cmd) + self.code = code + self.stdout = stdout + self.stderr = stderr + self._proces = process + + @classmethod + def from_completed_process(cls, p: CompletedProcess) -> "SwyddSubResult": + return cls(p.returncode, p.stdout, p.stderr, p) + + @classmethod + def from_popen( + cls, + p: Popen, + stdout: str = "", + stderr: str = "", + ) -> "SwyddSubResult": + return cls(p.returncode, stdout, stderr, p) + + +class SwyddProc: + """ + usage: + sub < proc("echo $WORD world", env={'WORD':'hello'}) + sub < (proc | "single proc") + + sub < (proc | "echo hello" | "wc -c") + sub(proc("hello world").pipe("wc -c")) + + sub < (proc & "cat -a" & "echo unreachable") + sub(proc("cat -a").then("echo unreachable") + """ + + def __init__( + self, cmd: str | None = None, output: bool = False, **kwargs: Any + ) -> None: + self._cmd = cmd + if cmd: + self.cmd = shlex.split(cmd) self.output = output self.cmd_kwargs = kwargs + def pipe(self, proc: "str | SwyddProc") -> "SwyddPipe | SwyddProc": + if isinstance(proc, str): + if self._cmd is None: + return SwyddPipe(SwyddProc(proc)) + else: + return SwyddPipe(self, proc) + elif isinstance(proc, SwyddProc): + return SwyddPipe(proc) + + def __or__(self, proc: "str | SwyddProc") -> "SwyddPipe | SwyddProc": + return self.pipe(proc) + + def then(self, proc: "str | SwyddProc | SwyddSeq") -> "SwyddSeq": + if self._cmd: + return SwyddSeq(self, proc) + + if isinstance(proc, SwyddProc): + return SwyddSeq(proc) + # should swydd seq even be supported here? + elif isinstance(proc, SwyddSeq): + return proc + else: + return SwyddSeq(SwyddProc(proc)) + + def __and__(self, proc: "str | SwyddProc | SwyddSeq") -> "SwyddSeq": + return self.then(proc) + def _build_kwargs(self) -> Dict[str, Any]: sub_kwargs: Dict[str, Any] = dict(env={**os.environ, **ctx._env}) - if self.shell: - sub_kwargs["shell"] = True - if self.output: sub_kwargs["text"] = True # assume text is the desired output sub_kwargs["capture_output"] = True @@ -178,25 +241,226 @@ class Exec: sub_kwargs.update(**self.cmd_kwargs) return sub_kwargs - def execute(self) -> subprocess.CompletedProcess: + def execute(self, output: bool = False) -> SwyddSubResult: if ctx.verbose: - sys.stdout.write(f"swydd exec | {self.cmd}\n") + sys.stdout.write(f"swydd exec | {self._cmd}\n") - return subprocess.run(self._cmd, **self._build_kwargs()) + self.output = self.output or output + return SwyddSubResult.from_completed_process( + subprocess.run(self.cmd, **self._build_kwargs()) + ) - def get(self): - p = self.execute() - if p.returncode != 0: - sys.stderr.write("non-zero exit status in Exec().get()\n") - sys.stderr.write(f" cmd: {self.cmd}\n") - sys.stderr.write(f" stdout: {p.stdout}\n") - sys.stderr.write(f" stderr: {p.stderr}\n") - sys.exit(p.returncode) - return p + def check(self) -> bool: + return self.execute().code == 0 -def sh(cmd: str, shell: bool = False, **kwargs: Any) -> subprocess.CompletedProcess: - return Exec(cmd, shell=shell, **kwargs).execute() +class SwyddPipe: + def __init__(self, *procs: "str | SwyddProc | SwyddPipe") -> None: + self._procs = [] + for proc in procs: + if isinstance(proc, str): + self._procs.append(SwyddProc(proc)) + elif isinstance(proc, SwyddProc): + self._procs.append(proc) + elif isinstance(proc, SwyddPipe): + self._procs.extend(proc._procs) + + @classmethod + def __call__(cls, *args, **kwargs) -> "SwyddPipe": + return cls(*args, **kwargs) + + def check(self) -> bool: + return self.execute().code == 0 + + def execute(self, output: bool = False) -> SwyddSubResult: + procs = [] + sub_kwargs: Dict[str, Any] = dict(env={**os.environ, **ctx._env}) + for i, cmd in enumerate(self._procs, 1): + kwargs = {} + if i > 1: + kwargs.update(stdin=procs[-1].stdout) + if i != len(self._procs): + kwargs.update(stdout=PIPE) + elif i == len(self._procs): + if output: + kwargs.update({"text": True, "stdout": PIPE, "stderr": PIPE}) + + procs.append(Popen(cmd.cmd, **{**sub_kwargs, **kwargs})) + + for p in procs[:-1]: + if p.stdout: + p.stdout.close() + + out, err = procs[-1].communicate() + return SwyddSubResult.from_popen(procs[-1], out, err) + + def pipe(self, proc: "str | SwyddProc | SwyddPipe") -> "SwyddPipe": + return SwyddPipe(self, proc) + + def __or__(self, proc: "str | SwyddProc | SwyddPipe") -> "SwyddPipe": + return self.pipe(proc) + + +class SwyddSeq: + def __init__(self, *procs: "str | SwyddProc | SwyddSeq") -> None: + self._procs = [] + for proc in procs: + if isinstance(proc, SwyddSeq): + self._procs.extend(self._procs) + if isinstance(proc, SwyddProc): + self._procs.append(proc) + elif isinstance(proc, str): + self._procs.append(SwyddProc(proc)) + + @classmethod + def __call__(cls, *args, **kwargs) -> "SwyddSeq": + return cls(*args, **kwargs) + + def then(self, proc: "str | SwyddProc | SwyddSeq") -> "SwyddSeq": + return SwyddSeq(*self._procs, proc) + + def __and__(self, proc: "str | SwyddProc | SwyddSeq") -> "SwyddSeq": + return self.then(proc) + + def execute(self, output: bool = False) -> "SwyddSubResult": + results = [] + for proc in self._procs: + results.append(result := proc.execute(output=output)) + if result.code != 0: + return result + + return results[-1] + + def run(self) -> int: + rc = 0 + for proc in self._procs: + if (rc := proc.execute().code) != 0: + return rc + return rc + + def check(self) -> bool: + return self.run() == 0 + + +class SwyddGet: + def __call__( + self, proc: str | SwyddProc | SwyddPipe | SwyddSeq, stdout=True, stderr=False + ) -> str: + if isinstance(proc, str): + result = SwyddProc(proc, output=True).execute() + elif isinstance(proc, SwyddPipe): + result = proc.execute(output=True) + elif isinstance(proc, SwyddProc): + result = proc.execute() + elif isinstance(proc, SwyddSeq): + result = proc.execute(output=True) + else: + raise NotImplementedError(f"not implemented for type: {type(exec)}") + + output = "" + if stdout and result.stdout: + output += result.stdout.strip() + if stderr and result.stderr: + output += result.stderr.strip() + return output + + def __lt__(self, proc: str | SwyddProc | SwyddPipe | SwyddSeq) -> str: + return self.__call__(proc) + + def __lshift__(self, proc: str | SwyddProc | SwyddPipe | SwyddSeq) -> str: + return self.__call__(proc, stdout=False, stderr=True) + + +def _get_caller_path() -> Path: + # NOTE: jupyter will hate this code I'm sure + for i, frame in enumerate(inspect.stack()): + if (name := frame.filename) != __file__: + return Path(name).parent + raise ValueError("failed to find root directory of runner") + + +class SwyddSub: + def __call__(self, proc: str | SwyddPipe | SwyddProc | SwyddSeq) -> bool: + if isinstance(proc, str): + return SwyddProc(proc).check() + elif ( + isinstance(proc, SwyddProc) + or isinstance(proc, SwyddSeq) + or isinstance(proc, SwyddPipe) + ): + return proc.check() + else: + raise ValueError(f"unspported type: {type(exec)}") + + def __lt__(self, proc: str | SwyddPipe | SwyddProc | SwyddSeq) -> bool: + return self.__call__(proc) + + +class SwyddPath: + _root = None + _path = None + + def __init__(self, p: Path | None = None) -> None: + if p: + self._path = p + + @classmethod + def from_str(cls, p: str) -> "SwyddPath": + return cls() / p + + def __truediv__(self, p: str | Path) -> "SwyddPath": + if not (root := self._root): + root = _get_caller_path() + + if not self._path: + if isinstance(p, str): + return SwyddPath(root / p) + elif isinstance(p, Path): + return SwyddPath(p) + else: + og = self._path.relative_to(root) + + return SwyddPath(og / p) + + def _check(self) -> Path: + if self._path is None: + raise ValueError("todo") + return self._path + + def write(self, txt: str) -> None: + p = self._check() + p.parent.mkdir(exist_ok=True) + p.write_text(txt + "\n") + + def append(self, txt: str) -> None: + p = self._check() + p.parent.mkdir(exist_ok=True) + with p.open("a") as f: + f.write(txt) + f.write("\n") + + def __mod__(self, dst: "str | SwyddPath | Path") -> None: + if isinstance(dst, str): + dst_p = SwyddPath.from_str( + dst + )._check() # <- TODO: ensure this uses self._root? + elif isinstance(dst, Path): + dst_p = dst + else: + dst_p = dst._check() + src_p = self._check() + src_p.rename(dst_p) + + def __lt__(self, src: "str | SwyddPath") -> None: + if isinstance(src, str): + self.write(src) + elif isinstance(src, SwyddPath): + dst_p = self._check() + src_p = src._check() + shutil.copyfile(src_p, dst_p) + + def __lshift__(self, txt: str) -> None: + self.append(txt) def task(func: Callable[..., Any]) -> Callable[..., None]: @@ -208,7 +472,7 @@ def task(func: Callable[..., Any]) -> Callable[..., None]: return wrap -def inspect_wrapper(place, func): +def _inspect_wrapper(place, func): if wrapped := getattr(func, "__wrapped__", None): print(place, "wrapped->", id(wrapped)) @@ -224,7 +488,7 @@ def task2( def wrapper(func: Callable[..., Any]) -> Callable[..., Callable[..., None]]: ctx._add_task(func, show=True) - inspect_wrapper("task", func) + _inspect_wrapper("task", func) @wraps(func) def inner(*args: Any, **kwargs: Any) -> Callable[..., None]: @@ -239,7 +503,7 @@ def targets( *args: str, ) -> Callable[[Callable[..., Any]], Callable[..., Callable[..., None]]]: def wrapper(func: Callable[..., Any]) -> Callable[..., Callable[..., None]]: - inspect_wrapper("targets", func) + _inspect_wrapper("targets", func) ctx._add_task(func) for arg in args: ctx._add_target(func, arg) @@ -296,7 +560,7 @@ def manage(version: bool = False) -> None: def noop(*args, **kwargs) -> Any: - pass + _ = args, kwargs def target_generator( @@ -325,7 +589,7 @@ def target_generator( return wrapper -def generate_task_subparser( +def _generate_task_subparser( shared: ArgumentParser, subparsers: _SubParsersAction, task: Task, @@ -338,7 +602,7 @@ def generate_task_subparser( name = task.name if not target else target doc = task.func.__doc__.splitlines()[0] if task.func.__doc__ else "" subparser = subparsers.add_parser( - name, + name.replace("_", "-"), help=doc, description=task.func.__doc__, parents=[shared], @@ -373,17 +637,30 @@ def generate_task_subparser( return subparser -def add_targets( +def _add_targets( shared: ArgumentParser, subparsers: _SubParsersAction, ctx: Context ) -> None: for target, id_ in ctx.targets.items(): - subp = generate_task_subparser(shared, subparsers, ctx._tasks[id_], str(target)) + subp = _generate_task_subparser( + shared, subparsers, ctx._tasks[id_], str(target) + ) if subp: subp.add_argument("--dag", help="show target dag", action="store_true") subp.add_argument("--force", help="force execution", action="store_true") +def _task_repr(func: Callable) -> str: + return ( + "\n".join( + f" {line}" + for line in inspect.getsource(func).splitlines() + if not line.startswith("@") + ) + + "\n" + ) + + def cli() -> None: ctx._generate_graph() @@ -406,15 +683,20 @@ def cli() -> None: title="tasks", required=True, dest="pos-arg", metavar="" ) - if len(sys.argv) > 1 and sys.argv[1] == "self": - generate_task_subparser( - shared, subparsers, Task(manage, name="self", show=True) + if len(sys.argv) > 1 and sys.argv[1] == "+swydd": + _generate_task_subparser( + shared, subparsers, Task(manage, name="+swydd", show=True) ) - add_targets(shared, subparsers, ctx) + _add_targets(shared, subparsers, ctx) for task in ctx._tasks.values(): - generate_task_subparser(shared, subparsers, task) + _generate_task_subparser(shared, subparsers, task) + + # TODO: add support for default arg? + if len(sys.argv) == 1: + parser.print_help(sys.stderr) + sys.exit(1) args = vars(parser.parse_args()) _ = args.pop("pos-arg", None) @@ -428,16 +710,7 @@ def cli() -> None: if f := args.pop("func", None): if ctx.dry: sys.stderr.write("dry run >>>\n" f" args: {args}\n") - sys.stderr.write( - ( - "\n".join( - f" {line}" - for line in inspect.getsource(f).splitlines() - if not line.startswith("@") - ) - + "\n" - ) - ) + sys.stderr.write(_task_repr(f)) elif ctx.dag: sys.stderr.write( "currently --dag is a noop\n" @@ -447,6 +720,22 @@ def cli() -> None: f(**args) +( + proc, + pipe, + seq, + sub, + get, + path, +) = ( + SwyddProc(), + SwyddPipe(), + SwyddSeq(), + SwyddSub(), + SwyddGet(), + SwyddPath(), +) + if __name__ == "__main__": sys.stderr.write("this module should not be invoked directly\n") sys.exit(1) diff --git a/tasks.py b/tasks.py index 75fda68..a7a1f77 100755 --- a/tasks.py +++ b/tasks.py @@ -1,53 +1,51 @@ #!/usr/bin/env python3 -__import__("sys").path.append("src") # noqa +__import__("sys").path.append("src") import shutil import sys -from pathlib import Path -import swydd as s +from swydd import cli, get, option, path, sub, task -@s.task +@task def bootstrap(): """setup swydd dev environment""" - if not shutil.which("pdm"): - sys.exit("pdm necessary for swydd development") - s.sh("pdm install") - s.sh("pdm run pre-commit install") + if not shutil.which("uv"): + sys.exit("uv necessary for swydd development") + sub < "uv sync" + sub < "uv run pre-commit install" -@s.task -@s.option("no-mypy", "skip mypy") -def check(no_mypy: bool = False): +@task +def tests(): + """run pytest""" + sub < "uv run pytest" + + +@task +@option("skip-mypy", "skip mypy") +def check(skip_mypy: bool = False): """run pre-commit (and mypy)""" - s.sh("pre-commit run --all") - if not no_mypy: - s.sh("mypy src/") - - -def write_docs_src(tag): - src_text = s.Exec(f"git show {tag}:src/swydd/__init__.py", output=True).get().stdout - (verdir := (Path(__file__).parent / "docs" / tag)).mkdir(exist_ok=True) - (verdir / "swydd.py").write_text(src_text) + sub < "uv run pre-commit run --all" + if not skip_mypy: + sub < "uv run mypy src/" def copy_source(): - p = s.Exec("git tag --list", output=True).get() - versions = [line for line in p.stdout.splitlines() if line.startswith("v")] - for ver in versions: - write_docs_src(ver) - shutil.copyfile( - Path(__file__).parent / "src" / "swydd" / "__init__.py", - Path(__file__).parent / "docs" / "swydd.py", - ) + tags = get < "git tag --list" + versions = [line for line in tags.splitlines() if line.startswith("v")] + for tag in versions: + (path / f"docs/{tag}/swydd.py") < ( + get < f"git show {tag}:src/swydd/__init__.py" + ) + (path / "docs/swydd.py") < (path / "src/swydd/__init__.py") -@s.task +@task def docs(): """build docs""" copy_source() -s.cli() +cli() diff --git a/tests/fixtures/input.txt b/tests/fixtures/input.txt new file mode 100644 index 0000000..f2a7c7f --- /dev/null +++ b/tests/fixtures/input.txt @@ -0,0 +1 @@ +data to copy to another file diff --git a/tests/test_operators.py b/tests/test_operators.py new file mode 100755 index 0000000..148dc92 --- /dev/null +++ b/tests/test_operators.py @@ -0,0 +1,57 @@ +from swydd import SwyddPath, get, path, pipe, proc, seq, sub + + +def test_operators(): + assert sub < "echo 'hello'" + + +def test_pipes(): + assert sub < ( + pipe | "cat ../src/swydd/__init__.py" | "grep '__version__'" | "wc -l" + ) + assert sub < ( + proc | "cat ../src/swydd/__init__.py" | "grep '__version__'" | "wc -l" + ) + + +def test_seqs(): + # -a is not an arg to cat so the subprocess should return false + assert not sub < (seq & "cat -a" & "echo hello") + assert not sub < (proc & "cat -a" & "echo hello") + + +def test_capture(): + result = get < "ls src not-src" + assert result == "src:\nswydd" + result = get << "ls src not-src" + assert result == "ls: cannot access 'not-src': No such file or directory" + assert "hello part deux" == (get < seq & "echo 'hello'" & "echo hello part deux") + assert "" == (get < (seq & "cp" & "echo hello part deux")) + assert "cp: missing file operand\nTry 'cp --help' for more information." == ( + get << (seq & "cp" & "echo hello part deux") + ) + + +def check_result_file(file: SwyddPath, text: str) -> bool: + if p := file._path: + return p.read_text() == text + return False + + +def test_write_to_path(): + result_f = path / "products/result.txt" + result_txt = "text to file" + result_f < result_txt + assert check_result_file(result_f, result_txt + "\n") + + +def test_copy_and_rename(): + src_f = path / "fixtures/input.txt" + dst_f = path / "products/input.txt" + dst_f < src_f + assert check_result_file(dst_f, "data to copy to another file\n") + + dst_f % "products/input2.txt" + assert check_result_file( + path / "products/input2.txt", "data to copy to another file\n" + ) diff --git a/tests/test_sub.py b/tests/test_sub.py new file mode 100755 index 0000000..9a777c7 --- /dev/null +++ b/tests/test_sub.py @@ -0,0 +1,23 @@ +from swydd import get, pipe, seq, sub + + +def test_pipes(): + assert sub( + pipe("cat ../src/swydd/__init__.py").pipe("grep '__version__'").pipe("wc -l") + ) + + +def test_seqs(): + # -a is not an arg to cat so the should return false + assert not sub(seq("cat -a").then("echo hello")) + + +def test_capture(): + result = get("ls src not-src") + assert result == "src:\nswydd" + result = get("ls src not-src", stdout=False, stderr=True) + assert result == "ls: cannot access 'not-src': No such file or directory" + assert "2" == get( + pipe("cat src/swydd/__init__.py").pipe("grep '__version__'").pipe("wc -l") + ) + assert "hello part deux" == get(seq("echo 'hello'").then("echo hello part deux")) diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..8932d74 --- /dev/null +++ b/uv.lock @@ -0,0 +1,288 @@ +version = 1 +requires-python = ">=3.9" + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "distlib" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + +[[package]] +name = "filelock" +version = "3.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, +] + +[[package]] +name = "identify" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/bb/25024dbcc93516c492b75919e76f389bac754a3e4248682fba32b250c880/identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98", size = 99097 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/0c/4ef72754c050979fdcc06c744715ae70ea37e734816bb6514f79df77a42f/identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0", size = 98972 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "mypy" +version = "1.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/03/744330105a74dc004578f47ec27e1bf66b1dd5664ea444d18423e41343bd/mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d", size = 3150767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/90/3a83d3bcff2eb85151723f116336bd545995b5260a49d3e0d95213fcc2d7/mypy-1.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d7d4371829184e22fda4015278fbfdef0327a4b955a483012bd2d423a788801", size = 11017908 }, + { url = "https://files.pythonhosted.org/packages/e4/5c/d6b32ddde2460fc63168ca0f7bf44f38474353547f7c0304a30023c40aa0/mypy-1.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f59f1dfbf497d473201356966e353ef09d4daec48caeacc0254db8ef633a28a5", size = 10184164 }, + { url = "https://files.pythonhosted.org/packages/42/5e/680aa37c938e6db23bd7e6dd4d38d7e609998491721e453b32ec10d31e7f/mypy-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b947097fae68004b8328c55161ac9db7d3566abfef72d9d41b47a021c2fba6b1", size = 12587852 }, + { url = "https://files.pythonhosted.org/packages/9e/0f/9cafea1c3aaf852cfa1d4a387f33923b6d9714b5c16eb0469da67c5c31e4/mypy-1.12.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96af62050971c5241afb4701c15189ea9507db89ad07794a4ee7b4e092dc0627", size = 13106489 }, + { url = "https://files.pythonhosted.org/packages/ea/c3/7f56d5d87a81e665de8dfa424120ab3a6954ae5854946cec0a46f78f6168/mypy-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90da248f4c2dba6c44ddcfea94bb361e491962f05f41990ff24dbd09969ce20", size = 9634753 }, + { url = "https://files.pythonhosted.org/packages/18/0a/70de7c97a86cb85535077ab5cef1cbc4e2812fd2e9cc21d78eb561a6b80f/mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735", size = 10940998 }, + { url = "https://files.pythonhosted.org/packages/c0/97/9ed6d4834d7549936ab88533b302184fb568a0940c4000d2aaee6dc07112/mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66", size = 10108523 }, + { url = "https://files.pythonhosted.org/packages/48/41/1686f37d09c915dfc5b683e20cc99dabac199900b5ca6d22747b99ddcb50/mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6", size = 12505553 }, + { url = "https://files.pythonhosted.org/packages/8d/2b/2dbcaa7e97b23f27ced77493256ee878f4a140ac750e198630ff1b9b60c6/mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931", size = 12988634 }, + { url = "https://files.pythonhosted.org/packages/54/55/710d082e91a2ccaea21214229b11f9215a9d22446f949491b5457655e82b/mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811", size = 9630747 }, + { url = "https://files.pythonhosted.org/packages/8a/74/b9e0e4f06e951e277058f878302faa154d282ca11274c59fe08353f52949/mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f", size = 11079902 }, + { url = "https://files.pythonhosted.org/packages/9f/62/fcad290769db3eb0de265094cef5c94d6075c70bc1e42b67eee4ca192dcc/mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0", size = 10072373 }, + { url = "https://files.pythonhosted.org/packages/cb/27/9ac78349c2952e4446288ec1174675ab9e0160ed18c2cb1154fa456c54e8/mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042", size = 12589779 }, + { url = "https://files.pythonhosted.org/packages/7c/4a/58cebd122cf1cba95680ac51303fbeb508392413ca64e3e711aa7d4877aa/mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179", size = 13044459 }, + { url = "https://files.pythonhosted.org/packages/5b/c7/672935e2a3f9bcc07b1b870395a653f665657bef3cdaa504ad99f56eadf0/mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a", size = 9731919 }, + { url = "https://files.pythonhosted.org/packages/bb/b0/092be5094840a401940c95224f63bb2a8f09bce9251ac1df180ec523830c/mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc", size = 11068611 }, + { url = "https://files.pythonhosted.org/packages/9a/86/f20f53b8f062876c39602243d7a59b5cabd6b24315d8de511d607fa4de6a/mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635", size = 10068036 }, + { url = "https://files.pythonhosted.org/packages/84/c7/1dbd6575785522da1d4c1ac2c419505fcf23bee74811880cac447a4a77ab/mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81", size = 12585671 }, + { url = "https://files.pythonhosted.org/packages/46/8a/f6ae18b446eb2bccce54c4bd94065bcfe417d6c67021dcc032bf1e720aff/mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4", size = 13036083 }, + { url = "https://files.pythonhosted.org/packages/59/e6/fc65fde3dc7156fce8d49ba21c7b1f5d866ad50467bf196ca94a7f6d2c9e/mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02", size = 9735467 }, + { url = "https://files.pythonhosted.org/packages/ee/dc/98c84202135943428963858bbd4d469b44f0fe0659c885f2e790fc77a9e3/mypy-1.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de5b2a8988b4e1269a98beaf0e7cc71b510d050dce80c343b53b4955fff45f19", size = 11014712 }, + { url = "https://files.pythonhosted.org/packages/20/6a/7fd68f58f457efb6d30206c5c454ef26cd71ed6f7dbc87dc9cee68e1d805/mypy-1.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843826966f1d65925e8b50d2b483065c51fc16dc5d72647e0236aae51dc8d77e", size = 10179252 }, + { url = "https://files.pythonhosted.org/packages/bd/31/4948ea5e9331d1fec202fdeb2a3184b53752f7e914533d884500dba3233f/mypy-1.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe20f89da41a95e14c34b1ddb09c80262edcc295ad891f22cc4b60013e8f78d", size = 12585725 }, + { url = "https://files.pythonhosted.org/packages/6d/6d/76c52b69799a0338e9a938eee3171a9794c4b9b7eba80080aee20355eb31/mypy-1.12.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8135ffec02121a75f75dc97c81af7c14aa4ae0dda277132cfcd6abcd21551bfd", size = 13103153 }, + { url = "https://files.pythonhosted.org/packages/5c/b1/e77a79a4895e1a4009f07bc1b8639f251bb5dd3029d7110a3c07d76f021b/mypy-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a7b76fa83260824300cc4834a3ab93180db19876bce59af921467fd03e692810", size = 9631585 }, + { url = "https://files.pythonhosted.org/packages/84/6b/1db9de4e0764778251fb2d64cb7455cf6db75dc99c9f72c8b7e74b6a8a17/mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e", size = 2646060 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "packaging" +version = "24.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pre-commit" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/c8/e22c292035f1bac8b9f5237a2622305bc0304e776080b246f3df57c4ff9f/pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2", size = 191678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/8f/496e10d51edd6671ebe0432e33ff800aa86775d2d147ce7d43389324a525/pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878", size = 218713 }, +] + +[[package]] +name = "pytest" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, +] + +[[package]] +name = "swydd" +version = "0.1.0" +source = { editable = "." } + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pre-commit" }, + { name = "pytest" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy", specifier = ">=1.8.0" }, + { name = "pre-commit", specifier = ">=3.6.2" }, + { name = "pytest", specifier = ">=8.3.2" }, +] + +[[package]] +name = "tomli" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "virtualenv" +version = "20.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/7f/192dd6ab6d91ebea7adf6c030eaf549b1ec0badda9f67a77b633602f66ac/virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2", size = 6483858 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/15/828ec11907aee2349a9342fa71fba4ba7f3af938162a382dd7da339dea16/virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655", size = 3110969 }, +]