From 610db00166cb74c02b01e26a98c13cb11dff1646 Mon Sep 17 00:00:00 2001 From: "Philipp.Stahlberg" <131917964+PhilippStahlbergGit@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:05:32 +0200 Subject: [PATCH 1/7] fix: reformatting --- .github/workflows/ci-cd.yaml | 12 +++++ pyproject.toml | 3 ++ src/configlock/helper.py | 8 +-- uv.lock | 96 ++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 0b62c5c..03c4871 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -35,3 +35,15 @@ jobs: run: configlock init project_info.yaml - name: check locked file run: configlock lock project_info.yaml + - name: linting + run: uvx ruff check . + - name: formatting + run: uvx ruff format --check . + - name: type_consitency + run: uv run pyright . + - name: tests + run: uv run pytest -v --durations=0 --cov --cov-report=xml + - name: complexity check + run: uvx radon cc -s -a . + - name: build + run: uv build diff --git a/pyproject.toml b/pyproject.toml index 3633d01..51e63d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,8 +24,11 @@ build-backend = "uv_build" [dependency-groups] dev = [ + "pyright>=1.1.410", "pytest>=9.0.3", + "radon>=6.0.1", "reloadserver>=1.0.0", + "ruff>=0.15.15", ] [tool.pytest.ini_options] diff --git a/src/configlock/helper.py b/src/configlock/helper.py index 2b7155d..29f4b35 100644 --- a/src/configlock/helper.py +++ b/src/configlock/helper.py @@ -24,7 +24,7 @@ def check_file_identicality(file_path:str, config_file_path: str | None = CONFIG filecmp.clear_cache() res = filecmp.cmp(file_path, config_file_path, shallow=False) return res - except Exception as exc: + except Exception: filecmp.clear_cache() res = filecmp.cmp(file_path, config_file_path, shallow=False) return res @@ -47,7 +47,7 @@ def read_yaml(file_path: str) -> dict: except FileNotFoundError: raise else: - typer.echo(f"Sucessfully read file") + typer.echo("Sucessfully read file") return data @@ -58,7 +58,7 @@ def read_json(file_path: str) -> dict: except FileNotFoundError: raise else: - typer.echo(f"Sucessfully read file") + typer.echo("Sucessfully read file") return data def write_json(data: dict, file_path: str | None = CONFIG_LOG_FILE_PATH) -> None: @@ -71,7 +71,7 @@ def write_json(data: dict, file_path: str | None = CONFIG_LOG_FILE_PATH) -> None except Exception: raise else: - typer.echo(f"Sucessfully wrote file") + typer.echo("Sucessfully wrote file") def check_file_and_read_file(file_path: str) -> dict: diff --git a/uv.lock b/uv.lock index 64e5c73..c373386 100644 --- a/uv.lock +++ b/uv.lock @@ -111,8 +111,11 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "pyright" }, { name = "pytest" }, + { name = "radon" }, { name = "reloadserver" }, + { name = "ruff" }, ] [package.metadata] @@ -125,8 +128,11 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "pyright", specifier = ">=1.1.410" }, { name = "pytest", specifier = ">=9.0.3" }, + { name = "radon", specifier = ">=6.0.1" }, { name = "reloadserver", specifier = ">=1.0.0" }, + { name = "ruff", specifier = ">=0.15.15" }, ] [[package]] @@ -158,6 +164,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "mando" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/35/24/cd70d5ae6d35962be752feccb7dca80b5e0c2d450e995b16abd6275f3296/mando-0.7.1.tar.gz", hash = "sha256:18baa999b4b613faefb00eac4efadcf14f510b59b924b66e08289aa1de8c3500", size = 37868, upload-time = "2022-02-24T08:12:27.316Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl", hash = "sha256:26ef1d70928b6057ee3ca12583d73c63e05c49de8972d620c278a7b206581a8a", size = 28149, upload-time = "2022-02-24T08:12:25.24Z" }, +] + [[package]] name = "markdown-it-py" version = "4.2.0" @@ -179,6 +197,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + [[package]] name = "packaging" version = "26.2" @@ -206,6 +233,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pyright" +version = "1.1.410" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/53/e4d8ea1391bd4355231be6f91bf239479aa0014260ed3fb5526eeb12a1f2/pyright-1.1.410.tar.gz", hash = "sha256:07a073b8ba6749826773c1269773efa11b93440d9a6aa60419d9a3172d6dc488", size = 4062013, upload-time = "2026-06-01T17:35:48.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/33/288b5868fa00846dacf249633719d747893e54aebd196b9968ac1878a5d3/pyright-1.1.410-py3-none-any.whl", hash = "sha256:5e961bed37cacf96b3f7cd7b1da39b350a9239aa2e69138d0e88f728cfaf296c", size = 6082448, upload-time = "2026-06-01T17:35:46.387Z" }, +] + [[package]] name = "pytest" version = "9.0.3" @@ -267,6 +307,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "radon" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "mando" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/6d/98e61600febf6bd929cf04154537c39dc577ce414bafbfc24a286c4fa76d/radon-6.0.1.tar.gz", hash = "sha256:d1ac0053943a893878940fedc8b19ace70386fc9c9bf0a09229a44125ebf45b5", size = 1874992, upload-time = "2023-03-26T06:24:38.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl", hash = "sha256:632cc032364a6f8bb1010a2f6a12d0f14bc7e5ede76585ef29dc0cecf4cd8859", size = 52784, upload-time = "2023-03-26T06:24:33.949Z" }, +] + [[package]] name = "reloadserver" version = "1.0.0" @@ -307,6 +360,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, ] +[[package]] +name = "ruff" +version = "0.15.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/6f/a76f7d96e5c962f5b69cee865e49c15c1116897c01990faa8a57edb62e7f/ruff-0.15.15.tar.gz", hash = "sha256:b8dff018130b46d8e5bf0f926ef6b60cf871d6d5ae45fc9334e09632daa741d6", size = 4706985, upload-time = "2026-05-28T14:16:57.784Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/9d/3a45c05b8ab04b4705989de70a79008e27c8003296a0feaee9edc18dd7e9/ruff-0.15.15-py3-none-linux_armv6l.whl", hash = "sha256:cf93e5388f412e1b108b1f8b34a6e036b70fe8aff89393befad96fe48670311b", size = 10710652, upload-time = "2026-05-28T14:16:06.701Z" }, + { url = "https://files.pythonhosted.org/packages/05/66/da974431624bf3b49f6ee1f9543c02d929ff1cba78b0d5a79c38cf21f744/ruff-0.15.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac5a646d1f6a7dadd5d50842dae2c1f9862ac887ef5d1b1375e02def791fde6e", size = 11096615, upload-time = "2026-05-28T14:16:23.313Z" }, + { url = "https://files.pythonhosted.org/packages/8c/09/7443452e5d290230a712103f2fdceeef7184f3ec99a2bd01c8be78aaceb5/ruff-0.15.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:77d955a431430c66f72dd94e379ad38a16daea3d25094872ac4edf9e797be530", size = 10436683, upload-time = "2026-05-28T14:16:40.974Z" }, + { url = "https://files.pythonhosted.org/packages/53/01/d330c26a57fa4f3943a14424904027428315b700fe4d14a84bb123a649e5/ruff-0.15.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7614ee79c69788cf6cedd568069ade9cecc22a1ad20494efe8d0c9ebb4b622d4", size = 10769064, upload-time = "2026-05-28T14:16:28.905Z" }, + { url = "https://files.pythonhosted.org/packages/1d/85/cc8770f8bdff541b1da8392d1634141fe4a0e3f4ee596605959b7906c27f/ruff-0.15.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cdb1679e06a1f6b47bc384714ae96f6e2fb65ca441eb78c43d2ca554176ce1f", size = 10511987, upload-time = "2026-05-28T14:16:43.732Z" }, + { url = "https://files.pythonhosted.org/packages/7c/29/8c190c1472b63013583ba391f3342036e02010544c1270455ed8e519bdf3/ruff-0.15.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2728b93d7b23a603ea2c0ac6eb73d760bd38ec9de35f35fb41e18f7a3fee7622", size = 11275100, upload-time = "2026-05-28T14:16:55.244Z" }, + { url = "https://files.pythonhosted.org/packages/9f/6b/7e145ce2cc8e63d6834eca03d83a0e18d121def5c69f91b4cf4011ed4879/ruff-0.15.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be582fcc0db438902c7792b08d6ddf6c9b9e21addaa10092c2c741cfb09e5a45", size = 12176903, upload-time = "2026-05-28T14:16:14.368Z" }, + { url = "https://files.pythonhosted.org/packages/80/a3/d5974637f68e451f7fadf015cf3101d1cd7d8ba5027cffe0b9e3826ebe6b/ruff-0.15.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7aa77465b8ecaf1a27bea098d696f7fed5e1eccbd10b321b682d6de586ae5627", size = 11404550, upload-time = "2026-05-28T14:16:20.138Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1c/e6e5e568f22be4fb05d6244234aba384c06b451252453b821e1a529263cf/ruff-0.15.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48decfa11d740de4889de623be1463308346312f2409a56e24aa280c86162dc4", size = 11382027, upload-time = "2026-05-28T14:16:46.615Z" }, + { url = "https://files.pythonhosted.org/packages/1d/01/170921b49fcd2e8858825593f91cf7146c3e40a5c3e6df763e4bb0484dde/ruff-0.15.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a5015088452ca0081387063649ec67f06d3d1d6b8b936a1f836b5e9657ecd48c", size = 11366041, upload-time = "2026-05-28T14:16:26.247Z" }, + { url = "https://files.pythonhosted.org/packages/87/54/a7bad711d7de93254e15e06a4c375b89a03d18de45d3e5dcc86a4472fb1a/ruff-0.15.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5294aab6356c81600fcdea3a62bb1b924dfd5e91767c12318d3f68f86af57cd", size = 10741795, upload-time = "2026-05-28T14:16:17.11Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/38c075963668f8b41c6914ee0f6f318727fbe30ab9145cb29e6df464c5fa/ruff-0.15.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db5bd4d802415cca656dc1616070b725952d6ae95eb5d4831e49fbd94a38f75f", size = 10511117, upload-time = "2026-05-28T14:16:31.767Z" }, + { url = "https://files.pythonhosted.org/packages/9d/96/6ff689e1f7e375d1d97075eca022f74c2bab59554a432fe4d2e6f091986a/ruff-0.15.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:587a6278ed42059191c1a466e490bd7930fb50bd2e255398bc29616c895a61cb", size = 10994867, upload-time = "2026-05-28T14:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c2/5dce0ab9f92a8d534fa62b9bf9caca3eddb8c1a81b616f5e195ada4f0d6e/ruff-0.15.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:df0c1c084f5f4be9812f61518a45c440d3c30d69ce4bf6c5270e66d38338f02a", size = 11482101, upload-time = "2026-05-28T14:16:49.598Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c0/1003b60edd697c649faf61f1a34094b1abb38fb3d1181e3f895781250a08/ruff-0.15.15-py3-none-win32.whl", hash = "sha256:29428ea79694afbe756d45fd59b36f22b6b020dc0443cf7de0173046236964b9", size = 10716774, upload-time = "2026-05-28T14:16:52.337Z" }, + { url = "https://files.pythonhosted.org/packages/02/a8/1269eddd6945a06c23f055ef7848886e37cf9d6a8bebb386a3115f01470c/ruff-0.15.15-py3-none-win_amd64.whl", hash = "sha256:8df0323902e15e24bc4bf246da830573d3cf3352bd0b9a164eab335d111ff4a4", size = 11868463, upload-time = "2026-05-28T14:16:11.333Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b2/920464c907b191e37469d477a1aa8bc048b8f36c4c1610dfa4ab87b39e18/ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7", size = 11138498, upload-time = "2026-05-28T14:16:38.425Z" }, +] + [[package]] name = "shellingham" version = "1.5.4" @@ -316,6 +394,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "typer" version = "0.25.1" @@ -331,6 +418,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, ] +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + [[package]] name = "urllib3" version = "2.7.0" From 850a638f307f608001ffe94a4cf6bf43ef330cd8 Mon Sep 17 00:00:00 2001 From: "Philipp.Stahlberg" <131917964+PhilippStahlbergGit@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:02:17 +0200 Subject: [PATCH 2/7] chore: adjust ci pipeline --- .github/workflows/ci-cd.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 03c4871..f98b8ae 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -5,8 +5,6 @@ on: [push, pull_request] permissions: contents: read - - jobs: test: runs-on: ubuntu-latest @@ -35,15 +33,21 @@ jobs: run: configlock init project_info.yaml - name: check locked file run: configlock lock project_info.yaml + - name: linting run: uvx ruff check . - name: formatting run: uvx ruff format --check . - name: type_consitency run: uv run pyright . - - name: tests - run: uv run pytest -v --durations=0 --cov --cov-report=xml - name: complexity check run: uvx radon cc -s -a . + - name: tests + run: uv run pytest -v --durations=0 --cov --cov-report=xml + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + - name: build run: uv build From d1257e2c2e2601b71c05b4a6c40dd4e666fba722 Mon Sep 17 00:00:00 2001 From: "Philipp.Stahlberg" <131917964+PhilippStahlbergGit@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:35:17 +0200 Subject: [PATCH 3/7] chore: adjust namings and add docs --- docs/index.html | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/index.html b/docs/index.html index 12da1d1..7cdcea7 100644 --- a/docs/index.html +++ b/docs/index.html @@ -7,17 +7,18 @@

