From a4c877c954323e1790df3c1421815ab3968988a5 Mon Sep 17 00:00:00 2001 From: Excidion <36995046+Excidion@users.noreply.github.com> Date: Sun, 1 Mar 2026 20:33:59 +0100 Subject: [PATCH 1/4] use just and typer to run scripts and load env vars --- constants.yml | 8 ++------ reproML/README.md.jinja | 2 +- reproML/docs/structure.md | 15 +++++++++------ reproML/just | 2 ++ reproML/justfile | 18 ++++++++++++++++++ reproML/pyproject.toml.jinja | 3 ++- reproML/src/model/predict.py | 4 ---- reproML/src/model/train.py | 4 ---- 8 files changed, 34 insertions(+), 22 deletions(-) create mode 100755 reproML/just create mode 100644 reproML/justfile diff --git a/constants.yml b/constants.yml index 0c4fdf3..4d879c0 100644 --- a/constants.yml +++ b/constants.yml @@ -21,13 +21,9 @@ md_project_setup: {%- endif %} Navigate to the project directory and install all dependencies: ``` - uv sync --all-groups --all-extras - ``` - This command will also create a virtual environment for the project. - To make sure that pre-commits are enabled run: - ``` - uv run pre-commit install --hook-type pre-push --hook-type post-checkout --hook-type pre-commit + ./just install ``` + This will also enable pre-commit hooks. {%- if use_dvc %} And make sure that you have all the necessary data available. ``` diff --git a/reproML/README.md.jinja b/reproML/README.md.jinja index c3b73c4..50fd3c9 100644 --- a/reproML/README.md.jinja +++ b/reproML/README.md.jinja @@ -11,5 +11,5 @@ For more details take look at the [documentation](#look-at-the-documentation) For more detailed information about the project check out the documentation. Use the following command to run the documentation webserver. ``` -uv run mkdocs serve +./just docs ``` diff --git a/reproML/docs/structure.md b/reproML/docs/structure.md index 88ad047..ec2929b 100644 --- a/reproML/docs/structure.md +++ b/reproML/docs/structure.md @@ -344,7 +344,7 @@ if __name__ == "__main__": Using this will automatically log the start and end of every function you decorate with it. Depening on the log level, you'll even be able to trace arguments and return values. ```bash -$ uv run src/model/train.py +$ ./just run src/model/train.py # 2038-01-19 03:14:08,000 INFO __main__.main START # 2038-01-19 03:14:08,001 DEBUG __main__.main INPUTS: # 2038-01-19 03:14:08,002 INFO src.model.io.save_model START @@ -370,24 +370,27 @@ All of these parameters should be handed to the code via environment variables. > A litmus test for whether an app has all config correctly factored out of the code is whether the codebase could be made open source at any moment, without compromising any credentials.[^6] -That is why this template comes with [`python-dotenv`](https://pypi.org/project/python-dotenv/) pre-installed. -Simply create a file named `.env` in the project root folder and enter your configration parameters: +To store your secrets create a file named `.env` in the project root folder and enter your configration parameters: ```ini DATABASE_URL=postgres://localhost:1337/dbname DATBASE_USER=myusername DATABASE_PASSWORD=topsneaky ``` +Scripts run via [`just`](https://github.com/casey/just) automatically load these entries as environment variables. +``` +./just run src/script.py +``` In your code you can access these screts like this: ```python +# src/script.py import os -from dotenv import load_dotenv - -load_dotenv() database_url = os.getenv("DATABASE_URL") ``` Thanks to the `.gitignore`, the `.env` file will not get committed into the git repository. +Alternatively you can also use [`python-dotenv`](https://pypi.org/project/python-dotenv/) to load secrets from `.env` files. + ## First steps ### Installing a package diff --git a/reproML/just b/reproML/just new file mode 100755 index 0000000..d7b6f14 --- /dev/null +++ b/reproML/just @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +uv run just $@ diff --git a/reproML/justfile b/reproML/justfile new file mode 100644 index 0000000..aceb8c6 --- /dev/null +++ b/reproML/justfile @@ -0,0 +1,18 @@ +set dotenv-load + +default: + uv run just --list + +run SCRIPT *ARGS: + uv run typer {{SCRIPT}} run {{ARGS}} + +install: + uv sync --all-groups --all-extras + uv run pre-commit install --hook-type pre-push --hook-type post-checkout --hook-type pre-commit + +check: + uv run pre-commit run --all-files + +docs: + -uv run interrogate + -uv run mkdocs serve diff --git a/reproML/pyproject.toml.jinja b/reproML/pyproject.toml.jinja index 7fd151c..f58522f 100644 --- a/reproML/pyproject.toml.jinja +++ b/reproML/pyproject.toml.jinja @@ -12,7 +12,8 @@ dependencies = [ {%- if use_dvc %} "dvc[{{ remote_type }}]>=3.55.2", {%- endif %} - "python-dotenv>=1.0.1", + "rust-just>=1.46.0", + "typer>=0.24.1", ] [dependency-groups] diff --git a/reproML/src/model/predict.py b/reproML/src/model/predict.py index 3dc03a3..728d90e 100644 --- a/reproML/src/model/predict.py +++ b/reproML/src/model/predict.py @@ -8,7 +8,3 @@ def main(): model = load_model("model") print(model.__class__.__name__) # TODO implement prediction - - -if __name__ == "__main__": - main() diff --git a/reproML/src/model/train.py b/reproML/src/model/train.py index e972539..56f30f9 100644 --- a/reproML/src/model/train.py +++ b/reproML/src/model/train.py @@ -7,7 +7,3 @@ def main(): """Builds a model and saves it to the file system.""" model = None # TODO implement training save_model(model, "model") - - -if __name__ == "__main__": - main() From 4e13447d8d03011feaca6d5576b2086d599b42ee Mon Sep 17 00:00:00 2001 From: Excidion <36995046+Excidion@users.noreply.github.com> Date: Thu, 5 Mar 2026 09:42:45 +0100 Subject: [PATCH 2/4] adapt logging example output --- reproML/docs/structure.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/reproML/docs/structure.md b/reproML/docs/structure.md index ec2929b..b1b64b1 100644 --- a/reproML/docs/structure.md +++ b/reproML/docs/structure.md @@ -336,17 +336,13 @@ def main(): model = 42 save_model(model, "model") - -if __name__ == "__main__": - main() - ``` Using this will automatically log the start and end of every function you decorate with it. Depening on the log level, you'll even be able to trace arguments and return values. ```bash $ ./just run src/model/train.py -# 2038-01-19 03:14:08,000 INFO __main__.main START -# 2038-01-19 03:14:08,001 DEBUG __main__.main INPUTS: +# 2038-01-19 03:14:08,000 INFO train.py.main START +# 2038-01-19 03:14:08,001 DEBUG train.py.main INPUTS: # 2038-01-19 03:14:08,002 INFO src.model.io.save_model START # 2038-01-19 03:14:08,003 DEBUG src.model.io.save_model INPUTS: model=42, model_name='model' # 2038-01-19 03:14:08,004 INFO src.model.io.get_path START @@ -355,8 +351,8 @@ $ ./just run src/model/train.py # 2038-01-19 03:14:08,007 DEBUG src.model.io.get_path OUTPUT: 'models/model.cldpkl' # 2038-01-19 03:14:08,008 INFO src.model.io.save_model END # 2038-01-19 03:14:08,009 DEBUG src.model.io.save_model OUTPUT: None -# 2038-01-19 03:14:08,010 INFO __main__.main END -# 2038-01-19 03:14:08,011 DEBUG __main__.main OUTPUT: None +# 2038-01-19 03:14:08,010 INFO train.py.main END +# 2038-01-19 03:14:08,011 DEBUG train.py.main OUTPUT: None ``` Forcing yourself to only log via decorators can have some positive side effects: If you feel like you would like to add some logging within a function, this can be an indicator that the code block in question is a candidate to be refactored into a separate function. From 463dddbaff4fe5ea43cc59c06ae2e9449eed1fa4 Mon Sep 17 00:00:00 2001 From: Excidion <36995046+Excidion@users.noreply.github.com> Date: Thu, 5 Mar 2026 10:07:19 +0100 Subject: [PATCH 3/4] use justfile to also automate tasks in meta project --- .gitignore | 4 ++-- ctt.toml | 10 ++++++++++ justfile | 16 ++++++++++++++++ pyproject.toml | 1 + uv.lock | 24 ++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 ctt.toml create mode 100644 justfile diff --git a/.gitignore b/.gitignore index 8d3dbf2..7e50dad 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,6 @@ __pycache__/ # uv .venv -# ctt +# testing ctt and mkdocs build .ctt -ctt.toml +test diff --git a/ctt.toml b/ctt.toml new file mode 100644 index 0000000..952cb74 --- /dev/null +++ b/ctt.toml @@ -0,0 +1,10 @@ +[defaults] +project_name = "A new Project" +project_description = "Not much." +author = "Excidion" +author_email = "36995046+Excidion@users.noreply.github.com" +ci = "" + +[output.".ctt/test"] +python_version = "3.10" +use_dvc = false diff --git a/justfile b/justfile new file mode 100644 index 0000000..fe96a59 --- /dev/null +++ b/justfile @@ -0,0 +1,16 @@ +install: + uv sync --all-groups --all-extras + uv run pre-commit install --hook-type pre-push --hook-type post-checkout --hook-type pre-commit + +hooks: + uv run pre-commit run --all-files + +docs: + uv run mkdocs build --strict --site-dir test + rm -rf test + +test: + uv run ctt + rm -rf .ctt + +check: install hooks docs test diff --git a/pyproject.toml b/pyproject.toml index c6766d2..e844c46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "mkdocs-literate-nav>=0.6.1", "mkdocs-material>=9.5.35", "mkdocstrings[python]>=0.26.1", + "rust-just>=1.46.0", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index 13a8b4b..9e74c13 100644 --- a/uv.lock +++ b/uv.lock @@ -1575,6 +1575,7 @@ dependencies = [ { name = "mkdocs-literate-nav" }, { name = "mkdocs-material" }, { name = "mkdocstrings", extra = ["python"] }, + { name = "rust-just" }, ] [package.dev-dependencies] @@ -1597,6 +1598,7 @@ requires-dist = [ { name = "mkdocs-literate-nav", specifier = ">=0.6.1" }, { name = "mkdocs-material", specifier = ">=9.5.35" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=0.26.1" }, + { name = "rust-just", specifier = ">=1.46.0" }, ] [package.metadata.requires-dev] @@ -1747,6 +1749,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/76/fbb4bd23dfb48fa7758d35b744413b650a9fd2ddd93bca77e30376864414/ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737", size = 8959621, upload-time = "2024-11-29T03:29:43.977Z" }, ] +[[package]] +name = "rust-just" +version = "1.46.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/50/3828817f76e19977a4048c2c8b39a7f48babc21dd9dbed4af2f3c18d4570/rust_just-1.46.0.tar.gz", hash = "sha256:84437481c814577529835132e2cc5fcc35a981c1712e4877cb20fc2f5ec5b2d6", size = 1447346, upload-time = "2026-01-03T02:03:17.948Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/0b/a5bf2707b02a484d91f8275efa39f76fe19304f5bfba82293fa4b18608d2/rust_just-1.46.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7d6d4c67a443f1acb1f78f9ba4b3349fa04f17e8be2d4448b771cdc93a382812", size = 1739556, upload-time = "2026-01-03T02:02:42.835Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ae/40bcd996ccb2fcb0152b5bfde7beaf3840877a8837611421c495b45c82da/rust_just-1.46.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0caf9b77d30455558d017c9e625ce94c373f88d81656477127727604fa5d36ab", size = 1620974, upload-time = "2026-01-03T02:02:45.341Z" }, + { url = "https://files.pythonhosted.org/packages/62/36/7067e0eaf674ed7c98b35ed50d713c0c885f2d2b57847a627e11502da1b8/rust_just-1.46.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b63521acd91c65164c202ded3ae730130c7fb4377f59cd2f9847b45161c94fd", size = 1703423, upload-time = "2026-01-03T02:02:47.681Z" }, + { url = "https://files.pythonhosted.org/packages/dd/47/3e98182f5e03c48880d647651385863552a3e24cfec5c51d116c06e6f180/rust_just-1.46.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a35110c7acf27bdded79cf3bbfea9eb80a53f6f81f374248fe3340584c92e5", size = 1666645, upload-time = "2026-01-03T02:02:50.38Z" }, + { url = "https://files.pythonhosted.org/packages/95/5e/b9badf6e6982e5744f076d12ab911e5ac8b4b03a0674bab4f498ed9d0b4c/rust_just-1.46.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a2407cefc2e5ed4527297747bd5bcb61a885776021cb2438c3a7b118b2cabc2", size = 1847430, upload-time = "2026-01-03T02:02:52.716Z" }, + { url = "https://files.pythonhosted.org/packages/98/10/6916d7c862b99de600a1fd3739d13353c220dfbc0229a0b2c5012c2f801d/rust_just-1.46.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0dab319619f600561b993242312a344953b1ea44637b30257af905a70ce6f568", size = 1926224, upload-time = "2026-01-03T02:02:55.194Z" }, + { url = "https://files.pythonhosted.org/packages/24/93/18bc615e68a80f43105d5e7cc3571e85776aec829ac40faae4de5d5dc2f3/rust_just-1.46.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3b4c26dd86e5d96047fc0935967f22cb9f49c687767d78b7d3fe511eba39ffa", size = 1902165, upload-time = "2026-01-03T02:02:57.636Z" }, + { url = "https://files.pythonhosted.org/packages/2c/1e/e3c19a24ff64e78a04df0bdf4c61e15c28dcac8b7b5c3a5505eb5749d40a/rust_just-1.46.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdb496ca26efc508be0e625309b74b1f6316b4f7295d13247c3b791dfa77eb1", size = 1835209, upload-time = "2026-01-03T02:03:00.256Z" }, + { url = "https://files.pythonhosted.org/packages/26/75/0850c38e41025794826165329a097f657152902a785c0579f213b7d61ae6/rust_just-1.46.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3cf1c32a258f8ee44af877ec271e2eea257923a3303a6d2610b0b5f1523daaab", size = 1719519, upload-time = "2026-01-03T02:03:02.774Z" }, + { url = "https://files.pythonhosted.org/packages/48/85/53c6ee2b9cdbbe1bd43cd0f8096036c29e9e6ba2d3d6344206c490e2ce18/rust_just-1.46.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:657ab85882c124b0fbcd75763035d0dbd20b06c582cc6d4f55017d7b517d5a89", size = 1685664, upload-time = "2026-01-03T02:03:05.514Z" }, + { url = "https://files.pythonhosted.org/packages/33/04/1ad3a66bef0d0f554f0f9971b048bbaf7b3955458f3fda47b48fbf8ff009/rust_just-1.46.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:644b71bfe68863b71ee2618a88dbfd446ea70e2dcfa7b0e5eaec7b6dc4faceca", size = 1838231, upload-time = "2026-01-03T02:03:07.618Z" }, + { url = "https://files.pythonhosted.org/packages/ab/75/33c2e887a68e57b356cda74d325d6ebe406bb72ad8c4e2d067d4fa9b697b/rust_just-1.46.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d8a879fb86eb1c7f7f83953999ae4ce53ea4e5c0ca531cf6ff09e1e9335ff7", size = 1900319, upload-time = "2026-01-03T02:03:09.873Z" }, + { url = "https://files.pythonhosted.org/packages/44/30/6b1677aa64a4f69f3ec174b5e2a9a49e0ffd06946d4b4dc8295366fbd9dd/rust_just-1.46.0-py3-none-win32.whl", hash = "sha256:100701de91bded3f6f2bf564d09c2f8e483b8dfb490d1c74008ce3c01ff0ff67", size = 1623463, upload-time = "2026-01-03T02:03:12.343Z" }, + { url = "https://files.pythonhosted.org/packages/64/61/97ad7a1ea67b9485404b18150c258015842cf116a1ce626421863fd8f0e1/rust_just-1.46.0-py3-none-win_amd64.whl", hash = "sha256:ccaf8e473f64f5c815b0039e883a1feaf5634b9cdffd1dbff9e5fde77b5926f4", size = 1801103, upload-time = "2026-01-03T02:03:15.256Z" }, +] + [[package]] name = "six" version = "1.17.0" From c0320960ae17feb77f6a217fc018f08a45bb6bcb Mon Sep 17 00:00:00 2001 From: Excidion <36995046+Excidion@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:06:41 +0100 Subject: [PATCH 4/4] rename bash script and add comments to justfile --- constants.yml | 4 ++-- reproML/README.md.jinja | 2 +- reproML/docs/structure.md | 6 +++--- reproML/{just => just.sh} | 0 reproML/justfile | 5 +++++ 5 files changed, 11 insertions(+), 6 deletions(-) rename reproML/{just => just.sh} (100%) diff --git a/constants.yml b/constants.yml index 4d879c0..f1ab799 100644 --- a/constants.yml +++ b/constants.yml @@ -20,8 +20,8 @@ md_project_setup: ``` {%- endif %} Navigate to the project directory and install all dependencies: - ``` - ./just install + ```bash + ./just.sh install ``` This will also enable pre-commit hooks. {%- if use_dvc %} diff --git a/reproML/README.md.jinja b/reproML/README.md.jinja index 50fd3c9..48ea13e 100644 --- a/reproML/README.md.jinja +++ b/reproML/README.md.jinja @@ -11,5 +11,5 @@ For more details take look at the [documentation](#look-at-the-documentation) For more detailed information about the project check out the documentation. Use the following command to run the documentation webserver. ``` -./just docs +just docs ``` diff --git a/reproML/docs/structure.md b/reproML/docs/structure.md index b1b64b1..c692784 100644 --- a/reproML/docs/structure.md +++ b/reproML/docs/structure.md @@ -340,7 +340,7 @@ def main(): Using this will automatically log the start and end of every function you decorate with it. Depening on the log level, you'll even be able to trace arguments and return values. ```bash -$ ./just run src/model/train.py +just run src/model/train.py # 2038-01-19 03:14:08,000 INFO train.py.main START # 2038-01-19 03:14:08,001 DEBUG train.py.main INPUTS: # 2038-01-19 03:14:08,002 INFO src.model.io.save_model START @@ -373,8 +373,8 @@ DATBASE_USER=myusername DATABASE_PASSWORD=topsneaky ``` Scripts run via [`just`](https://github.com/casey/just) automatically load these entries as environment variables. -``` -./just run src/script.py +```bash +just run src/script.py ``` In your code you can access these screts like this: ```python diff --git a/reproML/just b/reproML/just.sh similarity index 100% rename from reproML/just rename to reproML/just.sh diff --git a/reproML/justfile b/reproML/justfile index aceb8c6..35aef42 100644 --- a/reproML/justfile +++ b/reproML/justfile @@ -1,18 +1,23 @@ set dotenv-load +# shows this list default: uv run just --list +# runs a python script's main method run SCRIPT *ARGS: uv run typer {{SCRIPT}} run {{ARGS}} +# installs all dependecies and pre-commit hooks install: uv sync --all-groups --all-extras uv run pre-commit install --hook-type pre-push --hook-type post-checkout --hook-type pre-commit +# runs quality checks on all files check: uv run pre-commit run --all-files +# starts the documentation server docs: -uv run interrogate -uv run mkdocs serve