diff --git a/TEMPLATE.md b/TEMPLATE.md index 170ba87..b211219 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -81,7 +81,9 @@ git config core.hooksPath .githooks The version reads as something like `0.1.dev3+g1a2b3c4`. `hatch-vcs` derives the version from git tags, and a new repository has none. The first release creates the first tag, after which the version is ordinary. Do not reintroduce a static `version =` into `pyproject.toml`. -The template requires the published Groundworkers 0.7 stack: `groundworkers[tui]>=0.7,<1` and `oa-configurator>=2,<3`. Import secret annotations such as `Sensitive` and `Secret` from `oa_configurator.refs`. Keep these dependency floors when bootstrapping a new plugin so its wheel resolves to a compatible host and configurator. +The template requires the published Groundworkers 0.10 stack: `groundworkers[tui]>=0.10,<1` and `oa-configurator>=2.1,<3`. Import secret annotations such as `Sensitive` and `Secret` from `oa_configurator.refs`, and the lint types from `groundworkers.plugins` (re-exported by `linting.py`). Keep these dependency floors when bootstrapping a new plugin so its wheel resolves to a compatible host and configurator. A plugin that reads DuckDB vocabularies or DuckDB VSS embeddings also needs the `duckdb` extra: `groundworkers[tui,duckdb]>=0.10,<1`. + +Check a configured plugin's settings without starting the server: `groundworkers lint-plugins --config-path /path/to/config.toml` prints a JSON report and exits 1 on any error. ## Improving the template diff --git a/pyproject.toml b/pyproject.toml index 4a6786e..9c4ad82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,8 +5,8 @@ description = "Template for a Groundworkers plugin." readme = "README.md" requires-python = ">=3.12" dependencies = [ - "groundworkers[tui]>=0.7,<1", - "oa-configurator>=2,<3", + "groundworkers[tui]>=0.10,<1", + "oa-configurator>=2.1,<3", "pydantic>=2,<3", ] diff --git a/src/gw_plugin_template/linting.py b/src/gw_plugin_template/linting.py index 0385afc..4e6da3e 100644 --- a/src/gw_plugin_template/linting.py +++ b/src/gw_plugin_template/linting.py @@ -1,67 +1,12 @@ -"""Groundworkers' shared lint types, with a load-safe fallback for old hosts.""" +"""Groundworkers' shared lint types, re-exported for this plugin's modules. -from __future__ import annotations +The host has defined these since Groundworkers 0.8, so they are imported, not copied. +""" -from dataclasses import dataclass -from enum import StrEnum - -from groundworkers import plugins as _host_plugin_contract - - -class PluginLintSeverity(StrEnum): - ERROR = "error" - WARNING = "warning" - INFO = "info" - - -@dataclass(frozen=True) -class PluginLintDiagnostic: - code: str - severity: PluginLintSeverity - message: str - path: str | None = None - - def as_dict(self) -> dict[str, str | None]: - return { - "code": self.code, - "severity": self.severity.value, - "message": self.message, - "path": self.path, - } - - -@dataclass(frozen=True) -class PluginLintReport: - plugin: str - diagnostics: tuple[PluginLintDiagnostic, ...] = () - - @property - def valid(self) -> bool: - return not any( - item.severity is PluginLintSeverity.ERROR for item in self.diagnostics - ) - - def as_dict(self) -> dict[str, object]: - return { - "plugin": self.plugin, - "valid": self.valid, - "diagnostics": [item.as_dict() for item in self.diagnostics], - } - - -# Use one complete host contract when available. Keep the local types together -# on older or partially upgraded hosts so reports never mix incompatible classes. -_LOCAL_LINT_TYPES = (PluginLintSeverity, PluginLintDiagnostic, PluginLintReport) - - -def _host_lint_types() -> tuple[type, type, type]: - names = ("PluginLintSeverity", "PluginLintDiagnostic", "PluginLintReport") - host_types = tuple(getattr(_host_plugin_contract, name, None) for name in names) - if all(isinstance(item, type) for item in host_types): - return host_types # type: ignore[return-value] - return _LOCAL_LINT_TYPES - - -PluginLintSeverity, PluginLintDiagnostic, PluginLintReport = _host_lint_types() +from groundworkers.plugins import ( + PluginLintDiagnostic, + PluginLintReport, + PluginLintSeverity, +) __all__ = ["PluginLintDiagnostic", "PluginLintReport", "PluginLintSeverity"] diff --git a/src/gw_plugin_template/plugin.py b/src/gw_plugin_template/plugin.py index 4d90968..77e037d 100644 --- a/src/gw_plugin_template/plugin.py +++ b/src/gw_plugin_template/plugin.py @@ -143,9 +143,12 @@ def build( path = None # Prefer a resource Groundworkers already resolved. Only resolve your - # own when the plugin genuinely needs a different database. + # own when the plugin genuinely needs a different database. Read the + # shared CDM through `cdm_session_factory`: it routes vocabulary and + # primary roles, including on split-vocabulary stacks, where + # `cdm_engine` cannot reach the vocabulary. database_source = "Shared Groundworkers CDM database" - available = context.cdm_engine is not None + available = context.cdm_session_factory is not None if config.catalogue_db is not None: context.resolver.resolve_database(config.catalogue_db) database_source = f"Configured database: {config.catalogue_db}" diff --git a/tests/conftest.py b/tests/conftest.py index 3a2f18d..f316b70 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -57,6 +57,7 @@ class FakeContext: embedding_backend_factory: object | None = None embedding_model_backend_factory: object | None = None chat_backend_factory: object | None = None + cdm_session_factory: object | None = "session-factory" @pytest.fixture diff --git a/tests/test_plugin_contract.py b/tests/test_plugin_contract.py index 2e99d0a..56ac94b 100644 --- a/tests/test_plugin_contract.py +++ b/tests/test_plugin_contract.py @@ -3,17 +3,16 @@ from __future__ import annotations from importlib.metadata import entry_points -from types import SimpleNamespace -from groundworkers import plugins as host_plugin_contract from groundworkers.plugins import ( GroundworkersPluginConfigUI, GroundworkersPluginReadiness, GroundworkersPluginSetup, + PluginLintReport, + lint_plugin_config, ) from oa_configurator import PackageConfigBase -from gw_plugin_template import linting as linting_contract from gw_plugin_template.config import GwPluginTemplateConfig from gw_plugin_template.linting import PluginLintSeverity from gw_plugin_template.plugin import plugin @@ -105,31 +104,14 @@ def test_config_linter_accepts_a_valid_catalogue_snapshot(tmp_path) -> None: assert report.diagnostics == () -def test_host_accepts_template_lint_report_when_contract_is_available() -> None: - host_report_type = getattr(host_plugin_contract, "PluginLintReport", None) - host_dispatch = getattr(host_plugin_contract, "lint_plugin_config", None) - if host_report_type is None or host_dispatch is None: - import pytest +def test_host_accepts_template_lint_report() -> None: + report = lint_plugin_config(plugin, GwPluginTemplateConfig()) - pytest.skip("Installed Groundworkers does not expose plugin linting yet.") - - report = host_dispatch(plugin, GwPluginTemplateConfig()) - - assert isinstance(report, host_report_type) + assert isinstance(report, PluginLintReport) assert report.plugin == plugin.name assert report.valid -def test_partial_host_lint_contract_uses_local_types_as_a_set(monkeypatch) -> None: - monkeypatch.setattr( - linting_contract, - "_host_plugin_contract", - SimpleNamespace(PluginLintReport=object), - ) - - assert linting_contract._host_lint_types() == linting_contract._LOCAL_LINT_TYPES - - def test_build_returns_none_for_foreign_config(fake_context) -> None: """`register` is never called when `build` declines.""" diff --git a/uv.lock b/uv.lock index 4d5fa0c..74e2771 100644 --- a/uv.lock +++ b/uv.lock @@ -240,11 +240,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.7.22" +version = "2026.10.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/11/b524cbd9bbdaca58608e4cf4053fd463417549e1959fa8d3aa18319f9555/certifi-2026.10.23.tar.gz", hash = "sha256:a9895e6e2758b371e0545e321a3a8fd1fcfd9829ada8824a936dfb16aaaf36cd", size = 136225, upload-time = "2026-10-23T11:36:16.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, + { url = "https://files.pythonhosted.org/packages/32/d1/a3652fe2c19134d0d15922177821d863cbd7e842d53a21bbe7e8e3a1b5ac/certifi-2026.10.23-py3-none-any.whl", hash = "sha256:e9b54de76e530bb6cf0ccd036787103acb58fd42ba930ea8bf948ddbaa2f61d1", size = 135082, upload-time = "2026-10-23T11:36:14.763Z" }, ] [[package]] @@ -977,7 +977,7 @@ wheels = [ [[package]] name = "groundworkers" -version = "0.7.0" +version = "0.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastapi" }, @@ -997,9 +997,9 @@ dependencies = [ { name = "sqlalchemy" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/85/d7ed2aa2316feddd1cdcca6da87690ac96f63435b0892cb5d2db1b3a49d7/groundworkers-0.7.0.tar.gz", hash = "sha256:965f66b52081e791bfb68332c2b3bfe5546a6cb4e096516afc38a0ccaeff7a07", size = 1286181, upload-time = "2026-10-14T00:42:04.554Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/fe/6afbd445eee26a7510e4fc04edaad71687c5077f1c94bea6f66d01d71963/groundworkers-0.10.0.tar.gz", hash = "sha256:f7b5cc97dcab6d10a742cca2818dff11e6dde5ec679e19f0a534dfcf5be63abf", size = 1765674, upload-time = "2026-11-06T14:00:02.849Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/70/4a48f845c20fea0073ba80cefb21f25a63151a04e1202a725b5a61855671/groundworkers-0.7.0-py3-none-any.whl", hash = "sha256:e550470b202367dbac22fa07cbd222744035e2b36f21e8ec521f4ede87f6745f", size = 348804, upload-time = "2026-10-14T00:42:02.642Z" }, + { url = "https://files.pythonhosted.org/packages/97/6c/e4bbf6ebcdc8b87b9375cdcdfb21f8d9016901633dd55364d3eb1ea93e79/groundworkers-0.10.0-py3-none-any.whl", hash = "sha256:d363b6e4faf34732c2b2880c20d619acfa4b8832f1bf24df8ce76c1b1de24a36", size = 336452, upload-time = "2026-11-06T14:00:01.025Z" }, ] [package.optional-dependencies] @@ -1027,8 +1027,8 @@ dev = [ [package.metadata] requires-dist = [ - { name = "groundworkers", extras = ["tui"], specifier = ">=0.7,<1" }, - { name = "oa-configurator", specifier = ">=2,<3" }, + { name = "groundworkers", extras = ["tui"], specifier = ">=0.10,<1" }, + { name = "oa-configurator", specifier = ">=2.1,<3" }, { name = "pydantic", specifier = ">=2,<3" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=9,<10" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.4" }, @@ -2115,7 +2115,7 @@ wheels = [ [[package]] name = "oa-configurator" -version = "2.0.0" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -2126,9 +2126,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/62/64bf398b8fe381d138446199be69bc437ef93fdb70aab48cebf51467f6ea/oa_configurator-2.0.0.tar.gz", hash = "sha256:26406e90aeadd4c747646763b80f16025c4c6638afec687517788bc537d0d581", size = 278382, upload-time = "2026-10-10T22:48:42.275Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/5a/e1061864a6f9abc9c49e26ab84a51dfc8f40616906c853e01ccbc022b477/oa_configurator-2.1.0.tar.gz", hash = "sha256:89e33ab58bd428081b1c1c8ce1b4ad7d4a7d4b4627bcbc20d8cd6502811f627c", size = 298778, upload-time = "2026-10-26T21:01:07.87Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/9b/99f0073d97de3545d48cf28c8f39b724d543baf83e8af8d0ba5c8bffbb01/oa_configurator-2.0.0-py3-none-any.whl", hash = "sha256:2186beb5567850ba5bfe4950c2820b909d3746c6fa233a6074a85396a343af35", size = 118027, upload-time = "2026-10-10T22:48:40.571Z" }, + { url = "https://files.pythonhosted.org/packages/59/44/38cdd910fe7544b118d7a5fc741417f1be0a4a95fdb754d8d5f3a016b9f9/oa_configurator-2.1.0-py3-none-any.whl", hash = "sha256:44c2fc43cf01398e598fc4d0fd4b17f9a3090cc35b6a378cdaf8e40fbfab0a0a", size = 126679, upload-time = "2026-10-26T21:01:05.893Z" }, ] [[package]] @@ -2202,7 +2202,7 @@ wheels = [ [[package]] name = "omop-alchemy" -version = "2.0.0" +version = "3.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "oa-configurator" }, @@ -2214,14 +2214,14 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/4b/a925c1db558070a4a807a38456a90dab33ab766e18cd3de890dae94295ca/omop_alchemy-2.0.0.tar.gz", hash = "sha256:403848b2c377ef6200119f4d5280a1b7e1abe0c42e8681fe16a74ee87fa82259", size = 734827, upload-time = "2026-10-10T23:39:29.99Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/a4/330e86e324759626cbb48b6faeaa7bd903a480b4687515e486bdcdfaa3e2/omop_alchemy-3.0.0.tar.gz", hash = "sha256:084a873a84aa378614ad75b7791de6d5fa8dfdc7800484a6c0713d9f36f01c01", size = 757452, upload-time = "2026-11-05T06:16:28.04Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/d2/2c271ce7d09b978bf812b698baf9000f1bc56afa2d1915b58c9b079875db/omop_alchemy-2.0.0-py3-none-any.whl", hash = "sha256:17cdbf3d901f23da339b7e4d4598f22f178499733fa326884de6e0de40c78f02", size = 298023, upload-time = "2026-10-10T23:39:28.182Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b9/674e2dfac874a5c078f30e2cb63453c3bb40b78bbeea52bfbbaa7e139864/omop_alchemy-3.0.0-py3-none-any.whl", hash = "sha256:d369a452456364b4d679741cd2e6f2151aa4a070274714566ca56df019a9eeae", size = 307688, upload-time = "2026-11-05T06:16:26.131Z" }, ] [[package]] name = "omop-emb" -version = "3.0.0" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "h5py" }, @@ -2236,14 +2236,14 @@ dependencies = [ { name = "typing-extensions" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/68/54b0a9538da687f38e995d16fb335d585cd7dbca423be5874fb3f07217ae/omop_emb-3.0.0.tar.gz", hash = "sha256:6ceabae683e9f36cc9250fb0a62a6074a588b5ff5594e8fd8b06c1dcec5a826e", size = 249683, upload-time = "2026-10-11T00:20:20.468Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/5c/99fd350084bfc4b01ae4a257e8cb9b3c97b637f3ea1bc5000587e4430e63/omop_emb-3.1.0.tar.gz", hash = "sha256:8ebde36de4c269fd0c67d41bfda1b0cfee5ef4ac6fdd8912a42112441a54ebbc", size = 290209, upload-time = "2026-11-05T21:26:40.34Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/a6/e564d7d43b294c592a93f1da737a7e836cb50f592a9127e6b10ef4383471/omop_emb-3.0.0-py3-none-any.whl", hash = "sha256:0e78c7c0ef01a71d1a62498d9fab494aab81d7fa40aa1e712ec097530c5df730", size = 94080, upload-time = "2026-10-11T00:20:18.782Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ea/ef867376e7ad86acadbca8d23fba244dddbd015847f7578ec47f11cee52d/omop_emb-3.1.0-py3-none-any.whl", hash = "sha256:a0c11f3227d01d86d736c8cc009d79f66997d105189462af10651bbc1735a901", size = 113798, upload-time = "2026-11-05T21:26:39.019Z" }, ] [[package]] name = "omop-graph" -version = "3.0.0" +version = "4.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "oa-configurator" }, @@ -2254,9 +2254,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2d/40/609efc228073c4d712a4f0328229fa1ffaef317ce180cd52189ff607090b/omop_graph-3.0.0.tar.gz", hash = "sha256:2184dab10323a541b400d42c94e603f33286b36b0d66a7797ce4f35b64d82104", size = 470552, upload-time = "2026-10-11T00:33:15.749Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/8c/24f71eaf8fca19f18467dbdbb22943a40744bc42d6f5bb0bae1340b47a1e/omop_graph-4.0.0.tar.gz", hash = "sha256:d7f31d667150b0904beec40bcb93ea73b6b101bc2c71f6c87c0004a8d0f9f161", size = 478365, upload-time = "2026-11-06T04:38:47.823Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/14/e86bfac20c51dc4f70596d5b69d1107093b96356543157d7db1a555ec262/omop_graph-3.0.0-py3-none-any.whl", hash = "sha256:7bf0c99f325ba0796c8b0ff2239e68657a08686dcdd0b34aeeda750bd4566e44", size = 126600, upload-time = "2026-10-11T00:33:13.968Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a7/640ccdc1899d5d47d2fcabfcb18ae6b6683e4c56292952e4c40649d2656a/omop_graph-4.0.0-py3-none-any.whl", hash = "sha256:27654e1d2ecb9e58b20bd9d1d263b7e056b8cad65de4434766f81a7189b09a0f", size = 127067, upload-time = "2026-11-06T04:38:45.983Z" }, ] [[package]] @@ -2352,18 +2352,17 @@ wheels = [ [[package]] name = "orm-loader" -version = "2.0.0" +version = "3.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chardet" }, { name = "oa-configurator" }, - { name = "pandas" }, { name = "pyarrow" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/cd/b3a29d383b80e1c282a3ee12a28f00b5608802763dd98ed7b3adc637e630/orm_loader-2.0.0.tar.gz", hash = "sha256:73232ecb5f1474738e54f30fae89999620d57cc41456ae799669a74c690a76b3", size = 171586, upload-time = "2026-10-10T23:22:15.966Z" } +sdist = { url = "https://files.pythonhosted.org/packages/16/e4/3535ecb78ba058de22308d1b2abd6d143586c22a39e15526486cdabb845b/orm_loader-3.0.0.tar.gz", hash = "sha256:865d14bf853a312acb6f43c6d63825f73436f41d219aae7caae086da915eb5cd", size = 223323, upload-time = "2026-11-05T04:55:59.646Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/61/6687b9c1602deb1c364d4653c641698e3de298b9c4f5dc98c59698455ac7/orm_loader-2.0.0-py3-none-any.whl", hash = "sha256:a118ddc3fcb611d658b7be52f616b641804ae65067efd05753d1bc14a03b52c9", size = 74959, upload-time = "2026-10-10T23:22:14.645Z" }, + { url = "https://files.pythonhosted.org/packages/a0/96/22b96702ab51b867994839589408509a0e4173d0d5b27d4d7cf1c8d89ed6/orm_loader-3.0.0-py3-none-any.whl", hash = "sha256:0f91ce37c1bc930113d9476130521cb74b755010705173c281cf1d6262a1a4ef", size = 96094, upload-time = "2026-11-05T04:55:57.896Z" }, ] [[package]] @@ -3390,11 +3389,11 @@ wheels = [ [[package]] name = "setuptools" -version = "84.0.0" +version = "84.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/b3/c19426f783614d625699a4b247faf628ea9ab1db228674491767b417f9e1/setuptools-84.2.1.tar.gz", hash = "sha256:3ff8b3b5492a0aa66960f4419365a86b84e013070d4313e4d4a6894f5d64d6bc", size = 1230289, upload-time = "2026-10-20T19:01:37.742Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" }, + { url = "https://files.pythonhosted.org/packages/ef/55/6dc6d6bb953bdfc52519a0059592c52b6e3b8ad51cf8256dffceb55b0610/setuptools-84.2.1-py3-none-any.whl", hash = "sha256:ca140b45d5325a9478f880e351484f62fef15f336462e449ca0c03c873d589c2", size = 876263, upload-time = "2026-10-20T19:01:35.678Z" }, ] [[package]] @@ -3740,19 +3739,52 @@ wheels = [ [[package]] name = "tornado" -version = "6.5.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/06/61/53d562a57b28c08eda40b258c0f975e360541943ad7c7bef897a40caafda/tornado-6.5.10.tar.gz", hash = "sha256:a6b1ccd08c04b4a06fb5aeb381be99de5ad1e5375c1785e31d78c880feb57687", size = 537910, upload-time = "2026-09-15T13:47:48.73Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/5b/ff5fc58fa2427c30dea74c90053f4fc5eda1e7f3833ed3ecc7147fe2b311/tornado-6.5.10-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9261783640e23258694a9ff0795df430a5a7b0a651d3dd53dd0969ad6be16da7", size = 465883, upload-time = "2026-09-15T13:47:35.463Z" }, - { url = "https://files.pythonhosted.org/packages/ad/f5/cd7be26c34a3315532f3aef5f092465da8f59c334dd439d3c14aaef16461/tornado-6.5.10-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:83e6cf438b106c6b3852d70960967bb1b70c87438050dca0981e4b9aa751a4c1", size = 464046, upload-time = "2026-09-15T13:47:37.178Z" }, - { url = "https://files.pythonhosted.org/packages/60/33/df6d7d04854a58619f8349a51e3edb138324130a7562b0bb21f115bb940f/tornado-6.5.10-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bdf942448169e5336451d0494d7e3d81cfa726d5aa312affdc4682dd62a62f6d", size = 467096, upload-time = "2026-09-15T13:47:38.559Z" }, - { url = "https://files.pythonhosted.org/packages/29/17/cc35dff68272d685cffd8600ffafbd8067e7d05e7348d9f80caddffbbd5f/tornado-6.5.10-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69acca6501eed74582b76dbbceee2a91613f54728e3e418346000d7103101676", size = 468067, upload-time = "2026-09-15T13:47:40.085Z" }, - { url = "https://files.pythonhosted.org/packages/c3/01/6e5349b4e1a53a4b4972a6716785e1fe7407f312063c3972690af8ff301b/tornado-6.5.10-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:66aaa3f57d30c6e6becee83ff28055d5930ac724214bde99393eefda83d5e015", size = 467901, upload-time = "2026-09-15T13:47:41.576Z" }, - { url = "https://files.pythonhosted.org/packages/28/5e/b4facf94370dba006819c8d304376f8b9fbec6b935b5e51bf45823a9790b/tornado-6.5.10-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4bd192b959f9128fb99b8898148070ba4574c9589b78bce42d1851131fe85828", size = 467308, upload-time = "2026-09-15T13:47:43.145Z" }, - { url = "https://files.pythonhosted.org/packages/56/ae/047938e828cafc8eca4c908fafb6588fee944e3af39a0af9d7b602499ae5/tornado-6.5.10-cp39-abi3-win32.whl", hash = "sha256:302eb1e0e3e159314eb591920529fdea80acca92df5510a2cec5bbd4f099ec72", size = 468387, upload-time = "2026-09-15T13:47:44.556Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d4/5901517f05affd752490f6a654ba31b7474664e8dd80bd045a00c220bd88/tornado-6.5.10-cp39-abi3-win_amd64.whl", hash = "sha256:37ae8f150cecfdbf747fc4e12f5e9a97ecd8cf1d4cdb3f119e2de84b11196918", size = 468828, upload-time = "2026-09-15T13:47:45.961Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1a/fd497f3a7f7b74bb04f4b94536b5c9f80742b5d50501fd27977652ddec16/tornado-6.5.10-cp39-abi3-win_arm64.whl", hash = "sha256:ce045d3c298fddd30e89a2777f97039d1b641eb9518ac7b26a4721903539c694", size = 467847, upload-time = "2026-09-15T13:47:47.283Z" }, +version = "6.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/2b/8ff2b41620cbba0eeb0862fed21dcaa5a2ad36c6dd9bb465f9ff7a147be2/tornado-6.6.1.tar.gz", hash = "sha256:28e6311402af59c8b02e3fa0f5e7d8eb465b25081381803b127d97e6a4395d5a", size = 623388, upload-time = "2026-11-09T21:09:22.71Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/d3/ef3a4d38d1a72c52ad07483ed760dbb93027d28f19c3a2d98148abdd63e1/tornado-6.6.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8d3e6acde5a80d13b13b5e4c7c226ca682f31d0a62b6c33cea038714bc2de1b", size = 533249, upload-time = "2026-11-09T21:08:22.475Z" }, + { url = "https://files.pythonhosted.org/packages/38/4d/c65ea949a3cdc185f1afac8c337074d4f2d029b48293d2db4d2c3dea4849/tornado-6.6.1-cp311-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1b6ff57cc69b339f8feb9e7b567b0e0eacb16bfaf234159496623ef7a67ba640", size = 534470, upload-time = "2026-11-09T21:08:24.27Z" }, + { url = "https://files.pythonhosted.org/packages/03/ff/20911f85dd399b56761496e8138810e8608ea25f0912377f4205d52af853/tornado-6.6.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6ce2da215810d56539fa6cac77d9e27f4d8139c6868c53436b2fe933f43c79", size = 535446, upload-time = "2026-11-09T21:08:25.625Z" }, + { url = "https://files.pythonhosted.org/packages/be/70/a4a159dd4f1fef39ff8eadae655b133a85722b39087a89e21ce463af9832/tornado-6.6.1-cp311-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8cd28b2cacbe63d53e28a38546a3492b488417eeb3f7ecf10954476fc2508882", size = 535664, upload-time = "2026-11-09T21:08:27.191Z" }, + { url = "https://files.pythonhosted.org/packages/ed/d5/db70d5d23d3b4eaea0b26b710f28d2ca387bb44e68ccfede60ba3bbc2096/tornado-6.6.1-cp311-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:25b2619274bca77d0eb7b4d96dcf2d1142bb9bf172ad6a7491a1e1f82e6e48fa", size = 534660, upload-time = "2026-11-09T21:08:28.78Z" }, + { url = "https://files.pythonhosted.org/packages/f7/f6/0528ee039266902e707edd9996ee3ce7364dc1baf08bed585d8e6fb3ccd0/tornado-6.6.1-cp311-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77dd6e0c659fdc45824e0746988ac5c060c2a9fee9b8cbbcdb999b3f29d3cd10", size = 532834, upload-time = "2026-11-09T21:08:30.432Z" }, + { url = "https://files.pythonhosted.org/packages/a6/bb/b733df2d487a78283080663bd6d96955121b58b6d445d9cb832607da9786/tornado-6.6.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b62fded687e55acaf9e36c1230b006066a35b3f6df093ba1fd3b2ff592070e1d", size = 535289, upload-time = "2026-11-09T21:08:31.692Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7c/c81cb83f2bbb5740fcba1eb83d794b6d6fab2063b16a475cc9039266091e/tornado-6.6.1-cp311-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:ecef88054d8a77878f61061ff67dfc5e1c17d1f7334890ab508b310e8e68ee46", size = 535382, upload-time = "2026-11-09T21:08:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ae/c499da4cc8be1c1f31a5d8df2328f2aae091de2db13302ad5c20ed704ec4/tornado-6.6.1-cp311-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:ca95e762df1ca761f330e3f2a034c15ac144569844b52527a5a9eca9f524c0f1", size = 534534, upload-time = "2026-11-09T21:08:34.57Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/6b163c9e58ca9df0c941bda9ba2dcabbd87ecb81c5fa546f54eeac689232/tornado-6.6.1-cp311-abi3-musllinux_1_2_s390x.whl", hash = "sha256:8cae3d8f2585be01670da9fd5010c61724c51b61ecd4c06820e95874739daf7c", size = 534728, upload-time = "2026-11-09T21:08:35.828Z" }, + { url = "https://files.pythonhosted.org/packages/18/11/b5107ac6a278387635d73d283e7ab5a5c76fb8ccf402efa57006ea25852f/tornado-6.6.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6596f3e939962f13099ce8d1a96b83226e908ca369b193370a6efa84585a1601", size = 534676, upload-time = "2026-11-09T21:08:37.209Z" }, + { url = "https://files.pythonhosted.org/packages/65/df/0574c4479c565d9233def796afc9179fa840f41f8c240f740609f746da67/tornado-6.6.1-cp311-abi3-win32.whl", hash = "sha256:0f0f596a0dab6826aa21d103c7bc242e3fb7cfc99c5c3d44a35ba8c83abbdca4", size = 535565, upload-time = "2026-11-09T21:08:38.518Z" }, + { url = "https://files.pythonhosted.org/packages/36/57/35439a779582e3b19c98e799b6a41519fb3bdaa3a21b7a94ce4dd7b3b732/tornado-6.6.1-cp311-abi3-win_amd64.whl", hash = "sha256:5cb82ca38596e1350fceaf994f287847f82f7b8436380d36ba0937a03107d0ee", size = 535806, upload-time = "2026-11-09T21:08:39.892Z" }, + { url = "https://files.pythonhosted.org/packages/89/b8/2cb42845af01ea7e70ac23e6f6fe2f26b9b158bce186c6991c057c9abe1d/tornado-6.6.1-cp311-abi3-win_arm64.whl", hash = "sha256:58d2f47e7b54fded3407dbbbe4e59d29ca627f85bf6c0f21f0b151c6c7fc9c53", size = 535825, upload-time = "2026-11-09T21:08:41.339Z" }, + { url = "https://files.pythonhosted.org/packages/10/de/cfeae9e75c144ca22018ee3b858e343c1658d6edd2184f35d810ec1ef789/tornado-6.6.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ed0bc5a1a9f6346e7bdadc106fa2d4c6e79d71d8b2df4366260ab3baf36a8399", size = 533356, upload-time = "2026-11-09T21:08:42.603Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d8/7812a729c797fb8c15748c9a69faeaaa13630075cf9b76ba9f7fbcfb7f42/tornado-6.6.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:54c80f9bed0bf1533ecf5a90dc59651645622d62b0bcffa2692d108a1f794917", size = 537511, upload-time = "2026-11-09T21:08:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/70/47/ebef304487feea6d8efb6a7d2642964ecb1f5cc785f687628e1d7cac4326/tornado-6.6.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb67ba2cbf598e842f14684eab029a5a4ba7ab587e8c4e40940a77ed0596b31e", size = 538566, upload-time = "2026-11-09T21:08:45.203Z" }, + { url = "https://files.pythonhosted.org/packages/a3/6a/87532e6745c415eb2daa9bc1c9c89c69ce9e8255b1c0bde9938f3299440b/tornado-6.6.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d83bac141bd9360d8e995ec512097be19ead2e9213c3e7088510a036f3192b8d", size = 538793, upload-time = "2026-11-09T21:08:46.478Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c7/26af60f71f7c32f19257bad58711500628f579edf1a2c3d8328ad15aae04/tornado-6.6.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:60aad449dbcebaa8d69bff50c6ba39858fab8d256e779a99bb30ebf6724b9b48", size = 537691, upload-time = "2026-11-09T21:08:47.975Z" }, + { url = "https://files.pythonhosted.org/packages/ba/9d/af233b8bf4311d3466099ab267eb595ec6f1655607d290dc7f706b6dbd6f/tornado-6.6.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b9ce0d6548bd73b1329075b247d3fdc61538922f558ee42059e08f5df53fc197", size = 535877, upload-time = "2026-11-09T21:08:49.76Z" }, + { url = "https://files.pythonhosted.org/packages/95/66/78e7583ce69f66e5177c65988a98373be2ab25c1d22405d274e037acbc58/tornado-6.6.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ce5a18654f5d56d11400ab5d87103660e14deceab41818a06a42acc8dbbe427d", size = 538295, upload-time = "2026-11-09T21:08:51.221Z" }, + { url = "https://files.pythonhosted.org/packages/a3/3a/ad336d4994547cf268a3bd6d10f6ef691e1e888fc776940c0bfe02e8d85c/tornado-6.6.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3298fce72c35f836eab4c4db8b960104ff1ea718033a6b5e236f04efe3a1b6e1", size = 538349, upload-time = "2026-11-09T21:08:52.761Z" }, + { url = "https://files.pythonhosted.org/packages/cf/cf/563d24516afd266f6b3c3f1c7ac15ad39b8668966d2bccce917c169ab0c0/tornado-6.6.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:0223e827cedf4e35ebb035e57a0d9974563cd043e3a53f8bb72a859b6f53777e", size = 537469, upload-time = "2026-11-09T21:08:54.279Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ea/1705ff4014c8bad5d7045022647156cfebd6c7014886da47e13512d27c08/tornado-6.6.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533185c89be3ffb01656c9425da4ff42587a3af784b37d39fe762e8fce3b2dbe", size = 537624, upload-time = "2026-11-09T21:08:55.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/5d/421168e62c8b80e3ef88f80d642e9a7e5b1d0c6e8cd136d99064f663951c/tornado-6.6.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fbaa022a49a27b65668592cf43b898ff3978cbe4e6926fdfe24dbef59053bd26", size = 537632, upload-time = "2026-11-09T21:08:57.797Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b4/13b2f98a3141c152915fa3e69531a04bdc1cd7dd203f442136f3792a486f/tornado-6.6.1-cp314-cp314t-win32.whl", hash = "sha256:4bc6c60faf86162df25ba2ff5611d27b6ef8226de9d61c07a0fab53c169630a5", size = 535424, upload-time = "2026-11-09T21:08:59.145Z" }, + { url = "https://files.pythonhosted.org/packages/84/83/6d54c37b3e437ab0ff2bddd71249c6ea2a17051104dd2995cb4e181e95a7/tornado-6.6.1-cp314-cp314t-win_amd64.whl", hash = "sha256:71c16b382b22d3234913a3f74a1ea84bede197d11eb03871df17bb123088dfa5", size = 535715, upload-time = "2026-11-09T21:09:00.456Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/75419d8cd577b7c4f335f4c5e75f127af286545ad49047a73afffdf877fe/tornado-6.6.1-cp314-cp314t-win_arm64.whl", hash = "sha256:510c7261ed986ecd6f74edc1a13412759e7abc2e8be3a6c85e15f6c53e1f3c84", size = 535718, upload-time = "2026-11-09T21:09:01.783Z" }, + { url = "https://files.pythonhosted.org/packages/91/8b/96e460653f3e2ad83d70328414394132d73d5655047c692ce7b69ae2f6c0/tornado-6.6.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:01dc73e3697eb94d78c42e03931ab621da97d12fdfb6339e64ff5a934b220fcf", size = 533376, upload-time = "2026-11-09T21:09:03.097Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1e/644bace0700e533235f01ffa68a7b5e3d6cc9eb7380e3929039d04b648b9/tornado-6.6.1-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95bd4751357bef52aa655e236b9667ff054885de4b118e4ce9ba5c06e12ef5d0", size = 537696, upload-time = "2026-11-09T21:09:04.519Z" }, + { url = "https://files.pythonhosted.org/packages/e6/0a/afccc23d1ed71bcb1ce4a5b6e0f61f82c7a0b275f3b377ee7aee6680a565/tornado-6.6.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29bce3190c2973fcd2171c95eeab8bf06d2f7a7eb107455ce3e4662c704aecdb", size = 538796, upload-time = "2026-11-09T21:09:05.956Z" }, + { url = "https://files.pythonhosted.org/packages/e5/24/69692f8ba851ed2738c2623eab592360d646f0a2e16b6deea16516aa8595/tornado-6.6.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b777fbe0bdb2c4dc764aadf5f70e9b23b2e536c339d61c799d3ea1b575a7c285", size = 538951, upload-time = "2026-11-09T21:09:07.298Z" }, + { url = "https://files.pythonhosted.org/packages/00/82/234306b41b33600a470da7e595b8064b85e2f509f64f5c1d39387c0abbb8/tornado-6.6.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15443805d6b7aa11610cc6c5be7e71b4a93369aeb2ca5a13c2def53a3af5ba45", size = 537861, upload-time = "2026-11-09T21:09:08.857Z" }, + { url = "https://files.pythonhosted.org/packages/5b/87/6d13a60c320c0379cbfa4c2520b4ba076780c33e6a01ba3073fff74b71db/tornado-6.6.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b77ab23d1520c807d11e703e79b020d3bc3676a0f5ebc8531f46d3fac8aecb2f", size = 536178, upload-time = "2026-11-09T21:09:10.424Z" }, + { url = "https://files.pythonhosted.org/packages/96/c7/e27f2a21e05d7c5aa9886db7368436e6436880b85349072988b4f62422e3/tornado-6.6.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:872249311539281c1e8a5d8d6f27f478bf705a9bd931bf374b6b31b05177dfda", size = 538491, upload-time = "2026-11-09T21:09:11.968Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c7/1f981336039c51d81ee6f895e3412178edc409bd709e38767365c55c0daa/tornado-6.6.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:08b808e8d3312c268f9fe1ffebfcfaaf342e7fe4ac40d219a7e1b58bac547d6f", size = 538491, upload-time = "2026-11-09T21:09:13.226Z" }, + { url = "https://files.pythonhosted.org/packages/5d/4f/7dafc65726778ac148b68c91920605892e84bf0ffcc02890aaf1a7d0ba8d/tornado-6.6.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:8d475f4053c693b5c1483eb4845824129b0b51c65712e736b3c2ea1c8f6080cd", size = 537775, upload-time = "2026-11-09T21:09:14.697Z" }, + { url = "https://files.pythonhosted.org/packages/a1/25/85c852307f9e49896b6dc5119f6a0131f5f6d97c478a687654c2c0c3f0f4/tornado-6.6.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:e923e75c9d641cdb8bbac60070a2dccdb684fc555af2bff48b0eaded0f88c2dc", size = 537798, upload-time = "2026-11-09T21:09:15.946Z" }, + { url = "https://files.pythonhosted.org/packages/60/43/a37ec29b0f98c3c639da495e1d547d6c44c69a6727b9864cf149bf346433/tornado-6.6.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a72f70b1aa630fa2106d39a025e2be75c272b7ec8233da94e2f407ea754cc32d", size = 537809, upload-time = "2026-11-09T21:09:17.265Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d4/13973391071552e03acb308d93f6deb4fb535a769a6bbdafdb4cafcb2f91/tornado-6.6.1-cp315-cp315t-win32.whl", hash = "sha256:2fbfd2510b6163b189773155a8246c025cb260ccc7cd12ad18c36c54f5617bb9", size = 535422, upload-time = "2026-11-09T21:09:18.525Z" }, + { url = "https://files.pythonhosted.org/packages/19/94/c952b625904b63fdac5b747f2eeaca64ab5d36cdb8faff57effa57162d87/tornado-6.6.1-cp315-cp315t-win_amd64.whl", hash = "sha256:3c35091add5863feb9cf651851bb1b23592f1f9551b5dfed72837c83ea4e5b85", size = 535713, upload-time = "2026-11-09T21:09:20.061Z" }, + { url = "https://files.pythonhosted.org/packages/f8/62/8d0c0c42f3346f3f2455738d3bc689bbd8efba38abf7b90ab2b8294eae83/tornado-6.6.1-cp315-cp315t-win_arm64.whl", hash = "sha256:065bc741478b168df9baf754956a3010c04d1330bb45a84833d807c29e11b257", size = 535719, upload-time = "2026-11-09T21:09:21.317Z" }, ] [[package]]