Compare commits

...
Author SHA1 Message Date
Ludy 6d46afaec8 Merge branch 'main' into py_format_ruff_0_16_2 2026-08-20 21:51:58 +02:00
Ludy87 e934c3d6f0 Mark Python scripts as executable 2026-08-20 13:24:25 +02:00
Ludy 20cca7e911 Merge branch 'main' into py_format_ruff_0_16_2 2026-08-19 19:45:38 +02:00
Ludy87 5f0fc84409 Enable Ruff executable checks
Remove the Ruff EXE001 suppression from the Python engine configuration and pre-commit task so executable-file linting runs normally. This keeps the repo's Python checks consistent with the current lint rules and catches script-style issues during pre-commit.
2026-08-19 19:43:52 +02:00
Ludy87 845545aa38 Bump engine dev tooling
Updated the engine's Python development tooling versions and refreshed the lockfile. This moves datamodel-code-generator to 0.74.0 and ruff to 0.16.3, keeping the repo's formatting, linting, and generated model tooling aligned with the latest compatible releases.
2026-08-19 01:08:51 +02:00
Ludy87 eb625570df Update pre-commit.yml 2026-08-19 00:52:29 +02:00
Ludy87 3d8b84a699 Ignore EXE001 in Python pre-commit
This updates the Ruff pre-commit checks for the engine to ignore the EXE001 executable-file lint warning. The change applies to both ruff check and ruff format so repository Python files are not blocked by file permission warnings during pre-commit validation.
2026-08-19 00:45:48 +02:00
Ludy87 d649afb22f Ignore EXE001 in engine ruff config
Add EXE001 to the Ruff ignore list in engine/pyproject.toml. This suppresses the EXE001 lint rule for the engine package during ruff runs, preventing that specific check from blocking CI/linting in the engine workspace.
2026-08-19 00:38:02 +02:00
Ludy87 c835f2da06 Update Ruff and lint cleanup
Bump Python tooling to Ruff 0.16.2 and datamodel-code-generator 0.72.3, add the py313 target in pre-commit, and refresh the engine lockfile. This also narrows broad exception handling and applies small Python cleanups across the engine and helper scripts to satisfy newer lint rules without changing runtime behavior.
2026-08-19 00:26:55 +02:00
35 changed files with 119 additions and 127 deletions
View File
+4 -2
View File
@@ -65,6 +65,8 @@ tasks:
cmds:
# Auto-fixers first, then the report-only tools (codespell, gitleaks) so a
# finding there does not stop the fixers from running.
- task: ruff-format
vars: { FIX: '1' }
- task: ruff
vars: { FIX: '1' }
- task: ruff-format
@@ -101,12 +103,12 @@ tasks:
ruff:
deps: [install]
cmds:
- uv run --project engine --locked --group pre-commit ruff check --isolated --line-length=120 {{if .FIX}}--fix {{end}}$(git ls-files {{.PY_FILES}})
- uv run --project engine --locked --group pre-commit ruff check --isolated --target-version=py313 --line-length=120 {{if .FIX}}--fix {{end}}$(git ls-files {{.PY_FILES}})
ruff-format:
deps: [install]
cmds:
- uv run --project engine --locked --group pre-commit ruff format --isolated --line-length=120 {{if .FIX}}{{else}}--check {{end}}$(git ls-files {{.PY_FILES}})
- uv run --project engine --locked --group pre-commit ruff format --isolated --target-version=py313 --line-length=120 {{if .FIX}}{{else}}--check {{end}}$(git ls-files {{.PY_FILES}})
codespell:
deps: [install]
@@ -61,7 +61,7 @@ def resize_image(input_image_path, output_image_path, max_size=(16383, 16383)):
# If dimensions are within the allowed limits, save the image directly
image.save(output_image_path, format="WEBP", quality=100)
print(f"The image was successfully saved as WebP: {output_image_path}")
except Exception as e:
except (OSError, ValueError) as e:
print(f"An error occurred: {e}")
+3 -3
View File
@@ -26,11 +26,11 @@ engine = [
# Type checking, testing, model generation, and formatting tools for the engine.
engine-dev = [
"anyio>=4.14.2",
"datamodel-code-generator[ruff]==0.64.0",
"datamodel-code-generator[ruff]==0.74.0",
"pyright>=1.1.411",
"pytest>=9.1.1",
"referencing>=0.37.0",
"ruff==0.15.5",
"ruff==0.16.3",
]
# Dependencies for the Cucumber/Python integration test suite.
cucumber = [
@@ -65,7 +65,7 @@ updater-signatures = [
# Pinned repository-wide pre-commit tooling.
pre-commit = [
"codespell==2.4.3",
"ruff==0.15.5",
"ruff==0.16.3",
"tomli-w==1.2.0",
]
+1 -1
View File
@@ -218,7 +218,7 @@ async def apply_config(request: ConfigPushRequest, http_request: Request) -> Con
save_config(request)
# Claim the stamp we just wrote so this worker's watcher does not rebuild for it.
app.state.config_cache_stamp = cache_stamp()
except Exception: # noqa: BLE001 - best-effort persist, never fail the applied push
except Exception: # best-effort persist, never fail the applied push
logger.warning("Applied AI config but failed to persist the encrypted cache", exc_info=True)
notes.append(
"Config applied on this worker but could not be persisted; it will not survive an"
+1
View File
@@ -17,6 +17,7 @@ from pydantic_ai import Agent
from stirling.services import AppRuntime
class MyAgent:
def __init__(self, runtime: AppRuntime) -> None:
rag = runtime.rag_capability
+1 -1
View File
@@ -139,7 +139,7 @@ class DocumentService:
try:
results = await self._store.search(col_name, query_embedding, k, principals)
all_results.extend(results)
except Exception: # noqa: BLE001 - any backend error on one collection should not stop the others
except Exception: # any backend error on one collection should not stop the others
logger.warning(
"Skipping collection %s during cross-collection search",
col_name,
+25 -25
View File
@@ -534,7 +534,7 @@ wheels = [
[[package]]
name = "datamodel-code-generator"
version = "0.64.0"
version = "0.74.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "argcomplete" },
@@ -546,9 +546,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9c/d2/86c94a2836ed42231653a7ddaefa0a5bc23418167a876bba7376c96b3a35/datamodel_code_generator-0.64.0.tar.gz", hash = "sha256:9c592900a00b20e416494273c22435f5a9aef6ea8c7b9190747522a60497a1cb", size = 1316440, upload-time = "2026-06-14T17:24:50.528Z" }
sdist = { url = "https://files.pythonhosted.org/packages/57/e8/8a46c8de96ec5c3b891172bf5db4bcf4797e75c31aa03d540d528e61ccd8/datamodel_code_generator-0.74.0.tar.gz", hash = "sha256:db0998d920e774f48442ca2702b901049be60c35ccb547086c574b1d261f1444", size = 2055535, upload-time = "2026-08-17T17:06:40.393Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/23/94/71338e2f0146ac10747a5537b3a1e45256e66b7c229869eb0ee787111b41/datamodel_code_generator-0.64.0-py3-none-any.whl", hash = "sha256:b7cd8bd41a312aa997aec6150670bad781847c5b674f17e4d70e78208a0fb990", size = 374698, upload-time = "2026-06-14T17:24:48.809Z" },
{ url = "https://files.pythonhosted.org/packages/d3/81/5b79e507f1fab62ac953d3343708a345d8e74cbe862ca2f61ccc90c2c600/datamodel_code_generator-0.74.0-py3-none-any.whl", hash = "sha256:5e7d0b41d25077ac54f23775904573fc8b608bdabc7fc762885204322f596190", size = 592333, upload-time = "2026-08-17T17:06:38.185Z" },
]
[package.optional-dependencies]
@@ -711,15 +711,15 @@ engine = [
]
engine-dev = [
{ name = "anyio", specifier = ">=4.14.2" },
{ name = "datamodel-code-generator", extras = ["ruff"], specifier = "==0.64.0" },
{ name = "datamodel-code-generator", extras = ["ruff"], specifier = "==0.74.0" },
{ name = "pyright", specifier = ">=1.1.411" },
{ name = "pytest", specifier = ">=9.1.1" },
{ name = "referencing", specifier = ">=0.37.0" },
{ name = "ruff", specifier = "==0.15.5" },
{ name = "ruff", specifier = "==0.16.3" },
]
pre-commit = [
{ name = "codespell", specifier = "==2.4.3" },
{ name = "ruff", specifier = "==0.15.5" },
{ name = "ruff", specifier = "==0.16.3" },
{ name = "tomli-w", specifier = "==1.2.0" },
]
tools = [
@@ -2791,27 +2791,27 @@ wheels = [
[[package]]
name = "ruff"
version = "0.15.5"
version = "0.16.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/77/9b/840e0039e65fcf12758adf684d2289024d6140cde9268cc59887dc55189c/ruff-0.15.5.tar.gz", hash = "sha256:7c3601d3b6d76dce18c5c824fc8d06f4eef33d6df0c21ec7799510cde0f159a2", size = 4574214, upload-time = "2026-03-05T20:06:34.946Z" }
sdist = { url = "https://files.pythonhosted.org/packages/61/b3/3213589383f8f1b3938781bd1278713f6d18621a14992b3e81fefb8a5ef9/ruff-0.16.3.tar.gz", hash = "sha256:e76d33a347661a84b5be6d043d0347fdc745dfdcf825a8f4fed64b5e26eebdf2", size = 4891904, upload-time = "2026-08-13T15:17:13.381Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/47/20/5369c3ce21588c708bcbe517a8fbe1a8dfdb5dfd5137e14790b1da71612c/ruff-0.15.5-py3-none-linux_armv6l.whl", hash = "sha256:4ae44c42281f42e3b06b988e442d344a5b9b72450ff3c892e30d11b29a96a57c", size = 10478185, upload-time = "2026-03-05T20:06:29.093Z" },
{ url = "https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6edd3792d408ebcf61adabc01822da687579a1a023f297618ac27a5b51ef0080", size = 10859201, upload-time = "2026-03-05T20:06:32.632Z" },
{ url = "https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:89f463f7c8205a9f8dea9d658d59eff49db05f88f89cc3047fb1a02d9f344010", size = 10184752, upload-time = "2026-03-05T20:06:40.312Z" },
{ url = "https://files.pythonhosted.org/packages/66/0e/ba49e2c3fa0395b3152bad634c7432f7edfc509c133b8f4529053ff024fb/ruff-0.15.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba786a8295c6574c1116704cf0b9e6563de3432ac888d8f83685654fe528fd65", size = 10534857, upload-time = "2026-03-05T20:06:19.581Z" },
{ url = "https://files.pythonhosted.org/packages/59/71/39234440f27a226475a0659561adb0d784b4d247dfe7f43ffc12dd02e288/ruff-0.15.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd4b801e57955fe9f02b31d20375ab3a5c4415f2e5105b79fb94cf2642c91440", size = 10309120, upload-time = "2026-03-05T20:06:00.435Z" },
{ url = "https://files.pythonhosted.org/packages/f5/87/4140aa86a93df032156982b726f4952aaec4a883bb98cb6ef73c347da253/ruff-0.15.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391f7c73388f3d8c11b794dbbc2959a5b5afe66642c142a6effa90b45f6f5204", size = 11047428, upload-time = "2026-03-05T20:05:51.867Z" },
{ url = "https://files.pythonhosted.org/packages/5a/f7/4953e7e3287676f78fbe85e3a0ca414c5ca81237b7575bdadc00229ac240/ruff-0.15.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc18f30302e379fe1e998548b0f5e9f4dff907f52f73ad6da419ea9c19d66c8", size = 11914251, upload-time = "2026-03-05T20:06:22.887Z" },
{ url = "https://files.pythonhosted.org/packages/77/46/0f7c865c10cf896ccf5a939c3e84e1cfaeed608ff5249584799a74d33835/ruff-0.15.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc6e7f90087e2d27f98dc34ed1b3ab7c8f0d273cc5431415454e22c0bd2a681", size = 11333801, upload-time = "2026-03-05T20:05:57.168Z" },
{ url = "https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1cb7169f53c1ddb06e71a9aebd7e98fc0fea936b39afb36d8e86d36ecc2636a", size = 11206821, upload-time = "2026-03-05T20:06:03.441Z" },
{ url = "https://files.pythonhosted.org/packages/7a/0d/2132ceaf20c5e8699aa83da2706ecb5c5dcdf78b453f77edca7fb70f8a93/ruff-0.15.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9b037924500a31ee17389b5c8c4d88874cc6ea8e42f12e9c61a3d754ff72f1ca", size = 11133326, upload-time = "2026-03-05T20:06:25.655Z" },
{ url = "https://files.pythonhosted.org/packages/72/cb/2e5259a7eb2a0f87c08c0fe5bf5825a1e4b90883a52685524596bfc93072/ruff-0.15.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65bb414e5b4eadd95a8c1e4804f6772bbe8995889f203a01f77ddf2d790929dd", size = 10510820, upload-time = "2026-03-05T20:06:37.79Z" },
{ url = "https://files.pythonhosted.org/packages/ff/20/b67ce78f9e6c59ffbdb5b4503d0090e749b5f2d31b599b554698a80d861c/ruff-0.15.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d20aa469ae3b57033519c559e9bc9cd9e782842e39be05b50e852c7c981fa01d", size = 10302395, upload-time = "2026-03-05T20:05:54.504Z" },
{ url = "https://files.pythonhosted.org/packages/5f/e5/719f1acccd31b720d477751558ed74e9c88134adcc377e5e886af89d3072/ruff-0.15.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:15388dd28c9161cdb8eda68993533acc870aa4e646a0a277aa166de9ad5a8752", size = 10754069, upload-time = "2026-03-05T20:06:06.422Z" },
{ url = "https://files.pythonhosted.org/packages/c3/9c/d1db14469e32d98f3ca27079dbd30b7b44dbb5317d06ab36718dee3baf03/ruff-0.15.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b30da330cbd03bed0c21420b6b953158f60c74c54c5f4c1dabbdf3a57bf355d2", size = 11304315, upload-time = "2026-03-05T20:06:10.867Z" },
{ url = "https://files.pythonhosted.org/packages/28/3a/950367aee7c69027f4f422059227b290ed780366b6aecee5de5039d50fa8/ruff-0.15.5-py3-none-win32.whl", hash = "sha256:732e5ee1f98ba5b3679029989a06ca39a950cced52143a0ea82a2102cb592b74", size = 10551676, upload-time = "2026-03-05T20:06:13.705Z" },
{ url = "https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl", hash = "sha256:821d41c5fa9e19117616c35eaa3f4b75046ec76c65e7ae20a333e9a8696bc7fe", size = 11678972, upload-time = "2026-03-05T20:06:45.379Z" },
{ url = "https://files.pythonhosted.org/packages/fe/4e/cd76eca6db6115604b7626668e891c9dd03330384082e33662fb0f113614/ruff-0.15.5-py3-none-win_arm64.whl", hash = "sha256:b498d1c60d2fe5c10c45ec3f698901065772730b411f164ae270bb6bfcc4740b", size = 10965572, upload-time = "2026-03-05T20:06:16.984Z" },
{ url = "https://files.pythonhosted.org/packages/bf/96/493770daebd68c0a67f1549fdf519f53be51fc435186c0585bcc272fd76c/ruff-0.16.3-py3-none-linux_armv6l.whl", hash = "sha256:0c5710e247a58a4521e66e124ba9a74655b414f61ba3a2e9e3811e11098f48f7", size = 10902799, upload-time = "2026-08-13T15:16:27.382Z" },
{ url = "https://files.pythonhosted.org/packages/5e/e6/2becf3942fddc29a29b8df47691d456fb1085391a694f74d84513251418c/ruff-0.16.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fe155130631a2471fd2e14a7a664a4dfbd7194b8229c3d7b2a40b21178639081", size = 11135539, upload-time = "2026-08-13T15:16:30.87Z" },
{ url = "https://files.pythonhosted.org/packages/3e/1e/4b8b72f0d006dbf19326aa99f9ca0ee2ff374187c4d301cf529a51aa06fe/ruff-0.16.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e2ed719e14aa64d895c2ee922594a90a43c861a93f0575a95ff8c47cdbd13eb9", size = 10475095, upload-time = "2026-08-13T15:16:33.259Z" },
{ url = "https://files.pythonhosted.org/packages/92/32/2201fa49ba1f6c101ee321e83f051ac7a4b8d07b0ef6b4d3f2772b302275/ruff-0.16.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e0b1da805eb043654645d74d5de1e5ce2edc686e40790d2b86f56d71cc06a84", size = 10668771, upload-time = "2026-08-13T15:16:35.65Z" },
{ url = "https://files.pythonhosted.org/packages/c3/66/4afc5c8363bd04d45effce1b7c8713ca037d7a6740b7451a2403a6e3a972/ruff-0.16.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a37bdea0bbe21780f590bf437d6412c8c4e1b6cd010f91a65c2c40c5e5f5f870", size = 10699568, upload-time = "2026-08-13T15:16:38.195Z" },
{ url = "https://files.pythonhosted.org/packages/53/fd/c67d246bf36bf1698551c56de39e95cd07f70e64433e0098e6267d77061b/ruff-0.16.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09571e6d1288ed9be475207a3ac04ada404f1cd898104be0f6ab8d7df438575b", size = 11499365, upload-time = "2026-08-13T15:16:40.623Z" },
{ url = "https://files.pythonhosted.org/packages/67/0b/00ecbceb99a263af7b12f6f05ac3c92bc47b905e91adc3f207a836e3bc01/ruff-0.16.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c18c5a101eb540010638cc1ff3c84944d3adb3df62b8d98ca8f22ba484d3413", size = 12311728, upload-time = "2026-08-13T15:16:43.564Z" },
{ url = "https://files.pythonhosted.org/packages/54/b2/b7b3bb54f4d3f7db504e476ad4ab8de530dceebe2c061384b2757ee419e8/ruff-0.16.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8457c44f15033c85ddbb77b15d451df9e24e4bd03b628396dd3610cedc3b8f82", size = 11699896, upload-time = "2026-08-13T15:16:46.209Z" },
{ url = "https://files.pythonhosted.org/packages/c7/30/4c468429ac195addc5ee1b717b6ab1b66632786737ca3b2ed3443fb0c26a/ruff-0.16.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:294b95c4ae0cda9388525c2047778aa758d6b8d4bb876fd4e9eaa3ebc92343eb", size = 11058736, upload-time = "2026-08-13T15:16:48.823Z" },
{ url = "https://files.pythonhosted.org/packages/43/67/7a113cdaddf24b64d7f75b1242a99d04c82fcef4f6921fdbb832beaffb5f/ruff-0.16.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:3d0c7c40c87c2a820509c31ba007968da6e1306468c067b2d82fbfdbcd0e8474", size = 11586911, upload-time = "2026-08-13T15:16:51.913Z" },
{ url = "https://files.pythonhosted.org/packages/f1/c1/2e66f24c0f3ead25a5e660111778685e505e5da353c82802bf49f0cbe7b9/ruff-0.16.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9f738c0fdfa8eed0b2ce7fb27ee7258208a92a68d7949e62aa15164bc7b389da", size = 10954265, upload-time = "2026-08-13T15:16:54.763Z" },
{ url = "https://files.pythonhosted.org/packages/c2/ba/4cee23bf52cba9a058d3726de623624daf50ef9638868edd86f4126157f6/ruff-0.16.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fb785f0be25abe69d320415cd4f833b59e17ba7613d9ba6a958023b6bceb0a50", size = 10709886, upload-time = "2026-08-13T15:16:57.339Z" },
{ url = "https://files.pythonhosted.org/packages/82/df/7da7194fa5d9dc0a285f7e6fa5a4722e7c63faac0b45b614ded9314363a1/ruff-0.16.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c5536e3acfbf9563085aa2be7b13c629c3077e902afc5b941ac44024dbb9f506", size = 11210392, upload-time = "2026-08-13T15:17:00.171Z" },
{ url = "https://files.pythonhosted.org/packages/35/85/7795f6e817af050e7517bf3e7aa9b061cce70ef33d280aad902c956c1ecf/ruff-0.16.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a2d85c02f9b8e165d85e6779184d38c4132de12603dab59c51c28e22584f9e4d", size = 11626910, upload-time = "2026-08-13T15:17:03.299Z" },
{ url = "https://files.pythonhosted.org/packages/78/9b/475b927cf27a5cbbda3c7bafb69ed6ff77e1d7923d5d85f17c2749d7ae32/ruff-0.16.3-py3-none-win32.whl", hash = "sha256:388cdf2166642bd9b13d52b5932d3170f34f8abed7e8d9a855f1d84b83645a0a", size = 10931415, upload-time = "2026-08-13T15:17:05.726Z" },
{ url = "https://files.pythonhosted.org/packages/b2/99/e2a2bfc4fbf0a1e8a916bc9ebe6fe6c58cc34c28e0ffc6ce281d572d1c2e/ruff-0.16.3-py3-none-win_amd64.whl", hash = "sha256:e80a7d69ca2a6d1c4d352ec91458cdca6e56c83cdbcabd93e4abe1e53591d948", size = 11445993, upload-time = "2026-08-13T15:17:08.353Z" },
{ url = "https://files.pythonhosted.org/packages/69/3e/4132e539aed78c148854d4997a2685b0ed4dc4e87110b59ce528564e184e/ruff-0.16.3-py3-none-win_arm64.whl", hash = "sha256:b8ca152da82c1acc1fa8d5874b15951935f0eef46f10e6954c83859011b6178a", size = 11399302, upload-time = "2026-08-13T15:17:10.908Z" },
]
[[package]]
Regular → Executable
View File
Regular → Executable
+8 -8
View File
@@ -75,7 +75,7 @@ def parse_unicode_mapping(mapping_path):
print(f"Parsed ToUnicode CMap: {len(gid_to_unicode)} mappings", file=sys.stderr)
return gid_to_unicode
except Exception as e:
except (OSError, UnicodeError, ValueError, TypeError, KeyError) as e:
print(f"Warning: Failed to parse Unicode mapping: {e}", file=sys.stderr)
return {}
@@ -122,7 +122,7 @@ def wrap_cff_as_otf(input_path, output_path, tounicode_path=None):
else:
# Fallback to CharStrings keys
charstrings = cff_font.CharStrings
glyph_order = [".notdef"] + [name for name in charstrings.keys() if name != ".notdef"]
glyph_order = [".notdef"] + [name for name in charstrings if name != ".notdef"]
otf.setGlyphOrder(glyph_order)
@@ -186,10 +186,10 @@ def wrap_cff_as_otf(input_path, output_path, tounicode_path=None):
min_lsb = min(min_lsb, lsb)
min_rsb = min(min_rsb, rsb)
max_extent = max(max_extent, extent)
except Exception:
except (AttributeError, TypeError, ValueError):
pass # Some glyphs may not have outlines
except Exception:
except (AttributeError, KeyError, TypeError, ValueError):
pass # Use defaults
widths[glyph_name] = width
@@ -308,14 +308,14 @@ def wrap_cff_as_otf(input_path, output_path, tounicode_path=None):
unicode_val = int(glyph_name[3:], 16)
if unicode_val not in unicode_to_glyph:
unicode_to_glyph[unicode_val] = glyph_name
except Exception:
except ValueError:
pass
elif glyph_name.startswith("u") and len(glyph_name) >= 5:
try:
unicode_val = int(glyph_name[1:], 16)
if unicode_val not in unicode_to_glyph:
unicode_to_glyph[unicode_val] = glyph_name
except Exception:
except ValueError:
pass
# === Create cmap table ===
@@ -476,8 +476,8 @@ def wrap_cff_as_otf(input_path, output_path, tounicode_path=None):
return True
except Exception as e:
print(f"ERROR: Conversion failed: {str(e)}", file=sys.stderr)
except (OSError, ValueError, TypeError, KeyError, RuntimeError) as e:
print(f"ERROR: Conversion failed: {e!s}", file=sys.stderr)
import traceback
traceback.print_exc(file=sys.stderr)
+10 -12
View File
@@ -116,12 +116,11 @@ def write_readme(progress_list: list[tuple[str, int]]) -> None:
for i, line in enumerate(content[2:], start=2):
for progress in progress_list:
language, value = progress
if language in line:
if match := re.search(r"\!\[(\d+(\.\d+)?)%\]\(.*\)", line):
content[i] = line.replace(
match.group(0),
f"![{value}%](https://geps.dev/progress/{value})",
)
if language in line and (match := re.search(r"\!\[(\d+(\.\d+)?)%\]\(.*\)", line)):
content[i] = line.replace(
match.group(0),
f"![{value}%](https://geps.dev/progress/{value})",
)
with open(
os.path.join(os.getcwd(), "devGuide", "HowToAddNewLanguage.md"),
@@ -261,12 +260,11 @@ def compare_files(
# Missing translation (same as default and not ignored)
fails += 1
missing_str_keys.append(default_key)
if default_value != file_value:
if default_key in sort_ignore_translation[language]["ignore"]:
if default_key == "language.direction":
continue
# Remove from ignore if actually translated
sort_ignore_translation[language]["ignore"].remove(default_key)
if default_value != file_value and default_key in sort_ignore_translation[language]["ignore"]:
if default_key == "language.direction":
continue
# Remove from ignore if actually translated
sort_ignore_translation[language]["ignore"].remove(default_key)
if show_missing_keys:
if len(missing_str_keys) > 0:
Regular → Executable
+1 -5
View File
@@ -116,11 +116,7 @@ def _classify_backend(package_name: str) -> str | None:
return "saas"
if p.startswith("stirling.software.proprietary"):
return "proprietary"
if (
p.startswith("stirling.software.SPDF")
or p.startswith("stirling.software.common")
or p.startswith("org.apache.pdfbox")
):
if p.startswith(("stirling.software.SPDF", "stirling.software.common", "org.apache.pdfbox")):
return "core"
return None
Regular → Executable
+1 -1
View File
@@ -80,7 +80,7 @@ def _parse_jacoco_xml(path: Path) -> dict[str, CounterTotals]:
def _bar(pct: float, width: int = 20) -> str:
"""Render a fixed-width ASCII progress bar. Markdown-safe on all consoles."""
filled = int(round(pct / 100.0 * width))
filled = round(pct / 100.0 * width)
return "[" + "#" * filled + "-" * (width - filled) + "]"
Regular → Executable
+1 -1
View File
@@ -150,7 +150,7 @@ def download_pdf(
output_dir.mkdir(parents=True, exist_ok=True)
dest.write_bytes(content)
return url, dest, None
except Exception as exc: # pylint: disable=broad-except
except (OSError, ValueError, requests.RequestException) as exc:
return url, None, str(exc)
Regular → Executable
+6 -6
View File
@@ -118,7 +118,7 @@ def collect_known_signatures(signatures_dir: Path) -> dict[str, dict]:
for json_file in signatures_dir.rglob("*.json"):
try:
payload = load_signature_file(json_file)
except Exception:
except (OSError, ValueError, TypeError, json.JSONDecodeError):
continue
pdf = payload.get("pdf")
for font in payload.get("fonts", []):
@@ -148,9 +148,9 @@ def run_signature_tool(gradle_cmd: str, pdf: Path, output_path: Path, pretty: bo
cmd,
shell=True,
cwd=cwd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
capture_output=True,
text=True,
check=False,
)
if completed.returncode != 0:
raise RuntimeError(f"Gradle Type3SignatureTool failed for {pdf}:\n{completed.stderr.strip()}")
@@ -179,7 +179,7 @@ def extract_fonts_from_payload(payload: dict) -> list[dict]:
def write_report(report_path: Path, fonts_by_signature: dict[str, dict]) -> None:
ordered = sorted(fonts_by_signature.values(), key=lambda entry: entry["signature"])
report = {
"generatedAt": dt.datetime.utcnow().isoformat(timespec="seconds") + "Z",
"generatedAt": dt.datetime.now(dt.UTC).isoformat(timespec="seconds").replace("+00:00", "Z"),
"totalSignatures": len(ordered),
"fonts": ordered,
}
@@ -202,13 +202,13 @@ def main() -> None:
if signature_path.exists() and not args.force:
try:
payload = load_signature_file(signature_path)
except Exception as exc:
except (OSError, ValueError, TypeError, json.JSONDecodeError) as exc:
print(f"[WARN] Failed to parse cached signature {signature_path}: {exc}")
payload = None
else:
try:
run_signature_tool(args.gradle_cmd, pdf, signature_path, args.pretty, REPO_ROOT)
except Exception as exc:
except (OSError, RuntimeError, subprocess.SubprocessError) as exc:
print(f"[ERROR] Harvest failed for {pdf}: {exc}", file=sys.stderr)
continue
payload = load_signature_file(signature_path)
Regular → Executable
+2 -2
View File
@@ -8,7 +8,7 @@ from pathlib import Path
def run(cmd, cwd=None):
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True)
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, check=False)
if result.returncode != 0:
raise RuntimeError(f"Command {' '.join(cmd)} failed: {result.stderr}")
return result.stdout
@@ -55,7 +55,7 @@ def main():
for pdf in sorted(samples_dir.glob("*.pdf")):
try:
output = run(["pdffonts", str(pdf)])
except Exception as exc:
except (OSError, RuntimeError) as exc:
print(f"Skipping {pdf.name}: {exc}")
continue
for font_name, encoding in parse_pdffonts(output):
+2 -4
View File
@@ -48,11 +48,9 @@ SKIP_URL_FRAGMENTS = (
def _is_app_url(url: str) -> bool:
if not url:
return False
if not (url.startswith("http://") or url.startswith("https://")):
if not (url.startswith(("http://", "https://"))):
return False
if any(frag in url for frag in SKIP_URL_FRAGMENTS):
return False
return True
return not any(frag in url for frag in SKIP_URL_FRAGMENTS)
def aggregate(dump_dir: Path) -> dict:
+1 -1
View File
@@ -61,7 +61,7 @@ def cached_version() -> str | None:
if not BIN.exists():
return None
try:
return subprocess.run([str(BIN), "version"], capture_output=True, text=True).stdout.strip()
return subprocess.run([str(BIN), "version"], capture_output=True, text=True, check=False).stdout.strip()
except OSError:
return None
View File
Regular → Executable
View File
View File
Regular → Executable
View File
+7 -8
View File
@@ -11,7 +11,7 @@ import csv
import json
import re
import tomllib
from datetime import datetime
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
@@ -28,7 +28,7 @@ class AITranslationHelper:
try:
with open(file_path, "rb") as f:
return tomllib.load(f)
except (FileNotFoundError, Exception) as e:
except (OSError, tomllib.TOMLDecodeError) as e:
print(f"Error loading {file_path}: {e}")
return {}
@@ -47,7 +47,7 @@ class AITranslationHelper:
golden_truth = self._load_translation_file(self.golden_truth_file)
batch_data = {
"metadata": {
"created_at": datetime.now().isoformat(),
"created_at": datetime.now(UTC).isoformat(),
"source_language": "en-US",
"target_languages": languages,
"max_entries_per_language": max_entries_per_language,
@@ -105,9 +105,8 @@ class AITranslationHelper:
key not in lang_flat
or lang_flat[key] == value
or (isinstance(lang_flat[key], str) and lang_flat[key].startswith("[UNTRANSLATED]"))
):
if not self._is_expected_identical(key, value):
untranslated[key] = value
) and not self._is_expected_identical(key, value):
untranslated[key] = value
return untranslated
@@ -286,7 +285,7 @@ class AITranslationHelper:
golden_flat = self._flatten_dict(golden_truth)
if output_format == "csv":
output_file = Path(f"translations_export_{datetime.now().strftime('%Y%m%d')}.csv")
output_file = Path(f"translations_export_{datetime.now(UTC).strftime('%Y%m%d')}.csv")
with open(output_file, "w", newline="", encoding="utf-8") as csvfile:
fieldnames = ["key", "context", "en_US"] + languages
@@ -322,7 +321,7 @@ class AITranslationHelper:
print(f"Exported to {output_file}")
elif output_format == "json":
output_file = Path(f"translations_export_{datetime.now().strftime('%Y%m%d')}.json")
output_file = Path(f"translations_export_{datetime.now(UTC).strftime('%Y%m%d')}.json")
export_data = {"languages": languages, "translations": {}}
for key, en_value in golden_flat.items():
+4 -4
View File
@@ -10,9 +10,9 @@ import json
import os
import subprocess
import sys
from concurrent.futures import ThreadPoolExecutor
import time
import tomllib
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
@@ -23,7 +23,7 @@ def run_command(cmd, description=""):
print(f"Step: {description}")
print(f"{'=' * 60}")
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=False)
if result.stdout:
print(result.stdout)
@@ -155,7 +155,7 @@ def translate_batches(batch_files, language_code, api_key, timeout=600, model="g
cmd = f'python3 scripts/translations/batch_translator.py "{batch_file}" --language {language_code} --api-key "{api_key}" --model {model}'
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout, check=False)
except subprocess.TimeoutExpired:
print(f"✗ Timed out after {timeout}s: {batch_file}", file=sys.stderr)
return None
@@ -388,7 +388,7 @@ Examples:
except KeyboardInterrupt:
print("\n\n⚠ Translation interrupted by user")
sys.exit(1)
except Exception as e:
except (OSError, ValueError, RuntimeError, subprocess.SubprocessError) as e:
print(f"\n\n✗ Error: {e}")
import traceback
+4 -4
View File
@@ -160,7 +160,7 @@ Return ONLY the translated JSON. No markdown, no explanations, just the JSON obj
print(f"Error: AI returned invalid JSON: {e}")
print(f"Response: {translated_text[:500]}...")
raise
except Exception as e:
except (OSError, ValueError, KeyError, RuntimeError) as e:
print(f"Error during translation: {e}")
raise
@@ -182,11 +182,11 @@ Return ONLY the translated JSON. No markdown, no explanations, just the JSON obj
placeholder_pattern = r"\{[^}]+\}|\{\{[^}]+\}\}"
for key in original.keys():
for key, original_value in original.items():
if key not in translated:
continue
orig_value = str(original[key])
orig_value = str(original_value)
trans_value = str(translated[key])
# Find all placeholders in original
@@ -366,7 +366,7 @@ Examples:
if i < len(input_files):
time.sleep(args.delay)
except Exception as e:
except (OSError, ValueError, KeyError, RuntimeError) as e:
print(f"✗ Failed: {e}")
failed += 1
continue
+4 -3
View File
@@ -79,7 +79,7 @@ def get_language_completion(locales_dir: Path, language: str) -> float | None:
return (translated / total * 100) if total > 0 else 0.0
except Exception as e:
except (OSError, TypeError, KeyError, ValueError, tomllib.TOMLDecodeError) as e:
print(f"Warning: Could not calculate completion for {language}: {e}")
return None
@@ -124,6 +124,7 @@ def translate_language(
cmd,
capture_output=True,
text=True,
check=False,
timeout=timeout * 5, # Overall timeout = 5x per-batch timeout
)
@@ -144,8 +145,8 @@ def translate_language(
except subprocess.TimeoutExpired:
safe_print(f"[{language}] ✗ Timeout exceeded")
return (language, False, "Timeout exceeded")
except Exception as e:
safe_print(f"[{language}] ✗ Error: {str(e)}")
except (OSError, TypeError, KeyError, ValueError, tomllib.TOMLDecodeError) as e:
safe_print(f"[{language}] ✗ Error: {e!s}")
return (language, False, str(e))
+2 -2
View File
@@ -38,7 +38,7 @@ class CompactTranslationExtractor:
except FileNotFoundError:
print(f"Error: File not found: {file_path}", file=sys.stderr)
sys.exit(1)
except Exception as e:
except (OSError, tomllib.TOMLDecodeError) as e:
print(f"Error: Invalid TOML file {file_path}: {e}", file=sys.stderr)
sys.exit(1)
@@ -51,7 +51,7 @@ class CompactTranslationExtractor:
with open(self.ignore_file, "rb") as f:
ignore_data = tomllib.load(f)
return {lang: set(data.get("ignore", [])) for lang, data in ignore_data.items()}
except Exception as e:
except (OSError, tomllib.TOMLDecodeError, TypeError, AttributeError) as e:
print(
f"Warning: Could not load ignore file {self.ignore_file}: {e}",
file=sys.stderr,
+4 -4
View File
@@ -28,7 +28,7 @@ class TOMLBeautifier:
except FileNotFoundError:
print(f"Error: File not found: {file_path}")
sys.exit(1)
except Exception as e:
except (OSError, tomllib.TOMLDecodeError) as e:
print(f"Error: Invalid TOML in {file_path}: {e}")
sys.exit(1)
@@ -172,11 +172,11 @@ class TOMLBeautifier:
def get_key_order(obj: dict, path: str = "") -> list[str]:
keys = []
for key in obj.keys():
for key, value in obj.items():
new_path = f"{path}.{key}" if path else key
keys.append(new_path)
if isinstance(obj[key], dict):
keys.extend(get_key_order(obj[key], new_path))
if isinstance(value, dict):
keys.extend(get_key_order(value, new_path))
return keys
golden_order = get_key_order(self.golden_structure)
+3 -3
View File
@@ -33,7 +33,7 @@ def get_line_context(file_path, line_num, context_lines=3):
context.append(f"{marker}{i + 1:4d}: {lines[i].rstrip()}")
return "\n".join(context)
except Exception as e:
except (OSError, UnicodeError, IndexError) as e:
return f"Could not read context: {e}"
@@ -56,7 +56,7 @@ def get_character_context(file_path, char_pos, context_chars=100):
"after": after,
"display": f"{before}[{error_char}]{after}",
}
except Exception:
except (IndexError, ValueError):
return None
@@ -90,7 +90,7 @@ def validate_toml_file(file_path):
result["valid"] = True
result["entry_count"] = count_keys(data)
except Exception as e:
except (OSError, tomllib.TOMLDecodeError, TypeError, ValueError) as e:
error_msg = str(e)
result["error"] = error_msg
+4 -5
View File
@@ -31,7 +31,7 @@ class TranslationAnalyzer:
except FileNotFoundError:
print(f"Error: File not found: {file_path}")
sys.exit(1)
except Exception as e:
except (OSError, tomllib.TOMLDecodeError) as e:
print(f"Error: Invalid file {file_path}: {e}")
sys.exit(1)
@@ -51,7 +51,7 @@ class TranslationAnalyzer:
for patterns in [data.get("ignore", [])]
if patterns
}
except Exception as e:
except (OSError, tomllib.TOMLDecodeError, TypeError, AttributeError) as e:
print(f"Warning: Could not load ignore file {self.ignore_file}: {e}")
return {}
@@ -165,9 +165,8 @@ class TranslationAnalyzer:
for key in relevant_keys:
if key in target_flat:
value = target_flat[key]
if not (isinstance(value, str) and value.startswith("[UNTRANSLATED]")):
if key not in untranslated: # Not identical to en-US (unless expected)
properly_translated += 1
if not (isinstance(value, str) and value.startswith("[UNTRANSLATED]")) and key not in untranslated:
properly_translated += 1
completion_rate = (properly_translated / total_keys) * 100 if total_keys > 0 else 0
+6 -6
View File
@@ -12,7 +12,7 @@ import os
import shutil
import sys
import tomllib
from datetime import datetime
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
@@ -39,14 +39,14 @@ class TranslationMerger:
except FileNotFoundError:
print(f"Error: File not found: {file_path}")
sys.exit(1)
except Exception as e:
except (OSError, tomllib.TOMLDecodeError) as e:
print(f"Error: Invalid file {file_path}: {e}")
sys.exit(1)
def _save_translation_file(self, data: dict[str, Any], file_path: Path, backup: bool = False) -> None:
"""Save TOML translation file with backup option."""
if backup and file_path.exists():
backup_path = file_path.with_suffix(f".backup.{datetime.now().strftime('%Y%m%d_%H%M%S')}.toml")
backup_path = file_path.with_suffix(f".backup.{datetime.now(UTC).strftime('%Y%m%d_%H%M%S')}.toml")
shutil.copy2(file_path, backup_path)
print(f"Backup created: {backup_path}")
@@ -64,7 +64,7 @@ class TranslationMerger:
# Convert to sets for faster lookup
return {lang: set(data.get("ignore", [])) for lang, data in ignore_data.items()}
except Exception as e:
except (OSError, tomllib.TOMLDecodeError, TypeError, AttributeError) as e:
print(f"Warning: Could not load ignore file {self.ignore_file}: {e}")
return {}
@@ -264,7 +264,7 @@ class TranslationMerger:
self._set_nested_value(target_data, key, translation)
applied_count += 1
except Exception as e:
except (KeyError, TypeError, ValueError) as e:
errors.append(f"Error setting {key}: {e}")
if applied_count > 0:
@@ -316,7 +316,7 @@ class TranslationMerger:
"source_language": "en-US",
"target_language": target_file.parent.name,
"total_entries": len(untranslated),
"created_at": datetime.now().isoformat(),
"created_at": datetime.now(UTC).isoformat(),
"instructions": 'Translate the "original" values to the target language. Keep the same keys.',
},
"translations": {},
+4 -5
View File
@@ -37,8 +37,8 @@ def validate_translation_file(file_path: Path) -> tuple[bool, str]:
with open(file_path, "rb") as f:
tomllib.load(f)
return True, "Valid TOML"
except Exception as e:
return False, f"Error reading file: {str(e)}"
except (OSError, tomllib.TOMLDecodeError) as e:
return False, f"Error reading file: {e!s}"
def validate_structure(en_us_keys: set[str], lang_keys: set[str], lang_code: str) -> dict:
@@ -139,9 +139,8 @@ def main():
# Validate all languages except en-US
languages = []
for d in locales_dir.iterdir():
if d.is_dir() and d.name != "en-US":
if (d / "translation.toml").exists():
languages.append(d.name)
if d.is_dir() and d.name != "en-US" and (d / "translation.toml").exists():
languages.append(d.name)
results = []
json_errors = []
+5 -6
View File
@@ -40,11 +40,11 @@ def validate_language(en_us_flat: dict[str, str], lang_flat: dict[str, str], lan
"""Validate placeholders for a language against en-US."""
issues = []
for key in en_us_flat:
for key, en_text in en_us_flat.items():
if key not in lang_flat:
continue
en_placeholders = find_placeholders(en_us_flat[key])
en_placeholders = find_placeholders(en_text)
lang_placeholders = find_placeholders(lang_flat[key])
if en_placeholders != lang_placeholders:
@@ -56,7 +56,7 @@ def validate_language(en_us_flat: dict[str, str], lang_flat: dict[str, str], lan
"key": key,
"missing": missing,
"extra": extra,
"en_text": en_us_flat[key],
"en_text": en_text,
"lang_text": lang_flat[key],
}
issues.append(issue)
@@ -127,9 +127,8 @@ def main():
# Validate all languages except en-US
languages = []
for d in locales_dir.iterdir():
if d.is_dir() and d.name != "en-US":
if (d / "translation.toml").exists():
languages.append(d.name)
if d.is_dir() and d.name != "en-US" and (d / "translation.toml").exists():
languages.append(d.name)
all_issues = []
Regular → Executable
+3 -3
View File
@@ -74,7 +74,7 @@ def load_json(path: Path) -> dict[str, object]:
try:
with path.open("r", encoding="utf-8") as handle:
return json.load(handle)
except Exception as exc: # pragma: no cover - fatal configuration error
except (OSError, UnicodeError, json.JSONDecodeError) as exc: # pragma: no cover - fatal configuration error
print(f"ERROR: Failed to load glyph JSON '{path}': {exc}", file=sys.stderr)
sys.exit(2)
@@ -298,7 +298,7 @@ def build_ttf_glyph(glyph: GlyphSource, max_error: float) -> object | None:
try:
glyph_obj = pen.glyph()
except Exception:
except (AttributeError, TypeError, ValueError):
return None
return glyph_obj
@@ -472,7 +472,7 @@ def main() -> None:
units_per_em=args.units_per_em,
cu2qu_error=args.cu2qu_error,
)
except Exception as exc:
except (OSError, ValueError, TypeError, RuntimeError) as exc:
print(f"ERROR: Failed to generate fonts: {exc}", file=sys.stderr)
if otf_output.exists():
otf_output.unlink()
Regular → Executable
+1 -1
View File
@@ -100,7 +100,7 @@ def normalize_source_path(pdf_path: str | None) -> str | None:
try:
source = Path(pdf_path)
rel = source.relative_to(REPO_ROOT)
except Exception:
except ValueError:
rel = Path(pdf_path)
return str(rel).replace("\\", "/")