diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 253b206..ad1c6f2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Install uv - uses: astral-sh/setup-uv@v4 + uses: astral-sh/setup-uv@v5 with: enable-cache: true - name: Setup Python @@ -24,4 +24,4 @@ jobs: with: python-version-file: .python-version - name: Test - run: uv run invoke test + run: uv run just test diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fd9cdf2..efc69e0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,10 +2,10 @@ # https://pre-commit.com/hooks.html default_language_version: - python: python3.12 + python: python3.13 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: 'v5.0.0' hooks: - id: check-added-large-files - id: check-case-conflict diff --git a/.python-version b/.python-version index e4fba21..24ee5b1 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.12 +3.13 diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index f779320..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,17 +0,0 @@ -## v3.0.0 (2024-12-15) - -### Fix - -- **project**: switch from mlflow to uv - -### Refactor - -- **cicd**: fix ci/cd bug - -## v2.0.0 (2024-12-15) - -### Feat - -- **upgrade**: upgrade to v3 of the mlops-python-package, replacing poetry with uv - -## v1.0.0 (2024-07-28) diff --git a/README.md b/README.md index c7785a4..59ccecd 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ The template provides a robust foundation for building, testing, packaging, and This [Cookiecutter](https://cookiecutter.readthedocs.io/) is designed to be a common ground for diverse MLOps environments. Whether you're working with [Kubernetes](https://www.kubeflow.org/), [Vertex AI](https://cloud.google.com/vertex-ai), [Databricks](https://www.databricks.com/), [Azure ML](https://azure.microsoft.com/en-us/products/machine-learning), or [AWS SageMaker](https://aws.amazon.com/sagemaker/), the core principles of using Python packages and Docker images remain consistent. -This template equips you with the essentials for creating, testing, and packaging your AI/ML code, providing a solid base for integration into your chosen MLOps platform. To fully leverage its capabilities within a specific environment, you might need to combine it with external tools like [Airflow](https://airflow.apache.org/) for orchestration or platform-specific SDKs for deployment. +This template equips you with the essentials for creating, testing, and packaging your AI/ML code, providing a solid base for [integration into your chosen MLOps platform](https://fmind.medium.com/stop-building-rigid-ai-ml-pipelines-embrace-reusable-components-for-flexible-mlops-6e165d837110). To fully leverage its capabilities within a specific environment, you might need to combine it with external tools like [Airflow](https://airflow.apache.org/) for orchestration or platform-specific SDKs for deployment. You have the freedom to structure your `src/` and `tests/` directories according to your preferences. Alternatively, you can draw inspiration from the structure used in the [MLOps Python Package](https://github.com/fmind/mlops-python-package) project for a ready-made implementation. @@ -50,8 +50,8 @@ You'll be prompted for the following variables: - `license`: The license for your project. - `version`: The initial version of your project. - `description`: A brief description of your project. -- `python_version`: The Python version to use (e.g., 3.12). -- `mlflow_version`: The MLflow version to use (e.g., 2.19.0). +- `python_version`: The Python version to use (e.g., 3.13). +- `mlflow_version`: The MLflow version to use (e.g., 2.20.3). 2. **Initialize a git repository:** @@ -78,22 +78,25 @@ git init Use the provided Invoke tasks to manage your development workflow: -- `invoke installs`: Install dependencies and pre-commit hooks. -- `invoke formats`: Format your code. -- `invoke checks`: Run code quality, type, security, and test checks. -- `invoke docs`: Generate API documentation. -- `invoke packages`: Build your Python package. -- `invoke projects`: Run MLflow projects. -- `invoke containers`: Build and run your Docker image. +- `uv run just check`: Run code quality, type, security, and test checks. +- `uv run just clean`: Clean up generated files. +- `uv run just commit`: Commit changes to your repository. +- `uv run just doc`: Generate API documentation. +- `uv run just docker`: Build and run your Docker image. +- `uv run just format`: Format your code with Ruff. +- `uv run just install`: Install dependencies, pre-commit hooks, and GitHub rulesets. +- `uv run just mlflow`: Start an Mlflow server. +- `uv run just package`: Build your Python package. +- `uv run just project`: Run the project in the CLI. ## Example Usage -### Running an MLflow Project +### Running the Project Script After installing dependencies and setting up MLflow: ```bash -invoke projects +uv run just project ``` This will execute the job with the configuration file in your `confs` folder. @@ -101,7 +104,7 @@ This will execute the job with the configuration file in your `confs` folder. ### Building and Running Your Docker Image ```bash -invoke containers +invoke docker ``` This builds a Docker image based on your [`Dockerfile`](https://github.com/fmind/cookiecutter-mlops-package/blob/main/%7B%7Bcookiecutter.repository%7D%7D/Dockerfile) and runs it. The `CMD` in the Dockerfile executes your package with the `--help` flag. diff --git a/cookiecutter-mlops-package.code-workspace b/cookiecutter-mlops-package.code-workspace index f673397..8e0df21 100644 --- a/cookiecutter-mlops-package.code-workspace +++ b/cookiecutter-mlops-package.code-workspace @@ -13,7 +13,6 @@ }, "extensions": { "recommendations": [ - "dchanco.vsc-invoke", "ms-python.python" ] } diff --git a/cookiecutter.json b/cookiecutter.json index 5c941d2..9b3bf02 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -6,8 +6,8 @@ "license": "MIT", "version": "0.1.0", "description": "TODO", - "python_version": "3.12", - "mlflow_version": "2.19.0", + "python_version": "3.13", + "mlflow_version": "2.20.3", "__prompts__": { "user": "GitHub User", "name": "Project Name", diff --git a/invoke.yaml b/invoke.yaml deleted file mode 100644 index 4fd5e6e..0000000 --- a/invoke.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# https://docs.pyinvoke.org/en/latest/index.html - -run: - echo: true diff --git a/justfile b/justfile new file mode 100644 index 0000000..8b93c66 --- /dev/null +++ b/justfile @@ -0,0 +1,33 @@ +# https://just.systems/man/en/ + +# REQUIRES + +find := require("find") +rm := require("rm") +uv := require("uv") + +# DEFAULTS + +# display help information +default: + @just --list + +# TASKS + +# clean the project +clean: + rm -rf .venv/ + rm -rf .mypy_cache/ + rm -rf .pytest_cache/ + +# install the project +install: + uv sync --all-groups + +# setup the project hooks +hooks: + uv run pre-commit install + +# run the project unit tests +test: + uv run pytest tests/ diff --git a/pyproject.toml b/pyproject.toml index 52bd902..1083e03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,11 +5,11 @@ [project] name = "cookiecutter-mlops-package" -version = "3.0.0" +version = "4.1.0" description = "Build and deploy Python packages and Docker images for MLOps projects." authors = [{ name = "Médéric HURIER", email = "github@fmind.dev" }] readme = "README.md" -requires-python = ">=3.12" +requires-python = ">=3.13" license = { file = "LICENSE.txt" } # DEPENDENCIES @@ -17,11 +17,11 @@ license = { file = "LICENSE.txt" } [dependency-groups] dev = [ "commitizen>=4.1.0", - "invoke>=2.2.0", "pre-commit>=4.0.1", - "pytest>=8.3.4", "pytest-cookies>=0.7.0", "pytest-shell-utilities>=1.9.7", + "pytest>=8.3.4", + "rust-just>=1.39.0", ] # TOOLS diff --git a/tasks.py b/tasks.py deleted file mode 100644 index 9db1ca7..0000000 --- a/tasks.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Tasks of the project.""" - -# %% IMPORTS - -from invoke import task -from invoke.context import Context - -# %% TASKS - - -@task -def clean(ctx: Context) -> None: - """Clean the project.""" - ctx.run("rm -rf .venv/") - ctx.run("rm -rf .mypy_cache/") - ctx.run("rm -rf .pytest_cache/") - ctx.run("find . -type f -name '*.py[co]' -delete") - ctx.run(r"find . -type d -name __pycache__ -exec rm -r {} \+") - - -@task -def install(ctx: Context) -> None: - """Install the project.""" - ctx.run("uv sync --all-groups") - - -@task -def hooks(ctx: Context) -> None: - """Setup the project hooks.""" - ctx.run("uv run pre-commit install") - - -@task -def test(ctx: Context) -> None: - """Run the project unit tests.""" - ctx.run("uv run pytest tests/") diff --git a/tests/test_cookiecutter.py b/tests/test_cookiecutter.py index 9832aa2..4646f81 100644 --- a/tests/test_cookiecutter.py +++ b/tests/test_cookiecutter.py @@ -9,15 +9,15 @@ COMMANDS = [ "git init", - "uv run invoke cleans.reset", - "uv run invoke installs", - "uv run invoke formats", - "uv run invoke checks", - "uv run invoke docs", - "uv run invoke projects", - "uv run invoke packages", - "uv run invoke containers", - "uv run invoke mlflow.doctor", + "uv run just clean", + "uv run just install", + "uv run just format", + "uv run just check", + "uv run just doc", + "uv run just project", + "uv run just package", + "uv run just docker", + "uv run just mlflow-doctor", ] # %% TESTS @@ -26,14 +26,14 @@ def test_project_generation(cookies: Cookies) -> None: """Test the generation of the project.""" # given - context = { + context = { "user": "tester", "name": "MLOps 123", "license": "apache-2", "version": "1.0.0", "description": "A test project.", - "python_version": "3.12", - "mlflow_version": "2.19.0", + "python_version": "3.13", + "mlflow_version": "2.20.3", } repository = context['name'].lower().replace(' ', '-') package = repository.replace('-', '_') @@ -48,8 +48,8 @@ def test_project_generation(cookies: Cookies) -> None: assert result.context == { "user": context['user'], "name": context['name'], - "repository": repository, "package": package, + "repository": repository, "license": context['license'], "version": context['version'], "description": context['description'], diff --git a/uv.lock b/uv.lock index 836fe3f..55e726b 100644 --- a/uv.lock +++ b/uv.lock @@ -1,13 +1,13 @@ version = 1 -requires-python = ">=3.12" +requires-python = ">=3.13" [[package]] name = "argcomplete" -version = "3.5.2" +version = "3.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/03/581b1c29d88fffaa08abbced2e628c34dd92d32f1adaed7e42fc416938b0/argcomplete-3.5.2.tar.gz", hash = "sha256:23146ed7ac4403b70bd6026402468942ceba34a6732255b9edf5b7354f68a6bb", size = 82341 } +sdist = { url = "https://files.pythonhosted.org/packages/0c/be/6c23d80cb966fb8f83fb1ebfb988351ae6b0554d0c3a613ee4531c026597/argcomplete-3.5.3.tar.gz", hash = "sha256:c12bf50eded8aebb298c7b7da7a5ff3ee24dffd9f5281867dfe1424b58c55392", size = 72999 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/37/3fa718aaadd36e073891138dc3ebd919a71bafd4881c97d8a133265af191/argcomplete-3.5.2-py3-none-any.whl", hash = "sha256:036d020d79048a5d525bc63880d7a4b8d1668566b8a76daf1144c0bbe0f63472", size = 43506 }, + { url = "https://files.pythonhosted.org/packages/c4/08/2a4db06ec3d203124c967fc89295e85a202e5cbbcdc08fd6a64b65217d1e/argcomplete-3.5.3-py3-none-any.whl", hash = "sha256:2ab2c4a215c59fd6caaff41a869480a23e8f6a5f910b266c1808037f4e375b61", size = 43569 }, ] [[package]] @@ -25,11 +25,11 @@ wheels = [ [[package]] name = "attrs" -version = "24.2.0" +version = "25.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 } +sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, + { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, ] [[package]] @@ -46,11 +46,11 @@ wheels = [ [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.1.31" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, ] [[package]] @@ -73,53 +73,36 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.0" +version = "3.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/0b/4b7a70987abf9b8196845806198975b6aab4ce016632f817ad758a5aa056/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", size = 194445 }, - { url = "https://files.pythonhosted.org/packages/50/89/354cc56cf4dd2449715bc9a0f54f3aef3dc700d2d62d1fa5bbea53b13426/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", size = 125275 }, - { url = "https://files.pythonhosted.org/packages/fa/44/b730e2a2580110ced837ac083d8ad222343c96bb6b66e9e4e706e4d0b6df/charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", size = 119020 }, - { url = "https://files.pythonhosted.org/packages/9d/e4/9263b8240ed9472a2ae7ddc3e516e71ef46617fe40eaa51221ccd4ad9a27/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", size = 139128 }, - { url = "https://files.pythonhosted.org/packages/6b/e3/9f73e779315a54334240353eaea75854a9a690f3f580e4bd85d977cb2204/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", size = 149277 }, - { url = "https://files.pythonhosted.org/packages/1a/cf/f1f50c2f295312edb8a548d3fa56a5c923b146cd3f24114d5adb7e7be558/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", size = 142174 }, - { url = "https://files.pythonhosted.org/packages/16/92/92a76dc2ff3a12e69ba94e7e05168d37d0345fa08c87e1fe24d0c2a42223/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", size = 143838 }, - { url = "https://files.pythonhosted.org/packages/a4/01/2117ff2b1dfc61695daf2babe4a874bca328489afa85952440b59819e9d7/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", size = 146149 }, - { url = "https://files.pythonhosted.org/packages/f6/9b/93a332b8d25b347f6839ca0a61b7f0287b0930216994e8bf67a75d050255/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", size = 140043 }, - { url = "https://files.pythonhosted.org/packages/ab/f6/7ac4a01adcdecbc7a7587767c776d53d369b8b971382b91211489535acf0/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", size = 148229 }, - { url = "https://files.pythonhosted.org/packages/9d/be/5708ad18161dee7dc6a0f7e6cf3a88ea6279c3e8484844c0590e50e803ef/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", size = 151556 }, - { url = "https://files.pythonhosted.org/packages/5a/bb/3d8bc22bacb9eb89785e83e6723f9888265f3a0de3b9ce724d66bd49884e/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", size = 149772 }, - { url = "https://files.pythonhosted.org/packages/f7/fa/d3fc622de05a86f30beea5fc4e9ac46aead4731e73fd9055496732bcc0a4/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", size = 144800 }, - { url = "https://files.pythonhosted.org/packages/9a/65/bdb9bc496d7d190d725e96816e20e2ae3a6fa42a5cac99c3c3d6ff884118/charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", size = 94836 }, - { url = "https://files.pythonhosted.org/packages/3e/67/7b72b69d25b89c0b3cea583ee372c43aa24df15f0e0f8d3982c57804984b/charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", size = 102187 }, - { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, - { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, - { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, - { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, - { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, - { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, - { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, - { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, - { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, - { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, - { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, - { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, - { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, - { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, - { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, - { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, ] [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "platform_system == 'Windows'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, ] [[package]] @@ -133,7 +116,7 @@ wheels = [ [[package]] name = "commitizen" -version = "4.1.0" +version = "4.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, @@ -147,9 +130,9 @@ dependencies = [ { name = "termcolor" }, { name = "tomlkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/c5/66f1b977b48501a33f5fd33253aba14786483b08aba987718d272e99e732/commitizen-4.1.0.tar.gz", hash = "sha256:4f2d9400ec411aec1c738d4c63fc7fd5807cd6ddf6be970869e03e68b88ff718", size = 51252 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fd/cd449bed87a26ecb61c950410e2d94e97ac31bf1f3ec69cc718b215384ce/commitizen-4.4.1.tar.gz", hash = "sha256:626d9f545fb9b2db42305e16ef35d6348a35081a80527bad863a05a7ba0bec21", size = 52345 } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/f7/7f70adfbf3553ffdbe391eaacde72b21dbc1b4226ae56ca32e8ded1bf70b/commitizen-4.1.0-py3-none-any.whl", hash = "sha256:2e6c5fbd442cab4bcc5a04bc86ef2196ef84bcf611317d6c596e87f5bb4c09f5", size = 72282 }, + { url = "https://files.pythonhosted.org/packages/33/0a/03fd9b5b7d9de11ff5e7c5a73b66c17775d00c9eea07b585d4fd7bf45a31/commitizen-4.4.1-py3-none-any.whl", hash = "sha256:98dbee784cc74fd1b24915e265e99ce81caccd64e54cb42b347a37d1dd2a4cd8", size = 74882 }, ] [[package]] @@ -173,17 +156,17 @@ wheels = [ [[package]] name = "cookiecutter-mlops-package" -version = "2.0.0" +version = "4.1.0" source = { virtual = "." } [package.dev-dependencies] dev = [ { name = "commitizen" }, - { name = "invoke" }, { name = "pre-commit" }, { name = "pytest" }, { name = "pytest-cookies" }, { name = "pytest-shell-utilities" }, + { name = "rust-just" }, ] [package.metadata] @@ -191,11 +174,11 @@ dev = [ [package.metadata.requires-dev] dev = [ { name = "commitizen", specifier = ">=4.1.0" }, - { name = "invoke", specifier = ">=2.2.0" }, { name = "pre-commit", specifier = ">=4.0.1" }, { name = "pytest", specifier = ">=8.3.4" }, { name = "pytest-cookies", specifier = ">=0.7.0" }, { name = "pytest-shell-utilities", specifier = ">=1.9.7" }, + { name = "rust-just", specifier = ">=1.39.0" }, ] [[package]] @@ -227,20 +210,20 @@ wheels = [ [[package]] name = "filelock" -version = "3.16.1" +version = "3.17.0" 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 } +sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, + { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 }, ] [[package]] name = "identify" -version = "2.6.3" +version = "2.6.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/5f/05f0d167be94585d502b4adf8c7af31f1dc0b1c7e14f9938a88fdbbcf4a7/identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02", size = 99179 } +sdist = { url = "https://files.pythonhosted.org/packages/f9/fa/5eb460539e6f5252a7c5a931b53426e49258cde17e3d50685031c300a8fd/identify-2.6.8.tar.gz", hash = "sha256:61491417ea2c0c5c670484fd8abbb34de34cdae1e5f39a73ee65e48e4bb663fc", size = 99249 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/f5/09644a3ad803fae9eca8efa17e1f2aef380c7f0b02f7ec4e8d446e51d64a/identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd", size = 99049 }, + { url = "https://files.pythonhosted.org/packages/78/8c/4bfcab2d8286473b8d83ea742716f4b79290172e75f91142bc1534b05b9a/identify-2.6.8-py2.py3-none-any.whl", hash = "sha256:83657f0f766a3c8d0eaea16d4ef42494b39b34629a4b3192a9d020d349b3e255", size = 99109 }, ] [[package]] @@ -261,25 +244,16 @@ 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 = "invoke" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/42/127e6d792884ab860defc3f4d80a8f9812e48ace584ffc5a346de58cdc6c/invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5", size = 299835 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/66/7f8c48009c72d73bc6bbe6eb87ac838d6a526146f7dab14af671121eb379/invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820", size = 160274 }, -] - [[package]] name = "jinja2" -version = "3.1.4" +version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] @@ -300,16 +274,6 @@ version = "3.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, @@ -379,7 +343,7 @@ wheels = [ [[package]] name = "pre-commit" -version = "4.0.1" +version = "4.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -388,50 +352,50 @@ dependencies = [ { 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 } +sdist = { url = "https://files.pythonhosted.org/packages/2a/13/b62d075317d8686071eb843f0bb1f195eb332f48869d3c31a4c6f1e063ac/pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4", size = 193330 } 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 }, + { url = "https://files.pythonhosted.org/packages/43/b3/df14c580d82b9627d173ceea305ba898dca135feb360b6d84019d0803d3b/pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b", size = 220560 }, ] [[package]] name = "prompt-toolkit" -version = "3.0.36" +version = "3.0.50" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/93/180be2342f89f16543ec4eb3f25083b5b84eba5378f68efff05409fb39a9/prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63", size = 423863 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/e1/bd15cb8ffdcfeeb2bdc215de3c3cffca11408d829e4b8416dcfe71ba8854/prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab", size = 429087 } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/37/791f1a6edd13c61cac85282368aa68cb0f3f164440fdf60032f2cc6ca34e/prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305", size = 386414 }, + { url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816 }, ] [[package]] name = "psutil" -version = "6.1.0" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565 } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762 }, - { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777 }, - { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259 }, - { url = "https://files.pythonhosted.org/packages/58/4d/8245e6f76a93c98aab285a43ea71ff1b171bcd90c9d238bf81f7021fb233/psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", size = 287255 }, - { url = "https://files.pythonhosted.org/packages/27/c2/d034856ac47e3b3cdfa9720d0e113902e615f4190d5d1bdb8df4b2015fb2/psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", size = 288804 }, - { url = "https://files.pythonhosted.org/packages/ea/55/5389ed243c878725feffc0d6a3bc5ef6764312b6fc7c081faaa2cfa7ef37/psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", size = 250386 }, - { url = "https://files.pythonhosted.org/packages/11/91/87fa6f060e649b1e1a7b19a4f5869709fbf750b7c8c262ee776ec32f3028/psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", size = 254228 }, + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, ] [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, ] [[package]] name = "pytest" -version = "8.3.4" +version = "8.3.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -439,9 +403,9 @@ dependencies = [ { name = "packaging" }, { name = "pluggy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, ] [[package]] @@ -529,9 +493,6 @@ name = "pywin32" version = "308" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 }, - { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 }, - { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 }, { url = "https://files.pythonhosted.org/packages/a9/a4/aa562d8935e3df5e49c161b427a3a2efad2ed4e9cf81c3de636f1fdddfd0/pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", size = 5938579 }, { url = "https://files.pythonhosted.org/packages/c7/50/b0efb8bb66210da67a53ab95fd7a98826a97ee21f1d22949863e6d588b22/pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4", size = 6542056 }, { url = "https://files.pythonhosted.org/packages/26/df/2b63e3e4f2df0224f8aaf6d131f54fe4e8c96400eb9df563e2aae2e1a1f9/pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", size = 7974986 }, @@ -543,15 +504,6 @@ 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/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 }, @@ -565,14 +517,14 @@ wheels = [ [[package]] name = "questionary" -version = "2.0.1" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "prompt-toolkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/d0/d73525aeba800df7030ac187d09c59dc40df1c878b4fab8669bdc805535d/questionary-2.0.1.tar.gz", hash = "sha256:bcce898bf3dbb446ff62830c86c5c6fb9a22a54146f0f5597d3da43b10d8fc8b", size = 24726 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/b8/d16eb579277f3de9e56e5ad25280fab52fc5774117fb70362e8c2e016559/questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587", size = 26775 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/e7/2dd8f59d1d328773505f78b85405ddb1cfe74126425d076ce72e65540b8b/questionary-2.0.1-py3-none-any.whl", hash = "sha256:8ab9a01d0b91b68444dff7f6652c1e754105533f083cbe27597c8110ecc230a2", size = 34248 }, + { url = "https://files.pythonhosted.org/packages/ad/3f/11dd4cd4f39e05128bfd20138faea57bec56f9ffba6185d276e3107ba5b2/questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec", size = 36747 }, ] [[package]] @@ -603,6 +555,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, ] +[[package]] +name = "rust-just" +version = "1.39.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/e5/37254d353a23f567ac218ddd2c461337fafe3a43d44dcd6f0c8e72d096f4/rust_just-1.39.0.tar.gz", hash = "sha256:247d0b293924cc8089a73428c9c03a3c2c0627bb8f205addb976ded0681f0dac", size = 1395439 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/9e/c7151bfa84c1cd3ac4c11a60d1a2f074b7a244ae96959d968e8b9e8e3f29/rust_just-1.39.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3845ab10254c994ddebcf489b30c53a24c1d11585c9e0eeaf1cb0da422bee87f", size = 1781993 }, + { url = "https://files.pythonhosted.org/packages/e4/0f/c93ef08567835356033bee162c2e5e06b018811f5188b94ef3e74dafe7eb/rust_just-1.39.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fd5c12118a8d65266ccdacfbc24bab26f77d509caaf263095cb96611ea6ce7e8", size = 1652880 }, + { url = "https://files.pythonhosted.org/packages/f3/e7/22647f9c18537046940b3b4159377665912acccbacedd775917610c0390d/rust_just-1.39.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:428d07b1e798777c4e9a8c245539d72743be095558010f0a86823e1c442930f9", size = 1771788 }, + { url = "https://files.pythonhosted.org/packages/d5/1f/2a36afad5eeca6756fc8c87e08d102cdadf8e4b31c5f8bcb6f12c109b5b4/rust_just-1.39.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:135a7a65a8641b00a2fe7f3156a97ab7052e4830a922a71e67ca4e38ccd54cd2", size = 1778377 }, + { url = "https://files.pythonhosted.org/packages/97/5b/45effb44bbfab892774a239446920cfb9b3d999921fe7cff3d99b36394a7/rust_just-1.39.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94eb45e585fda019f7f9cbac198e10e31f81c704371887cbdec9b7a1ae2e0d29", size = 1896235 }, + { url = "https://files.pythonhosted.org/packages/b5/12/7e88a7e917c2e933846a32a2f537786829b48c827a6029e85943d5eeadc0/rust_just-1.39.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de4a8566ca1eb87b5ff2669a6dd9474b16977c5d712534a5a9c7a950271da2d0", size = 1954347 }, + { url = "https://files.pythonhosted.org/packages/9a/36/5dc917a2c0a0b66f0cc4bb0be4985090deefa33433dd869649a4ce2bc8f3/rust_just-1.39.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7ecd8fd862729c243498951caa54d778ff480c2524039280ff3ebb9a64299f", size = 2450909 }, + { url = "https://files.pythonhosted.org/packages/da/fc/b9224b354da8a89b38cd4145ec0e9e089635a45c1459231e349647cdad64/rust_just-1.39.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:576229024d2ef8fc696d5a049ecd0d8f3d9b920a32e76f65e95840d24d804101", size = 1895058 }, + { url = "https://files.pythonhosted.org/packages/0b/ef/985cb93c9dd36f9bc41f26b3ce6d420c69fb18166ac61783bced2c328f75/rust_just-1.39.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c1cd9240e2c1b352d7ccc6b89ce84fcc0352f15bb9660cdc6bc34802b36251b6", size = 1767816 }, + { url = "https://files.pythonhosted.org/packages/50/43/fc644746a7479fadbe6878ae9fa1da83860104d1e64a49de47306fa1a591/rust_just-1.39.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d6eff0461df7e36eba6e7f0addf16ef98563cf8cb483e4c8393be5456d6af5c6", size = 1791992 }, + { url = "https://files.pythonhosted.org/packages/32/d9/6924547c02afba3a49b14f53bed09b54d4292016b830f366d7ed1f80288a/rust_just-1.39.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0dfcc49a5fa126ba923b58e48921fd117e429660495577a854494c6ced3134c9", size = 1884357 }, + { url = "https://files.pythonhosted.org/packages/15/96/7e8d3795588c2e8e629f9f0d9cdd0cf1ba1bef5bf9c8b64aae33c78f1993/rust_just-1.39.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:826203ad02c869ad8621993a608adb01394ef9c9c9ca6aa7dd7875b1f272aa46", size = 1936706 }, + { url = "https://files.pythonhosted.org/packages/23/28/2492c4b7c8f0e526f52a263f20b951880387f992151bbc46c6c8914786a8/rust_just-1.39.0-py3-none-win32.whl", hash = "sha256:dcef0926b287449e853b878f6f34759a797d017cefb83afbcd74820d37259b78", size = 1577519 }, + { url = "https://files.pythonhosted.org/packages/6f/c2/1d576be1ca714df4a90255cfbcb4ec06d9aafbf7b14924c2881a8596a68e/rust_just-1.39.0-py3-none-win_amd64.whl", hash = "sha256:3139f3f76434a8ebbf35b213d149e647c4d9546312b438e262df7ec41e7ef7bc", size = 1705761 }, +] + [[package]] name = "six" version = "1.17.0" @@ -650,25 +624,25 @@ wheels = [ [[package]] name = "urllib3" -version = "2.2.3" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, ] [[package]] name = "virtualenv" -version = "20.28.0" +version = "20.29.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/75/53316a5a8050069228a2f6d11f32046cfa94fbb6cc3f08703f59b873de2e/virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa", size = 7650368 } +sdist = { url = "https://files.pythonhosted.org/packages/f1/88/dacc875dd54a8acadb4bcbfd4e3e86df8be75527116c91d8f9784f5e9cab/virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728", size = 4320272 } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/f9/0919cf6f1432a8c4baa62511f8f8da8225432d22e83e3476f5be1a1edc6e/virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0", size = 4276702 }, + { url = "https://files.pythonhosted.org/packages/93/fa/849483d56773ae29740ae70043ad88e068f98a6401aa819b5d6bee604683/virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a", size = 4301478 }, ] [[package]] diff --git a/{{cookiecutter.repository}}/.env.example b/{{cookiecutter.repository}}/.env.example new file mode 100644 index 0000000..e69de29 diff --git a/{{cookiecutter.repository}}/.github/actions/setup/action.yml b/{{cookiecutter.repository}}/.github/actions/setup/action.yml index 3426d72..cd4f2ef 100644 --- a/{{cookiecutter.repository}}/.github/actions/setup/action.yml +++ b/{{cookiecutter.repository}}/.github/actions/setup/action.yml @@ -4,7 +4,7 @@ runs: using: composite steps: - name: Install uv - uses: astral-sh/setup-uv@v4 + uses: astral-sh/setup-uv@v5 with: enable-cache: true - name: Setup Python diff --git a/{{cookiecutter.repository}}/.github/rulesets/main.json b/{{cookiecutter.repository}}/.github/rulesets/main.json new file mode 100644 index 0000000..98d3035 --- /dev/null +++ b/{{cookiecutter.repository}}/.github/rulesets/main.json @@ -0,0 +1,58 @@ +{ + "name": "main", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "exclude": [], + "include": [ + "~DEFAULT_BRANCH" + ] + } + }, + "rules": [ + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 0, + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_review_thread_resolution": false, + "allowed_merge_methods": [ + "squash", + "rebase" + ] + } + }, + { + "type": "required_status_checks", + "parameters": { + "strict_required_status_checks_policy": true, + "do_not_enforce_on_create": false, + "required_status_checks": [ + { + "context": "checks", + "integration_id": 15368 + } + ] + } + }, + { + "type": "non_fast_forward" + } + ], + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "always" + } + ] +} diff --git a/{{cookiecutter.repository}}/.github/workflows/check.yml b/{{cookiecutter.repository}}/.github/workflows/check.yml index ec648b9..53508f1 100644 --- a/{{cookiecutter.repository}}/.github/workflows/check.yml +++ b/{{cookiecutter.repository}}/.github/workflows/check.yml @@ -12,9 +12,9 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup - - run: uv sync --group=checks - - run: uv run invoke checks.format - - run: uv run invoke checks.type - - run: uv run invoke checks.code - - run: uv run invoke checks.security - - run: uv run invoke checks.coverage + - run: uv sync --group=check + - run: uv run just check-code + - run: uv run just check-type + - run: uv run just check-format + - run: uv run just check-security + - run: uv run just check-coverage diff --git a/{{cookiecutter.repository}}/.github/workflows/publish.yml b/{{cookiecutter.repository}}/.github/workflows/publish.yml index e1e3db0..c3a4814 100644 --- a/{{cookiecutter.repository}}/.github/workflows/publish.yml +++ b/{{cookiecutter.repository}}/.github/workflows/publish.yml @@ -5,7 +5,7 @@ on: - edited - published env: - DOCKER_IMAGE: ghcr.io/{{cookiecutter.user}}/{{cookiecutter.repository}} + DOCKER_IMAGE: ghcr.io/fmind/mlops-python-package concurrency: cancel-in-progress: true group: publish-workflow @@ -15,8 +15,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup - - run: uv sync --group=docs - - run: uv run invoke docs + - run: uv sync --group=doc + - run: uv run just doc - uses: JamesIves/github-pages-deploy-action@v4 with: folder: docs/ @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/setup - run: uv sync --only-dev - - run: uv run invoke packages + - run: uv run just package - uses: docker/login-action@v3 with: registry: ghcr.io diff --git a/{{cookiecutter.repository}}/.pre-commit-config.yaml b/{{cookiecutter.repository}}/.pre-commit-config.yaml index 5746885..9eebedd 100644 --- a/{{cookiecutter.repository}}/.pre-commit-config.yaml +++ b/{{cookiecutter.repository}}/.pre-commit-config.yaml @@ -2,10 +2,10 @@ # https://pre-commit.com/hooks.html default_language_version: - python: python{{cookiecutter.python_version}} + python: python3.13 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: 'v5.0.0' hooks: - id: check-added-large-files - id: check-case-conflict @@ -17,12 +17,16 @@ repos: - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.1 + rev: 'v0.9.9' hooks: - id: ruff - id: ruff-format + - repo: https://github.com/PyCQA/bandit + rev: '1.8.3' + hooks: + - id: bandit # - repo: https://github.com/commitizen-tools/commitizen - # rev: v4.0.0 + # rev: 'v4.4.1' # hooks: # - id: commitizen # - id: commitizen-branch diff --git a/{{cookiecutter.repository}}/invoke.yaml b/{{cookiecutter.repository}}/invoke.yaml deleted file mode 100644 index 272d689..0000000 --- a/{{cookiecutter.repository}}/invoke.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# https://docs.pyinvoke.org/en/latest/index.html - -run: - echo: true -project: - name: {{cookiecutter.name}} - package: {{cookiecutter.package}} - repository: {{cookiecutter.repository}} diff --git a/{{cookiecutter.repository}}/justfile b/{{cookiecutter.repository}}/justfile new file mode 100644 index 0000000..0fdd396 --- /dev/null +++ b/{{cookiecutter.repository}}/justfile @@ -0,0 +1,38 @@ +# https://just.systems/man/en/ + +# REQUIRES + +docker := require("docker") +find := require("find") +rm := require("rm") +uv := require("uv") + +# SETTINGS + +set dotenv-load := true + +# VARIABLES + +PACKAGE := "{{cookiecutter.package}}" +REPOSITORY := "{{cookiecutter.repository}}" +SOURCES := "src" +TESTS := "tests" + +# DEFAULTS + +# display help information +default: + @just --list + +# IMPORTS + +import 'tasks/check.just' +import 'tasks/clean.just' +import 'tasks/commit.just' +import 'tasks/doc.just' +import 'tasks/docker.just' +import 'tasks/format.just' +import 'tasks/install.just' +import 'tasks/mlflow.just' +import 'tasks/package.just' +import 'tasks/project.just' diff --git a/{{cookiecutter.repository}}/pyproject.toml b/{{cookiecutter.repository}}/pyproject.toml index 9416f63..0e7b3d7 100644 --- a/{{cookiecutter.repository}}/pyproject.toml +++ b/{{cookiecutter.repository}}/pyproject.toml @@ -9,12 +9,13 @@ version = "{{cookiecutter.version}}" description = "{{cookiecutter.description}}" authors = [{ name = "{{cookiecutter.user}}" }] readme = "README.md" +license = { file = "LICENSE.txt" } +keywords = ["mlops", "python", "package"] requires-python = ">={{cookiecutter.python_version}}" dependencies = [ + "hatchling>=1.27.0", "mlflow>={{cookiecutter.mlflow_version}}", ] -license = { file = "LICENSE.txt" } -keywords = ["mlops", "python", "package"] # LINKS @@ -33,25 +34,25 @@ Changelog = "https://github.com/{{cookiecutter.user}}/{{cookiecutter.repository} # DEPENDENCIES [dependency-groups] -checks = [ - "bandit>=1.8.0", - "coverage>=7.6.8", - "mypy>=1.13.0", +check = [ + "bandit>=1.8.3", + "coverage>=7.6.12", + "mypy>=1.15.0", + "pytest>=8.3.5", "pytest-cov>=6.0.0", "pytest-mock>=3.14.0", "pytest-xdist>=3.6.1", - "pytest>=8.3.3", - "ruff>=0.8.1", + "ruff>=0.9.9", ] -commits = ["commitizen>=4.0.0", "pre-commit>=4.0.1"] -dev = ["invoke>=2.2.0"] -docs = ["pdoc>=15.0.0"] -notebooks = ["ipykernel>=6.29.5", "nbformat>=5.10.4"] +commit = ["commitizen>=4.4.1", "pre-commit>=4.1.0"] +dev = ["rust-just>=1.39.0"] +doc = ["pdoc>=15.0.1"] +notebook = ["ipykernel>=6.29.5", "nbformat>=5.10.4"] # TOOLS [tool.uv] -default-groups = ["checks", "commits", "dev", "docs", "notebooks"] +default-groups = ["check", "commit", "dev", "doc", "notebook"] [tool.bandit] targets = ["src"] @@ -70,7 +71,7 @@ omit = ["__main__.py"] [tool.mypy] pretty = true -python_version = "{{cookiecutter.python_version}}" +python_version = "3.13" check_untyped_defs = true ignore_missing_imports = true @@ -82,7 +83,7 @@ pythonpath = ["src"] fix = true indent-width = 4 line-length = 100 -target-version = "py{{cookiecutter.python_version.replace('.', '')}}" +target-version = "py313" [tool.ruff.format] docstring-code-format = true diff --git a/{{cookiecutter.repository}}/tasks/__init__.py b/{{cookiecutter.repository}}/tasks/__init__.py deleted file mode 100644 index a7e7b04..0000000 --- a/{{cookiecutter.repository}}/tasks/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Task collections of the project.""" - -# mypy: ignore-errors - -# %% IMPORTS - -from invoke import Collection - -from . import ( - checks, - cleans, - commits, - containers, - docs, - formats, - installs, - mlflow, - packages, - projects, -) - -# %% NAMESPACES - -ns = Collection() - -# %% COLLECTIONS - -ns.add_collection(checks) -ns.add_collection(cleans) -ns.add_collection(commits) -ns.add_collection(containers) -ns.add_collection(docs) -ns.add_collection(formats) -ns.add_collection(installs) -ns.add_collection(mlflow) -ns.add_collection(packages) -ns.add_collection(projects, default=True) diff --git a/{{cookiecutter.repository}}/tasks/check.just b/{{cookiecutter.repository}}/tasks/check.just new file mode 100644 index 0000000..f7b84ba --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/check.just @@ -0,0 +1,33 @@ +# run check tasks +[group('check')] +check: check-code check-type check-format check-security check-coverage + +# check code quality +[group('check')] +check-code: + {% raw %}uv run ruff check {{SOURCES}} {{TESTS}}{% endraw %} + +# check code coverage +[group('check')] +check-coverage numprocesses="auto" cov_fail_under="80": + {% raw %}uv run pytest --numprocesses={{numprocesses}} --cov={{SOURCES}} --cov-fail-under={{cov_fail_under}} {{TESTS}}{% endraw %} + +# check code format +[group('check')] +check-format: + {% raw %}uv run ruff format --check {{SOURCES}} {{TESTS}}{% endraw %} + +# check code security +[group('check')] +check-security: + {% raw %}uv run bandit --recursive --configfile=pyproject.toml {{SOURCES}}{% endraw %} + +# check unit tests +[group('check')] +check-test numprocesses="auto": + {% raw %}uv run pytest --numprocesses={{numprocesses}} {{TESTS}}{% endraw %} + +# check code typing +[group('check')] +check-type: + {% raw %}uv run mypy {{SOURCES}} {{TESTS}}{% endraw %} diff --git a/{{cookiecutter.repository}}/tasks/checks.py b/{{cookiecutter.repository}}/tasks/checks.py deleted file mode 100644 index 1046835..0000000 --- a/{{cookiecutter.repository}}/tasks/checks.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Check tasks of the project.""" - -# %% IMPORTS - -from invoke.context import Context -from invoke.tasks import task - -# %% TASKS - - -@task -def format(ctx: Context) -> None: - """Check the formats with ruff.""" - ctx.run("uv run ruff format --check src/ tasks/ tests/") - - -@task -def type(ctx: Context) -> None: - """Check the types with mypy.""" - ctx.run("uv run mypy src/ tasks/ tests/") - - -@task -def code(ctx: Context) -> None: - """Check the codes with ruff.""" - ctx.run("uv run ruff check src/ tasks/ tests/") - - -@task -def test(ctx: Context) -> None: - """Check the tests with pytest.""" - ctx.run("uv run pytest --numprocesses=auto tests/") - - -@task -def security(ctx: Context) -> None: - """Check the security with bandit.""" - ctx.run("uv run bandit --recursive --configfile=pyproject.toml src/") - - -@task -def coverage(ctx: Context) -> None: - """Check the coverage with coverage.""" - ctx.run("uv run pytest --numprocesses=auto --cov=src/ --cov-fail-under=80 tests/") - - -@task(pre=[format, type, code, security, coverage], default=True) -def all(_: Context) -> None: - """Run all check tasks.""" diff --git a/{{cookiecutter.repository}}/tasks/clean.just b/{{cookiecutter.repository}}/tasks/clean.just new file mode 100644 index 0000000..21061a5 --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/clean.just @@ -0,0 +1,71 @@ +# run clean tasks +[group('clean')] +clean: clean-build clean-cache clean-constraints clean-coverage clean-docs clean-mlruns clean-mypy clean-outputs clean-pytest clean-python clean-requirements clean-ruff + +# clean build folders +[group('clean')] +clean-build: + rm -rf dist/ + rm -rf build/ + +# clean cache folder +[group('clean')] +clean-cache: + rm -rf .cache/ + +# clean constraints file +[group('clean')] +clean-constraints: + rm -rf constraints.txt + +# clean coverage files +[group('clean')] +clean-coverage: + rm -rf .coverage* + +# clean docs folder +[group('clean')] +clean-docs: + rm -rf docs/ + +# clean mlruns folder +[group('clean')] +clean-mlruns: + rm -rf mlruns/* + +# clean mypy folders +[group('clean')] +clean-mypy: + rm -rf .mypy_cache/ + +# clean outputs folder +[group('clean')] +clean-outputs: + rm -rf outputs/* + +# clean pytest cache +[group('clean')] +clean-pytest: + rm -rf .pytest_cache/ + +# clean python caches +[group('clean')] +clean-python: + find . -type f -name '*.py[co]' -delete + find . -type d -name __pycache__ -exec rm -r {} \+ + +# clean requirements file +[group('clean')] +clean-requirements: + rm -f requirements.txt + +# clean ruff cache +[group('clean')] +clean-ruff: + rm -rf .ruff_cache/ + +# clean venv folder +[confirm] +[group('clean')] +clean-venv: + rm -rf .venv/ diff --git a/{{cookiecutter.repository}}/tasks/cleans.py b/{{cookiecutter.repository}}/tasks/cleans.py deleted file mode 100644 index 9b13acd..0000000 --- a/{{cookiecutter.repository}}/tasks/cleans.py +++ /dev/null @@ -1,137 +0,0 @@ -"""Cleaning tasks of the project.""" - -# %% IMPORTS - -from invoke.context import Context -from invoke.tasks import task - -# %% TASKS - -# %% - Tools - - -@task -def mypy(ctx: Context) -> None: - """Clean the mypy tool.""" - ctx.run("rm -rf .mypy_cache/") - - -@task -def ruff(ctx: Context) -> None: - """Clean the ruff tool.""" - ctx.run("rm -rf .ruff_cache/") - - -@task -def pytest(ctx: Context) -> None: - """Clean the pytest tool.""" - ctx.run("rm -rf .pytest_cache/") - - -@task -def coverage(ctx: Context) -> None: - """Clean the coverage tool.""" - ctx.run("rm -f .coverage*") - - -# %% - Folders - - -@task -def dist(ctx: Context) -> None: - """Clean the dist folder.""" - ctx.run("rm -f dist/*") - - -@task -def docs(ctx: Context) -> None: - """Clean the docs folder.""" - ctx.run("rm -rf docs/*") - - -@task -def cache(ctx: Context) -> None: - """Clean the cache folder.""" - ctx.run("rm -rf .cache/") - - -@task -def mlruns(ctx: Context) -> None: - """Clean the mlruns folder.""" - ctx.run("rm -rf mlruns/*") - - -@task -def outputs(ctx: Context) -> None: - """Clean the outputs folder.""" - ctx.run("rm -rf outputs/*") - - -# %% - Sources - - -@task -def venv(ctx: Context) -> None: - """Clean the venv folder.""" - ctx.run("rm -rf .venv/") - - -@task -def uv(ctx: Context) -> None: - """Clean uv lock file.""" - ctx.run("rm -f uv.lock") - - -@task -def python(ctx: Context) -> None: - """Clean python caches and bytecodes.""" - ctx.run("find . -type f -name '*.py[co]' -delete") - ctx.run(r"find . -type d -name __pycache__ -exec rm -r {} \+") - - -# %% PROJECTS - - -@task -def requirements(ctx: Context) -> None: - """Clean the project requirements file.""" - ctx.run("rm -f requirements.txt") - - -@task -def environment(ctx: Context) -> None: - """Clean the project environment file.""" - ctx.run("rm -f python_env.yaml") - - -# %% - Combines - - -@task(pre=[mypy, ruff, pytest, coverage]) -def tools(_: Context) -> None: - """Run all tools tasks.""" - - -@task(pre=[dist, docs, cache, mlruns, outputs]) -def folders(_: Context) -> None: - """Run all folders tasks.""" - - -@task(pre=[venv, uv, python]) -def sources(_: Context) -> None: - """Run all sources tasks.""" - - -@task(pre=[requirements, environment]) -def projects(_: Context) -> None: - """Run all projects tasks.""" - - -@task(pre=[tools, folders], default=True) -def all(_: Context) -> None: - """Run all tools and folders tasks.""" - - -@task(pre=[all, sources, projects]) -def reset(_: Context) -> None: - """Run all tools, folders, sources, and projects tasks.""" diff --git a/{{cookiecutter.repository}}/tasks/commit.just b/{{cookiecutter.repository}}/tasks/commit.just new file mode 100644 index 0000000..5171dbe --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/commit.just @@ -0,0 +1,14 @@ +# bump package +[group('commit')] +commit-bump: + uv run cz bump + +# commit package +[group('commit')] +commit-files: + uv run cz commit + +# get commit info +[group('commit')] +commit-info: + uv run cz info diff --git a/{{cookiecutter.repository}}/tasks/commits.py b/{{cookiecutter.repository}}/tasks/commits.py deleted file mode 100644 index 2f48bad..0000000 --- a/{{cookiecutter.repository}}/tasks/commits.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Commit tasks of the project.""" - -# %% IMPORTS - -from invoke.context import Context -from invoke.tasks import task - -# %% TASKS - - -@task -def info(ctx: Context) -> None: - """Print a guide for messages.""" - ctx.run("uv run cz info") - - -@task -def bump(ctx: Context) -> None: - """Bump the version of the package.""" - ctx.run("uv run cz bump", pty=True) - - -@task -def commit(ctx: Context) -> None: - """Commit all changes with a message.""" - ctx.run("uv run cz commit", pty=True) - - -@task(pre=[commit], default=True) -def all(_: Context) -> None: - """Run all commit tasks.""" diff --git a/{{cookiecutter.repository}}/tasks/containers.py b/{{cookiecutter.repository}}/tasks/containers.py deleted file mode 100644 index b6401fb..0000000 --- a/{{cookiecutter.repository}}/tasks/containers.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Container tasks of the project.""" - -# %% IMPORTS - -from invoke.context import Context -from invoke.tasks import task - -from . import packages - -# %% CONFIGS - -IMAGE_TAG = "latest" - -# %% TASKS - - -@task -def compose(ctx: Context) -> None: - """Start up docker compose.""" - ctx.run("docker compose up") - - -@task(pre=[packages.build]) -def build(ctx: Context, tag: str = IMAGE_TAG) -> None: - """Build the container image.""" - ctx.run(f"docker build --tag={ctx.project.repository}:{tag} .") - - -@task -def run(ctx: Context, tag: str = IMAGE_TAG) -> None: - """Run the container image.""" - ctx.run(f"docker run --rm {ctx.project.repository}:{tag}") - - -@task(pre=[build, run], default=True) -def all(_: Context) -> None: - """Run all container tasks.""" diff --git a/{{cookiecutter.repository}}/tasks/doc.just b/{{cookiecutter.repository}}/tasks/doc.just new file mode 100644 index 0000000..9763649 --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/doc.just @@ -0,0 +1,13 @@ +# run doc tasks +[group('doc')] +doc: doc-build + +# build documentation +[group('doc')] +doc-build format="google" output="docs": clean-docs + {% raw %}uv run pdoc --docformat={{format}} --output-directory={{output}} {{SOURCES}}/{{PACKAGE}}{% endraw %} + +# serve documentation +[group('doc')] +doc-serve format="google" port="8088": + {% raw %}uv run pdoc --docformat={{format}} --port={{port}} {{SOURCES}}/{{PACKAGE}}{% endraw %} diff --git a/{{cookiecutter.repository}}/tasks/docker.just b/{{cookiecutter.repository}}/tasks/docker.just new file mode 100644 index 0000000..432ea70 --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/docker.just @@ -0,0 +1,18 @@ +# run docker tasks +[group('docker')] +docker: docker-build docker-run + +# build docker image +[group('docker')] +docker-build tag="latest": package-build + {% raw %}docker build --tag={{REPOSITORY}}:{{tag}} .{% endraw %} + +# start docker compose +[group('docker')] +docker-compose: + docker compose up + +# run latest docker image +[group('docker')] +docker-run tag="latest": + {% raw %}docker run --rm {{REPOSITORY}}:{{tag}}{% endraw %} diff --git a/{{cookiecutter.repository}}/tasks/docs.py b/{{cookiecutter.repository}}/tasks/docs.py deleted file mode 100644 index 3f0b34c..0000000 --- a/{{cookiecutter.repository}}/tasks/docs.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Documentation tasks of the project.""" - -# %% IMPORTS - -from invoke.context import Context -from invoke.tasks import task - -from . import cleans - -# %% CONFIGS - -DOC_FORMAT = "google" -OUTPUT_DIR = "docs/" - -# %% TASKS - - -@task -def serve(ctx: Context, format: str = DOC_FORMAT, port: int = 8088) -> None: - """Serve the API docs with pdoc.""" - ctx.run(f"uv run pdoc --docformat={format} --port={port} src/{ctx.project.package}") - - -@task -def api(ctx: Context, format: str = DOC_FORMAT, output_dir: str = OUTPUT_DIR) -> None: - """Generate the API docs with pdoc.""" - ctx.run( - f"uv run pdoc --docformat={format} --output-directory={output_dir} src/{ctx.project.package}" - ) - - -@task(pre=[cleans.docs, api], default=True) -def all(_: Context) -> None: - """Run all docs tasks.""" diff --git a/{{cookiecutter.repository}}/tasks/format.just b/{{cookiecutter.repository}}/tasks/format.just new file mode 100644 index 0000000..78bea80 --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/format.just @@ -0,0 +1,13 @@ +# run format tasks +[group('format')] +format: format-import format-source + +# format code import +[group('format')] +format-import: + {% raw %}uv run ruff check --select=I --fix {{SOURCES}} {{TESTS}}{% endraw %} + +# format code source +[group('format')] +format-source: + {% raw %}uv run ruff format {{SOURCES}} {{TESTS}}{% endraw %} diff --git a/{{cookiecutter.repository}}/tasks/formats.py b/{{cookiecutter.repository}}/tasks/formats.py deleted file mode 100644 index cd83b17..0000000 --- a/{{cookiecutter.repository}}/tasks/formats.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Format tasks of the project.""" - -# %% IMPORTS - -from invoke.context import Context -from invoke.tasks import task - -# %% TASKS - - -@task -def imports(ctx: Context) -> None: - """Format python imports with ruff.""" - ctx.run("uv run ruff check --select I --fix") - - -@task -def sources(ctx: Context) -> None: - """Format python sources with ruff.""" - ctx.run("uv run ruff format src/ tasks/ tests/") - - -@task(pre=[imports, sources], default=True) -def all(_: Context) -> None: - """Run all format tasks.""" diff --git a/{{cookiecutter.repository}}/tasks/install.just b/{{cookiecutter.repository}}/tasks/install.just new file mode 100644 index 0000000..e8a74df --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/install.just @@ -0,0 +1,24 @@ +# run install tasks +[group('install')] +install: install-project install-hooks + +# install git hooks +[group('install')] +install-hooks: + uv run pre-commit install --hook-type=pre-push + uv run pre-commit install --hook-type=commit-msg + +# install the project +[group('install')] +install-project: + uv sync --all-groups + +# install github rulesets +[group('install')] +install-rulesets: + #!/usr/bin/env bash + set -euo pipefail + repo=$(gh repo view --json=name --jq=.name) + owner=$(gh repo view --json=owner --jq=.owner.login) + gh api --method POST -H "Accept: application/vnd.github+json" \ + "/repos/$owner/$repo/rulesets" --input=".github/rulesets/main.json" diff --git a/{{cookiecutter.repository}}/tasks/installs.py b/{{cookiecutter.repository}}/tasks/installs.py deleted file mode 100644 index f6754a2..0000000 --- a/{{cookiecutter.repository}}/tasks/installs.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Install tasks of the project.""" - -# %% IMPORTS - -from invoke.context import Context -from invoke.tasks import task - -# %% TASKS - - -@task -def uv(ctx: Context) -> None: - """Install uv packages.""" - ctx.run("uv sync --all-groups") - - -@task -def pre_commit(ctx: Context) -> None: - """Install pre-commit hooks on git.""" - ctx.run("uv run pre-commit install --hook-type=pre-push") - ctx.run("uv run pre-commit install --hook-type=commit-msg") - - -@task(pre=[uv, pre_commit], default=True) -def all(_: Context) -> None: - """Run all install tasks.""" diff --git a/{{cookiecutter.repository}}/tasks/mlflow.just b/{{cookiecutter.repository}}/tasks/mlflow.just new file mode 100644 index 0000000..b7f584d --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/mlflow.just @@ -0,0 +1,13 @@ +# run mlflow tasks +[group('mlflow')] +mlflow: mlflow-doctor mlflow-serve + +# run mlflow doctor +[group('mlflow')] +mlflow-doctor: + uv run mlflow doctor + +# start mlflow server +[group('mlflow')] +mlflow-serve host="127.0.0.1" port="5000" uri="./mlruns": + {% raw %}uv run mlflow server --host={{host}} --port={{port}} --backend-store-uri={{uri}}{% endraw %} diff --git a/{{cookiecutter.repository}}/tasks/mlflow.py b/{{cookiecutter.repository}}/tasks/mlflow.py deleted file mode 100644 index 3ccf363..0000000 --- a/{{cookiecutter.repository}}/tasks/mlflow.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Mlflow tasks of the project.""" - -# %% IMPORTS - -from invoke.context import Context -from invoke.tasks import task - -# %% TASKS - - -@task -def doctor(ctx: Context) -> None: - """Run mlflow doctor.""" - ctx.run("uv run mlflow doctor") - - -@task -def serve( - ctx: Context, - host: str = "127.0.0.1", - port: str = "5000", - backend_store_uri: str = "./mlruns", -) -> None: - """Start an mlflow server.""" - ctx.run( - f"uv run mlflow server --host={host} --port={port} --backend-store-uri={backend_store_uri}" - ) - - -@task(pre=[doctor, serve], default=True) -def all(_: Context) -> None: - """Run all mlflow tasks.""" diff --git a/{{cookiecutter.repository}}/tasks/package.just b/{{cookiecutter.repository}}/tasks/package.just new file mode 100644 index 0000000..765ca2b --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/package.just @@ -0,0 +1,13 @@ +# run package tasks +[group('package')] +package: package-build + +# build package constraints +[group('package')] +package-constraints constraints="constraints.txt": + {% raw %}uv pip compile pyproject.toml --generate-hashes --output-file={{constraints}}{% endraw %} + +# build python package +[group('package')] +package-build constraints="constraints.txt": clean-build package-constraints + {% raw %}uv build --build-constraint={{constraints}} --require-hashes --wheel{% endraw %} diff --git a/{{cookiecutter.repository}}/tasks/packages.py b/{{cookiecutter.repository}}/tasks/packages.py deleted file mode 100644 index e476a60..0000000 --- a/{{cookiecutter.repository}}/tasks/packages.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Package tasks of the project.""" - -# %% IMPORTS - -from invoke.context import Context -from invoke.tasks import task - -from . import cleans - -# %% TASKS - - -@task(pre=[cleans.dist]) -def build(ctx: Context) -> None: - """Build the python package.""" - ctx.run("uv build --wheel") - - -@task(pre=[build], default=True) -def all(_: Context) -> None: - """Run all package tasks.""" diff --git a/{{cookiecutter.repository}}/tasks/project.just b/{{cookiecutter.repository}}/tasks/project.just new file mode 100644 index 0000000..318dc74 --- /dev/null +++ b/{{cookiecutter.repository}}/tasks/project.just @@ -0,0 +1,14 @@ +# run project tasks +[group('project')] +project: project-requirements (project-run "tuning") (project-run "training") (project-run "inference") + +# export requirements file +[group('project')] +project-requirements: + uv export --format=requirements-txt --no-dev --no-hashes \ + --no-editable --no-emit-project --output-file=requirements.txt + +# run project job with a config +[group('project')] +project-run job: + {% raw %}uv run {{REPOSITORY}} confs/{job}.yaml{% endraw %} diff --git a/{{cookiecutter.repository}}/tasks/projects.py b/{{cookiecutter.repository}}/tasks/projects.py deleted file mode 100644 index c175183..0000000 --- a/{{cookiecutter.repository}}/tasks/projects.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Project tasks of the project.""" - -# mypy: disable-error-code="arg-type" - -# %% IMPORTS - -import json - -from invoke.context import Context -from invoke.tasks import call, task - -# %% CONFIGS - -PYTHON_VERSION = ".python-version" -REQUIREMENTS = "requirements.txt" -ENVIRONMENT = "python_env.yaml" - -# %% TASKS - - -@task -def requirements(ctx: Context) -> None: - """Export the project requirements file.""" - ctx.run( - "uv export --format=requirements-txt --no-dev " - "--no-hashes --no-editable --no-emit-project " - f"--output-file={REQUIREMENTS}" - ) - - -@task(pre=[requirements]) -def environment(ctx: Context) -> None: - """Export the project environment file.""" - with open(PYTHON_VERSION, "r") as reader: - python = reader.read().strip() # version - configuration: dict[str, object] = {"python": python} - with open(REQUIREMENTS, "r") as reader: - dependencies: list[str] = [] - for line in reader.readlines(): - dependency = line.split(" ")[0].strip() - if "pywin32" in dependency or "#" in dependency: - continue - dependencies.append(dependency) - configuration["dependencies"] = dependencies - with open(ENVIRONMENT, "w") as writer: - # Safe as YAML is a superset of JSON - json.dump(configuration, writer, indent=4) - writer.write("\n") # add new line at the end - - -@task -def run(ctx: Context, job: str) -> None: - """Run the project for the given job file.""" - ctx.run(f"uv run {ctx.project.repository} confs/{job}.yaml") - - -@task( - pre=[ - environment, - call(run, job="tuning"), - call(run, job="training"), - call(run, job="inference"), - ], - default=True, -) -def all(_: Context) -> None: - """Run all project tasks.""" diff --git a/{{cookiecutter.repository}}/{{cookiecutter.repository}}.code-workspace b/{{cookiecutter.repository}}/{{cookiecutter.repository}}.code-workspace index e064b71..bf84e6f 100644 --- a/{{cookiecutter.repository}}/{{cookiecutter.repository}}.code-workspace +++ b/{{cookiecutter.repository}}/{{cookiecutter.repository}}.code-workspace @@ -21,7 +21,6 @@ "extensions": { "recommendations": [ "charliermarsh.ruff", - "dchanco.vsc-invoke", "ms-python.mypy-type-checker", "ms-python.python", "ms-python.vscode-pylance",