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.
This commit is contained in:
Ludy87
2026-08-19 00:26:55 +02:00
parent ec3de16c08
commit c835f2da06
29 changed files with 119 additions and 127 deletions
+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.72.3",
"pyright>=1.1.411",
"pytest>=9.1.1",
"referencing>=0.37.0",
"ruff==0.15.5",
"ruff==0.16.2",
]
# 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.2",
"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.72.3"
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/14/4b/4652f0bb085a564982c3e515a0b02bcee21765ddac967bcf152a70d2af14/datamodel_code_generator-0.72.3.tar.gz", hash = "sha256:a20160de09b76d4a293ccba5a9ee5c341b5365890237ac1ff812f420e4c09f3b", size = 1965678, upload-time = "2026-08-10T18:58:41.636Z" }
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/84/fd/9d8a0594aedcc6ee8133b21dd3543d6021842e013058803a69918720d0aa/datamodel_code_generator-0.72.3-py3-none-any.whl", hash = "sha256:4536f6dd12dd86c9c7a78b16ef83ae9aa7a9cbdb8bef8a3b63baa4ad9d3d3829", size = 554226, upload-time = "2026-08-10T18:58:40.087Z" },
]
[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.72.3" },
{ 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.2" },
]
pre-commit = [
{ name = "codespell", specifier = "==2.4.3" },
{ name = "ruff", specifier = "==0.15.5" },
{ name = "ruff", specifier = "==0.16.2" },
{ name = "tomli-w", specifier = "==1.2.0" },
]
tools = [
@@ -2791,27 +2791,27 @@ wheels = [
[[package]]
name = "ruff"
version = "0.15.5"
version = "0.16.2"
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/73/e1/4508a569211b35599016e84ba65c1a992b7a4004b4b6c4bea02a851cba1b/ruff-0.16.2.tar.gz", hash = "sha256:c3d7828d12e8927a6fc65fe38e2c2541b9e762d360a1786d752cb1b8883b3c9c", size = 4885811, upload-time = "2026-08-07T13:31:01.432Z" }
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/14/57/db19951540f98859c956b50bdb4d31089b4d91e9f15e2968e7d5193806d5/ruff-0.16.2-py3-none-linux_armv6l.whl", hash = "sha256:3c8de4cf2181f01d57946d87d777aa52916976fc09942aed89938fab5e013318", size = 10847925, upload-time = "2026-08-07T13:30:14.468Z" },
{ url = "https://files.pythonhosted.org/packages/13/5a/995fe85a8470d3e391ac0f7fa8054bb454eaf33ee138196d6172ed1079c0/ruff-0.16.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a48cc05c6fbc811ca81b5d7ba95375affea6582d1b8024e455e41afbbf55344", size = 11072662, upload-time = "2026-08-07T13:30:18.143Z" },
{ url = "https://files.pythonhosted.org/packages/32/53/370d767c61c71a971a4ace36703a7ecd8c393956349a7325d7fab2b56827/ruff-0.16.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a2c0d14fcbb26c91f0f867a6dc9bd71bbc30b1b6151829c884f23faeab2e5700", size = 10566771, upload-time = "2026-08-07T13:30:20.899Z" },
{ url = "https://files.pythonhosted.org/packages/85/d6/9d96948caf5a632be62d62202d5ec914d6856f204fd79eb036e5915e79ea/ruff-0.16.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:335c621622c4650330be50842561c6586ac6971bb8ab5407fe34dcc9efb16bbe", size = 10975825, upload-time = "2026-08-07T13:30:23.517Z" },
{ url = "https://files.pythonhosted.org/packages/3b/92/ea87129b3414acb0b5770563779c51804d37ac67675c7ba35447ddb14773/ruff-0.16.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20e66910f2c37cc753f9ef6580c914a621b80c4fa3549d3e3521e29d0f5bfc3f", size = 10649437, upload-time = "2026-08-07T13:30:26.097Z" },
{ url = "https://files.pythonhosted.org/packages/ac/43/f8f291dcd4af5bb7872b74fdfa41a7cd7c856ca1d4069670971cf1b9f5cb/ruff-0.16.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7e36fbfba65510548156902bcf1350a979a958ce0347ce0f90d73894036b39f", size = 11446761, upload-time = "2026-08-07T13:30:28.752Z" },
{ url = "https://files.pythonhosted.org/packages/71/4a/ef991fb2fcf516ab71f0808adcdd8da5e18c8cde447f4ceaf5f47a5132a5/ruff-0.16.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f0eab35f80df8f134aae5d1630e751901321d317cc8e50dc39e36fa3ed34cd12", size = 12336364, upload-time = "2026-08-07T13:30:31.468Z" },
{ url = "https://files.pythonhosted.org/packages/f3/24/f615e74f307e6ca0e56a482872477b856c70d530aa356abfb6dfe5ca8a80/ruff-0.16.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40ea8c0594feb894e89c8c61ab9c103d38b0ea72dfde6c594107147ca31b1140", size = 11630720, upload-time = "2026-08-07T13:30:34.426Z" },
{ url = "https://files.pythonhosted.org/packages/c5/d3/8ef50149e8412a77f7ab409efdef0e2b23803707a3863da4fc64cb23d459/ruff-0.16.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab3d62dde0b19facdd632008cc4827fc28ada7736c6bd35ab6f1050f0bfed53f", size = 11466130, upload-time = "2026-08-07T13:30:36.958Z" },
{ url = "https://files.pythonhosted.org/packages/dd/a7/a19334985c4dea8c381981fa252cd854c7ee52dc4b1686dc16f4a911c702/ruff-0.16.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:e43e1f5b8388da9eca1b9e88328d47a5cec794633ccf6f7484ac2dd15eee92c0", size = 11523634, upload-time = "2026-08-07T13:30:39.822Z" },
{ url = "https://files.pythonhosted.org/packages/6e/6c/96d192b0e742412ceda08c0a50f9669b253dde9fd6a60ea1a10c9fa79a63/ruff-0.16.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c24788a980581e1d7ea3a0cbe4344c4fbeb0a6a9b1f4713aa46bb104f8294690", size = 10949807, upload-time = "2026-08-07T13:30:42.745Z" },
{ url = "https://files.pythonhosted.org/packages/fa/51/e26599ceca11e79ee255c7df515995561edf87e9ca1893284e44d98f5a86/ruff-0.16.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:81806b08329130005dd4a8a8394a0c9da8c6f4cafb16ba438d2a2ee6a18bedf1", size = 10646891, upload-time = "2026-08-07T13:30:45.522Z" },
{ url = "https://files.pythonhosted.org/packages/68/01/800c4b1f97bc8d7c6029e06b1f20473a3cf1e13c4933d8f3342add83fc55/ruff-0.16.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4ce4e02bad779bef557f541a1b31f20d6abeae1cc05ed1b1ac019d4ffd1044c8", size = 11162063, upload-time = "2026-08-07T13:30:48.131Z" },
{ url = "https://files.pythonhosted.org/packages/e4/d0/1477ea50fc5a0d4b0b71d1d63d50770bdd794d90b43e37a7618e63ec9894/ruff-0.16.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e0422abdf70070255fc4073ce9dfc814cc03db577013761ddd09bc1e4a9a4fbd", size = 11556038, upload-time = "2026-08-07T13:30:50.686Z" },
{ url = "https://files.pythonhosted.org/packages/b8/76/a7776f32048d991e16d4fa8ff91790b877342d3596cc3ed04acdbf1aaedc/ruff-0.16.2-py3-none-win32.whl", hash = "sha256:bf3a63d78fb39f4bf5ac8ae52051c5520505301abe19ba4e204c453b3f09bb0b", size = 10872850, upload-time = "2026-08-07T13:30:53.471Z" },
{ url = "https://files.pythonhosted.org/packages/00/0d/929c800d920e61397d82a01b60bffc68da3052c17d31de59efaad2e4ed75/ruff-0.16.2-py3-none-win_amd64.whl", hash = "sha256:bcabe2f6d0fc7819f1431793005af4e4de7371927d037345bf941252b195b9fa", size = 12023338, upload-time = "2026-08-07T13:30:56.193Z" },
{ url = "https://files.pythonhosted.org/packages/5b/6c/93e26c22c5f78ff87363e07da49c84955affbeb1098bd1936bf3b3f293bf/ruff-0.16.2-py3-none-win_arm64.whl", hash = "sha256:d614e95cedf38a2053fd351c55b103ba30d017d61688fdbfd40ee0412852a99f", size = 11374065, upload-time = "2026-08-07T13:30:58.775Z" },
]
[[package]]
+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:
+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
+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) + "]"
+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)
+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)
+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
@@ -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": {},
@@ -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 = []
@@ -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 = []
+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()
+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("\\", "/")