ConfigLock Web Portal

- - - - + + + +

Result:

No data yet...
- + + \ No newline at end of file From c360320032728a9d7b4e90c70bb6936c46568409 Mon Sep 17 00:00:00 2001 From: "Philipp.Stahlberg" <131917964+PhilippStahlbergGit@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:41:13 +0200 Subject: [PATCH 4/7] chore: adjust fetching form inputs to not be in async func --- docs/script.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/script.js b/docs/script.js index 4832efb..e77697a 100644 --- a/docs/script.js +++ b/docs/script.js @@ -1,14 +1,24 @@ import YAML from 'https://esm.sh/yaml'; +function formInputs(){ + const user = document.getElementById('userName').value; + const repo = document.getElementById('repoName').value; + const branch = document.getElementById('branchName').value; + const path = document.getElementById('pathToFile').value; -async function fetchFile() { - const user = document.getElementById('user').value; - const repo = document.getElementById('repo').value; - const branch = document.getElementById('branch').value; - const path = document.getElementById('path').value; const output = document.getElementById('output'); const urlBox = document.getElementById('url'); + return [user, repo, branch, path, output, urlBox]; + +} + + +async function fetchFile() { + + const [user, repo, branch, path, output, urlBox] = formInputs() + + output.innerText = 'Fetching...'; const lockFile = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${path}`; const validatorFile = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/src/configlock/validator.py`; From 048dfdf9166acc5b99b5991b71275c295898185a Mon Sep 17 00:00:00 2001 From: "Philipp.Stahlberg" <131917964+PhilippStahlbergGit@users.noreply.github.com> Date: Mon, 8 Jun 2026 20:11:26 +0200 Subject: [PATCH 5/7] chore: adjust to more modular code --- docs/script.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/script.js b/docs/script.js index e77697a..a639bd0 100644 --- a/docs/script.js +++ b/docs/script.js @@ -13,16 +13,24 @@ function formInputs(){ } +function retriveGithubCodeblocks(user, repo, branch, path){ + + const lockFilePath = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${path}`; + const validatorFilePath = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/src/configlock/validator.py`; + + return [lockFilePath, validatorFilePath]; + +} + async function fetchFile() { - const [user, repo, branch, path, output, urlBox] = formInputs() + const [user, repo, branch, path, output, urlBox] = formInputs(); + const [lockFilePath, validatorFilePath] = retriveGithubCodeblocks(user,repo,branch,path); output.innerText = 'Fetching...'; - const lockFile = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${path}`; - const validatorFile = `https://raw.githubusercontent.com/${user}/${repo}/${branch}/src/configlock/validator.py`; - urlBox.innerText = lockFile; + urlBox.innerText = lockFilePath; let timeoutId; @@ -35,9 +43,9 @@ async function fetchFile() { let pyodide = await loadPyodide({ indexURL: "https://cdn.jsdelivr.net/pyodide/v0.26.1/full/"}); - const lockFileContents = await fetch(lockFile); + const lockFileContents = await fetch(lockFilePath); - const validatorCodeContents = await fetch(validatorFile); + const validatorCodeContents = await fetch(validatorFilePath); const pythonCode = await validatorCodeContents.text(); pyodide.runPython(pythonCode); From a71a80b86da43ca99d66ed22456bfda9bdc34e14 Mon Sep 17 00:00:00 2001 From: "Philipp.Stahlberg" <131917964+PhilippStahlbergGit@users.noreply.github.com> Date: Mon, 8 Jun 2026 21:18:18 +0200 Subject: [PATCH 6/7] chore: adding test cases for validator --- src/configlock/validator.py | 7 +++ tests/unit_test/test_validator_class.py | 74 +++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tests/unit_test/test_validator_class.py diff --git a/src/configlock/validator.py b/src/configlock/validator.py index efd112b..b4b339f 100644 --- a/src/configlock/validator.py +++ b/src/configlock/validator.py @@ -4,6 +4,13 @@ keys_to_ignore = {"version"} +''' +Note: +This is a strict class, meaning that webassembly will use this class. +Beware of the contents, and try to be efficient, keep the class minimal. +''' + + @dataclass class ValidationContext: new_path: str diff --git a/tests/unit_test/test_validator_class.py b/tests/unit_test/test_validator_class.py new file mode 100644 index 0000000..bb0bb3c --- /dev/null +++ b/tests/unit_test/test_validator_class.py @@ -0,0 +1,74 @@ + +import inspect + +from configlock import validator + + +def helper_sig(name_class) -> inspect.Signature: + + sig = inspect.getfullargspec(name_class).annotations + + return sig + + +def test_sig_context(): + + val_context_sig = helper_sig(name_class=validator.ValidationContext) + + sig_list = ["new_path", "current_path", "order_matters"] + + assert ("return", None) in val_context_sig.items() + assert(all(key in val_context_sig for key in sig_list)) + assert ("new_path", str) in val_context_sig.items() + assert ("current_path", str) in val_context_sig.items() + + +def test_sig_walk_n_order(): + + + walk_n_order_sig = helper_sig(name_class=validator.walk_yaml_with_no_order) + + + sig_list = ["current_data", "new_data", "context", "depth"] + + assert(all(key in walk_n_order_sig for key in sig_list)) + assert ("context", validator.ValidationContext) in walk_n_order_sig.items() + + + +def test_sig_walk_w_order(): + + walk_w_order = helper_sig(name_class=validator.walk_yaml_in_order) + + + sig_list = ["current_data", "new_data", "context", "depth"] + + assert(all(key in walk_w_order for key in sig_list)) + assert ("context", validator.ValidationContext) in walk_w_order.items() + + + + + + accept_n_key = helper_sig(name_class=validator.accept_new_keys) + + + accept_n_val = helper_sig(name_class=validator.accept_new_value) + + + + + + + + + + +if __name__ == "__main__": + #print(inspect.getfullargspec(validator.ValidationContext)) + sig = inspect.getfullargspec(validator.ValidationContext).annotations + print(sig) + print("new_path" in sig) + print( ("return", None) in sig.items()) + print(helper_sig(name_class=validator.walk_yaml_with_no_order)) + print(inspect.signature(validator.walk_yaml_in_order(0,0,0))) From 605b073f97d2f67597a7a15e19122408d7d49c65 Mon Sep 17 00:00:00 2001 From: "Philipp.Stahlberg" <131917964+PhilippStahlbergGit@users.noreply.github.com> Date: Tue, 9 Jun 2026 20:49:15 +0200 Subject: [PATCH 7/7] fix: formatting and linting --- .pre-commit-config.yaml | 12 ++++ pyproject.toml | 1 + src/configlock/__init__.py | 4 -- src/configlock/docs_server.py | 28 ++++---- src/configlock/extraction.py | 14 ++-- src/configlock/helper.py | 28 ++++---- src/configlock/main.py | 39 +++++++--- src/configlock/validator.py | 95 ++++++++++++++----------- tests/integration_test/test_init.py | 3 +- tests/unit_test/test_init_command.py | 30 ++++---- tests/unit_test/test_lock_command.py | 66 ++++++++++------- tests/unit_test/test_sync_command.py | 4 +- tests/unit_test/test_validator_class.py | 66 +++++++---------- uv.lock | 91 +++++++++++++++++++++++ 14 files changed, 306 insertions(+), 175 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..9412ab7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,12 @@ +repos: +- repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.11.19 + hooks: + - id: uv-lock +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.16 + hooks: + - id: ruff-check + args: [ --fix ] + + - id: ruff-format diff --git a/pyproject.toml b/pyproject.toml index 51e63d4..efc744b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ build-backend = "uv_build" [dependency-groups] dev = [ + "pre-commit>=4.6.0", "pyright>=1.1.410", "pytest>=9.0.3", "radon>=6.0.1", diff --git a/src/configlock/__init__.py b/src/configlock/__init__.py index ba6edc9..699f998 100644 --- a/src/configlock/__init__.py +++ b/src/configlock/__init__.py @@ -6,7 +6,3 @@ load_dotenv(env_file) else: load_dotenv(Path(".env.example")) - - - - diff --git a/src/configlock/docs_server.py b/src/configlock/docs_server.py index a40ec8b..aadc73f 100644 --- a/src/configlock/docs_server.py +++ b/src/configlock/docs_server.py @@ -60,12 +60,12 @@ def __init__(self, *args, docs_root: Path, state: ReloadState, **kwargs): super().__init__(*args, directory=str(docs_root), **kwargs) def end_headers(self) -> None: - if self.path.endswith('.html') or self.path in {'/', ''}: - self.send_header('Cache-Control', 'no-store') + if self.path.endswith(".html") or self.path in {"/", ""}: + self.send_header("Cache-Control", "no-store") super().end_headers() def do_GET(self) -> None: - if self.path == '/__reload__': + if self.path == "/__reload__": generation = self.state.generation self.state.wait_for_next(generation) self.send_response(204) @@ -75,20 +75,20 @@ def do_GET(self) -> None: def copyfile(self, source, outputfile) -> None: content = source.read() - if b'' in content: - content = content.replace(b'', RELOAD_SCRIPT + b'') - elif self.path.endswith('.html') or self.path in {'/', ''}: + if b"" in content: + content = content.replace(b"", RELOAD_SCRIPT + b"") + elif self.path.endswith(".html") or self.path in {"/", ""}: content += RELOAD_SCRIPT outputfile.write(content) def main() -> None: - parser = argparse.ArgumentParser(description='Serve docs/ with live reload.') - parser.add_argument('port', nargs='?', type=int, default=8000) - parser.add_argument('--bind', default='127.0.0.1') + parser = argparse.ArgumentParser(description="Serve docs/ with live reload.") + parser.add_argument("port", nargs="?", type=int, default=8000) + parser.add_argument("--bind", default="127.0.0.1") args = parser.parse_args() - docs_root = Path(__file__).resolve().parents[2] / 'docs' + docs_root = Path(__file__).resolve().parents[2] / "docs" state = ReloadState() observer = Observer() @@ -104,9 +104,11 @@ def main() -> None: try: with contextlib.suppress(KeyboardInterrupt): - server = http.server.ThreadingHTTPServer((args.bind, args.port), handler_factory) - print(f'Serving {docs_root} at http://{args.bind}:{args.port}/') + server = http.server.ThreadingHTTPServer( + (args.bind, args.port), handler_factory + ) + print(f"Serving {docs_root} at http://{args.bind}:{args.port}/") server.serve_forever() finally: observer.stop() - observer.join() \ No newline at end of file + observer.join() diff --git a/src/configlock/extraction.py b/src/configlock/extraction.py index 7921eb0..cb80959 100644 --- a/src/configlock/extraction.py +++ b/src/configlock/extraction.py @@ -1,18 +1,24 @@ import requests import json -def ext_fr_gh_public(user:str | None = "phalberg" , repo:str | None = "configlock", branch: str | None = "main", file:str | None = "config.lock.json"): + +def ext_fr_gh_public( + user: str | None = "phalberg", + repo: str | None = "configlock", + branch: str | None = "main", + file: str | None = "config.lock.json", +): """ Extract from a public github repo in GitHub, the text content """ raw_gh_url = "https://raw.githubusercontent.com" com_url = f"{raw_gh_url}/{user}/{repo}/{branch}/{file}" response = requests.get(com_url) - data = response.json() + data = response.json() return data - + if __name__ == "__main__": - json_data =(json.dumps(ext_fr_gh_public(), indent=4)) + json_data = json.dumps(ext_fr_gh_public(), indent=4) with open("test_config.json", "w", encoding="utf-8") as f: f.write(json_data) diff --git a/src/configlock/helper.py b/src/configlock/helper.py index 29f4b35..c7be142 100644 --- a/src/configlock/helper.py +++ b/src/configlock/helper.py @@ -6,15 +6,19 @@ from dotenv import load_dotenv import os -from configlock.validator import ValidationContext, walk_yaml_in_order, walk_yaml_with_no_order +from configlock.validator import ( + ValidationContext, + walk_yaml_in_order, + walk_yaml_with_no_order, +) load_dotenv() -CONFIG_LOG_FILE_PATH = os.getenv('CONFIG_LOG_FILE_PATH', 'config.lock.json') +CONFIG_LOG_FILE_PATH = os.getenv("CONFIG_LOG_FILE_PATH", "config.lock.json") - - -def check_file_identicality(file_path:str, config_file_path: str | None = CONFIG_LOG_FILE_PATH): +def check_file_identicality( + file_path: str, config_file_path: str | None = CONFIG_LOG_FILE_PATH +): """Checks if files are identical, if they are it returns True, False otherwise""" try: a = check_file_and_read_file(file_path) @@ -28,8 +32,7 @@ def check_file_identicality(file_path:str, config_file_path: str | None = CONFIG filecmp.clear_cache() res = filecmp.cmp(file_path, config_file_path, shallow=False) return res - - + def check_file_exists(file_path: str | None = CONFIG_LOG_FILE_PATH) -> bool: path = Path(file_path) @@ -39,7 +42,6 @@ def check_file_exists(file_path: str | None = CONFIG_LOG_FILE_PATH) -> bool: return exists - def read_yaml(file_path: str) -> dict: try: with open(file_path, "r") as f: @@ -60,11 +62,12 @@ def read_json(file_path: str) -> dict: else: typer.echo("Sucessfully read file") return data - + + def write_json(data: dict, file_path: str | None = CONFIG_LOG_FILE_PATH) -> None: data.update({"version": 1}) try: - with open(file_path, 'w') as json_file: + with open(file_path, "w") as json_file: json.dump(data, json_file, indent=4) except TypeError: raise @@ -99,9 +102,8 @@ def check_file_and_read_file(file_path: str) -> dict: return data - def check_comp_cli(new_file_path: str, order_matters: bool | None = False) -> None: - """"" + """ "" Check compatiblity for the cli version Keys => same names (must be the same, and (!) in the same (?order?)/precedence) Values => must be of the same types @@ -113,7 +115,7 @@ def check_comp_cli(new_file_path: str, order_matters: bool | None = False) -> No context = ValidationContext( new_path=new_file_path, current_path=current_file_path, - order_matters=bool(order_matters) + order_matters=bool(order_matters), ) current_data = read_json(current_file_path) diff --git a/src/configlock/main.py b/src/configlock/main.py index d220d0a..10f14c7 100644 --- a/src/configlock/main.py +++ b/src/configlock/main.py @@ -2,7 +2,13 @@ from configlock.validator import ConfigLockError -from .helper import write_json, check_file_exists, check_file_identicality, check_comp_cli, check_file_and_read_file +from .helper import ( + write_json, + check_file_exists, + check_file_identicality, + check_comp_cli, + check_file_and_read_file, +) from typing import Annotated app = typer.Typer() @@ -13,7 +19,11 @@ def main() -> None: @app.command() -def init(file_path: Annotated[str, typer.Argument(help="the path for the newly proposed file")]) -> None: +def init( + file_path: Annotated[ + str, typer.Argument(help="the path for the newly proposed file") + ], +) -> None: """ Reads a YAML config and generates a lockfile. """ @@ -26,7 +36,9 @@ def init(file_path: Annotated[str, typer.Argument(help="the path for the newly p @app.command() def sync( - file_path: Annotated[str, typer.Argument(help="the path for the newly proposed file")], + file_path: Annotated[ + str, typer.Argument(help="the path for the newly proposed file") + ], ) -> None: """ Used to check if lock file and proposed file are out of sync @@ -34,25 +46,30 @@ def sync( if check_file_identicality(file_path): typer.echo("The file has not changed.") else: - raise ConfigLockError("The lock file is outdated, run sync to update the lock file!", error_code=1) + raise ConfigLockError( + "The lock file is outdated, run sync to update the lock file!", error_code=1 + ) @app.command() def lock( - file_path: Annotated[str, typer.Argument(help="the path for the newly proposed file")], - order_matters: bool = typer.Option(False, "--order-matters/--no-order-matters", help="choose if the order of the keys matter or not"), - ) -> None: + file_path: Annotated[ + str, typer.Argument(help="the path for the newly proposed file") + ], + order_matters: bool = typer.Option( + False, + "--order-matters/--no-order-matters", + help="choose if the order of the keys matter or not", + ), +) -> None: """ Used to update the lock file, IF compatible """ - + check_comp_cli(file_path, order_matters) data = check_file_and_read_file(file_path) write_json(data) - if __name__ == "__main__": main() - - diff --git a/src/configlock/validator.py b/src/configlock/validator.py index b4b339f..f54ea67 100644 --- a/src/configlock/validator.py +++ b/src/configlock/validator.py @@ -4,11 +4,11 @@ keys_to_ignore = {"version"} -''' +""" Note: This is a strict class, meaning that webassembly will use this class. Beware of the contents, and try to be efficient, keep the class minimal. -''' +""" @dataclass @@ -17,8 +17,10 @@ class ValidationContext: current_path: str order_matters: bool + # add other metadata if needed. + class ConfigLockError(Exception): """Basic Error""" @@ -31,9 +33,16 @@ def __init__(self, message, error_code=1): class ValidationError(ConfigLockError): """Error for validation for syncing file""" - def __init__(self, path, expected_value, actual_value, message="Validation Failed", order_matters=False): + def __init__( + self, + path, + expected_value, + actual_value, + message="Validation Failed", + order_matters=False, + ): super().__init__(message, error_code=100) - self.path = path + self.path = path self.expected_value = expected_value self.actual_value = actual_value self.order_matters = order_matters @@ -49,13 +58,16 @@ def __str__(self): return f"{base_msg}\n(Error Code: {self.error_code})" - -def walk_yaml_with_no_order(current_data, new_data, context: ValidationContext, depth=0): +def walk_yaml_with_no_order( + current_data, new_data, context: ValidationContext, depth=0 +): """Recursively walks through a YAML-loaded object with no order.""" if isinstance(current_data, dict): if not isinstance(new_data, dict): - accept_new_value(current_value=current_data, new_value=new_data, context=context) + accept_new_value( + current_value=current_data, new_value=new_data, context=context + ) return for curr_k, curr_v in current_data.items(): @@ -69,31 +81,35 @@ def walk_yaml_with_no_order(current_data, new_data, context: ValidationContext, new_v = new_data[curr_k] walk_yaml_with_no_order(curr_v, new_v, context, depth + 1) - + else: - accept_new_value(current_value=current_data, new_value=new_data, context=context) + accept_new_value( + current_value=current_data, new_value=new_data, context=context + ) def walk_yaml_in_order(current_data, new_data, context: ValidationContext, depth=0): """Recursively walks through a YAML-loaded object in order.""" if isinstance(current_data, dict): - for new_pair, curr_pair in zip_longest(new_data.items(), current_data.items(), fillvalue=(None, None)): + for new_pair, curr_pair in zip_longest( + new_data.items(), current_data.items(), fillvalue=(None, None) + ): new_k, new_v = new_pair curr_k, curr_v = curr_pair # specific values for our own interpretation of versionings etc. if curr_k in keys_to_ignore: continue - + accept_new_keys(current_key=curr_k, new_key=new_k, context=context) walk_yaml_with_no_order(new_v, curr_v, context, depth + 1) - - else: - accept_new_value(current_value=current_data, new_value=new_data, context=context) - + else: + accept_new_value( + current_value=current_data, new_value=new_data, context=context + ) def accept_new_keys(current_key: str, new_key: str, context: ValidationContext) -> None: @@ -105,21 +121,20 @@ def accept_new_keys(current_key: str, new_key: str, context: ValidationContext) """ if current_key != new_key: - if context.order_matters: - raise ValidationError( - path= context.new_path, - expected_value=current_key, - actual_value=new_key, - order_matters=True - ) - else: - raise ValidationError( - path=context.new_path, - expected_value=current_key, - actual_value=new_key, - order_matters=False - ) - + if context.order_matters: + raise ValidationError( + path=context.new_path, + expected_value=current_key, + actual_value=new_key, + order_matters=True, + ) + else: + raise ValidationError( + path=context.new_path, + expected_value=current_key, + actual_value=new_key, + order_matters=False, + ) def accept_new_value(current_value, new_value, context: ValidationContext) -> None: @@ -129,15 +144,13 @@ def accept_new_value(current_value, new_value, context: ValidationContext) -> No """ type_curr = type(current_value) type_new = type(new_value) - - if type_curr != type_new: - - type_curr_val = f"<{type_curr.__name__}> with value: {current_value}" - type_next_val = f"<{type_new.__name__}> with value: {new_value}" - raise ValidationError( - path = context.new_path, - expected_value=type_curr_val, - actual_value=type_next_val - ) - + if type_curr != type_new: + type_curr_val = f"<{type_curr.__name__}> with value: {current_value}" + type_next_val = f"<{type_new.__name__}> with value: {new_value}" + + raise ValidationError( + path=context.new_path, + expected_value=type_curr_val, + actual_value=type_next_val, + ) diff --git a/tests/integration_test/test_init.py b/tests/integration_test/test_init.py index e206106..adfe368 100644 --- a/tests/integration_test/test_init.py +++ b/tests/integration_test/test_init.py @@ -7,7 +7,6 @@ runner = CliRunner() - def test_app(): with runner.isolated_filesystem(): with open("config.yaml", "w", encoding="utf-8") as f: @@ -19,4 +18,4 @@ def test_app(): with open("config.lock.json", "r", encoding="utf-8") as f: lock_data = json.load(f) - assert lock_data["version"] == 1 \ No newline at end of file + assert lock_data["version"] == 1 diff --git a/tests/unit_test/test_init_command.py b/tests/unit_test/test_init_command.py index 0192f8e..6c30f4f 100644 --- a/tests/unit_test/test_init_command.py +++ b/tests/unit_test/test_init_command.py @@ -2,36 +2,30 @@ def test_init_valueerror_path_error(runner_setup): - + result = runner_setup.invoke(main.app, ["init", "not_available_path"]) - + assert result.exit_code == 1 assert isinstance(result.exception, ValueError) assert "read the file" in str(result.exception).lower() - + + def test_init_path_already_exits(runner_with_lock_file_setup): - - - result = runner_with_lock_file_setup.invoke(main.app, ["init", "not_needed.json"] ) - + + result = runner_with_lock_file_setup.invoke(main.app, ["init", "not_needed.json"]) + # confirming the files contents, as to not overwrite anything with open("config.lock.json", "r") as f: output = f.read() - + assert "example" in str(output) assert "file already exists" in str(result.output).lower() - + + def test_not_supported_file(runner_setup): - + result = runner_setup.invoke(main.app, ["init", "not_supported.toml"]) - + assert result.exit_code == 1 assert isinstance(result.exception, ValueError) assert "not able to read the file" in str(result.exception).lower() - - - - - - - diff --git a/tests/unit_test/test_lock_command.py b/tests/unit_test/test_lock_command.py index 330f5ac..8a75328 100644 --- a/tests/unit_test/test_lock_command.py +++ b/tests/unit_test/test_lock_command.py @@ -2,21 +2,23 @@ from configlock.validator import ValidationError + def test_not_available_file(runner_setup): - + result = runner_setup.invoke(main.app, ["lock", "some_file.json"]) assert result.exit_code == 1 assert isinstance(result.exception, FileNotFoundError) - - + def test_compatibility_file(runner_with_lock_file_setup): with open("incompatible_file.toml", "w", encoding="utf-8") as f: f.write('[section]\nkey = "value"') - result = runner_with_lock_file_setup.invoke(main.app, ["lock", "incompatible_file.toml"]) + result = runner_with_lock_file_setup.invoke( + main.app, ["lock", "incompatible_file.toml"] + ) assert result.exit_code == 1 assert isinstance(result.exception, ValueError) @@ -24,19 +26,19 @@ def test_compatibility_file(runner_with_lock_file_setup): def test_fail_new_key(runner_with_lock_file_setup): - - + with open("new_file.yaml", "w", encoding="utf-8") as f: f.write("first_name: example") result = runner_with_lock_file_setup.invoke(main.app, ["lock", "new_file.yaml"]) - + assert result.exit_code == 1 assert isinstance(result.exception, ValidationError) exc_str = str(result.exception).lower() - assert all(s in exc_str for s in ["in path", "expected", "found", "error code"]) - + assert all(s in exc_str for s in ["in path", "expected", "found", "error code"]) + + def test_fail_new_value(runner_with_lock_file_setup): - + with open("new_file.yaml", "w", encoding="utf-8") as f: f.write("name: 12") result = runner_with_lock_file_setup.invoke(main.app, ["lock", "new_file.yaml"]) @@ -44,29 +46,45 @@ def test_fail_new_value(runner_with_lock_file_setup): assert result.exit_code == 1 assert isinstance(result.exception, ValidationError) exec_str = str(result.exception).lower() - assert all(s in exec_str for s in ["in path", "", "", "example", "12", "found", "expected", "error code"]) - - + assert all( + s in exec_str + for s in [ + "in path", + "", + "", + "example", + "12", + "found", + "expected", + "error code", + ] + ) + + def test_order_matters_works(runner_with_lock_file_setup): - + with open("new_file.yaml", "w", encoding="utf-8") as f: f.write("object: false") - - result = runner_with_lock_file_setup.invoke(main.app, ["lock", "new_file.yaml", "--order-matters"]) - + + result = runner_with_lock_file_setup.invoke( + main.app, ["lock", "new_file.yaml", "--order-matters"] + ) + assert result.exit_code == 1 assert isinstance(result.exception, ValidationError) exc_str = str(result.exception).lower() - assert all(s in exc_str for s in ["in path", "expected", "found", "error code", "additional", "order"]) - + assert all( + s in exc_str + for s in ["in path", "expected", "found", "error code", "additional", "order"] + ) + + def test_no_order_matters_works(runner_with_lock_file_setup): - + with open("new_file.yaml", "w", encoding="utf-8") as f: f.write("object: false\nname: example") - + result = runner_with_lock_file_setup.invoke(main.app, ["lock", "new_file.yaml"]) - - + assert result.exit_code == 0 assert result.exception == None - diff --git a/tests/unit_test/test_sync_command.py b/tests/unit_test/test_sync_command.py index e5dc625..72bf6c0 100644 --- a/tests/unit_test/test_sync_command.py +++ b/tests/unit_test/test_sync_command.py @@ -2,8 +2,6 @@ from configlock.validator import ConfigLockError - - def test_check_file_identicality(runner_with_lock_file_setup): with open("config.yaml", "w", encoding="utf-8") as f: f.write("name: example\nobject: false") @@ -13,7 +11,7 @@ def test_check_file_identicality(runner_with_lock_file_setup): def test_outdated_lock_file(runner_with_lock_file_setup): - + with open("config.yaml", "w", encoding="utf-8") as f: f.write("name: example") result = runner_with_lock_file_setup.invoke(main.app, ["sync", "config.yaml"]) diff --git a/tests/unit_test/test_validator_class.py b/tests/unit_test/test_validator_class.py index bb0bb3c..7d9cbfd 100644 --- a/tests/unit_test/test_validator_class.py +++ b/tests/unit_test/test_validator_class.py @@ -1,74 +1,56 @@ - import inspect from configlock import validator def helper_sig(name_class) -> inspect.Signature: - + sig = inspect.getfullargspec(name_class).annotations - + return sig def test_sig_context(): - + val_context_sig = helper_sig(name_class=validator.ValidationContext) - + sig_list = ["new_path", "current_path", "order_matters"] - + assert ("return", None) in val_context_sig.items() - assert(all(key in val_context_sig for key in sig_list)) + assert all(key in val_context_sig for key in sig_list) assert ("new_path", str) in val_context_sig.items() assert ("current_path", str) in val_context_sig.items() - - + + def test_sig_walk_n_order(): - - + walk_n_order_sig = helper_sig(name_class=validator.walk_yaml_with_no_order) - - + sig_list = ["current_data", "new_data", "context", "depth"] - - assert(all(key in walk_n_order_sig for key in sig_list)) + + assert all(key in walk_n_order_sig for key in sig_list) assert ("context", validator.ValidationContext) in walk_n_order_sig.items() - - - + + def test_sig_walk_w_order(): - + walk_w_order = helper_sig(name_class=validator.walk_yaml_in_order) - sig_list = ["current_data", "new_data", "context", "depth"] - - assert(all(key in walk_w_order for key in sig_list)) + + assert all(key in walk_w_order for key in sig_list) assert ("context", validator.ValidationContext) in walk_w_order.items() - - - - - - accept_n_key = helper_sig(name_class=validator.accept_new_keys) - - + + accept_n_key = helper_sig(name_class=validator.accept_new_keys) + accept_n_val = helper_sig(name_class=validator.accept_new_value) - - - - - - - - - + if __name__ == "__main__": - #print(inspect.getfullargspec(validator.ValidationContext)) + # print(inspect.getfullargspec(validator.ValidationContext)) sig = inspect.getfullargspec(validator.ValidationContext).annotations print(sig) print("new_path" in sig) - print( ("return", None) in sig.items()) + print(("return", None) in sig.items()) print(helper_sig(name_class=validator.walk_yaml_with_no_order)) - print(inspect.signature(validator.walk_yaml_in_order(0,0,0))) + print(inspect.signature(validator.walk_yaml_in_order(0, 0, 0))) diff --git a/uv.lock b/uv.lock index c373386..3b5a2b8 100644 --- a/uv.lock +++ b/uv.lock @@ -20,6 +20,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, ] +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + [[package]] name = "charset-normalizer" version = "3.4.7" @@ -111,6 +120,7 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "pre-commit" }, { name = "pyright" }, { name = "pytest" }, { name = "radon" }, @@ -128,6 +138,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "pre-commit", specifier = ">=4.6.0" }, { name = "pyright", specifier = ">=1.1.410" }, { name = "pytest", specifier = ">=9.0.3" }, { name = "radon", specifier = ">=6.0.1" }, @@ -135,6 +146,15 @@ dev = [ { name = "ruff", specifier = ">=0.15.15" }, ] +[[package]] +name = "distlib" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/8d/873e9252ea2c0e0c857884e0a2899ec43ade132345df1925ef24cbe64f18/distlib-0.4.2.tar.gz", hash = "sha256:baeb401c90f27acd15c4861ae0847d1e731c27ac3dbf4210643ba61fa1e813db", size = 614914, upload-time = "2026-06-08T16:24:15.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/60/aa891c893821d4d127292ed66c6940d1d715894bd5a0ce048056bc641773/distlib-0.4.2-py2.py3-none-any.whl", hash = "sha256:ca4cb11e5d746b5ec13c199cbf19ae27a241f89702b54e153a74332955446067", size = 470510, upload-time = "2026-06-08T16:24:13.208Z" }, +] + [[package]] name = "dotenv" version = "0.9.9" @@ -146,6 +166,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" }, ] +[[package]] +name = "filelock" +version = "3.29.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/f9/f38573ed5844586db374d085911740a501ccfa373b455fc9413f09f85237/filelock-3.29.1.tar.gz", hash = "sha256:d97e6b1b9757569626c58caa07dc4beb1613f4a2938b1e8cc81afca398906c9e", size = 59335, upload-time = "2026-06-03T15:19:04.053Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/a0/614c5fe402fd88951df45f4dda2fa3b4e17a99ecd92340771929169b3b95/filelock-3.29.1-py3-none-any.whl", hash = "sha256:85199dfd706869641b72b2e8955d5416a4b2b7dc4b0e8e6d97b4cc1299a6983b", size = 40750, upload-time = "2026-06-03T15:19:02.959Z" }, +] + +[[package]] +name = "identify" +version = "2.6.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, +] + [[package]] name = "idna" version = "3.17" @@ -215,6 +253,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] +[[package]] +name = "platformdirs" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -224,6 +271,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "pre-commit" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, +] + [[package]] name = "pygments" version = "2.20.0" @@ -262,6 +325,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] +[[package]] +name = "python-discovery" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/12/38c1a0b1e64806780c9563e3fc9f6e472251839662587cfbe9bfaf2ae10a/python_discovery-1.4.0.tar.gz", hash = "sha256:eb8bc7daad3c226c147e45bb4e970a1feb1bf4048ee178e6db59e197b8010ce3", size = 68455, upload-time = "2026-05-28T01:15:37.639Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/8d/3d316429f65029532bb1e28ff77b797d86b5ac3915bb44ca4e19aa283d43/python_discovery-1.4.0-py3-none-any.whl", hash = "sha256:26ed78d703e234879a66244c7d4114563fb13ec5cd30a2d1357e5fb4850782da", size = 33217, upload-time = "2026-05-28T01:15:36.573Z" }, +] + [[package]] name = "python-dotenv" version = "1.2.2" @@ -436,6 +512,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] +[[package]] +name = "virtualenv" +version = "21.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0d/4e93c8e6d1001a75763f87d8f5ecda8ebc7f4aa2153dddfaf4ae8892821a/virtualenv-21.4.2.tar.gz", hash = "sha256:38e6ee0a555615c0ea9da2ac7e9998fe8dc3b911dd33ad8eaad2020957653b0c", size = 7613326, upload-time = "2026-05-31T17:01:22.827Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/c4/557dc082be035381b85fdb2b74e21d3d21b57750b74f2b47a32f3a639ff9/virtualenv-21.4.2-py3-none-any.whl", hash = "sha256:854210ca524a1a4d0d744734f4acbc721c3ffe163b85bbf5d56d14d5ae2f0fae", size = 7594079, upload-time = "2026-05-31T17:01:20.735Z" }, +] + [[package]] name = "watchdog" version = "6.0.0"