From 0bc282cecf8ef107525f5f18b63a75c05dd1632d Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:46:01 +0000 Subject: [PATCH 01/19] Add AsyncAPI router and update dependencies This commit integrates a new AsyncAPI router in the app's routes and updates the `pyproject.toml` and `uv.lock` with new dependency versions and requirements. Minimum Python version is updated to 3.10, and the `pydantic-asyncapi` library is now included to facilitate AsyncAPI integration. --- pyproject.toml | 5 +- src/http_app/routes/__init__.py | 3 +- src/http_app/routes/asyncapi_docs.py | 22 +++ uv.lock | 279 ++------------------------- 4 files changed, 42 insertions(+), 267 deletions(-) create mode 100644 src/http_app/routes/asyncapi_docs.py diff --git a/pyproject.toml b/pyproject.toml index 9b40455b..38b22e63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ authors = [ {name = "Federico Busetti", email = "729029+febus982@users.noreply.github.com"}, ] -requires-python = "<3.14,>=3.9" +requires-python = "<3.14,>=3.10" name = "bootstrap-fastapi-service" version = "0.1.0" description = "" @@ -14,7 +14,7 @@ dependencies = [ "cloudevents-pydantic<1.0.0,>=0.0.3", "dependency-injector[pydantic]<5.0.0,>=4.41.0", "dramatiq[redis,watch]<2.0.0,>=1.17.1", - "hiredis<4.0.0,>=3.1.0", # Recommended by dramatiq + "hiredis<4.0.0,>=3.1.0", # Recommended by dramatiq "httpx>=0.23.0", "opentelemetry-distro[otlp]", "opentelemetry-instrumentation", @@ -28,6 +28,7 @@ dependencies = [ "SQLAlchemy[asyncio,mypy]<3.0.0,>=2.0.0", "sqlalchemy-bind-manager", "structlog<25.1.1,>=25.1.0", + "pydantic-asyncapi>=0.2.1", ] [dependency-groups] diff --git a/src/http_app/routes/__init__.py b/src/http_app/routes/__init__.py index f914c039..10a003ff 100644 --- a/src/http_app/routes/__init__.py +++ b/src/http_app/routes/__init__.py @@ -1,10 +1,11 @@ from fastapi import FastAPI -from http_app.routes import api, events, graphql, hello, ping, user_registered_hook +from http_app.routes import api, events, graphql, hello, ping, user_registered_hook, asyncapi_docs def init_routes(app: FastAPI) -> None: app.include_router(api.router) + app.include_router(asyncapi_docs.router) app.include_router(ping.router) app.include_router(hello.router) app.include_router(events.router) diff --git a/src/http_app/routes/asyncapi_docs.py b/src/http_app/routes/asyncapi_docs.py new file mode 100644 index 00000000..df9485d4 --- /dev/null +++ b/src/http_app/routes/asyncapi_docs.py @@ -0,0 +1,22 @@ +import pydantic_asyncapi as pa +from fastapi import APIRouter + +schema = pa.AsyncAPIV3( + id="http://aa.aa.aa", + info=pa.v3.Info( + title="Bookstore API", + version="1.0.0", + description="test", + ), +) + +router = APIRouter(prefix="/asyncapi") + +@router.get("/asyncapi.json", response_model_exclude_unset=True) +def asyncapi_raw() -> pa.AsyncAPIV3: + return schema + +@router.get("/docs", response_model_exclude_unset=True) +def asyncapi_docs() -> pa.AsyncAPIV3: + return schema + diff --git a/uv.lock b/uv.lock index 3c40b0b5..c57f3f91 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -requires-python = ">=3.9, <3.14" +requires-python = ">=3.10, <3.14" [[package]] name = "aiosqlite" @@ -122,6 +122,7 @@ dependencies = [ { name = "opentelemetry-instrumentor-dramatiq" }, { name = "orjson" }, { name = "pydantic" }, + { name = "pydantic-asyncapi" }, { name = "pydantic-settings" }, { name = "rich" }, { name = "sqlalchemy", extra = ["asyncio", "mypy"] }, @@ -176,6 +177,7 @@ requires-dist = [ { name = "opentelemetry-instrumentor-dramatiq" }, { name = "orjson", specifier = ">=3.10.12,<4.0.0" }, { name = "pydantic", specifier = ">=2.2.1,<3.0.0" }, + { name = "pydantic-asyncapi", specifier = ">=0.2.1" }, { name = "pydantic-settings", specifier = ">=2.0.3,<3.0.0" }, { name = "rich", specifier = ">=13.2.0,<14.0.0" }, { name = "sqlalchemy", extras = ["asyncio", "mypy"], specifier = ">=2.0.0,<3.0.0" }, @@ -286,18 +288,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, - { url = "https://files.pythonhosted.org/packages/b9/ea/8bb50596b8ffbc49ddd7a1ad305035daa770202a6b782fc164647c2673ad/cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", size = 182220 }, - { url = "https://files.pythonhosted.org/packages/ae/11/e77c8cd24f58285a82c23af484cf5b124a376b32644e445960d1a4654c3a/cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", size = 178605 }, - { url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910 }, - { url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200 }, - { url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565 }, - { url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635 }, - { url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218 }, - { url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486 }, - { url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911 }, - { url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632 }, - { url = "https://files.pythonhosted.org/packages/cb/b5/fd9f8b5a84010ca169ee49f4e4ad6f8c05f4e3545b72ee041dbbcb159882/cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", size = 171820 }, - { url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290 }, ] [[package]] @@ -358,19 +348,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, - { url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 }, - { url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 }, - { url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 }, - { url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 }, - { url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 }, - { url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 }, - { url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 }, - { url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 }, - { url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 }, - { url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 }, - { url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 }, - { url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 }, - { url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 }, { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, ] @@ -477,16 +454,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/69/b3f2416725621e9f112e74e8470793d5b5995f146f596f133678a633b77e/coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", size = 248737 }, { url = "https://files.pythonhosted.org/packages/3c/6e/fe899fb937657db6df31cc3e61c6968cb56d36d7326361847440a430152e/coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", size = 211611 }, { url = "https://files.pythonhosted.org/packages/1c/55/52f5e66142a9d7bc93a15192eba7a78513d2abf6b3558d77b4ca32f5f424/coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", size = 212781 }, - { url = "https://files.pythonhosted.org/packages/40/41/473617aadf9a1c15bc2d56be65d90d7c29bfa50a957a67ef96462f7ebf8e/coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a", size = 207978 }, - { url = "https://files.pythonhosted.org/packages/10/f6/480586607768b39a30e6910a3c4522139094ac0f1677028e1f4823688957/coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27", size = 208415 }, - { url = "https://files.pythonhosted.org/packages/f1/af/439bb760f817deff6f4d38fe7da08d9dd7874a560241f1945bc3b4446550/coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4", size = 236452 }, - { url = "https://files.pythonhosted.org/packages/d0/13/481f4ceffcabe29ee2332e60efb52e4694f54a402f3ada2bcec10bb32e43/coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f", size = 234374 }, - { url = "https://files.pythonhosted.org/packages/c5/59/4607ea9d6b1b73e905c7656da08d0b00cdf6e59f2293ec259e8914160025/coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25", size = 235505 }, - { url = "https://files.pythonhosted.org/packages/85/60/d66365723b9b7f29464b11d024248ed3523ce5aab958e4ad8c43f3f4148b/coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315", size = 234616 }, - { url = "https://files.pythonhosted.org/packages/74/f8/2cf7a38e7d81b266f47dfcf137fecd8fa66c7bdbd4228d611628d8ca3437/coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90", size = 233099 }, - { url = "https://files.pythonhosted.org/packages/50/2b/bff6c1c6b63c4396ea7ecdbf8db1788b46046c681b8fcc6ec77db9f4ea49/coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d", size = 234089 }, - { url = "https://files.pythonhosted.org/packages/bf/b5/baace1c754d546a67779358341aa8d2f7118baf58cac235db457e1001d1b/coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18", size = 210701 }, - { url = "https://files.pythonhosted.org/packages/b1/bf/9e1e95b8b20817398ecc5a1e8d3e05ff404e1b9fb2185cd71561698fe2a2/coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59", size = 211482 }, { url = "https://files.pythonhosted.org/packages/a1/70/de81bfec9ed38a64fc44a77c7665e20ca507fc3265597c28b0d989e4082e/coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f", size = 200223 }, ] @@ -574,25 +541,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/2f/bae5beaf6ac9c5464e6016d0f94d10898c192b4528e2978a2ada44af0c21/dependency_injector-4.45.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d785dacb136479b0536e8b05f83a7375f09404d63eabd1be9b694eca6defea4", size = 6762151 }, { url = "https://files.pythonhosted.org/packages/13/6f/068b17dc76765e5344c72b85d46711a596eca1d4ee441bac7b7275dadc94/dependency_injector-4.45.0-cp313-cp313-win32.whl", hash = "sha256:3df40ff15bd31058b24cd79781b1dc2f31e0c157ee2067566dfd7a92191969e9", size = 1660060 }, { url = "https://files.pythonhosted.org/packages/53/d3/890c4be056256e56e97264e72b376ecad273cfa8c8dc70ce5eb623f06472/dependency_injector-4.45.0-cp313-cp313-win_amd64.whl", hash = "sha256:f018f634edf57d4b4214e007eaa2432d2410f304b170f5684bd3fdbffd244b1f", size = 1775930 }, - { url = "https://files.pythonhosted.org/packages/df/7f/ca64dcac3c38168e50e97eb259dab9ced7f7c887ddd5e62d91b462ad8457/dependency_injector-4.45.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:326109be456ffa80698e27fe7f32411bb120c4fb2f65059abff2616910a261d3", size = 1855300 }, - { url = "https://files.pythonhosted.org/packages/5b/8a/ac35bb481d0de73c6b6afe0cc70d8abbcabf76abb82f1907abb1294919f2/dependency_injector-4.45.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b0cb3cdb0523f75bc8bf3e07406670fc8f79a295e8309c2c638a485f3ae9ee0", size = 6534344 }, - { url = "https://files.pythonhosted.org/packages/ab/e7/10c158fcc59861125a3d0a8d9d9f9c27473780cb445b7feec3626c74ad4a/dependency_injector-4.45.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4207434037f31f98c36f39519d217aa3683dbb12ec2a3a69f1490441935c5f2", size = 6570968 }, - { url = "https://files.pythonhosted.org/packages/05/c5/7bbe162fa3e6acc28506b7b6b957739bbdb3adece52aef5479a80c19576d/dependency_injector-4.45.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aad4e274ec08e359dcf8cf391e08b399b9be67cc5d3c41e2d7b61a8967bb7a29", size = 6152240 }, - { url = "https://files.pythonhosted.org/packages/ae/67/1fea714e609c4aba87dde2c186bc7d3db8ff04b336183311b2f22ba56f59/dependency_injector-4.45.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ee0e2b1dbda5f19f2751d78a8662f2094fa315e4da054e83093ffb64ccf6b8dd", size = 6130185 }, - { url = "https://files.pythonhosted.org/packages/07/98/c4f28d1903a50ab023bc6fe57d8146fc251ed18bcc7ff829a06454705188/dependency_injector-4.45.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c12c44c8c5a168059cff22a0e9a9f7f3ff089b0b38114d037ca1ead48ead178d", size = 5994519 }, - { url = "https://files.pythonhosted.org/packages/e1/97/46eb6175c6073821bafd5f5978f8c10a8c909e0de5416b3c9b2f6086a1d0/dependency_injector-4.45.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:60de7a0a8bd105b2e11121f81a3e5ec67fce34d2470eb7af2546bca2df76b101", size = 6332369 }, - { url = "https://files.pythonhosted.org/packages/1c/05/6c3fa8302d790f853427eeafa008a9d3193c54dc38e5203d258fccbce511/dependency_injector-4.45.0-cp39-cp39-win32.whl", hash = "sha256:8d351a5f36bc04838c4884acbc5f57315e51437a19f2a03d225a3af9bac1f60c", size = 1666357 }, - { url = "https://files.pythonhosted.org/packages/58/0c/fa518f7ee6f3213f74f39737dd313da3ad7cf687e52d05ae05184030161b/dependency_injector-4.45.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f556713c34335afaec011e3e436668686af90605992415be60aea1d625bf91b", size = 1799822 }, { url = "https://files.pythonhosted.org/packages/02/8e/915f9b6b7105a2a55933b808a2b4c2d2bd73ac2ccf320d4ebd3d2729c2a2/dependency_injector-4.45.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f2cd876d9726db63df33ecbfb097a06de0306ddae5861c6ba71363606184428e", size = 1698714 }, { url = "https://files.pythonhosted.org/packages/b4/4f/08c59f93522429944c941f49de8d2ae99c352f84a0825bb7d580d057c522/dependency_injector-4.45.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35baac99140143aed5f7dbf098c523fc534884063ccacc3f51aaccbacc7f9f3", size = 1834874 }, { url = "https://files.pythonhosted.org/packages/a3/aa/0ed6ae263af7e3b3f12ca69c63be13a42863b954e8e73cb66d4a6d29d4f9/dependency_injector-4.45.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c76c944e4732d72787f99b18dd5b3bcb012427efe8373d8b895f9774880f0cc", size = 1853759 }, { url = "https://files.pythonhosted.org/packages/50/08/5c8c8bf937e84d0b020eea06811ac5c7b23e05333a5f3f3925dc23b8f6a6/dependency_injector-4.45.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beabb619969c1c87085bcb228b10e2f8751bf7cca66d77dac99c85c3b7a29847", size = 1872820 }, { url = "https://files.pythonhosted.org/packages/cc/61/8fb364abeb494a43001856a6433cd228544af9d2aa3fc57955b8c1b1095d/dependency_injector-4.45.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d2bdf7c6e4f7a74dedf4f0aea774eec4c3d6f4f909d411c769c9cd1346243518", size = 1718692 }, - { url = "https://files.pythonhosted.org/packages/7b/1a/cf3fb31442fb5a70f4f26f586b2156dfdb347385bb0f264c1295cb182067/dependency_injector-4.45.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:2e6861b14a52b8ab0956bde2379aad43a5f05ddc8dbfb0233c6c4f71bc029481", size = 1697311 }, - { url = "https://files.pythonhosted.org/packages/1f/3f/b6e1c83135d06d6d8befeffecf2450b7f90fd3ced436f249736998172dbb/dependency_injector-4.45.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60034184b6dedf7d0486c565eb846a56ccc6fdb25511f1bedf0fbbb7dab23239", size = 1834405 }, - { url = "https://files.pythonhosted.org/packages/ac/ab/cd12805676b7157a70eb9804cf42c8c19cc962d8057b32af20c8c133adf4/dependency_injector-4.45.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dae95379e1a55cccef583d717f2a18481001fe9519162e480d1e2c3e429c246a", size = 1850580 }, - { url = "https://files.pythonhosted.org/packages/d0/e6/fd67b2720655dae0277762111fd46856c88c156b76e47a1a7ede12792d9c/dependency_injector-4.45.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d35c638544a929bfaf150e280b582bd776e3929b9ffdffa4c7a9ee9dff9f448e", size = 1872001 }, - { url = "https://files.pythonhosted.org/packages/e2/16/f555b8ba3ca72f466525526eab6a59674d3c14e29685dc399d2ede3be57f/dependency_injector-4.45.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d1cd1034fdc8c4c9daeafdab146570260d5c8fe70fa87a2d428621fc5a07ea8c", size = 1718287 }, ] [package.optional-dependencies] @@ -755,10 +708,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ed/6e/b2eed8dec617264f0046d50a13a42d3f0a06c50071b9fc1eae00285a03f1/gevent-24.11.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:58851f23c4bdb70390f10fc020c973ffcf409eb1664086792c8b1e20f25eef43", size = 5449436 }, { url = "https://files.pythonhosted.org/packages/63/c2/eca6b95fbf9af287fa91c327494e4b74a8d5bfa0156cd87b233f63f118dc/gevent-24.11.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1ea50009ecb7f1327347c37e9eb6561bdbc7de290769ee1404107b9a9cba7cf1", size = 6866470 }, { url = "https://files.pythonhosted.org/packages/b7/e6/51824bd1f2c1ce70aa01495aa6ffe04ab789fa819fa7e6f0ad2388fb03c6/gevent-24.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:ec68e270543ecd532c4c1d70fca020f90aa5486ad49c4f3b8b2e64a66f5c9274", size = 1540088 }, - { url = "https://files.pythonhosted.org/packages/a0/73/263d0f63186d27d205b3dc157efe838afe3aba10a3baca15d85e97b90eae/gevent-24.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9347690f4e53de2c4af74e62d6fabc940b6d4a6cad555b5a379f61e7d3f2a8e", size = 6658480 }, - { url = "https://files.pythonhosted.org/packages/8a/fd/ec7b5c764a3d1340160b82f7394fdc1220d18e11ae089c472cf7bcc2fe6a/gevent-24.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8619d5c888cb7aebf9aec6703e410620ef5ad48cdc2d813dd606f8aa7ace675f", size = 6808247 }, - { url = "https://files.pythonhosted.org/packages/95/82/2ce68dc8dbc2c3ed3f4e73f21e1b7a45d80b5225670225a48e695f248850/gevent-24.11.1-cp39-cp39-win32.whl", hash = "sha256:c6b775381f805ff5faf250e3a07c0819529571d19bb2a9d474bee8c3f90d66af", size = 1483133 }, - { url = "https://files.pythonhosted.org/packages/76/96/aa4cbcf1807187b65a9c9ff15b32b08c2014968be852dda34d212cf8cc58/gevent-24.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c3443b0ed23dcb7c36a748d42587168672953d368f2956b17fad36d43b58836", size = 1566354 }, { url = "https://files.pythonhosted.org/packages/86/63/197aa67250943b508b34995c2aa6b46402e7e6f11785487740c2057bfb20/gevent-24.11.1-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:f43f47e702d0c8e1b8b997c00f1601486f9f976f84ab704f8f11536e3fa144c9", size = 1271676 }, ] @@ -790,9 +739,6 @@ wheels = [ name = "graphql-core" version = "3.2.6" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] sdist = { url = "https://files.pythonhosted.org/packages/c4/16/7574029da84834349b60ed71614d66ca3afe46e9bf9c7b9562102acb7d4f/graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab", size = 505353 } wheels = [ { url = "https://files.pythonhosted.org/packages/ae/4f/7297663840621022bc73c22d7d9d80dbc78b4db6297f764b545cd5dd462d/graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f", size = 203416 }, @@ -847,16 +793,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, - { url = "https://files.pythonhosted.org/packages/8c/82/8051e82af6d6b5150aacb6789a657a8afd48f0a44d8e91cb72aaaf28553a/greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3", size = 270027 }, - { url = "https://files.pythonhosted.org/packages/f9/74/f66de2785880293780eebd18a2958aeea7cbe7814af1ccef634f4701f846/greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42", size = 634822 }, - { url = "https://files.pythonhosted.org/packages/68/23/acd9ca6bc412b02b8aa755e47b16aafbe642dde0ad2f929f836e57a7949c/greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f", size = 646866 }, - { url = "https://files.pythonhosted.org/packages/a9/ab/562beaf8a53dc9f6b2459f200e7bc226bb07e51862a66351d8b7817e3efd/greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437", size = 641985 }, - { url = "https://files.pythonhosted.org/packages/03/d3/1006543621f16689f6dc75f6bcf06e3c23e044c26fe391c16c253623313e/greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145", size = 641268 }, - { url = "https://files.pythonhosted.org/packages/2f/c1/ad71ce1b5f61f900593377b3f77b39408bce5dc96754790311b49869e146/greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c", size = 597376 }, - { url = "https://files.pythonhosted.org/packages/f7/ff/183226685b478544d61d74804445589e069d00deb8ddef042699733950c7/greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e", size = 1123359 }, - { url = "https://files.pythonhosted.org/packages/c0/8b/9b3b85a89c22f55f315908b94cd75ab5fed5973f7393bbef000ca8b2c5c1/greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e", size = 1147458 }, - { url = "https://files.pythonhosted.org/packages/b8/1c/248fadcecd1790b0ba793ff81fa2375c9ad6442f4c748bf2cc2e6563346a/greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c", size = 281131 }, - { url = "https://files.pythonhosted.org/packages/ae/02/e7d0aef2354a38709b764df50b2b83608f0621493e47f47694eb80922822/greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22", size = 298306 }, ] [[package]] @@ -901,15 +837,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/6a/5df64b6df405a1ed1482cb6c10044b06ec47fd28e87c2232dbcf435ecb33/grpcio-1.70.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0a5c78d5198a1f0aa60006cd6eb1c912b4a1520b6a3968e677dbcba215fabb40", size = 6190982 }, { url = "https://files.pythonhosted.org/packages/42/aa/aeaac87737e6d25d1048c53b8ec408c056d3ed0c922e7c5efad65384250c/grpcio-1.70.0-cp313-cp313-win32.whl", hash = "sha256:fe9dbd916df3b60e865258a8c72ac98f3ac9e2a9542dcb72b7a34d236242a5ce", size = 3598359 }, { url = "https://files.pythonhosted.org/packages/1f/79/8edd2442d2de1431b4a3de84ef91c37002f12de0f9b577fb07b452989dbc/grpcio-1.70.0-cp313-cp313-win_amd64.whl", hash = "sha256:4119fed8abb7ff6c32e3d2255301e59c316c22d31ab812b3fbcbaf3d0d87cc68", size = 4293938 }, - { url = "https://files.pythonhosted.org/packages/9d/0e/64061c9746a2dd6e07cb0a0f3829f0a431344add77ec36397cc452541ff6/grpcio-1.70.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:4f1937f47c77392ccd555728f564a49128b6a197a05a5cd527b796d36f3387d0", size = 5231123 }, - { url = "https://files.pythonhosted.org/packages/72/9f/c93501d5f361aecee0146ab19300d5acb1c2747b00217c641f06fffbcd62/grpcio-1.70.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:0cd430b9215a15c10b0e7d78f51e8a39d6cf2ea819fd635a7214fae600b1da27", size = 11467217 }, - { url = "https://files.pythonhosted.org/packages/0a/1a/980d115b701023450a304881bf3f6309f6fb15787f9b78d2728074f3bf86/grpcio-1.70.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e27585831aa6b57b9250abaf147003e126cd3a6c6ca0c531a01996f31709bed1", size = 5710913 }, - { url = "https://files.pythonhosted.org/packages/a0/84/af420067029808f9790e98143b3dd0f943bebba434a4706755051a520c91/grpcio-1.70.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1af8e15b0f0fe0eac75195992a63df17579553b0c4af9f8362cc7cc99ccddf4", size = 6330947 }, - { url = "https://files.pythonhosted.org/packages/24/1c/e1f06a7d29a1fa5053dcaf5352a50f8e1f04855fd194a65422a9d685d375/grpcio-1.70.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbce24409beaee911c574a3d75d12ffb8c3e3dd1b813321b1d7a96bbcac46bf4", size = 5943913 }, - { url = "https://files.pythonhosted.org/packages/41/8f/de13838e4467519a50cd0693e98b0b2bcc81d656013c38a1dd7dcb801526/grpcio-1.70.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ff4a8112a79464919bb21c18e956c54add43ec9a4850e3949da54f61c241a4a6", size = 6643236 }, - { url = "https://files.pythonhosted.org/packages/ac/73/d68c745d34e43a80440da4f3d79fa02c56cb118c2a26ba949f3cfd8316d7/grpcio-1.70.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5413549fdf0b14046c545e19cfc4eb1e37e9e1ebba0ca390a8d4e9963cab44d2", size = 6199038 }, - { url = "https://files.pythonhosted.org/packages/7e/dd/991f100b8c31636b4bb2a941dbbf54dbcc55d69c722cfa038c3d017eaa0c/grpcio-1.70.0-cp39-cp39-win32.whl", hash = "sha256:b745d2c41b27650095e81dea7091668c040457483c9bdb5d0d9de8f8eb25e59f", size = 3617512 }, - { url = "https://files.pythonhosted.org/packages/4d/80/1aa2ba791207a13e314067209b48e1a0893ed8d1f43ef012e194aaa6c2de/grpcio-1.70.0-cp39-cp39-win_amd64.whl", hash = "sha256:a31d7e3b529c94e930a117b2175b2efd179d96eb3c7a21ccb0289a8ab05b645c", size = 4303506 }, ] [[package]] @@ -987,33 +914,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/82/10/bd8f39423b0cb9624ccaf08d5e9c04f72dd46e9e9fc82e95cec42a42428d/hiredis-3.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8a45ff7915392a55d9386bb235ea1d1eb9960615f301979f02143fc20036b699", size = 163902 }, { url = "https://files.pythonhosted.org/packages/0b/77/00b420ad567875e5a4b37a16f1a89fef1a22c6a9e1a12195c77bb5b101dd/hiredis-3.1.0-cp313-cp313-win32.whl", hash = "sha256:539e5bb725b62b76a5319a4e68fc7085f01349abc2316ef3df608ea0883c51d2", size = 20211 }, { url = "https://files.pythonhosted.org/packages/cc/04/eaa88433249ddfc282018d3da4198d0b0018e48768e137bfad304aacb1ec/hiredis-3.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9020fd7e58f489fda6a928c31355add0e665fd6b87b21954e675cf9943eafa32", size = 22004 }, - { url = "https://files.pythonhosted.org/packages/f1/5d/12ce85507c025ed685f583dade7ed1aee90d451de098b463441e4f76da89/hiredis-3.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:a26fa888025badb5563f283cc19594c215a413e905729e59a5f7cf3f46d66c32", size = 81236 }, - { url = "https://files.pythonhosted.org/packages/50/3b/e016ed691f24059aca9eab3a9c03bc72b8dcbe5f128b710124efcc4a8a54/hiredis-3.1.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:f50763cd819d4a52a47b5966d4bb47dee34b637c5fa6402509800eee6ecb61e6", size = 44492 }, - { url = "https://files.pythonhosted.org/packages/08/9f/9cf4b72ae9567d65746574250a558445b667531b6716688815f05e4aa09f/hiredis-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b6d1c9e1fce5e0a94072667ae2bf0142b89ebbb1917d3531184e060a43f3ee11", size = 42441 }, - { url = "https://files.pythonhosted.org/packages/68/72/d7d96ae7cd5a6a5abc057c6596ee8b76621b6d49d40668f418658b404555/hiredis-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e38d7a56b1a79ed0bbb9e6fe376d82e3f4dcc646ae47472f2c858e19a597c112", size = 164880 }, - { url = "https://files.pythonhosted.org/packages/92/5d/077cdf38c979c15bfd1e44c0104bd719ab3e217da3f13f01e7f7ed37d280/hiredis-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ef5ad8b91530e4d10a68562b0a380ea22705a60e88cecee086d7c63a38564ce", size = 176048 }, - { url = "https://files.pythonhosted.org/packages/9d/83/2603691a8b6fdc29030c9954b9dcf934ab78d2f8152be88d7ee19c75bdaa/hiredis-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf3d2299b054e57a9f97ca08704c2843e44f29b57dc69b76a2592ecd212efe1a", size = 165448 }, - { url = "https://files.pythonhosted.org/packages/e3/a3/416029981647c08ae56d58d900e0a1b47f344ba05bbf087522656eba2044/hiredis-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93811d60b0f73d0f049c86f4373a3833b4a38fce374ab151074d929553eb4304", size = 165233 }, - { url = "https://files.pythonhosted.org/packages/b3/49/25499af7bfb5a736910a131ab0f7592cab72bebd5f3d85057bc527dd5eaa/hiredis-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e703ff860c1d83abbcf57012b309ead02b56b60e85150c6c3bfb37cbb16ebf", size = 161558 }, - { url = "https://files.pythonhosted.org/packages/45/9b/6b37534856f90e0f5c298200db327313558290086419c1fd667662c6952e/hiredis-3.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f9ea0678806c53d96758e74c6a898f9d506a2e3367a344757f768bef9e069366", size = 159571 }, - { url = "https://files.pythonhosted.org/packages/d6/99/f43db5f95e8591054efcb3c583175deebda5013bd1703ceb587e7e51efbb/hiredis-3.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cf6844035abf47d52a1c3f4257255af3bf3b0f14d559b08eaa45885418c6c55d", size = 158596 }, - { url = "https://files.pythonhosted.org/packages/c3/e1/7ad062b10aefe156981ed00f449f09eeceb96a83dcf7145d0f22d8726d09/hiredis-3.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7acf35cfa7ec9e1e7559c04e7095628f7d06049b5f24dcb58c1a55ef6dc689f8", size = 170387 }, - { url = "https://files.pythonhosted.org/packages/51/d9/15cdcf8c4d581b388e6e5a06b5cea4627dca722a06b621579c66d5f0bc62/hiredis-3.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b885695dce7a39b1fd9a609ed9c4cf312e53df2ec028d5a78af7a891b5fbea4d", size = 162670 }, - { url = "https://files.pythonhosted.org/packages/18/55/7dee03709eed445382017ee8bd6b61441eca914f7bc42551ce34a1e888c9/hiredis-3.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c22fa74ddd063396b19fe8445a1ae8b4190eff755d5750dda48e860a45b2ee7", size = 160353 }, - { url = "https://files.pythonhosted.org/packages/bf/bf/cbe289fa2964487e24f22d22129617dc82943e95439c05b99c73452f1ad3/hiredis-3.1.0-cp39-cp39-win32.whl", hash = "sha256:0614e16339f1784df3bbd2800322e20b4127d3f3a3509f00a5562efddb2521aa", size = 20074 }, - { url = "https://files.pythonhosted.org/packages/07/b8/aaf8b940557c0fc5c07a177de3dfdc40bfe3f32902087b68008f7a7dd829/hiredis-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:c2bc713ee73ab9de4a0d68b0ab0f29612342b63173714742437b977584adb2d8", size = 21927 }, { url = "https://files.pythonhosted.org/packages/dd/11/13f2af303ed3891ed459527b0183bb743c43eeffd22b8771e7260a0b0281/hiredis-3.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:07ab990d0835f36bf358dbb84db4541ac0a8f533128ec09af8f80a576eef2e88", size = 39509 }, { url = "https://files.pythonhosted.org/packages/ba/3e/0938e733ad08b6cabd1c56d973207769278b9d971fe6d5ed6480232a7b37/hiredis-3.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c54a88eb9d8ebc4e5eefaadbe2102a4f7499f9e413654172f40aefd25350959", size = 36900 }, { url = "https://files.pythonhosted.org/packages/a7/a7/39d9521519b69056365892a51e2614275f3ae1f149e2c5d9885a909586fe/hiredis-3.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8095ef159896e5999a795b0f80e4d64281301a109e442a8d29cd750ca6bd8303", size = 47771 }, { url = "https://files.pythonhosted.org/packages/e5/0a/e82918ac75213a47d8fbdcf7f6e2d3fd09a1eeb4e253d6babe47c00602f7/hiredis-3.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f8ca13e2476ffd6d5be4763f5868133506ddcfa5ce54b4dac231ebdc19be6c6", size = 48160 }, { url = "https://files.pythonhosted.org/packages/56/cf/8de09573adcaa0906dd689904e24250561bc792c7f9ae7910f154fbba9b0/hiredis-3.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d25aa25c10f966d5415795ed271da84605044dbf436c054966cea5442451b3", size = 55458 }, { url = "https://files.pythonhosted.org/packages/5d/ff/e1603c3c6926c1fa6ae85595e983d7206def21e455ee6f4578bbf31c479f/hiredis-3.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4180dc5f646b426e5fa1212e1348c167ee2a864b3a70d56579163d64a847dd1e", size = 21976 }, - { url = "https://files.pythonhosted.org/packages/b5/f6/d00ce8575a2f7ff1cfb3f8ae49225a061ac7b0a3554bbbad7ef66d4f07b2/hiredis-3.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4663a319ab7d22c597b9421e5ea384fd583e044f2f1ca9a1b98d4fef8a0fea2f", size = 39506 }, - { url = "https://files.pythonhosted.org/packages/e1/b7/3fa0cd5fada761a0ae0eddf939cabf03b5c38503f2f28313bbe292a0fc5f/hiredis-3.1.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8060fa256862b0c3de64a73ab45bc1ccf381caca464f2647af9075b200828948", size = 36858 }, - { url = "https://files.pythonhosted.org/packages/30/c0/602207ece39e30bd65f59f51b85f185bf9a70cdb3a2586421eabee377634/hiredis-3.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e9445b7f117a9c8c8ccad97cb44daa55ddccff3cbc9079984eac56d982ba01f", size = 47746 }, - { url = "https://files.pythonhosted.org/packages/43/6b/649862029bc518d8103c81875633183244af77ba28d4a0166c60bd7b8ff3/hiredis-3.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:732cf1c5cf1324f7bf3b6086976fe62a2ca98f0bf6316f31063c2c67be8797bc", size = 48129 }, - { url = "https://files.pythonhosted.org/packages/32/c6/e120df272b4b94bd52d0eff8a5b53afffc8cd2b832704a961b5f077c72f1/hiredis-3.1.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2102a94063d878c40df92f55199637a74f535e3a0b79ceba4a00538853a21be3", size = 55442 }, - { url = "https://files.pythonhosted.org/packages/eb/ee/d21cbad5bf8f1ebaad9492174b5eb624712fc4b83b17fe5bf187c4199320/hiredis-3.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d968dde69e3fe903bf9ef00667669dcf04a3e096e33aaf138775106ead138bc8", size = 21965 }, ] [[package]] @@ -1072,13 +978,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858 }, { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042 }, { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682 }, - { url = "https://files.pythonhosted.org/packages/51/b1/4fc6f52afdf93b7c4304e21f6add9e981e4f857c2fa622a55dfe21b6059e/httptools-0.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85797e37e8eeaa5439d33e556662cc370e474445d5fab24dcadc65a8ffb04003", size = 201123 }, - { url = "https://files.pythonhosted.org/packages/c2/01/e6ecb40ac8fdfb76607c7d3b74a41b464458d5c8710534d8f163b0c15f29/httptools-0.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:db353d22843cf1028f43c3651581e4bb49374d85692a85f95f7b9a130e1b2cab", size = 104507 }, - { url = "https://files.pythonhosted.org/packages/dc/24/c70c34119d209bf08199d938dc9c69164f585ed3029237b4bdb90f673cb9/httptools-0.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ffd262a73d7c28424252381a5b854c19d9de5f56f075445d33919a637e3547", size = 449615 }, - { url = "https://files.pythonhosted.org/packages/2b/62/e7f317fed3703bd81053840cacba4e40bcf424b870e4197f94bd1cf9fe7a/httptools-0.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c346571fa50d2e9856a37d7cd9435a25e7fd15e236c397bf224afaa355fe9", size = 448819 }, - { url = "https://files.pythonhosted.org/packages/2a/13/68337d3be6b023260139434c49d7aa466aaa98f9aee7ed29270ac7dde6a2/httptools-0.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aafe0f1918ed07b67c1e838f950b1c1fabc683030477e60b335649b8020e1076", size = 422093 }, - { url = "https://files.pythonhosted.org/packages/fc/b3/3a1bc45be03dda7a60c7858e55b6cd0489a81613c1908fb81cf21d34ae50/httptools-0.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0e563e54979e97b6d13f1bbc05a96109923e76b901f786a5eae36e99c01237bd", size = 423898 }, - { url = "https://files.pythonhosted.org/packages/05/72/2ddc2ae5f7ace986f7e68a326215b2e7c32e32fd40e6428fa8f1d8065c7e/httptools-0.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:b799de31416ecc589ad79dd85a0b2657a8fe39327944998dea368c1d4c9e55e6", size = 89552 }, ] [[package]] @@ -1194,13 +1093,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/39/dcfe6c02e087e3241e8e799b2ddb92e32a65076eec846205e96e3a928139/libcst-1.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3fb953fc0155532f366ff40f6a23f191250134d6928e02074ae4eb3531fa6c30", size = 2248820 }, { url = "https://files.pythonhosted.org/packages/20/5b/db239fcf1417bdff283ed76b027b4039e1c377d38aa3b979f32bcf34fa94/libcst-1.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2f3c85602e5a6d3aec0a8fc74230363f943004d7c2b2a6a1c09b320b61692241", size = 2365139 }, { url = "https://files.pythonhosted.org/packages/38/7f/ed56f5724305c08235d1edc580275aa13c8303e93d374d4fe73162907e88/libcst-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:c4486921bebd33d67bbbd605aff8bfaefd2d13dc73c20c1fde2fb245880b7fd6", size = 2070695 }, - { url = "https://files.pythonhosted.org/packages/0f/66/20afe2de02a30d74b2a3e0aacb5938a926664db73c42e430948915d02e81/libcst-1.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b3d274115d134a550fe8a0b38780a28a659d4a35ac6068c7c92fffe6661b519c", size = 2049393 }, - { url = "https://files.pythonhosted.org/packages/14/32/54e9f017d0824711f4a8e5f990f17ba0ff10e5763de5ecf673b590a27e28/libcst-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d45513f6cd3dbb2a80cf21a53bc6e6e560414edea17c474c784100e10aebe921", size = 2207738 }, - { url = "https://files.pythonhosted.org/packages/25/29/87aa47fc8e8b233ee831629444b792161cc29934b75f1232f523f785e1c2/libcst-1.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8c70a124d7a7d326abdc9a6261013c57d36f21c6c6370de5dd3e6a040c4ee5e", size = 2311951 }, - { url = "https://files.pythonhosted.org/packages/27/9d/b5879b34b739fcc9826dec90686fa28aade096edd5b729cb518d31371509/libcst-1.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc95df61838d708adb37e18af1615491f6cac59557fd11077664dd956fe4528", size = 2394329 }, - { url = "https://files.pythonhosted.org/packages/ee/16/f404cdbcb4748a2184a83be33987a98c382e1f95f927b3305cc23f04a89d/libcst-1.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:05c32de72553cb93ff606c7d2421ce1eab1f0740c8c4b715444e2ae42f42b1b6", size = 2254964 }, - { url = "https://files.pythonhosted.org/packages/9b/51/674b34974cd1c5484b29c72cf472d74f6c01b5228b231a1d97121c9fe573/libcst-1.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69b705f5b1faa66f115ede52a970d7613d3a8fb988834f853f7fb46870a041d2", size = 2372717 }, - { url = "https://files.pythonhosted.org/packages/85/b4/f230eddb5afdd37e96d4de6e5314cdd38b2d8ece145b7a4a523851ab21a3/libcst-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:984512829a80f963bfc1803342219a4264a8d4206df0a30eae9bce921357a938", size = 2076934 }, ] [[package]] @@ -1219,9 +1111,6 @@ wheels = [ name = "markdown" version = "3.7" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, -] sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } wheels = [ { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, @@ -1295,16 +1184,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, - { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344 }, - { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389 }, - { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607 }, - { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728 }, - { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826 }, - { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843 }, - { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219 }, - { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946 }, - { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063 }, - { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506 }, ] [[package]] @@ -1345,7 +1224,6 @@ dependencies = [ { name = "click" }, { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "ghp-import" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "jinja2" }, { name = "markdown" }, { name = "markupsafe" }, @@ -1381,7 +1259,6 @@ name = "mkdocs-get-deps" version = "0.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "mergedeep" }, { name = "platformdirs" }, { name = "pyyaml" }, @@ -1509,12 +1386,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 }, { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 }, { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 }, - { url = "https://files.pythonhosted.org/packages/ca/1f/186d133ae2514633f8558e78cd658070ba686c0e9275c5a5c24a1e1f0d67/mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", size = 11200493 }, - { url = "https://files.pythonhosted.org/packages/af/fc/4842485d034e38a4646cccd1369f6b1ccd7bc86989c52770d75d719a9941/mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", size = 10357702 }, - { url = "https://files.pythonhosted.org/packages/b4/e6/457b83f2d701e23869cfec013a48a12638f75b9d37612a9ddf99072c1051/mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", size = 12091104 }, - { url = "https://files.pythonhosted.org/packages/f1/bf/76a569158db678fee59f4fd30b8e7a0d75bcbaeef49edd882a0d63af6d66/mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", size = 12830167 }, - { url = "https://files.pythonhosted.org/packages/43/bc/0bc6b694b3103de9fed61867f1c8bd33336b913d16831431e7cb48ef1c92/mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", size = 13013834 }, - { url = "https://files.pythonhosted.org/packages/b0/79/5f5ec47849b6df1e6943d5fd8e6632fbfc04b4fd4acfa5a5a9535d11b4e2/mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", size = 9781231 }, { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 }, ] @@ -1826,19 +1697,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/d3/6dc91156cf12ed86bed383bcb942d84d23304a1e57b7ab030bf60ea130d6/orjson-3.10.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825", size = 129826 }, { url = "https://files.pythonhosted.org/packages/b3/38/c47c25b86f6996f1343be721b6ea4367bc1c8bc0fc3f6bbcd995d18cb19d/orjson-3.10.15-cp313-cp313-win32.whl", hash = "sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890", size = 142542 }, { url = "https://files.pythonhosted.org/packages/27/f1/1d7ec15b20f8ce9300bc850de1e059132b88990e46cd0ccac29cbf11e4f9/orjson-3.10.15-cp313-cp313-win_amd64.whl", hash = "sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf", size = 133444 }, - { url = "https://files.pythonhosted.org/packages/56/39/b2123d8d98a62ee89626dc7ecb782d9b60a5edb0b5721bc894ee3470df5a/orjson-3.10.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ffe19f3e8d68111e8644d4f4e267a069ca427926855582ff01fc012496d19969", size = 250031 }, - { url = "https://files.pythonhosted.org/packages/65/4d/a058dc6476713cbd5647e5fd0be8d40c27e9ed77d37a788b594c424caa0e/orjson-3.10.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d433bf32a363823863a96561a555227c18a522a8217a6f9400f00ddc70139ae2", size = 125021 }, - { url = "https://files.pythonhosted.org/packages/3d/cb/4d1450bb2c3276f8bf9524df6b01af4d01f55e9a9772555cf119275eb1d0/orjson-3.10.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da03392674f59a95d03fa5fb9fe3a160b0511ad84b7a3914699ea5a1b3a38da2", size = 149957 }, - { url = "https://files.pythonhosted.org/packages/93/7b/d1fae6d4393a9fa8f5d3fb173f0a9c778135569c50e5390811b74c45b4b3/orjson-3.10.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a63bb41559b05360ded9132032239e47983a39b151af1201f07ec9370715c82", size = 139515 }, - { url = "https://files.pythonhosted.org/packages/7f/b2/e0c0b8197c709983093700f9a59aa64478d80edc55fe620bceadb92004e3/orjson-3.10.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3766ac4702f8f795ff3fa067968e806b4344af257011858cc3d6d8721588b53f", size = 154314 }, - { url = "https://files.pythonhosted.org/packages/db/94/eeb94ca3aa7564f753fe352101bcfc8179febaa1888f55ba3cad25b05f71/orjson-3.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a1c73dcc8fadbd7c55802d9aa093b36878d34a3b3222c41052ce6b0fc65f8e8", size = 130145 }, - { url = "https://files.pythonhosted.org/packages/ca/10/54c0118a38eaa5ae832c27306834bdc13954bd0a443b80da63faebf17ffe/orjson-3.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b299383825eafe642cbab34be762ccff9fd3408d72726a6b2a4506d410a71ab3", size = 138344 }, - { url = "https://files.pythonhosted.org/packages/78/87/3c15eeb315171aa27f96bcca87ed54ee292b72d755973a66e3a6800e8ae9/orjson-3.10.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:abc7abecdbf67a173ef1316036ebbf54ce400ef2300b4e26a7b843bd446c2480", size = 130730 }, - { url = "https://files.pythonhosted.org/packages/8a/dc/522430fb24445b9cc8301a5954f80ce8ee244c5159ba913578acc36b078f/orjson-3.10.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:3614ea508d522a621384c1d6639016a5a2e4f027f3e4a1c93a51867615d28829", size = 414482 }, - { url = "https://files.pythonhosted.org/packages/c8/01/83b2e80b9c96ca9753d06e01d325037b2f3e404b14c7a8e875b2f2b7c171/orjson-3.10.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:295c70f9dc154307777ba30fe29ff15c1bcc9dfc5c48632f37d20a607e9ba85a", size = 140792 }, - { url = "https://files.pythonhosted.org/packages/96/40/f211084b0e0267b6b515f05967048d8957839d80ff534bde0dc7f9df9ae0/orjson-3.10.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:63309e3ff924c62404923c80b9e2048c1f74ba4b615e7584584389ada50ed428", size = 129536 }, - { url = "https://files.pythonhosted.org/packages/b2/8c/014d96f5c6446adcd2403fe2d4007ff582f8867f5028b0cd994f0174d61c/orjson-3.10.15-cp39-cp39-win32.whl", hash = "sha256:a2f708c62d026fb5340788ba94a55c23df4e1869fec74be455e0b2f5363b8507", size = 142302 }, - { url = "https://files.pythonhosted.org/packages/47/bd/81da73ef8e66434c51a4ea7db45e3a0b62bff2c3e7ebc723aa4eeead2feb/orjson-3.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:efcf6c735c3d22ef60c4aa27a5238f1a477df85e9b15f2142f9d669beb2d13fd", size = 133401 }, ] [[package]] @@ -1906,8 +1764,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/04/3eaedc2ba17a088961d0e3bd396eac764450f431621b58a04ce898acd126/protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e", size = 417825 }, { url = "https://files.pythonhosted.org/packages/4f/06/7c467744d23c3979ce250397e26d8ad8eeb2bea7b18ca12ad58313c1b8d5/protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84", size = 319573 }, { url = "https://files.pythonhosted.org/packages/a8/45/2ebbde52ad2be18d3675b6bee50e68cd73c9e0654de77d595540b5129df8/protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f", size = 319672 }, - { url = "https://files.pythonhosted.org/packages/85/a6/bf65a38f8be5ab8c3b575822acfd338702fdf7ac9abd8c81630cc7c9f4bd/protobuf-5.29.3-cp39-cp39-win32.whl", hash = "sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7", size = 422676 }, - { url = "https://files.pythonhosted.org/packages/ac/e2/48d46adc86369ff092eaece3e537f76b3baaab45ca3dde257838cde831d2/protobuf-5.29.3-cp39-cp39-win_amd64.whl", hash = "sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da", size = 434593 }, { url = "https://files.pythonhosted.org/packages/fd/b2/ab07b09e0f6d143dfb839693aa05765257bceaa13d03bf1a696b78323e7a/protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f", size = 172550 }, ] @@ -1934,6 +1790,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 }, ] +[[package]] +name = "pydantic-asyncapi" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/86/699fb6125a321c536f3a17d4623bd0e743fb41fe240ca842cacb9e898a2c/pydantic_asyncapi-0.2.1.tar.gz", hash = "sha256:d9894c09b5d5ad308d42da01c5f75f6dd0425d47744cd58e6abadc999b5edbe2", size = 11333 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/a5/9648ef0235d78ca1220e8c8bddc9067092664336bc4bc4105d94d63667d6/pydantic_asyncapi-0.2.1-py3-none-any.whl", hash = "sha256:eefb5e37f27556cc42809b58481d6e3416cca3c2219b4155a1a67c4da747e137", size = 10783 }, +] + [[package]] name = "pydantic-core" version = "2.27.2" @@ -1998,19 +1866,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, - { url = "https://files.pythonhosted.org/packages/27/97/3aef1ddb65c5ccd6eda9050036c956ff6ecbfe66cb7eb40f280f121a5bb0/pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993", size = 1896475 }, - { url = "https://files.pythonhosted.org/packages/ad/d3/5668da70e373c9904ed2f372cb52c0b996426f302e0dee2e65634c92007d/pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308", size = 1772279 }, - { url = "https://files.pythonhosted.org/packages/8a/9e/e44b8cb0edf04a2f0a1f6425a65ee089c1d6f9c4c2dcab0209127b6fdfc2/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4", size = 1829112 }, - { url = "https://files.pythonhosted.org/packages/1c/90/1160d7ac700102effe11616e8119e268770f2a2aa5afb935f3ee6832987d/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf", size = 1866780 }, - { url = "https://files.pythonhosted.org/packages/ee/33/13983426df09a36d22c15980008f8d9c77674fc319351813b5a2739b70f3/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76", size = 2037943 }, - { url = "https://files.pythonhosted.org/packages/01/d7/ced164e376f6747e9158c89988c293cd524ab8d215ae4e185e9929655d5c/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118", size = 2740492 }, - { url = "https://files.pythonhosted.org/packages/8b/1f/3dc6e769d5b7461040778816aab2b00422427bcaa4b56cc89e9c653b2605/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630", size = 1995714 }, - { url = "https://files.pythonhosted.org/packages/07/d7/a0bd09bc39283530b3f7c27033a814ef254ba3bd0b5cfd040b7abf1fe5da/pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54", size = 1997163 }, - { url = "https://files.pythonhosted.org/packages/2d/bb/2db4ad1762e1c5699d9b857eeb41959191980de6feb054e70f93085e1bcd/pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f", size = 2005217 }, - { url = "https://files.pythonhosted.org/packages/53/5f/23a5a3e7b8403f8dd8fc8a6f8b49f6b55c7d715b77dcf1f8ae919eeb5628/pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362", size = 2127899 }, - { url = "https://files.pythonhosted.org/packages/c2/ae/aa38bb8dd3d89c2f1d8362dd890ee8f3b967330821d03bbe08fa01ce3766/pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96", size = 2155726 }, - { url = "https://files.pythonhosted.org/packages/98/61/4f784608cc9e98f70839187117ce840480f768fed5d386f924074bf6213c/pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e", size = 1817219 }, - { url = "https://files.pythonhosted.org/packages/57/82/bb16a68e4a1a858bb3768c2c8f1ff8d8978014e16598f001ea29a25bf1d1/pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67", size = 1985382 }, { url = "https://files.pythonhosted.org/packages/46/72/af70981a341500419e67d5cb45abe552a7c74b66326ac8877588488da1ac/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e", size = 1891159 }, { url = "https://files.pythonhosted.org/packages/ad/3d/c5913cccdef93e0a6a95c2d057d2c2cba347815c845cda79ddd3c0f5e17d/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8", size = 1768331 }, { url = "https://files.pythonhosted.org/packages/f6/f0/a3ae8fbee269e4934f14e2e0e00928f9346c5943174f2811193113e58252/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3", size = 1822467 }, @@ -2020,15 +1875,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/86/aa/837821ecf0c022bbb74ca132e117c358321e72e7f9702d1b6a03758545e2/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50", size = 2116582 }, { url = "https://files.pythonhosted.org/packages/81/b0/5e74656e95623cbaa0a6278d16cf15e10a51f6002e3ec126541e95c29ea3/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9", size = 2151985 }, { url = "https://files.pythonhosted.org/packages/63/37/3e32eeb2a451fddaa3898e2163746b0cffbbdbb4740d38372db0490d67f3/pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151", size = 2004715 }, - { url = "https://files.pythonhosted.org/packages/29/0e/dcaea00c9dbd0348b723cae82b0e0c122e0fa2b43fa933e1622fd237a3ee/pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656", size = 1891733 }, - { url = "https://files.pythonhosted.org/packages/86/d3/e797bba8860ce650272bda6383a9d8cad1d1c9a75a640c9d0e848076f85e/pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278", size = 1768375 }, - { url = "https://files.pythonhosted.org/packages/41/f7/f847b15fb14978ca2b30262548f5fc4872b2724e90f116393eb69008299d/pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb", size = 1822307 }, - { url = "https://files.pythonhosted.org/packages/9c/63/ed80ec8255b587b2f108e514dc03eed1546cd00f0af281e699797f373f38/pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd", size = 1979971 }, - { url = "https://files.pythonhosted.org/packages/a9/6d/6d18308a45454a0de0e975d70171cadaf454bc7a0bf86b9c7688e313f0bb/pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc", size = 1987616 }, - { url = "https://files.pythonhosted.org/packages/82/8a/05f8780f2c1081b800a7ca54c1971e291c2d07d1a50fb23c7e4aef4ed403/pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b", size = 1998943 }, - { url = "https://files.pythonhosted.org/packages/5e/3e/fe5b6613d9e4c0038434396b46c5303f5ade871166900b357ada4766c5b7/pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b", size = 2116654 }, - { url = "https://files.pythonhosted.org/packages/db/ad/28869f58938fad8cc84739c4e592989730bfb69b7c90a8fff138dff18e1e/pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2", size = 2152292 }, - { url = "https://files.pythonhosted.org/packages/a1/0c/c5c5cd3689c32ed1fe8c5d234b079c12c281c051759770c05b8bed6412b5/pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35", size = 2004961 }, ] [[package]] @@ -2215,15 +2061,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, - { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, - { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, - { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, - { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, - { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, - { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, - { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, - { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, - { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, ] [[package]] @@ -2317,22 +2154,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, - { url = "https://files.pythonhosted.org/packages/89/23/c4a86df398e57e26f93b13ae63acce58771e04bdde86092502496fa57f9c/regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839", size = 482682 }, - { url = "https://files.pythonhosted.org/packages/3c/8b/45c24ab7a51a1658441b961b86209c43e6bb9d39caf1e63f46ce6ea03bc7/regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e", size = 287679 }, - { url = "https://files.pythonhosted.org/packages/7a/d1/598de10b17fdafc452d11f7dada11c3be4e379a8671393e4e3da3c4070df/regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf", size = 284578 }, - { url = "https://files.pythonhosted.org/packages/49/70/c7eaa219efa67a215846766fde18d92d54cb590b6a04ffe43cef30057622/regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b", size = 782012 }, - { url = "https://files.pythonhosted.org/packages/89/e5/ef52c7eb117dd20ff1697968219971d052138965a4d3d9b95e92e549f505/regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0", size = 820580 }, - { url = "https://files.pythonhosted.org/packages/5f/3f/9f5da81aff1d4167ac52711acf789df13e789fe6ac9545552e49138e3282/regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b", size = 809110 }, - { url = "https://files.pythonhosted.org/packages/86/44/2101cc0890c3621b90365c9ee8d7291a597c0722ad66eccd6ffa7f1bcc09/regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef", size = 780919 }, - { url = "https://files.pythonhosted.org/packages/ce/2e/3e0668d8d1c7c3c0d397bf54d92fc182575b3a26939aed5000d3cc78760f/regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48", size = 771515 }, - { url = "https://files.pythonhosted.org/packages/a6/49/1bc4584254355e3dba930a3a2fd7ad26ccba3ebbab7d9100db0aff2eedb0/regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13", size = 696957 }, - { url = "https://files.pythonhosted.org/packages/c8/dd/42879c1fc8a37a887cd08e358af3d3ba9e23038cd77c7fe044a86d9450ba/regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2", size = 768088 }, - { url = "https://files.pythonhosted.org/packages/89/96/c05a0fe173cd2acd29d5e13c1adad8b706bcaa71b169e1ee57dcf2e74584/regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95", size = 774752 }, - { url = "https://files.pythonhosted.org/packages/b5/f3/a757748066255f97f14506483436c5f6aded7af9e37bca04ec30c90ca683/regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9", size = 838862 }, - { url = "https://files.pythonhosted.org/packages/5c/93/c6d2092fd479dcaeea40fc8fa673822829181ded77d294a7f950f1dda6e2/regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f", size = 842622 }, - { url = "https://files.pythonhosted.org/packages/ff/9c/daa99532c72f25051a90ef90e1413a8d54413a9e64614d9095b0c1c154d0/regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b", size = 772713 }, - { url = "https://files.pythonhosted.org/packages/13/5d/61a533ccb8c231b474ac8e3a7d70155b00dfc61af6cafdccd1947df6d735/regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57", size = 261756 }, - { url = "https://files.pythonhosted.org/packages/dc/7b/e59b7f7c91ae110d154370c24133f947262525b5d6406df65f23422acc17/regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983", size = 274110 }, ] [[package]] @@ -2476,14 +2297,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/27/11/fa63a77c88eb2f79bb8b438271fbacd66a546a438e4eaba32d62f11298e2/SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6", size = 3149589 }, { url = "https://files.pythonhosted.org/packages/b6/04/fcdd103b6871f2110460b8275d1c4828daa806997b0fa5a01c1cd7fd522d/SQLAlchemy-2.0.37-cp313-cp313-win32.whl", hash = "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2", size = 2070746 }, { url = "https://files.pythonhosted.org/packages/d4/7c/e024719205bdc1465b7b7d3d22ece8e1ad57bc7d76ef6ed78bb5f812634a/SQLAlchemy-2.0.37-cp313-cp313-win_amd64.whl", hash = "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2", size = 2094612 }, - { url = "https://files.pythonhosted.org/packages/70/c9/f199edc09a630ac62079977cbb8a50888cb920c1f635ce254cb4d61e1dda/SQLAlchemy-2.0.37-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:648ec5acf95ad59255452ef759054f2176849662af4521db6cb245263ae4aa33", size = 2105789 }, - { url = "https://files.pythonhosted.org/packages/e7/cc/9286318598bb26af535f480636ed6cf368794f2b483122ce7f2a56acef57/SQLAlchemy-2.0.37-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:35bd2df269de082065d4b23ae08502a47255832cc3f17619a5cea92ce478b02b", size = 2097013 }, - { url = "https://files.pythonhosted.org/packages/db/41/efaa216b3ebe2989f233ac72abed7281c8fe45a40a2cae7a06c3b1cef870/SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f581d365af9373a738c49e0c51e8b18e08d8a6b1b15cc556773bcd8a192fa8b", size = 3090933 }, - { url = "https://files.pythonhosted.org/packages/65/ee/b99bb446b0dc8fa5e2dbd47bf379bc62c5f2823681732fd3a253b1c49a6e/SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82df02816c14f8dc9f4d74aea4cb84a92f4b0620235daa76dde002409a3fbb5a", size = 3098730 }, - { url = "https://files.pythonhosted.org/packages/dd/61/a9eac6696ca4791895784871f79b32bcf1b0dd17614479734558036af8d8/SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94b564e38b344d3e67d2e224f0aec6ba09a77e4582ced41e7bfd0f757d926ec9", size = 3057751 }, - { url = "https://files.pythonhosted.org/packages/95/be/d70fa8a42287976dad0d590f75633ec203694d2f9bafd1cdba41d8e4db55/SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:955a2a765aa1bd81aafa69ffda179d4fe3e2a3ad462a736ae5b6f387f78bfeb8", size = 3084290 }, - { url = "https://files.pythonhosted.org/packages/18/e9/a00e73a7e8eb620ea030592c7d3a9b66c31bc6b36effdf04f10c7ada8dc1/SQLAlchemy-2.0.37-cp39-cp39-win32.whl", hash = "sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278", size = 2077561 }, - { url = "https://files.pythonhosted.org/packages/2a/52/f3fff9216b9df07e8142001e638d5ba8c298299a2a9006b9ab3b068fb0f1/SQLAlchemy-2.0.37-cp39-cp39-win_amd64.whl", hash = "sha256:4b12885dc85a2ab2b7d00995bac6d967bffa8594123b02ed21e8eb2205a7584b", size = 2101760 }, { url = "https://files.pythonhosted.org/packages/3b/36/59cc97c365f2f79ac9f3f51446cae56dfd82c4f2dd98497e6be6de20fb91/SQLAlchemy-2.0.37-py3-none-any.whl", hash = "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1", size = 1894113 }, ] @@ -2514,7 +2327,6 @@ version = "0.45.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 } wheels = [ @@ -2732,12 +2544,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068 }, { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428 }, { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018 }, - { url = "https://files.pythonhosted.org/packages/3c/a4/646a9d0edff7cde25fc1734695d3dfcee0501140dd0e723e4df3f0a50acb/uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b", size = 1439646 }, - { url = "https://files.pythonhosted.org/packages/01/2e/e128c66106af9728f86ebfeeb52af27ecd3cb09336f3e2f3e06053707a15/uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2", size = 800931 }, - { url = "https://files.pythonhosted.org/packages/2d/1a/9fbc2b1543d0df11f7aed1632f64bdf5ecc4053cf98cdc9edb91a65494f9/uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0", size = 3829660 }, - { url = "https://files.pythonhosted.org/packages/b8/c0/392e235e4100ae3b95b5c6dac77f82b529d2760942b1e7e0981e5d8e895d/uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75", size = 3827185 }, - { url = "https://files.pythonhosted.org/packages/e1/24/a5da6aba58f99aed5255eca87d58d1760853e8302d390820cc29058408e3/uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd", size = 3705833 }, - { url = "https://files.pythonhosted.org/packages/1a/5c/6ba221bb60f1e6474474102e17e38612ec7a06dc320e22b687ab563d877f/uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff", size = 3804696 }, ] [[package]] @@ -2758,13 +2564,8 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, - { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", size = 96390 }, - { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", size = 88386 }, - { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", size = 89017 }, { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902 }, { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380 }, - { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", size = 87903 }, - { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", size = 88381 }, { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, @@ -2849,26 +2650,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f2/e1/0025d365cf6248c4d1ee4c3d2e3d373bdd3f6aff78ba4298f97b4fad2740/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303", size = 611911 }, { url = "https://files.pythonhosted.org/packages/55/55/035838277d8c98fc8c917ac9beeb0cd6c59d675dc2421df5f9fcf44a0070/watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80", size = 271152 }, { url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 }, - { url = "https://files.pythonhosted.org/packages/15/81/54484fc2fa715abe79694b975692af963f0878fb9d72b8251aa542bf3f10/watchfiles-1.0.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:d3452c1ec703aa1c61e15dfe9d482543e4145e7c45a6b8566978fbb044265a21", size = 394967 }, - { url = "https://files.pythonhosted.org/packages/14/b3/557f0cd90add86586fe3deeebd11e8299db6bc3452b44a534f844c6ab831/watchfiles-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7b75fee5a16826cf5c46fe1c63116e4a156924d668c38b013e6276f2582230f0", size = 384707 }, - { url = "https://files.pythonhosted.org/packages/03/a3/34638e1bffcb85a405e7b005e30bb211fd9be2ab2cb1847f2ceb81bef27b/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e997802d78cdb02623b5941830ab06f8860038faf344f0d288d325cc9c5d2ff", size = 450442 }, - { url = "https://files.pythonhosted.org/packages/8f/9f/6a97460dd11a606003d634c7158d9fea8517e98daffc6f56d0f5fde2e86a/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0611d244ce94d83f5b9aff441ad196c6e21b55f77f3c47608dcf651efe54c4a", size = 455959 }, - { url = "https://files.pythonhosted.org/packages/9d/bb/e0648c6364e4d37ec692bc3f0c77507d17d8bb8f75689148819142010bbf/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9745a4210b59e218ce64c91deb599ae8775c8a9da4e95fb2ee6fe745fc87d01a", size = 483187 }, - { url = "https://files.pythonhosted.org/packages/dd/ad/d9290586a25288a81dfa8ad6329cf1de32aa1a9798ace45259eb95dcfb37/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4810ea2ae622add560f4aa50c92fef975e475f7ac4900ce5ff5547b2434642d8", size = 519733 }, - { url = "https://files.pythonhosted.org/packages/4e/a9/150c1666825cc9637093f8cae7fc6f53b3296311ab8bd65f1389acb717cb/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:740d103cd01458f22462dedeb5a3382b7f2c57d07ff033fbc9465919e5e1d0f3", size = 502275 }, - { url = "https://files.pythonhosted.org/packages/44/dc/5bfd21e20a330aca1706ac44713bc322838061938edf4b53130f97a7b211/watchfiles-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdbd912a61543a36aef85e34f212e5d2486e7c53ebfdb70d1e0b060cc50dd0bf", size = 452907 }, - { url = "https://files.pythonhosted.org/packages/50/fe/8f4fc488f1699f564687b697456eb5c0cb8e2b0b8538150511c234c62094/watchfiles-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0bc80d91ddaf95f70258cf78c471246846c1986bcc5fd33ccc4a1a67fcb40f9a", size = 615927 }, - { url = "https://files.pythonhosted.org/packages/ad/19/2e45f6f6eec89dd97a4d281635e3d73c17e5f692e7432063bdfdf9562c89/watchfiles-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab0311bb2ffcd9f74b6c9de2dda1612c13c84b996d032cd74799adb656af4e8b", size = 613435 }, - { url = "https://files.pythonhosted.org/packages/91/17/dc5ac62ca377827c24321d68050efc2eaee2ebaf3f21d055bbce2206d309/watchfiles-1.0.4-cp39-cp39-win32.whl", hash = "sha256:02a526ee5b5a09e8168314c905fc545c9bc46509896ed282aeb5a8ba9bd6ca27", size = 270810 }, - { url = "https://files.pythonhosted.org/packages/82/2b/dad851342492d538e7ffe72a8c756f747dd147988abb039ac9d6577d2235/watchfiles-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:a5ae5706058b27c74bac987d615105da17724172d5aaacc6c362a40599b6de43", size = 284866 }, { url = "https://files.pythonhosted.org/packages/6f/06/175d5ac6b838fb319008c0cd981d7bf289317c510154d411d3584ca2b67b/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdcc92daeae268de1acf5b7befcd6cfffd9a047098199056c72e4623f531de18", size = 396269 }, { url = "https://files.pythonhosted.org/packages/86/ee/5db93b0b57dc0587abdbac4149296ee73275f615d790a82cb5598af0557f/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8d3d9203705b5797f0af7e7e5baa17c8588030aaadb7f6a86107b7247303817", size = 386010 }, { url = "https://files.pythonhosted.org/packages/75/61/fe0dc5fedf152bfc085a53711f740701f6bdb8ab6b5c950402b681d4858b/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdef5a1be32d0b07dcea3318a0be95d42c98ece24177820226b56276e06b63b0", size = 450913 }, { url = "https://files.pythonhosted.org/packages/9f/dd/3c7731af3baf1a9957afc643d176f94480921a690ec3237c9f9d11301c08/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:342622287b5604ddf0ed2d085f3a589099c9ae8b7331df3ae9845571586c4f3d", size = 453474 }, - { url = "https://files.pythonhosted.org/packages/6b/b4/c3998f54c91a35cee60ee6d3a855a069c5dff2bae6865147a46e9090dccd/watchfiles-1.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9fe37a2de80aa785d340f2980276b17ef697ab8db6019b07ee4fd28a8359d2f3", size = 395565 }, - { url = "https://files.pythonhosted.org/packages/3f/05/ac1a4d235beb9ddfb8ac26ce93a00ba6bd1b1b43051ef12d7da957b4a9d1/watchfiles-1.0.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9d1ef56b56ed7e8f312c934436dea93bfa3e7368adfcf3df4c0da6d4de959a1e", size = 385406 }, - { url = "https://files.pythonhosted.org/packages/4c/ea/36532e7d86525f4e52a10efed182abf33efb106a93d49f5fbc994b256bcd/watchfiles-1.0.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b42cac65beae3a362629950c444077d1b44f1790ea2772beaea95451c086bb", size = 450424 }, - { url = "https://files.pythonhosted.org/packages/7a/e9/3cbcf4d70cd0b6d3f30631deae1bf37cc0be39887ca327a44462fe546bf5/watchfiles-1.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e0227b8ed9074c6172cf55d85b5670199c99ab11fd27d2c473aa30aec67ee42", size = 452488 }, ] [[package]] @@ -2933,29 +2718,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/c9/67a8f08923cf55ce61aadda72089e3ed4353a95a3a4bc8bf42082810e580/websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307", size = 169856 }, { url = "https://files.pythonhosted.org/packages/17/b1/1ffdb2680c64e9c3921d99db460546194c40d4acbef999a18c37aa4d58a3/websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc", size = 163974 }, { url = "https://files.pythonhosted.org/packages/14/13/8b7fc4cb551b9cfd9890f0fd66e53c18a06240319915533b033a56a3d520/websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f", size = 164420 }, - { url = "https://files.pythonhosted.org/packages/6f/eb/367e0ed7b8a960b4fc12c7c6bf3ebddf06875037de641637994849560d47/websockets-14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7cd5706caec1686c5d233bc76243ff64b1c0dc445339bd538f30547e787c11fe", size = 163087 }, - { url = "https://files.pythonhosted.org/packages/96/f7/1f18d028ec4a2c14598dfec6a73381a915c27464b693873198c1de872095/websockets-14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec607328ce95a2f12b595f7ae4c5d71bf502212bddcea528290b35c286932b12", size = 160740 }, - { url = "https://files.pythonhosted.org/packages/5c/db/b4b353fb9c3f0eaa8138ea4c76e6fa555b6d2821ed2d51d0ac3c320bc57e/websockets-14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da85651270c6bfb630136423037dd4975199e5d4114cae6d3066641adcc9d1c7", size = 160992 }, - { url = "https://files.pythonhosted.org/packages/b9/b1/9149e420c61f375e432654d5c1545e563b90ac1f829ee1a8d1dccaf0869d/websockets-14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ecadc7ce90accf39903815697917643f5b7cfb73c96702318a096c00aa71f5", size = 169757 }, - { url = "https://files.pythonhosted.org/packages/2b/33/0bb58204191e113212360f1392b6b1e9f85f62c7ca5b3b15f52f2f835516/websockets-14.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1979bee04af6a78608024bad6dfcc0cc930ce819f9e10342a29a05b5320355d0", size = 168762 }, - { url = "https://files.pythonhosted.org/packages/be/3d/c3c192f16210d7b7535fbf4ee9a299612f4dccff665587617b13fa0a6aa3/websockets-14.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dddacad58e2614a24938a50b85969d56f88e620e3f897b7d80ac0d8a5800258", size = 169060 }, - { url = "https://files.pythonhosted.org/packages/a6/73/75efa8d9e4b1b257818a7b7a0b9ac84a07c91120b52148941370ef2c8f16/websockets-14.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:89a71173caaf75fa71a09a5f614f450ba3ec84ad9fca47cb2422a860676716f0", size = 169457 }, - { url = "https://files.pythonhosted.org/packages/a4/11/300cf36cfd6990ffb218394862f0513be8c21917c9ff5e362f94599caedd/websockets-14.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6af6a4b26eea4fc06c6818a6b962a952441e0e39548b44773502761ded8cc1d4", size = 168860 }, - { url = "https://files.pythonhosted.org/packages/c0/3d/5fd82500714ab7c09f003bde671dad1a3a131ac77b6b11ada72e466de4f6/websockets-14.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:80c8efa38957f20bba0117b48737993643204645e9ec45512579132508477cfc", size = 168825 }, - { url = "https://files.pythonhosted.org/packages/88/16/715580eb6caaacc232f303e9619103a42dcd354b0854baa5ed26aacaf828/websockets-14.2-cp39-cp39-win32.whl", hash = "sha256:2e20c5f517e2163d76e2729104abc42639c41cf91f7b1839295be43302713661", size = 163960 }, - { url = "https://files.pythonhosted.org/packages/63/a7/a1035cb198eaa12eaa9621aaaa3ec021b0e3bac96e1df9ceb6bfe5e53e5f/websockets-14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4c8cef610e8d7c70dea92e62b6814a8cd24fbd01d7103cc89308d2bfe1659ef", size = 164424 }, { url = "https://files.pythonhosted.org/packages/10/3d/91d3d2bb1325cd83e8e2c02d0262c7d4426dc8fa0831ef1aa4d6bf2041af/websockets-14.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7d9cafbccba46e768be8a8ad4635fa3eae1ffac4c6e7cb4eb276ba41297ed29", size = 160773 }, { url = "https://files.pythonhosted.org/packages/33/7c/cdedadfef7381939577858b1b5718a4ab073adbb584e429dd9d9dc9bfe16/websockets-14.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c76193c1c044bd1e9b3316dcc34b174bbf9664598791e6fb606d8d29000e070c", size = 161007 }, { url = "https://files.pythonhosted.org/packages/ca/35/7a20a3c450b27c04e50fbbfc3dfb161ed8e827b2a26ae31c4b59b018b8c6/websockets-14.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2", size = 162264 }, { url = "https://files.pythonhosted.org/packages/e8/9c/e3f9600564b0c813f2448375cf28b47dc42c514344faed3a05d71fb527f9/websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6c0097a41968b2e2b54ed3424739aab0b762ca92af2379f152c1aef0187e1c", size = 161873 }, { url = "https://files.pythonhosted.org/packages/3f/37/260f189b16b2b8290d6ae80c9f96d8b34692cf1bb3475df54c38d3deb57d/websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7ff794c8b36bc402f2e07c0b2ceb4a2424147ed4785ff03e2a7af03711d60a", size = 161818 }, { url = "https://files.pythonhosted.org/packages/ff/1e/e47dedac8bf7140e59aa6a679e850c4df9610ae844d71b6015263ddea37b/websockets-14.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dec254fcabc7bd488dab64846f588fc5b6fe0d78f641180030f8ea27b76d72c3", size = 164465 }, - { url = "https://files.pythonhosted.org/packages/f7/c0/8e9325c4987dcf66d4a0d63ec380d4aefe8dcc1e521af71ad17adf2c1ae2/websockets-14.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bbe03eb853e17fd5b15448328b4ec7fb2407d45fb0245036d06a3af251f8e48f", size = 160773 }, - { url = "https://files.pythonhosted.org/packages/5a/6e/c9a7f2edd4afddc4f8cccfc4e12468b7f6ec40f28d1b1e966a8d0298b875/websockets-14.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3c4aa3428b904d5404a0ed85f3644d37e2cb25996b7f096d77caeb0e96a3b42", size = 161006 }, - { url = "https://files.pythonhosted.org/packages/f3/10/b90ece894828c954e674a81cb0db250e6c324c54db30a8b19e96431f928f/websockets-14.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:577a4cebf1ceaf0b65ffc42c54856214165fb8ceeba3935852fc33f6b0c55e7f", size = 162260 }, - { url = "https://files.pythonhosted.org/packages/52/93/1147b6b5464a5fb6e8987da3ec7991dcc44f9090f67d9c841d7382fed429/websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad1c1d02357b7665e700eca43a31d52814ad9ad9b89b58118bdabc365454b574", size = 161868 }, - { url = "https://files.pythonhosted.org/packages/32/ab/f7d80b4049bff0aa617507330db3a27389d0e70df54e29f7a3d76bbd2086/websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f390024a47d904613577df83ba700bd189eedc09c57af0a904e5c39624621270", size = 161813 }, - { url = "https://files.pythonhosted.org/packages/cd/cc/adc9fb85f031b8df8e9f3d96cc004df25d2643e503953af5223c5b6825b7/websockets-14.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c1426c021c38cf92b453cdf371228d3430acd775edee6bac5a4d577efc72365", size = 164457 }, { url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416 }, ] @@ -3020,17 +2788,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, - { url = "https://files.pythonhosted.org/packages/8a/f4/6ed2b8f6f1c832933283974839b88ec7c983fd12905e01e97889dadf7559/wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a", size = 53308 }, - { url = "https://files.pythonhosted.org/packages/a2/a9/712a53f8f4f4545768ac532619f6e56d5d0364a87b2212531685e89aeef8/wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061", size = 38489 }, - { url = "https://files.pythonhosted.org/packages/fa/9b/e172c8f28a489a2888df18f953e2f6cb8d33b1a2e78c9dfc52d8bf6a5ead/wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82", size = 38776 }, - { url = "https://files.pythonhosted.org/packages/cf/cb/7a07b51762dcd59bdbe07aa97f87b3169766cadf240f48d1cbe70a1be9db/wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9", size = 83050 }, - { url = "https://files.pythonhosted.org/packages/a5/51/a42757dd41032afd6d8037617aa3bc6803ba971850733b24dfb7d5c627c4/wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f", size = 74718 }, - { url = "https://files.pythonhosted.org/packages/bf/bb/d552bfe47db02fcfc950fc563073a33500f8108efa5f7b41db2f83a59028/wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b", size = 82590 }, - { url = "https://files.pythonhosted.org/packages/77/99/77b06b3c3c410dbae411105bf22496facf03a5496bfaca8fbcf9da381889/wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f", size = 81462 }, - { url = "https://files.pythonhosted.org/packages/2d/21/cf0bd85ae66f92600829ea1de8e1da778e5e9f6e574ccbe74b66db0d95db/wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8", size = 74309 }, - { url = "https://files.pythonhosted.org/packages/6d/16/112d25e9092398a0dd6fec50ab7ac1b775a0c19b428f049785096067ada9/wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9", size = 81081 }, - { url = "https://files.pythonhosted.org/packages/2b/49/364a615a0cc0872685646c495c7172e4fc7bf1959e3b12a1807a03014e05/wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb", size = 36423 }, - { url = "https://files.pythonhosted.org/packages/00/ad/5d2c1b34ba3202cd833d9221833e74d6500ce66730974993a8dc9a94fb8c/wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb", size = 38772 }, { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, ] @@ -3088,10 +2845,4 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b8/f6/54548df6dc73e30ac6c8a7ff1da73ac9007ba38f866397091d5a82237bd3/zope.interface-7.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398", size = 259237 }, { url = "https://files.pythonhosted.org/packages/b6/66/ac05b741c2129fdf668b85631d2268421c5cd1a9ff99be1674371139d665/zope.interface-7.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b", size = 264696 }, { url = "https://files.pythonhosted.org/packages/0a/2f/1bccc6f4cc882662162a1158cda1a7f616add2ffe322b28c99cb031b4ffc/zope.interface-7.2-cp313-cp313-win_amd64.whl", hash = "sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd", size = 212472 }, - { url = "https://files.pythonhosted.org/packages/8c/2c/1f49dc8b4843c4f0848d8e43191aed312bad946a1563d1bf9e46cf2816ee/zope.interface-7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bd449c306ba006c65799ea7912adbbfed071089461a19091a228998b82b1fdb", size = 208349 }, - { url = "https://files.pythonhosted.org/packages/ed/7d/83ddbfc8424c69579a90fc8edc2b797223da2a8083a94d8dfa0e374c5ed4/zope.interface-7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a19a6cc9c6ce4b1e7e3d319a473cf0ee989cbbe2b39201d7c19e214d2dfb80c7", size = 208799 }, - { url = "https://files.pythonhosted.org/packages/36/22/b1abd91854c1be03f5542fe092e6a745096d2eca7704d69432e119100583/zope.interface-7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cd1790b48c16db85d51fbbd12d20949d7339ad84fd971427cf00d990c1f137", size = 254267 }, - { url = "https://files.pythonhosted.org/packages/2a/dd/fcd313ee216ad0739ae00e6126bc22a0af62a74f76a9ca668d16cd276222/zope.interface-7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52e446f9955195440e787596dccd1411f543743c359eeb26e9b2c02b077b0519", size = 248614 }, - { url = "https://files.pythonhosted.org/packages/88/d4/4ba1569b856870527cec4bf22b91fe704b81a3c1a451b2ccf234e9e0666f/zope.interface-7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad9913fd858274db8dd867012ebe544ef18d218f6f7d1e3c3e6d98000f14b75", size = 253800 }, - { url = "https://files.pythonhosted.org/packages/69/da/c9cfb384c18bd3a26d9fc6a9b5f32ccea49ae09444f097eaa5ca9814aff9/zope.interface-7.2-cp39-cp39-win_amd64.whl", hash = "sha256:1090c60116b3da3bfdd0c03406e2f14a1ff53e5771aebe33fec1edc0a350175d", size = 211980 }, ] From 04d3d9189fdde505795d5439e99e0cb91ade8bb3 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Tue, 28 Jan 2025 00:36:54 +0000 Subject: [PATCH 02/19] Create stub HTML rendered AsyncAPI docs --- src/http_app/routes/asyncapi_docs.py | 92 ++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 5 deletions(-) diff --git a/src/http_app/routes/asyncapi_docs.py b/src/http_app/routes/asyncapi_docs.py index df9485d4..345f548f 100644 --- a/src/http_app/routes/asyncapi_docs.py +++ b/src/http_app/routes/asyncapi_docs.py @@ -1,8 +1,11 @@ +import json + import pydantic_asyncapi as pa from fastapi import APIRouter +from starlette.responses import HTMLResponse schema = pa.AsyncAPIV3( - id="http://aa.aa.aa", + asyncapi="3.0.0", info=pa.v3.Info( title="Bookstore API", version="1.0.0", @@ -10,13 +13,92 @@ ), ) -router = APIRouter(prefix="/asyncapi") +router = APIRouter(prefix="/docs") @router.get("/asyncapi.json", response_model_exclude_unset=True) def asyncapi_raw() -> pa.AsyncAPIV3: return schema -@router.get("/docs", response_model_exclude_unset=True) -def asyncapi_docs() -> pa.AsyncAPIV3: - return schema +ASYNCAPI_JS_DEFAULT_URL = "https://unpkg.com/@asyncapi/react-component@2.5.0/browser/standalone/index.js" +NORMALIZE_CSS_DEFAULT_URL = "https://cdn.jsdelivr.net/npm/modern-normalize/modern-normalize.min.css" +ASYNCAPI_CSS_DEFAULT_URL = ( + "https://unpkg.com/@asyncapi/react-component@2.5.0/styles/default.min.css" +) + + +# https://github.com/asyncapi/asyncapi-react/blob/v2.5.0/docs/usage/standalone-bundle.md +@router.get("/asyncapi") +def get_asyncapi_html( + sidebar: bool = True, + info: bool = True, + servers: bool = True, + operations: bool = True, + messages: bool = True, + schemas: bool = True, + errors: bool = True, + expand_message_examples: bool = True, + title: str = "Bookstore API", +) -> HTMLResponse: + """Generate HTML for displaying an AsyncAPI document.""" + schema_json = schema.model_dump_json(exclude_unset=True) + + config = { + "schema": schema_json, + "config": { + "show": { + "sidebar": sidebar, + "info": info, + "servers": servers, + "operations": operations, + "messages": messages, + "schemas": schemas, + "errors": errors, + }, + "expand": { + "messageExamples": expand_message_examples, + }, + "sidebar": { + "showServers": "byDefault", + "showOperations": "byDefault", + }, + }, + } + + return HTMLResponse(""" + + + + """ + f""" + {title} AsyncAPI + """ + """ + + + + + """ + f""" + + + """ + """ + + + +
+ """ + f""" + + + + + """ + ) From bc1d4d4a0253f49ae01f6140b629a91f5b9d4141 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Tue, 28 Jan 2025 21:22:35 +0000 Subject: [PATCH 03/19] Add example websocket endpoint --- src/http_app/routes/__init__.py | 2 ++ src/http_app/routes/websocket/__init__.py | 6 ++++ src/http_app/routes/websocket/chat.py | 39 +++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/http_app/routes/websocket/__init__.py create mode 100644 src/http_app/routes/websocket/chat.py diff --git a/src/http_app/routes/__init__.py b/src/http_app/routes/__init__.py index 10a003ff..559ccff7 100644 --- a/src/http_app/routes/__init__.py +++ b/src/http_app/routes/__init__.py @@ -1,6 +1,7 @@ from fastapi import FastAPI from http_app.routes import api, events, graphql, hello, ping, user_registered_hook, asyncapi_docs +from . import websocket def init_routes(app: FastAPI) -> None: @@ -11,3 +12,4 @@ def init_routes(app: FastAPI) -> None: app.include_router(events.router) app.include_router(user_registered_hook.router) app.include_router(graphql.router, prefix="/graphql") + app.include_router(websocket.router) diff --git a/src/http_app/routes/websocket/__init__.py b/src/http_app/routes/websocket/__init__.py new file mode 100644 index 00000000..3ba28ba3 --- /dev/null +++ b/src/http_app/routes/websocket/__init__.py @@ -0,0 +1,6 @@ +from fastapi import APIRouter + +from . import chat + +router = APIRouter(prefix="/ws") +router.include_router(chat.router) \ No newline at end of file diff --git a/src/http_app/routes/websocket/chat.py b/src/http_app/routes/websocket/chat.py new file mode 100644 index 00000000..d0964fa0 --- /dev/null +++ b/src/http_app/routes/websocket/chat.py @@ -0,0 +1,39 @@ +from fastapi import APIRouter +from starlette.websockets import WebSocket, WebSocketDisconnect + +router = APIRouter(prefix="/chat") + + +class ConnectionManager: + def __init__(self): + self.active_connections: list[WebSocket] = [] + + async def connect(self, websocket: WebSocket): + await websocket.accept() + self.active_connections.append(websocket) + + def disconnect(self, websocket: WebSocket): + self.active_connections.remove(websocket) + + async def send_personal_message(self, message: str, websocket: WebSocket): + await websocket.send_text(message) + + async def broadcast(self, message: str): + for connection in self.active_connections: + await connection.send_text(message) + + +manager = ConnectionManager() + + +@router.websocket("/{client_id}") +async def websocket_endpoint(websocket: WebSocket, client_id: int): + await manager.connect(websocket) + try: + while True: + data = await websocket.receive_text() + await manager.send_personal_message(f"You wrote: {data}", websocket) + await manager.broadcast(f"Client #{client_id} says: {data}") + except WebSocketDisconnect: + manager.disconnect(websocket) + await manager.broadcast(f"Client #{client_id} left the chat") \ No newline at end of file From d56ea397f7ee682b4a7ea6e1d7aa654d5705c79c Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:14:54 +0000 Subject: [PATCH 04/19] Example rendering AsyncAPI from pydantic models and mapping --- src/http_app/routes/__init__.py | 17 +- src/http_app/routes/asyncapi_docs.py | 104 ----------- src/http_app/routes/docs_ws.py | 165 ++++++++++++++++++ .../routes/{websocket => ws}/__init__.py | 2 +- src/http_app/routes/{websocket => ws}/chat.py | 2 +- 5 files changed, 180 insertions(+), 110 deletions(-) delete mode 100644 src/http_app/routes/asyncapi_docs.py create mode 100644 src/http_app/routes/docs_ws.py rename src/http_app/routes/{websocket => ws}/__init__.py (70%) rename src/http_app/routes/{websocket => ws}/chat.py (99%) diff --git a/src/http_app/routes/__init__.py b/src/http_app/routes/__init__.py index 559ccff7..b8d96a91 100644 --- a/src/http_app/routes/__init__.py +++ b/src/http_app/routes/__init__.py @@ -1,15 +1,24 @@ from fastapi import FastAPI -from http_app.routes import api, events, graphql, hello, ping, user_registered_hook, asyncapi_docs -from . import websocket +from http_app.routes import ( + api, + docs_ws, + events, + graphql, + hello, + ping, + user_registered_hook, +) + +from . import ws def init_routes(app: FastAPI) -> None: app.include_router(api.router) - app.include_router(asyncapi_docs.router) + app.include_router(docs_ws.router) app.include_router(ping.router) app.include_router(hello.router) app.include_router(events.router) app.include_router(user_registered_hook.router) app.include_router(graphql.router, prefix="/graphql") - app.include_router(websocket.router) + app.include_router(ws.router) diff --git a/src/http_app/routes/asyncapi_docs.py b/src/http_app/routes/asyncapi_docs.py deleted file mode 100644 index 345f548f..00000000 --- a/src/http_app/routes/asyncapi_docs.py +++ /dev/null @@ -1,104 +0,0 @@ -import json - -import pydantic_asyncapi as pa -from fastapi import APIRouter -from starlette.responses import HTMLResponse - -schema = pa.AsyncAPIV3( - asyncapi="3.0.0", - info=pa.v3.Info( - title="Bookstore API", - version="1.0.0", - description="test", - ), -) - -router = APIRouter(prefix="/docs") - -@router.get("/asyncapi.json", response_model_exclude_unset=True) -def asyncapi_raw() -> pa.AsyncAPIV3: - return schema - -ASYNCAPI_JS_DEFAULT_URL = "https://unpkg.com/@asyncapi/react-component@2.5.0/browser/standalone/index.js" -NORMALIZE_CSS_DEFAULT_URL = "https://cdn.jsdelivr.net/npm/modern-normalize/modern-normalize.min.css" -ASYNCAPI_CSS_DEFAULT_URL = ( - "https://unpkg.com/@asyncapi/react-component@2.5.0/styles/default.min.css" -) - - -# https://github.com/asyncapi/asyncapi-react/blob/v2.5.0/docs/usage/standalone-bundle.md -@router.get("/asyncapi") -def get_asyncapi_html( - sidebar: bool = True, - info: bool = True, - servers: bool = True, - operations: bool = True, - messages: bool = True, - schemas: bool = True, - errors: bool = True, - expand_message_examples: bool = True, - title: str = "Bookstore API", -) -> HTMLResponse: - """Generate HTML for displaying an AsyncAPI document.""" - schema_json = schema.model_dump_json(exclude_unset=True) - - config = { - "schema": schema_json, - "config": { - "show": { - "sidebar": sidebar, - "info": info, - "servers": servers, - "operations": operations, - "messages": messages, - "schemas": schemas, - "errors": errors, - }, - "expand": { - "messageExamples": expand_message_examples, - }, - "sidebar": { - "showServers": "byDefault", - "showOperations": "byDefault", - }, - }, - } - - return HTMLResponse(""" - - - - """ - f""" - {title} AsyncAPI - """ - """ - - - - - """ - f""" - - - """ - """ - - - - -
- """ - f""" - - - - - """ - ) diff --git a/src/http_app/routes/docs_ws.py b/src/http_app/routes/docs_ws.py new file mode 100644 index 00000000..8e9768e8 --- /dev/null +++ b/src/http_app/routes/docs_ws.py @@ -0,0 +1,165 @@ +import json + +import pydantic_asyncapi as pa +from fastapi import APIRouter +from starlette.responses import HTMLResponse + +from domains.books.events import BookCreatedV1 + +message_map = { + "chat_channel": [BookCreatedV1] +} + +components_schemas = {} +channel_messages = {} + +# Prepare some data from a map +for channel, messages in message_map.items(): + channel_messages[channel] = {} + for message in messages: + components_schemas[message.__name__] = message.model_json_schema(ref_template="#/components/schemas/{model}") + components_schemas.update(message.model_json_schema(ref_template="#/components/schemas/{model}")["$defs"]) + channel_messages[channel][message.__name__] = pa.v3.Message( + payload=pa.v3.Reference(ref=f"#/components/schemas/{message.__name__}") + ) + + +schema = pa.AsyncAPIV3( + asyncapi="3.0.0", + info=pa.v3.Info( + title="Bookstore API", + version="1.0.0", + description="A bookstore aysncapi specification", + ), + components=pa.v3.Components( + schemas=components_schemas, + ), + servers={ + "chat": pa.v3.Server( + host="localhost", + protocol="websocket", + ) + }, + channels={ + "chat_channel": pa.v3.Channel( + title="chat_channel_title", + servers=[pa.v3.Reference(ref="#/servers/chat")], + messages=channel_messages["chat_channel"], + ) + }, + operations={ + "chat_operation": pa.v3.Operation( + action="receive", + channel=pa.v3.Reference(ref="#/channels/chat_channel"), + ) + }, + +) + +router = APIRouter(prefix="/docs/ws") + + +@router.get( + "/asyncapi.json", + response_model_exclude_unset=True, + include_in_schema=False, +) +def asyncapi_raw() -> pa.AsyncAPIV3: + return schema + + +ASYNCAPI_COMPONENT_VERSION = "latest" + +ASYNCAPI_JS_DEFAULT_URL = ( + f"https://unpkg.com/@asyncapi/react-component@{ASYNCAPI_COMPONENT_VERSION}/browser/standalone/index.js" +) +NORMALIZE_CSS_DEFAULT_URL = ( + "https://cdn.jsdelivr.net/npm/modern-normalize/modern-normalize.min.css" +) +ASYNCAPI_CSS_DEFAULT_URL = ( + f"https://unpkg.com/@asyncapi/react-component@{ASYNCAPI_COMPONENT_VERSION}/styles/default.min.css" +) + + +# https://github.com/asyncapi/asyncapi-react/blob/v2.5.0/docs/usage/standalone-bundle.md +@router.get("", include_in_schema=False) +def get_asyncapi_html( + sidebar: bool = True, + info: bool = True, + servers: bool = True, + operations: bool = True, + messages: bool = True, + schemas: bool = True, + errors: bool = True, + expand_message_examples: bool = True, + title: str = "Websocket", +) -> HTMLResponse: + + """Generate HTML for displaying an AsyncAPI document.""" + config = { + # "schema": schema_json, + "schema": { + "url": "/docs/ws/asyncapi.json", + }, + "config": { + "show": { + "sidebar": sidebar, + "info": info, + "servers": servers, + "operations": operations, + "messages": messages, + "schemas": schemas, + "errors": errors, + }, + "expand": { + "messageExamples": expand_message_examples, + }, + "sidebar": { + "showServers": "byDefault", + "showOperations": "byDefault", + }, + }, + } + + return HTMLResponse( + """ + + + + """ + f""" + {title} AsyncAPI + """ + """ + + + + + """ + f""" + + + """ + """ + + + + +
+ """ + f""" + + + + + """ + ) diff --git a/src/http_app/routes/websocket/__init__.py b/src/http_app/routes/ws/__init__.py similarity index 70% rename from src/http_app/routes/websocket/__init__.py rename to src/http_app/routes/ws/__init__.py index 3ba28ba3..a0c6d0ba 100644 --- a/src/http_app/routes/websocket/__init__.py +++ b/src/http_app/routes/ws/__init__.py @@ -3,4 +3,4 @@ from . import chat router = APIRouter(prefix="/ws") -router.include_router(chat.router) \ No newline at end of file +router.include_router(chat.router) diff --git a/src/http_app/routes/websocket/chat.py b/src/http_app/routes/ws/chat.py similarity index 99% rename from src/http_app/routes/websocket/chat.py rename to src/http_app/routes/ws/chat.py index d0964fa0..382366e5 100644 --- a/src/http_app/routes/websocket/chat.py +++ b/src/http_app/routes/ws/chat.py @@ -36,4 +36,4 @@ async def websocket_endpoint(websocket: WebSocket, client_id: int): await manager.broadcast(f"Client #{client_id} says: {data}") except WebSocketDisconnect: manager.disconnect(websocket) - await manager.broadcast(f"Client #{client_id} left the chat") \ No newline at end of file + await manager.broadcast(f"Client #{client_id} left the chat") From 3ba952367b529d3342abc3f639739ac6251c1567 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:37:18 +0000 Subject: [PATCH 05/19] Stub router class to attempt auto-asyncapi-registration --- src/http_app/routes/ws/chat.py | 40 ++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/http_app/routes/ws/chat.py b/src/http_app/routes/ws/chat.py index 382366e5..6e31e3e5 100644 --- a/src/http_app/routes/ws/chat.py +++ b/src/http_app/routes/ws/chat.py @@ -1,8 +1,44 @@ -from fastapi import APIRouter +from typing import Optional, Sequence, Callable + +from fastapi.types import DecoratedCallable +from typing_extensions import Annotated, Doc + +from fastapi import APIRouter, params from starlette.websockets import WebSocket, WebSocketDisconnect -router = APIRouter(prefix="/chat") +class AsyncAPIEnabledRouter(APIRouter): + def websocket(self, path: Annotated[ + str, + Doc( + """ + WebSocket path. + """ + ), + ], name: Annotated[ + Optional[str], + Doc( + """ + A name for the WebSocket. Only used internally. + """ + ), + ] = None, *, dependencies: Annotated[ + Optional[Sequence[params.Depends]], + Doc( + """ + A list of dependencies (using `Depends()`) to be used for this + WebSocket. + + Read more about it in the + [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/). + """ + ), + ] = None) -> Callable[[DecoratedCallable], DecoratedCallable]: + route = super().websocket(path, name, dependencies=dependencies) + return route + + +router = AsyncAPIEnabledRouter(prefix="/chat") class ConnectionManager: def __init__(self): From 53b53fccdb1cd5fc2929fa971a0fa758a2e2e232 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:43:06 +0000 Subject: [PATCH 06/19] Add TODO for overlapping models --- src/http_app/routes/docs_ws.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/http_app/routes/docs_ws.py b/src/http_app/routes/docs_ws.py index 8e9768e8..992c46d9 100644 --- a/src/http_app/routes/docs_ws.py +++ b/src/http_app/routes/docs_ws.py @@ -16,6 +16,7 @@ # Prepare some data from a map for channel, messages in message_map.items(): channel_messages[channel] = {} + # TODO: Check for overlapping model schemas, if they are different log a warning! for message in messages: components_schemas[message.__name__] = message.model_json_schema(ref_template="#/components/schemas/{model}") components_schemas.update(message.model_json_schema(ref_template="#/components/schemas/{model}")["$defs"]) From aebbfb5f36696443da6ee3fb44cc09402c2811c4 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 29 Jan 2025 00:06:05 +0000 Subject: [PATCH 07/19] Batch-create json schemas --- src/http_app/routes/docs_ws.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/http_app/routes/docs_ws.py b/src/http_app/routes/docs_ws.py index 992c46d9..01df0329 100644 --- a/src/http_app/routes/docs_ws.py +++ b/src/http_app/routes/docs_ws.py @@ -1,13 +1,16 @@ import json +import logging import pydantic_asyncapi as pa from fastapi import APIRouter +from pydantic.json_schema import models_json_schema from starlette.responses import HTMLResponse -from domains.books.events import BookCreatedV1 +from domains.books.events import BookCreatedV1, BookUpdatedV1 message_map = { - "chat_channel": [BookCreatedV1] + # Use validation if receiving message, serialization if sending message + "chat_channel": [(BookCreatedV1, "validation"), (BookUpdatedV1, "validation")] } components_schemas = {} @@ -16,12 +19,24 @@ # Prepare some data from a map for channel, messages in message_map.items(): channel_messages[channel] = {} + a, b = models_json_schema(models=messages, ref_template="#/components/schemas/{model}") + + logging.error(a) + """ + {(, 'validation'): {'$ref': '#/components/schemas/BookCreatedV1'}, (, 'validation'): {'$ref': '#/components/schemas/BookUpdatedV1'}} + """ + logging.error(b) + + components_schemas = b["$defs"] # TODO: Check for overlapping model schemas, if they are different log a warning! for message in messages: - components_schemas[message.__name__] = message.model_json_schema(ref_template="#/components/schemas/{model}") - components_schemas.update(message.model_json_schema(ref_template="#/components/schemas/{model}")["$defs"]) - channel_messages[channel][message.__name__] = pa.v3.Message( - payload=pa.v3.Reference(ref=f"#/components/schemas/{message.__name__}") + # components_schemas[message.__name__] = message.model_json_schema(ref_template="#/components/schemas/{model}") + # components_schemas.update(message.model_json_schema(ref_template="#/components/schemas/{model}")["$defs"]) + # channel_messages[channel][message.__name__] = pa.v3.Message( + # payload=pa.v3.Reference(ref=f"#/components/schemas/{message.__name__}") + # ) + channel_messages[channel][message[0].__name__] = pa.v3.Message( + payload=pa.v3.Reference(ref=f"#/components/schemas/{message[0].__name__}") ) @@ -92,7 +107,7 @@ def get_asyncapi_html( messages: bool = True, schemas: bool = True, errors: bool = True, - expand_message_examples: bool = True, + expand_message_examples: bool = False, title: str = "Websocket", ) -> HTMLResponse: From b7d99e77847d90f6d575c6b00a9302d994c18946 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:30:02 +0000 Subject: [PATCH 08/19] Restructure registry --- src/http_app/routes/docs_ws.py | 78 ++++++++++++++++------------------ 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/src/http_app/routes/docs_ws.py b/src/http_app/routes/docs_ws.py index 01df0329..bbb11f25 100644 --- a/src/http_app/routes/docs_ws.py +++ b/src/http_app/routes/docs_ws.py @@ -1,43 +1,51 @@ import json -import logging +from typing import Dict, Literal, List import pydantic_asyncapi as pa from fastapi import APIRouter -from pydantic.json_schema import models_json_schema from starlette.responses import HTMLResponse from domains.books.events import BookCreatedV1, BookUpdatedV1 -message_map = { - # Use validation if receiving message, serialization if sending message - "chat_channel": [(BookCreatedV1, "validation"), (BookUpdatedV1, "validation")] +asyncapi_registry: Dict[str, Dict[Literal["receive", "send"], List]] = { + "chat_channel": { + "receive": [BookCreatedV1], + "send": [BookUpdatedV1], + } } components_schemas = {} -channel_messages = {} - -# Prepare some data from a map -for channel, messages in message_map.items(): - channel_messages[channel] = {} - a, b = models_json_schema(models=messages, ref_template="#/components/schemas/{model}") - logging.error(a) - """ - {(, 'validation'): {'$ref': '#/components/schemas/BookCreatedV1'}, (, 'validation'): {'$ref': '#/components/schemas/BookUpdatedV1'}} - """ - logging.error(b) - - components_schemas = b["$defs"] - # TODO: Check for overlapping model schemas, if they are different log a warning! - for message in messages: - # components_schemas[message.__name__] = message.model_json_schema(ref_template="#/components/schemas/{model}") - # components_schemas.update(message.model_json_schema(ref_template="#/components/schemas/{model}")["$defs"]) - # channel_messages[channel][message.__name__] = pa.v3.Message( - # payload=pa.v3.Reference(ref=f"#/components/schemas/{message.__name__}") - # ) - channel_messages[channel][message[0].__name__] = pa.v3.Message( - payload=pa.v3.Reference(ref=f"#/components/schemas/{message[0].__name__}") +channels = {} +operations = {} + +for channel, channel_operations in asyncapi_registry.items(): + _channel_messages = {} + for operation, messages in channel_operations.items(): + _operation_message_refs = [] + for message in messages: + # TODO: Check for overlapping model schemas, if they are different log a warning! + components_schemas[message.__name__] = message.model_json_schema( + mode="validation" if operation == "receive" else "serialization", + ref_template="#/components/schemas/{model}" + ) + components_schemas.update(message.model_json_schema(mode="serialization", ref_template="#/components/schemas/{model}")["$defs"]) + _channel_messages[message.__name__] = pa.v3.Message( + payload=pa.v3.Reference(ref=f"#/components/schemas/{message.__name__}") + ) + # Cannot point to the /components path + _operation_message_refs.append(pa.v3.Reference(ref=f"#/channels/chat_channel/messages/{message.__name__}")) + operations[operation] = pa.v3.Operation( + action=operation, + channel=pa.v3.Reference(ref=f"#/channels/{channel}"), + messages=_operation_message_refs, ) + channels[channel] = pa.v3.Channel( + title=channel, + servers=[pa.v3.Reference(ref="#/servers/chat")], + messages=_channel_messages, + ) + schema = pa.AsyncAPIV3( @@ -56,20 +64,8 @@ protocol="websocket", ) }, - channels={ - "chat_channel": pa.v3.Channel( - title="chat_channel_title", - servers=[pa.v3.Reference(ref="#/servers/chat")], - messages=channel_messages["chat_channel"], - ) - }, - operations={ - "chat_operation": pa.v3.Operation( - action="receive", - channel=pa.v3.Reference(ref="#/channels/chat_channel"), - ) - }, - + channels=channels, + operations=operations, ) router = APIRouter(prefix="/docs/ws") From 271ac7e9c0becde53c6b927b9f6eb5bb59686681 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 29 Jan 2025 23:21:41 +0000 Subject: [PATCH 09/19] Create global registry with a decorator to register channels and function to generate the schema --- src/common/asyncapi.py | 115 +++++++++++++++++++++++++++++++++ src/http_app/routes/docs_ws.py | 89 +++++-------------------- 2 files changed, 130 insertions(+), 74 deletions(-) create mode 100644 src/common/asyncapi.py diff --git a/src/common/asyncapi.py b/src/common/asyncapi.py new file mode 100644 index 00000000..b0c01697 --- /dev/null +++ b/src/common/asyncapi.py @@ -0,0 +1,115 @@ +from asyncio import iscoroutinefunction +from functools import wraps +from typing import List, Dict, Literal, Type, Optional + +from pydantic import BaseModel +import pydantic_asyncapi.v3 as pa + + +_asyncapi_registry: Dict[str, Dict[Literal["receive", "send"], List]] = { + +} + + +# asyncapi_registry: Dict[str, Dict[Literal["receive", "send"], List]] = { +# "chat_channel": { +# "receive": [BookCreatedV1], +# "send": [BookUpdatedV1], +# } +# } + + +def add_channel_to_asyncapi_schema( + receive: Optional[List[Type[BaseModel]]] = None, + send: Optional[List[Type[BaseModel]]] = None, + channel_name: Optional[str] = None, +): + def decorator(func): + _channel_name = channel_name or func.__name__ + if _asyncapi_registry.get(_channel_name) is not None: + raise ValueError(f"The schema already contains a definition for function {_channel_name}. Please rename the function or provide a different channel name.") + + _asyncapi_registry[_channel_name] = {} + if receive: + _asyncapi_registry[_channel_name]["receive"] = receive + if send: + _asyncapi_registry[_channel_name]["send"] = send + + @wraps(func) + def wrapper(*args, **kwargs): + # You can optionally use decorator_args and decorator_kwargs here if needed + return func(*args, **kwargs) + + @wraps(func) + async def a_wrapper(*args, **kwargs): + # You can optionally use decorator_args and decorator_kwargs here if needed + return await func(*args, **kwargs) + + return a_wrapper if iscoroutinefunction(func) else wrapper + + return decorator + + +def get_asyncapi_schema(): + components_schemas = {} + channels = {} + operations = {} + + for channel, channel_operations in _asyncapi_registry.items(): + _channel_messages = {} + for operation, messages in channel_operations.items(): + _operation_message_refs = [] + for message in messages: + # TODO: Check for overlapping model schemas, if they are different log a warning! + components_schemas[message.__name__] = message.model_json_schema( + mode="validation" if operation == "receive" else "serialization", + ref_template="#/components/schemas/{model}" + ) + components_schemas.update( + message.model_json_schema(mode="serialization", ref_template="#/components/schemas/{model}")[ + "$defs"]) + _channel_messages[message.__name__] = pa.Message( + payload=pa.Reference(ref=f"#/components/schemas/{message.__name__}") + ) + # Cannot point to the /components path + _operation_message_refs.append( + pa.Reference(ref=f"#/channels/{channel}/messages/{message.__name__}")) + + # TODO: Define operation names in decorator + operations[f"{channel}-{operation}"] = pa.Operation( + action=operation, + channel=pa.Reference(ref=f"#/channels/{channel}"), + messages=_operation_message_refs, + ) + + # TODO: Define channel metadata in decorator + channels[channel] = pa.Channel( + address=channel, + description=f"Description for channel {channel}", + title=f"Title for channel {channel}", + servers=[pa.Reference(ref="#/servers/chat")], + messages=_channel_messages, + ) + + # TODO: Implement function to initialize application and servers + schema = pa.AsyncAPI( + asyncapi="3.0.0", + info=pa.Info( + title="Bookstore API", + version="1.0.0", + description="A bookstore asyncapi specification", + ), + components=pa.Components( + schemas=components_schemas, + ), + servers={ + "chat": pa.Server( + host="localhost", + protocol="ws", + ) + }, + channels=channels, + operations=operations, + ) + + return schema diff --git a/src/http_app/routes/docs_ws.py b/src/http_app/routes/docs_ws.py index bbb11f25..4f254d23 100644 --- a/src/http_app/routes/docs_ws.py +++ b/src/http_app/routes/docs_ws.py @@ -1,72 +1,12 @@ import json -from typing import Dict, Literal, List import pydantic_asyncapi as pa from fastapi import APIRouter from starlette.responses import HTMLResponse +from common.asyncapi import add_channel_to_asyncapi_schema, get_asyncapi_schema from domains.books.events import BookCreatedV1, BookUpdatedV1 -asyncapi_registry: Dict[str, Dict[Literal["receive", "send"], List]] = { - "chat_channel": { - "receive": [BookCreatedV1], - "send": [BookUpdatedV1], - } -} - -components_schemas = {} - -channels = {} -operations = {} - -for channel, channel_operations in asyncapi_registry.items(): - _channel_messages = {} - for operation, messages in channel_operations.items(): - _operation_message_refs = [] - for message in messages: - # TODO: Check for overlapping model schemas, if they are different log a warning! - components_schemas[message.__name__] = message.model_json_schema( - mode="validation" if operation == "receive" else "serialization", - ref_template="#/components/schemas/{model}" - ) - components_schemas.update(message.model_json_schema(mode="serialization", ref_template="#/components/schemas/{model}")["$defs"]) - _channel_messages[message.__name__] = pa.v3.Message( - payload=pa.v3.Reference(ref=f"#/components/schemas/{message.__name__}") - ) - # Cannot point to the /components path - _operation_message_refs.append(pa.v3.Reference(ref=f"#/channels/chat_channel/messages/{message.__name__}")) - operations[operation] = pa.v3.Operation( - action=operation, - channel=pa.v3.Reference(ref=f"#/channels/{channel}"), - messages=_operation_message_refs, - ) - channels[channel] = pa.v3.Channel( - title=channel, - servers=[pa.v3.Reference(ref="#/servers/chat")], - messages=_channel_messages, - ) - - - -schema = pa.AsyncAPIV3( - asyncapi="3.0.0", - info=pa.v3.Info( - title="Bookstore API", - version="1.0.0", - description="A bookstore aysncapi specification", - ), - components=pa.v3.Components( - schemas=components_schemas, - ), - servers={ - "chat": pa.v3.Server( - host="localhost", - protocol="websocket", - ) - }, - channels=channels, - operations=operations, -) router = APIRouter(prefix="/docs/ws") @@ -76,8 +16,9 @@ response_model_exclude_unset=True, include_in_schema=False, ) -def asyncapi_raw() -> pa.AsyncAPIV3: - return schema +@add_channel_to_asyncapi_schema(send=[BookUpdatedV1]) +def asyncapi_raw() -> pa.v3.AsyncAPI: + return get_asyncapi_schema() ASYNCAPI_COMPONENT_VERSION = "latest" @@ -95,21 +36,21 @@ def asyncapi_raw() -> pa.AsyncAPIV3: # https://github.com/asyncapi/asyncapi-react/blob/v2.5.0/docs/usage/standalone-bundle.md @router.get("", include_in_schema=False) -def get_asyncapi_html( - sidebar: bool = True, - info: bool = True, - servers: bool = True, - operations: bool = True, - messages: bool = True, - schemas: bool = True, - errors: bool = True, - expand_message_examples: bool = False, - title: str = "Websocket", +@add_channel_to_asyncapi_schema(receive=[BookCreatedV1], send=[BookUpdatedV1]) +async def get_asyncapi_html( + sidebar: bool = True, + info: bool = True, + servers: bool = True, + operations: bool = True, + messages: bool = True, + schemas: bool = True, + errors: bool = True, + expand_message_examples: bool = False, + title: str = "Websocket", ) -> HTMLResponse: """Generate HTML for displaying an AsyncAPI document.""" config = { - # "schema": schema_json, "schema": { "url": "/docs/ws/asyncapi.json", }, From 8109c875575ec74cf6548193ae826197d5b7984d Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 29 Jan 2025 23:25:43 +0000 Subject: [PATCH 10/19] Update dependencies --- uv.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/uv.lock b/uv.lock index c57f3f91..efe2a08d 100644 --- a/uv.lock +++ b/uv.lock @@ -1206,14 +1206,14 @@ wheels = [ [[package]] name = "mistune" -version = "3.1.0" +version = "3.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/6e/96fc7cb3288666c5de2c396eb0e338dc95f7a8e4920e43e38783a22d0084/mistune-3.1.0.tar.gz", hash = "sha256:dbcac2f78292b9dc066cd03b7a3a26b62d85f8159f2ea5fd28e55df79908d667", size = 94401 } +sdist = { url = "https://files.pythonhosted.org/packages/c6/1d/6b2b634e43bacc3239006e61800676aa6c41ac1836b2c57497ed27a7310b/mistune-3.1.1.tar.gz", hash = "sha256:e0740d635f515119f7d1feb6f9b192ee60f0cc649f80a8f944f905706a21654c", size = 94645 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/b3/743ffc3f59da380da504d84ccd1faf9a857a1445991ff19bf2ec754163c2/mistune-3.1.0-py3-none-any.whl", hash = "sha256:b05198cf6d671b3deba6c87ec6cf0d4eb7b72c524636eddb6dbf13823b52cee1", size = 53694 }, + { url = "https://files.pythonhosted.org/packages/c6/02/c66bdfdadbb021adb642ca4e8a5ed32ada0b4a3e4b39c5d076d19543452f/mistune-3.1.1-py3-none-any.whl", hash = "sha256:02106ac2aa4f66e769debbfa028509a275069dcffce0dfa578edd7b991ee700a", size = 53696 }, ] [[package]] @@ -1910,15 +1910,15 @@ wheels = [ [[package]] name = "pymdown-extensions" -version = "10.14.1" +version = "10.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/24/f7a412dc1630b1a6d7b288e7c736215ce878ee4aad24359f7f67b53bbaa9/pymdown_extensions-10.14.1.tar.gz", hash = "sha256:b65801996a0cd4f42a3110810c306c45b7313c09b0610a6f773730f2a9e3c96b", size = 845243 } +sdist = { url = "https://files.pythonhosted.org/packages/aa/7b/de388047c577e43dc45ce35c23b9b349ec3df8c7023c3e3c4d413a850982/pymdown_extensions-10.14.2.tar.gz", hash = "sha256:7a77b8116dc04193f2c01143760a43387bd9dc4aa05efacb7d838885a7791253", size = 846777 } wheels = [ - { url = "https://files.pythonhosted.org/packages/09/fb/79a8d27966e90feeeb686395c8b1bff8221727abcbd80d2485841393a955/pymdown_extensions-10.14.1-py3-none-any.whl", hash = "sha256:637951cbfbe9874ba28134fb3ce4b8bcadd6aca89ac4998ec29dcbafd554ae08", size = 264283 }, + { url = "https://files.pythonhosted.org/packages/e7/a3/61527d80d84e9fd4d97649322e83bd7efde8200fc07fe34469c8c2bd0d91/pymdown_extensions-10.14.2-py3-none-any.whl", hash = "sha256:f45bc5892410e54fd738ab8ccd736098b7ff0cb27fdb4bf24d0a0c6584bc90e1", size = 264459 }, ] [[package]] From 6a0dd9b64a1ccc34737e014e2c0047a51cd88756 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 5 Feb 2025 12:02:38 +0000 Subject: [PATCH 11/19] Change decorator implementation to functions --- src/common/asyncapi.py | 274 +++++++++++++++++++++------------ src/common/bootstrap.py | 2 + src/http_app/routes/docs_ws.py | 13 +- src/http_app/routes/ws/chat.py | 75 ++++----- 4 files changed, 215 insertions(+), 149 deletions(-) diff --git a/src/common/asyncapi.py b/src/common/asyncapi.py index b0c01697..011522fe 100644 --- a/src/common/asyncapi.py +++ b/src/common/asyncapi.py @@ -1,115 +1,185 @@ -from asyncio import iscoroutinefunction -from functools import wraps -from typing import List, Dict, Literal, Type, Optional +from typing import List, Literal, Optional, Type -from pydantic import BaseModel import pydantic_asyncapi.v3 as pa +from pydantic import BaseModel +_info: pa.Info = pa.Info( + title="AsyncAPI", + version="1.0.0", +) -_asyncapi_registry: Dict[str, Dict[Literal["receive", "send"], List]] = { - -} +_servers = {} # type: ignore +_channels = {} # type: ignore +_operations = {} # type: ignore +_components_schemas = {} # type: ignore -# asyncapi_registry: Dict[str, Dict[Literal["receive", "send"], List]] = { -# "chat_channel": { -# "receive": [BookCreatedV1], -# "send": [BookUpdatedV1], -# } -# } +def get_schema() -> pa.AsyncAPI: + """ + Function `get_schema` provides the complete AsyncAPI schema for the application, complying with + version 3.0.0 of the AsyncAPI specification. It includes detailed information about info metadata, + components, servers, channels, and operations required to set up and describe the asynchronous + communication layer. -def add_channel_to_asyncapi_schema( - receive: Optional[List[Type[BaseModel]]] = None, - send: Optional[List[Type[BaseModel]]] = None, - channel_name: Optional[str] = None, -): - def decorator(func): - _channel_name = channel_name or func.__name__ - if _asyncapi_registry.get(_channel_name) is not None: - raise ValueError(f"The schema already contains a definition for function {_channel_name}. Please rename the function or provide a different channel name.") - - _asyncapi_registry[_channel_name] = {} - if receive: - _asyncapi_registry[_channel_name]["receive"] = receive - if send: - _asyncapi_registry[_channel_name]["send"] = send - - @wraps(func) - def wrapper(*args, **kwargs): - # You can optionally use decorator_args and decorator_kwargs here if needed - return func(*args, **kwargs) - - @wraps(func) - async def a_wrapper(*args, **kwargs): - # You can optionally use decorator_args and decorator_kwargs here if needed - return await func(*args, **kwargs) - - return a_wrapper if iscoroutinefunction(func) else wrapper - - return decorator - - -def get_asyncapi_schema(): - components_schemas = {} - channels = {} - operations = {} - - for channel, channel_operations in _asyncapi_registry.items(): - _channel_messages = {} - for operation, messages in channel_operations.items(): - _operation_message_refs = [] - for message in messages: - # TODO: Check for overlapping model schemas, if they are different log a warning! - components_schemas[message.__name__] = message.model_json_schema( - mode="validation" if operation == "receive" else "serialization", - ref_template="#/components/schemas/{model}" - ) - components_schemas.update( - message.model_json_schema(mode="serialization", ref_template="#/components/schemas/{model}")[ - "$defs"]) - _channel_messages[message.__name__] = pa.Message( - payload=pa.Reference(ref=f"#/components/schemas/{message.__name__}") - ) - # Cannot point to the /components path - _operation_message_refs.append( - pa.Reference(ref=f"#/channels/{channel}/messages/{message.__name__}")) - - # TODO: Define operation names in decorator - operations[f"{channel}-{operation}"] = pa.Operation( - action=operation, - channel=pa.Reference(ref=f"#/channels/{channel}"), - messages=_operation_message_refs, - ) - - # TODO: Define channel metadata in decorator - channels[channel] = pa.Channel( - address=channel, - description=f"Description for channel {channel}", - title=f"Title for channel {channel}", - servers=[pa.Reference(ref="#/servers/chat")], - messages=_channel_messages, - ) - - # TODO: Implement function to initialize application and servers - schema = pa.AsyncAPI( + Returns: + pa.AsyncAPI: A fully constructed AsyncAPI schema object based on predefined configurations. + """ + return pa.AsyncAPI( asyncapi="3.0.0", - info=pa.Info( - title="Bookstore API", - version="1.0.0", - description="A bookstore asyncapi specification", - ), + info=_info, components=pa.Components( - schemas=components_schemas, + schemas=_components_schemas, ), - servers={ - "chat": pa.Server( - host="localhost", - protocol="ws", - ) - }, - channels=channels, - operations=operations, + servers=_servers, + channels=_channels, + operations=_operations, + ) + + +def init_asyncapi_info( + title: str, + version: str = "1.0.0", +) -> None: + """ + Initializes the AsyncAPI information object with the specified title and version. + + This function creates and initializes an AsyncAPI Info object, which includes + mandatory fields such as title and version. The title represents the name of the + AsyncAPI document, and the version represents the version of the API. + + Parameters: + title (str): The title of the AsyncAPI document. + version (str): The version of the AsyncAPI document. Defaults to "1.0.0". + + Returns: + None + """ + # We can potentially add the other info supported by pa.Info + global _info + _info = pa.Info( + title=title, + version=version, ) - return schema + +def register_server( + id: str, + host: str, + protocol: str, + pathname: Optional[str] = None, +) -> None: + """ + Registers a server with a unique identifier and its associated properties. + This function accepts information about the server such as its host, + protocol, and optionally its pathname, and stores it in the internal + server registry identified by the unique ID. The parameters must be + provided appropriately for proper registration. The server registry + ensures that server configurations can be retrieved and managed based + on the assigned identifier. + + Args: + id: str + A unique identifier for the server being registered. It is used + as the key in the internal server registry. + host: str + The host address of the server. This may be an IP address or + a domain name. + protocol: str + Communication protocol used by the server, such as "http" or "https". + pathname: Optional[str] + The optional pathname of the server. If provided, it will be + associated with the registered server. + + Returns: + None + This function does not return a value. It modifies the internal + server registry to include the provided server details. + """ + # TODO: Implement other server parameters + _servers[id] = pa.Server( + host=host, + protocol=protocol, + ) + if pathname is not None: + _servers[id].pathname = pathname + + +def register_channel( + address: str, + id: Optional[str] = None, + description: Optional[str] = None, + title: Optional[str] = None, + server_id: Optional[str] = None, +) -> None: + """ + Registers a communication channel with the specified parameters and updates the + internal dictionary holding channel metadata. The function allows optional + parameters to set additional properties such as description and title, and + optionally associates the channel with a predefined server. + + Args: + address (str): The address of the channel. + id (Optional[str]): Unique identifier for the channel. Defaults to None. + description (Optional[str]): Description of the channel. Defaults to None. + title (Optional[str]): Title to be associated with the channel. Defaults to None. + server_id (Optional[str]): Server identifier to link this channel to. + Must exist in the internal server registry. Defaults to None. + + Returns: + None + """ + # TODO: Define channel metadata in decorator + _channels[id or address] = pa.Channel( + address=address, + servers=[], + messages={}, + ) + if description is not None: + _channels[id or address].description = description + if title is not None: + _channels[id or address].title = title + if server_id is not None and server_id in _servers: + _channels[id or address].servers.append(pa.Reference(ref=f"#/servers/{server_id}")) # type: ignore + + +def register_channel_operation( + channel_id: str, + operation_type: Literal["receive", "send"], + messages: List[Type[BaseModel]], + operation_name: Optional[str] = None, +): + if not _channels.get(channel_id): + raise ValueError(f"Channel {channel_id} does not exist.") + + _operation_message_refs = [] + for message in messages: + # TODO: Check for overlapping model schemas, if they are different log a warning! + _components_schemas[message.__name__] = message.model_json_schema( + mode="validation" if operation_type == "receive" else "serialization", + ref_template="#/components/schemas/{model}", + ) + _components_schemas.update( + message.model_json_schema(mode="serialization", ref_template="#/components/schemas/{model}")["$defs"] + ) + _channels[channel_id].messages[message.__name__] = pa.Message( # type: ignore + payload=pa.Reference(ref=f"#/components/schemas/{message.__name__}") + ) + # Cannot point to the /components path + _operation_message_refs.append(pa.Reference(ref=f"#/channels/{channel_id}/messages/{message.__name__}")) + + _operations[operation_name or f"{channel_id}-{operation_type}"] = pa.Operation( + action=operation_type, + channel=pa.Reference(ref=f"#/channels/{channel_id}"), + messages=_operation_message_refs, + traits=[], + ) + # TODO: Define operation traits + # if operation_name is not None: + # _operations[operation_name or f"{channel_id}-{operation_type}"].traits.append( + # pa.OperationTrait( + # title=operation_name, + # summary=f"{operation_name} operation summary", + # description=f"{operation_name} operation description", + # ) + # ) diff --git a/src/common/bootstrap.py b/src/common/bootstrap.py index b86fac20..dc59aab1 100644 --- a/src/common/bootstrap.py +++ b/src/common/bootstrap.py @@ -4,6 +4,7 @@ from dependency_injector.providers import Object from pydantic import BaseModel, ConfigDict +from .asyncapi import init_asyncapi_info from .config import AppConfig from .di_container import Container from .dramatiq import init_dramatiq @@ -27,6 +28,7 @@ def application_init(app_config: AppConfig) -> InitReference: init_logger(app_config) init_storage() init_dramatiq(app_config) + init_asyncapi_info(app_config.APP_NAME) return InitReference( di_container=container, diff --git a/src/http_app/routes/docs_ws.py b/src/http_app/routes/docs_ws.py index 4f254d23..3ceefee2 100644 --- a/src/http_app/routes/docs_ws.py +++ b/src/http_app/routes/docs_ws.py @@ -4,9 +4,7 @@ from fastapi import APIRouter from starlette.responses import HTMLResponse -from common.asyncapi import add_channel_to_asyncapi_schema, get_asyncapi_schema -from domains.books.events import BookCreatedV1, BookUpdatedV1 - +from common.asyncapi import get_schema router = APIRouter(prefix="/docs/ws") @@ -16,9 +14,8 @@ response_model_exclude_unset=True, include_in_schema=False, ) -@add_channel_to_asyncapi_schema(send=[BookUpdatedV1]) def asyncapi_raw() -> pa.v3.AsyncAPI: - return get_asyncapi_schema() + return get_schema() ASYNCAPI_COMPONENT_VERSION = "latest" @@ -26,9 +23,7 @@ def asyncapi_raw() -> pa.v3.AsyncAPI: ASYNCAPI_JS_DEFAULT_URL = ( f"https://unpkg.com/@asyncapi/react-component@{ASYNCAPI_COMPONENT_VERSION}/browser/standalone/index.js" ) -NORMALIZE_CSS_DEFAULT_URL = ( - "https://cdn.jsdelivr.net/npm/modern-normalize/modern-normalize.min.css" -) +NORMALIZE_CSS_DEFAULT_URL = "https://cdn.jsdelivr.net/npm/modern-normalize/modern-normalize.min.css" ASYNCAPI_CSS_DEFAULT_URL = ( f"https://unpkg.com/@asyncapi/react-component@{ASYNCAPI_COMPONENT_VERSION}/styles/default.min.css" ) @@ -36,7 +31,6 @@ def asyncapi_raw() -> pa.v3.AsyncAPI: # https://github.com/asyncapi/asyncapi-react/blob/v2.5.0/docs/usage/standalone-bundle.md @router.get("", include_in_schema=False) -@add_channel_to_asyncapi_schema(receive=[BookCreatedV1], send=[BookUpdatedV1]) async def get_asyncapi_html( sidebar: bool = True, info: bool = True, @@ -48,7 +42,6 @@ async def get_asyncapi_html( expand_message_examples: bool = False, title: str = "Websocket", ) -> HTMLResponse: - """Generate HTML for displaying an AsyncAPI document.""" config = { "schema": { diff --git a/src/http_app/routes/ws/chat.py b/src/http_app/routes/ws/chat.py index 6e31e3e5..01a66716 100644 --- a/src/http_app/routes/ws/chat.py +++ b/src/http_app/routes/ws/chat.py @@ -1,45 +1,16 @@ -from typing import Optional, Sequence, Callable - -from fastapi.types import DecoratedCallable -from typing_extensions import Annotated, Doc - -from fastapi import APIRouter, params +from fastapi import APIRouter from starlette.websockets import WebSocket, WebSocketDisconnect +from common.asyncapi import ( + register_channel, + register_channel_operation, + register_server, +) +from domains.books.events import BookUpdatedV1 -class AsyncAPIEnabledRouter(APIRouter): - def websocket(self, path: Annotated[ - str, - Doc( - """ - WebSocket path. - """ - ), - ], name: Annotated[ - Optional[str], - Doc( - """ - A name for the WebSocket. Only used internally. - """ - ), - ] = None, *, dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be used for this - WebSocket. - - Read more about it in the - [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/). - """ - ), - ] = None) -> Callable[[DecoratedCallable], DecoratedCallable]: - route = super().websocket(path, name, dependencies=dependencies) - return route +router = APIRouter(prefix="/chat") -router = AsyncAPIEnabledRouter(prefix="/chat") - class ConnectionManager: def __init__(self): self.active_connections: list[WebSocket] = [] @@ -61,6 +32,36 @@ async def broadcast(self, message: str): manager = ConnectionManager() +""" +In websocket case we create a server per route. +If we create other routes we can create more servers +""" +register_server( + id="chat", + # TODO: Inject host using config? + host="localhost/endpoint", + protocol="ws", +) +register_channel( + id="ChatChannel", + address="chat", + title="Chat channel", + description="A channel supporting send and receive chat messages between clients", + server_id="chat", +) +register_channel_operation( + channel_id="ChatChannel", + operation_type="send", + messages=[BookUpdatedV1], + operation_name="SendMessage", +) +register_channel_operation( + channel_id="ChatChannel", + operation_type="receive", + messages=[BookUpdatedV1], + operation_name="ReceiveMessage", +) + @router.websocket("/{client_id}") async def websocket_endpoint(websocket: WebSocket, client_id: int): From b87baadb3d82b1aa8c13f59bc450091d5773aea3 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 5 Feb 2025 12:04:02 +0000 Subject: [PATCH 12/19] Update line-length to 120 and update dependencies --- Makefile | 1 + pyproject.toml | 1 + src/common/di_container.py | 4 +- src/common/dramatiq.py | 4 +- src/common/logs/__init__.py | 4 +- src/common/utils.py | 4 +- src/domains/books/_gateway_interfaces.py | 4 +- src/domains/books/_service.py | 20 +- src/domains/books/events.py | 8 +- src/gateways/event.py | 4 +- src/http_app/routes/api/books.py | 11 +- src/http_app/routes/auth.py | 4 +- src/http_app/routes/events.py | 21 +- src/http_app/routes/hello.py | 4 +- src/migrations/env.py | 48 +--- tests/common/test_dramatiq.py | 12 +- tests/common/test_tracing.py | 8 +- tests/http_app/routes/books/conftest.py | 4 +- .../routes/books/graphql/test_query_books.py | 6 +- tests/http_app/routes/test_auth.py | 4 +- tests/http_app/routes/test_events.py | 20 +- tests/storage/tables/test_book_table.py | 4 +- tests/storage/test_sqlalchemy_init.py | 4 +- uv.lock | 243 +++++++++--------- 24 files changed, 174 insertions(+), 273 deletions(-) diff --git a/Makefile b/Makefile index 79d841f4..8d5abab0 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,7 @@ dev-dependencies: update-dependencies: uv lock --upgrade + uv sync --all-groups --frozen migrate: uv run alembic upgrade heads diff --git a/pyproject.toml b/pyproject.toml index 38b22e63..66d7c517 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -116,6 +116,7 @@ testpaths = [ [tool.ruff] target-version = "py39" +line-length = 120 extend-exclude = [ "docs", ] diff --git a/src/common/di_container.py b/src/common/di_container.py index e9ceb93b..bcf83578 100644 --- a/src/common/di_container.py +++ b/src/common/di_container.py @@ -65,6 +65,4 @@ def function( bind=SQLAlchemyBindManager.provided.get_bind.call(), model_class=BookModel, ) - BookEventGatewayInterface: Factory[BookEventGatewayInterface] = Factory( - NullEventGateway - ) + BookEventGatewayInterface: Factory[BookEventGatewayInterface] = Factory(NullEventGateway) diff --git a/src/common/dramatiq.py b/src/common/dramatiq.py index cf9234d8..5c410548 100644 --- a/src/common/dramatiq.py +++ b/src/common/dramatiq.py @@ -22,9 +22,7 @@ def decode(self, data: bytes) -> MessageData: try: return orjson.loads(data) except orjson.JSONDecodeError as e: - raise DecodeError( - "failed to decode message %r" % (data,), data, e - ) from None + raise DecodeError("failed to decode message %r" % (data,), data, e) from None def init_dramatiq(config: AppConfig): diff --git a/src/common/logs/__init__.py b/src/common/logs/__init__.py index 8bfe1dcf..1e2ed3a8 100644 --- a/src/common/logs/__init__.py +++ b/src/common/logs/__init__.py @@ -44,9 +44,7 @@ def init_logger(config: AppConfig) -> None: log_level = logging.DEBUG if config.DEBUG else logging.INFO if config.ENVIRONMENT in ["local", "test"]: - shared_processors.append( - structlog.processors.TimeStamper(fmt="%d-%m-%Y %H:%M:%S", utc=True) - ) + shared_processors.append(structlog.processors.TimeStamper(fmt="%d-%m-%Y %H:%M:%S", utc=True)) stdlib_processors.append(structlog.dev.ConsoleRenderer()) else: shared_processors.append(structlog.processors.TimeStamper(fmt="iso", utc=True)) diff --git a/src/common/utils.py b/src/common/utils.py index 979a0c22..57afc3e3 100644 --- a/src/common/utils.py +++ b/src/common/utils.py @@ -1,6 +1,4 @@ -def apply_decorator_to_methods( - decorator, protected_methods: bool = False, private_methods: bool = False -): +def apply_decorator_to_methods(decorator, protected_methods: bool = False, private_methods: bool = False): """ Class decorator to apply a given function or coroutine decorator to all functions and coroutines within a class. diff --git a/src/domains/books/_gateway_interfaces.py b/src/domains/books/_gateway_interfaces.py index 380a603c..3fcf96e4 100644 --- a/src/domains/books/_gateway_interfaces.py +++ b/src/domains/books/_gateway_interfaces.py @@ -12,9 +12,7 @@ async def save(self, book: BookModel) -> BookModel: ... async def find( self, search_params: Union[None, Mapping[str, Any]] = None, - order_by: Union[ - None, Iterable[Union[str, Tuple[str, Literal["asc", "desc"]]]] - ] = None, + order_by: Union[None, Iterable[Union[str, Tuple[str, Literal["asc", "desc"]]]]] = None, ) -> List[BookModel]: ... diff --git a/src/domains/books/_service.py b/src/domains/books/_service.py index 60e4ca9c..c78b6882 100644 --- a/src/domains/books/_service.py +++ b/src/domains/books/_service.py @@ -22,12 +22,8 @@ class BookService: @inject def __init__( self, - book_repository: BookRepositoryInterface = Provide[ - BookRepositoryInterface.__name__ - ], - event_gateway: BookEventGatewayInterface = Provide[ - BookEventGatewayInterface.__name__ - ], + book_repository: BookRepositoryInterface = Provide[BookRepositoryInterface.__name__], + event_gateway: BookEventGatewayInterface = Provide[BookEventGatewayInterface.__name__], ) -> None: super().__init__() self._book_repository = book_repository @@ -37,14 +33,10 @@ async def create_book(self, book: BookData) -> Book: # Example of CPU intensive task ran in a different thread # Using processes could be better, but it would bring technical complexity # https://anyio.readthedocs.io/en/3.x/subprocesses.html#running-functions-in-worker-processes - book_data_altered: dict = await to_thread.run_sync( - self._some_cpu_intensive_blocking_task, book.model_dump() - ) + book_data_altered: dict = await to_thread.run_sync(self._some_cpu_intensive_blocking_task, book.model_dump()) book_model = BookModel(**book_data_altered) - book = Book.model_validate( - await self._book_repository.save(book_model), from_attributes=True - ) + book = Book.model_validate(await self._book_repository.save(book_model), from_attributes=True) # Example of CPU intensive task ran in a dramatiq task. We should not rely on # dramatiq if we need to wait the operation result. @@ -53,9 +45,7 @@ async def create_book(self, book: BookData) -> Book: book_cpu_intensive_task.send(book_id=str(book.book_id)) await self._event_gateway.emit( - BookCreatedV1.event_factory( - data=BookCreatedV1Data.model_validate(book_model, from_attributes=True) - ) + BookCreatedV1.event_factory(data=BookCreatedV1Data.model_validate(book_model, from_attributes=True)) ) return book diff --git a/src/domains/books/events.py b/src/domains/books/events.py index bf806e5c..296de641 100644 --- a/src/domains/books/events.py +++ b/src/domains/books/events.py @@ -23,9 +23,7 @@ class BookCreatedV1(CloudEvent): Field(default="/book_service", validate_default=True), metadata.FieldSource, ] - type: Annotated[ - Literal["book.created.v1"], Field(default="book.created.v1"), metadata.FieldType - ] + type: Annotated[Literal["book.created.v1"], Field(default="book.created.v1"), metadata.FieldType] dataschema: Annotated[ URI, Field(default=_dataschema_url("book.created.v1"), validate_default=True), @@ -61,9 +59,7 @@ class BookUpdatedV1(CloudEvent): Field(default="/book_service", validate_default=True), metadata.FieldSource, ] - type: Annotated[ - Literal["book.updated.v1"], Field(default="book.updated.v1"), metadata.FieldType - ] + type: Annotated[Literal["book.updated.v1"], Field(default="book.updated.v1"), metadata.FieldType] dataschema: Annotated[ URI, Field(default=_dataschema_url("book.updated.v1"), validate_default=True), diff --git a/src/gateways/event.py b/src/gateways/event.py index 09006b2b..c641a5d0 100644 --- a/src/gateways/event.py +++ b/src/gateways/event.py @@ -3,9 +3,7 @@ class NullEventGateway: - async def emit( - self, event: CloudEvent - ) -> None: # pragma: no cover # No need to test this + async def emit(self, event: CloudEvent) -> None: # pragma: no cover # No need to test this logger = get_logger() await logger.ainfo( "Event emitted", diff --git a/src/http_app/routes/api/books.py b/src/http_app/routes/api/books.py index 79821d42..27abb65e 100644 --- a/src/http_app/routes/api/books.py +++ b/src/http_app/routes/api/books.py @@ -34,8 +34,7 @@ class ListBooksResponse(BaseModel): "book_id": 123, }, { - "title": "Clean Architecture: " - "A Craftsman's Guide to Software Structure and Design", + "title": "Clean Architecture: A Craftsman's Guide to Software Structure and Design", "author_name": "Robert C. 'Uncle Bob' Martin", "book_id": 321, }, @@ -82,9 +81,7 @@ async def create_book( data: CreateBookRequest, ) -> CreateBookResponse: book_service = BookService() - created_book = await book_service.create_book( - book=dto.BookData.model_validate(data, from_attributes=True) - ) + created_book = await book_service.create_book(book=dto.BookData.model_validate(data, from_attributes=True)) return CreateBookResponse(book=created_book) @@ -94,7 +91,5 @@ async def create_book_v2( some_optional_query_param: bool = False, ) -> CreateBookResponse: book_service = BookService() - created_book = await book_service.create_book( - book=dto.BookData.model_validate(data, from_attributes=True) - ) + created_book = await book_service.create_book(book=dto.BookData.model_validate(data, from_attributes=True)) return CreateBookResponse(book=created_book) diff --git a/src/http_app/routes/auth.py b/src/http_app/routes/auth.py index e534298f..f7ee29e1 100644 --- a/src/http_app/routes/auth.py +++ b/src/http_app/routes/auth.py @@ -23,9 +23,7 @@ def __init__(self, detail: str, **kwargs): class UnauthenticatedException(HTTPException): def __init__(self): - super().__init__( - status_code=status.HTTP_401_UNAUTHORIZED, detail="Requires authentication" - ) + super().__init__(status_code=status.HTTP_401_UNAUTHORIZED, detail="Requires authentication") def _jwks_client(config: Annotated[AppConfig, Depends(app_config)]) -> jwt.PyJWKClient: diff --git a/src/http_app/routes/events.py b/src/http_app/routes/events.py index 16d1b194..a6248e58 100644 --- a/src/http_app/routes/events.py +++ b/src/http_app/routes/events.py @@ -22,9 +22,7 @@ In a real application these events would be the events the app RECEIVES and not the ones our application SENDS. This is only an example to illustrate how to handle different CloudEvent classes in FastAPI""" -_EVENTS_UNION_TYPE = Annotated[ - Union[BookCreatedV1, BookUpdatedV1], Field(discriminator="type") -] +_EVENTS_UNION_TYPE = Annotated[Union[BookCreatedV1, BookUpdatedV1], Field(discriminator="type")] def _parse_event_registry() -> Dict[str, Type[_EVENTS_UNION_TYPE]]: @@ -48,16 +46,11 @@ def _event_schema_examples(mode: Literal["single", "batch"]) -> dict[str, Exampl " message!)" ) examples: Dict[str, Union[dict, str]] = { - k: getattr(v, "model_config", {}) - .get("json_schema_extra", {}) - .get("examples", [missing_example_message])[0] + k: getattr(v, "model_config", {}).get("json_schema_extra", {}).get("examples", [missing_example_message])[0] for k, v in _EVENT_REGISTRY.items() } - return { - k: Example(value=v if (mode == "single" or isinstance(v, str)) else [v]) - for k, v in examples.items() - } + return {k: Example(value=v if (mode == "single" or isinstance(v, str)) else [v]) for k, v in examples.items()} @router.get( @@ -103,9 +96,7 @@ async def submit_event( discriminator="type", ), ], - content_type: Annotated[ - Literal["application/cloudevents+json; charset=UTF-8"], Header() - ], + content_type: Annotated[Literal["application/cloudevents+json; charset=UTF-8"], Header()], ) -> None: # Some routing will be necessary when multiple event types will be supported await BookService().book_created_event_handler(event_data.data.book_id) @@ -128,9 +119,7 @@ async def submit_event_batch( openapi_examples=_event_schema_examples(mode="batch"), ), ], - content_type: Annotated[ - Literal["application/cloudevents-batch+json; charset=UTF-8"], Header() - ], + content_type: Annotated[Literal["application/cloudevents-batch+json; charset=UTF-8"], Header()], ) -> None: for event in event_batch: await BookService().book_created_event_handler(event.data.book_id) diff --git a/src/http_app/routes/hello.py b/src/http_app/routes/hello.py index c912751b..678b92ec 100644 --- a/src/http_app/routes/hello.py +++ b/src/http_app/routes/hello.py @@ -10,6 +10,4 @@ @router.get("/", response_class=HTMLResponse, include_in_schema=True) async def hello(request: Request, jwt_token=Security(decode_jwt)): - return templates.TemplateResponse( - "hello.html", {"request": request, "token_payload": jwt_token} - ) + return templates.TemplateResponse("hello.html", {"request": request, "token_payload": jwt_token}) diff --git a/src/migrations/env.py b/src/migrations/env.py index 75abf1ae..a6f87376 100644 --- a/src/migrations/env.py +++ b/src/migrations/env.py @@ -54,18 +54,14 @@ class FixtureMigration(declarative_base): String(), nullable=True, default=str(context.get_head_revision()) ) - processed_at: Mapped[datetime] = mapped_column( - DateTime(), nullable=False, default=datetime.now - ) + processed_at: Mapped[datetime] = mapped_column(DateTime(), nullable=False, default=datetime.now) return FixtureMigration fixture_migration_models = {} for name in db_names: - fixture_migration_models[name] = generate_fixture_migration_model( - sa_manager.get_bind(name).declarative_base - ) + fixture_migration_models[name] = generate_fixture_migration_model(sa_manager.get_bind(name).declarative_base) # add your model's MetaData objects here @@ -137,9 +133,7 @@ def _get_fixture_modules(cls) -> List[ModuleType]: return [ importlib.import_module(f"fixtures.{f[:-3]}") for f in listdir(cls.fixtures_path) - if isfile(join(cls.fixtures_path, f)) - and f.endswith(".py") - and f != "__init__.py" + if isfile(join(cls.fixtures_path, f)) and f.endswith(".py") and f != "__init__.py" ] @classmethod @@ -217,9 +211,7 @@ def _add_fixture_data_to_session( ) @classmethod - async def a_migrate_fixtures( - cls, bind_name: str, session: async_sessionmaker[AsyncSession] - ): + async def a_migrate_fixtures(cls, bind_name: str, session: async_sessionmaker[AsyncSession]): """ Perform asynchronous migration of fixture data modules for a specific database bind. @@ -242,9 +234,7 @@ async def a_migrate_fixtures( modules = cls._get_fixture_modules() async with session() as session: for fixture_module in modules: - cls.logger.debug( - f"Creating `{fixture_module.__name__}` fixtures for `{bind_name}` bind" - ) + cls.logger.debug(f"Creating `{fixture_module.__name__}` fixtures for `{bind_name}` bind") fixture_migration = await session.get( fixture_migration_models[bind_name], (bind_name, f"{fixture_module.__name__}"), @@ -254,14 +244,10 @@ async def a_migrate_fixtures( if cls._fixture_already_migrated(fixture_migration, signature): continue - cls._add_fixture_data_to_session( - bind_name, fixture_module, session, signature - ) + cls._add_fixture_data_to_session(bind_name, fixture_module, session, signature) try: await session.commit() - cls.logger.info( - f"`{fixture_module.__name__}` fixtures correctly created for `{bind_name}` bind" - ) + cls.logger.info(f"`{fixture_module.__name__}` fixtures correctly created for `{bind_name}` bind") except Exception: cls.logger.error( f"`{fixture_module.__name__}` fixtures failed to apply to `{bind_name}` bind", @@ -291,9 +277,7 @@ def migrate_fixtures(cls, bind_name: str, session: sessionmaker[Session]): modules = cls._get_fixture_modules() with session() as session: for fixture_module in modules: - cls.logger.debug( - f"Creating `{fixture_module.__name__}` fixtures for `{bind_name}` bind" - ) + cls.logger.debug(f"Creating `{fixture_module.__name__}` fixtures for `{bind_name}` bind") fixture_migration = session.get( fixture_migration_models[bind_name], (bind_name, f"{fixture_module.__name__}"), @@ -303,19 +287,13 @@ def migrate_fixtures(cls, bind_name: str, session: sessionmaker[Session]): if cls._fixture_already_migrated(fixture_migration, signature): continue - cls._add_fixture_data_to_session( - bind_name, fixture_module, session, signature - ) + cls._add_fixture_data_to_session(bind_name, fixture_module, session, signature) try: session.commit() - cls.logger.info( - f"`{fixture_module.__name__}` fixtures correctly created for `{bind_name}` bind" - ) + cls.logger.info(f"`{fixture_module.__name__}` fixtures correctly created for `{bind_name}` bind") except Exception: session.rollback() - cls.logger.error( - f"`{fixture_module.__name__}` fixtures failed to apply to `{bind_name}` bind" - ) + cls.logger.error(f"`{fixture_module.__name__}` fixtures failed to apply to `{bind_name}` bind") def run_migrations_offline() -> None: @@ -429,9 +407,7 @@ def migration_callable(*args, **kwargs): session=async_sessionmaker(bind=rec["connection"]), ) else: - FixtureHandler.migrate_fixtures( - bind_name=name, session=sessionmaker(bind=rec["connection"]) - ) + FixtureHandler.migrate_fixtures(bind_name=name, session=sessionmaker(bind=rec["connection"])) except: for rec in engines.values(): if isinstance(rec["engine"], AsyncEngine): diff --git a/tests/common/test_dramatiq.py b/tests/common/test_dramatiq.py index 4fba5ad0..89d648ae 100644 --- a/tests/common/test_dramatiq.py +++ b/tests/common/test_dramatiq.py @@ -52,9 +52,7 @@ def test_init_dramatiq_with_test_env(): def test_init_dramatiq_with_redis(): """Test if the RedisBroker is set with a valid Redis URL.""" redis_url = "redis://localhost:6379/0" - config = AppConfig( - ENVIRONMENT="production", DRAMATIQ=DramatiqConfig(REDIS_URL=redis_url) - ) # Mock config + config = AppConfig(ENVIRONMENT="production", DRAMATIQ=DramatiqConfig(REDIS_URL=redis_url)) # Mock config with patch("common.dramatiq.RedisBroker") as mock_redis_broker: init_dramatiq(config) mock_redis_broker.assert_called_once_with(url=redis_url) @@ -64,12 +62,8 @@ def test_init_dramatiq_with_redis(): def test_init_dramatiq_without_redis_url(caplog): """Test if an exception is raised when in non-test environment without Redis URL.""" - config = AppConfig( - ENVIRONMENT="production", DRAMATIQ=DramatiqConfig(REDIS_URL=None) - ) # Mock config + config = AppConfig(ENVIRONMENT="production", DRAMATIQ=DramatiqConfig(REDIS_URL=None)) # Mock config with caplog.at_level(logging.CRITICAL): init_dramatiq(config) - assert ( - "Running a non-test/non-local environment without Redis URL set" in caplog.text - ) + assert "Running a non-test/non-local environment without Redis URL set" in caplog.text diff --git a/tests/common/test_tracing.py b/tests/common/test_tracing.py index b064af4c..91f67d7e 100644 --- a/tests/common/test_tracing.py +++ b/tests/common/test_tracing.py @@ -84,9 +84,7 @@ def sync_func(a, b): assert result == 4 mock_tracer.start_as_current_span.assert_called_once_with("sync_func") mock_span.set_attribute.assert_any_call("function.result", "4") - assert ( - call("function.args", "(10, 6)") not in mock_span.set_attribute.call_args_list - ) + assert call("function.args", "(10, 6)") not in mock_span.set_attribute.call_args_list async def test_disable_function_attributes_async(mock_tracer): @@ -107,9 +105,7 @@ async def async_func(a, b): assert result == 4 mock_tracer.start_as_current_span.assert_called_once_with("async_func") mock_span.set_attribute.assert_any_call("function.result", "4") - assert ( - call("function.args", "(10, 6)") not in mock_span.set_attribute.call_args_list - ) + assert call("function.args", "(10, 6)") not in mock_span.set_attribute.call_args_list def test_disable_result_in_span_sync(mock_tracer): diff --git a/tests/http_app/routes/books/conftest.py b/tests/http_app/routes/books/conftest.py index 1966453d..fb43a444 100644 --- a/tests/http_app/routes/books/conftest.py +++ b/tests/http_app/routes/books/conftest.py @@ -12,9 +12,7 @@ @pytest.fixture def book_service() -> Iterator[MagicMock]: svc = MagicMock(autospec=BookService) - svc.create_book = AsyncMock( - side_effect=lambda book: dto.Book(book_id=randbelow(1000), **book.model_dump()) - ) + svc.create_book = AsyncMock(side_effect=lambda book: dto.Book(book_id=randbelow(1000), **book.model_dump())) svc.list_books = AsyncMock( return_value=[ dto.Book( diff --git a/tests/http_app/routes/books/graphql/test_query_books.py b/tests/http_app/routes/books/graphql/test_query_books.py index d3d6a6c0..f31fb582 100644 --- a/tests/http_app/routes/books/graphql/test_query_books.py +++ b/tests/http_app/routes/books/graphql/test_query_books.py @@ -11,11 +11,7 @@ async def test_create_book(testapp): ) assert response.status_code == status.HTTP_200_OK assert response.json() == { - "data": { - "books": [ - {"authorName": "Stephen King", "bookId": 123, "title": "The Shining"} - ] - } + "data": {"books": [{"authorName": "Stephen King", "bookId": 123, "title": "The Shining"}]} } """ diff --git a/tests/http_app/routes/test_auth.py b/tests/http_app/routes/test_auth.py index 98ff7a05..0d210afc 100644 --- a/tests/http_app/routes/test_auth.py +++ b/tests/http_app/routes/test_auth.py @@ -45,9 +45,7 @@ async def test_decode_jwt_raises_if_jwks_client_fails(exception): security_scopes=SecurityScopes(), config=AppConfig(), jwks_client=mock_jwks_client, - token=HTTPAuthorizationCredentials( - scheme="bearer", credentials="some_token" - ), + token=HTTPAuthorizationCredentials(scheme="bearer", credentials="some_token"), ) diff --git a/tests/http_app/routes/test_events.py b/tests/http_app/routes/test_events.py index 3f46d065..bd17b11f 100644 --- a/tests/http_app/routes/test_events.py +++ b/tests/http_app/routes/test_events.py @@ -15,9 +15,7 @@ class FakeEvent(CloudEvent): async def test_event_schema_returns_data_if_present_in_registry(testapp): - with patch.dict( - "http_app.routes.events._EVENT_REGISTRY", {"test_event": FakeEvent}, clear=True - ): + with patch.dict("http_app.routes.events._EVENT_REGISTRY", {"test_event": FakeEvent}, clear=True): ac = TestClient(app=testapp, base_url="http://test") response = ac.get("/events/dataschemas/test_event") assert response.status_code == 200 @@ -30,9 +28,7 @@ async def test_event_schema_returns_404_when_not_present_in_registry(testapp): async def test_event_schema_list_returns_data_from_registry(testapp): - with patch.dict( - "http_app.routes.events._EVENT_REGISTRY", {"test_event": FakeEvent}, clear=True - ): + with patch.dict("http_app.routes.events._EVENT_REGISTRY", {"test_event": FakeEvent}, clear=True): ac = TestClient(app=testapp, base_url="http://test") response = ac.get("/events/dataschemas") assert response.status_code == 200 @@ -64,9 +60,7 @@ async def test_event_endpoints_returns_204(testapp, batch): response = ac.post( url, headers={"content-type": content_type}, - content=fake_event.model_dump_json() - if not batch - else f"[{fake_event.model_dump_json()}]", + content=fake_event.model_dump_json() if not batch else f"[{fake_event.model_dump_json()}]", ) svc.book_created_event_handler.assert_called_once() assert response.status_code == 204 @@ -98,9 +92,7 @@ class MalformedBookCreatedV1(BookCreatedV1): response = ac.post( url, headers={"content-type": content_type}, - content=fake_event.model_dump_json() - if not batch - else f"[{fake_event.model_dump_json()}]", + content=fake_event.model_dump_json() if not batch else f"[{fake_event.model_dump_json()}]", ) assert response.status_code == 422 @@ -122,8 +114,6 @@ async def test_wrong_content_type_returns_422(testapp, batch): response = ac.post( url, headers={"content-type": "application/json"}, - content=fake_event.model_dump_json() - if not batch - else f"[{fake_event.model_dump_json()}]", + content=fake_event.model_dump_json() if not batch else f"[{fake_event.model_dump_json()}]", ) assert response.status_code == 422 diff --git a/tests/storage/tables/test_book_table.py b/tests/storage/tables/test_book_table.py index 4e1868a3..7d87609d 100644 --- a/tests/storage/tables/test_book_table.py +++ b/tests/storage/tables/test_book_table.py @@ -5,9 +5,7 @@ # This test is to ensure the book table is initialised correctly async def test_book_table_works(test_sa_manager): - repo = SQLAlchemyAsyncRepository( - bind=test_sa_manager.get_bind(), model_class=BookModel - ) + repo = SQLAlchemyAsyncRepository(bind=test_sa_manager.get_bind(), model_class=BookModel) async with repo._get_session() as session: book = await session.get(BookModel, 1) assert book is None diff --git a/tests/storage/test_sqlalchemy_init.py b/tests/storage/test_sqlalchemy_init.py index d721d5ce..96ae994d 100644 --- a/tests/storage/test_sqlalchemy_init.py +++ b/tests/storage/test_sqlalchemy_init.py @@ -56,9 +56,7 @@ def test_init_tables_calls_only_supported_bind_initialisation(): def test_init_storage_calls_sqlalchemy_init_tables(): - with patch( - "common.storage.SQLAlchemy.init_tables", return_value=None - ) as mocked_init_tables: + with patch("common.storage.SQLAlchemy.init_tables", return_value=None) as mocked_init_tables: init_storage() mocked_init_tables.assert_called_once() diff --git a/uv.lock b/uv.lock index efe2a08d..1cff8db5 100644 --- a/uv.lock +++ b/uv.lock @@ -3,14 +3,14 @@ requires-python = ">=3.10, <3.14" [[package]] name = "aiosqlite" -version = "0.20.0" +version = "0.21.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/3a/22ff5415bf4d296c1e92b07fd746ad42c96781f13295a074d58e77747848/aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7", size = 21691 } +sdist = { url = "https://files.pythonhosted.org/packages/13/7d/8bca2bf9a247c2c5dfeec1d7a5f40db6518f88d314b8bca9da29670d2671/aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3", size = 13454 } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c4/c93eb22025a2de6b83263dfe3d7df2e19138e345bca6f18dba7394120930/aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6", size = 15564 }, + { url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792 }, ] [[package]] @@ -83,23 +83,24 @@ wheels = [ [[package]] name = "babel" -version = "2.16.0" +version = "2.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, ] [[package]] name = "beautifulsoup4" -version = "4.12.3" +version = "4.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/3c/adaf39ce1fb4afdd21b611e3d530b183bb7759c9b673d60db0e347fd4439/beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", size = 619516 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, + { url = "https://files.pythonhosted.org/packages/f9/49/6abb616eb3cbab6a7cca303dc02fdf3836de2e0b834bf966a7f5271a34d8/beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16", size = 186015 }, ] [[package]] @@ -226,11 +227,11 @@ wheels = [ [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.1.31" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, ] [[package]] @@ -627,41 +628,41 @@ wheels = [ [[package]] name = "factory-boy" -version = "3.3.1" +version = "3.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "faker" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/3d/8070dde623341401b1c80156583d4c793058fe250450178218bb6e45526c/factory_boy-3.3.1.tar.gz", hash = "sha256:8317aa5289cdfc45f9cae570feb07a6177316c82e34d14df3c2e1f22f26abef0", size = 163924 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146 } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/cf/44ec67152f3129d0114c1499dd34f0a0a0faf43d9c2af05bc535746ca482/factory_boy-3.3.1-py2.py3-none-any.whl", hash = "sha256:7b1113c49736e1e9995bc2a18f4dbf2c52cf0f841103517010b1d825712ce3ca", size = 36878 }, + { url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036 }, ] [[package]] name = "faker" -version = "35.0.0" +version = "35.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/18/86fe668976308d09e0178041c3756e646a1f5ddc676aa7fb0cf3cd52f5b9/faker-35.0.0.tar.gz", hash = "sha256:42f2da8cf561e38c72b25e9891168b1e25fec42b6b0b5b0b6cd6041da54af885", size = 1855098 } +sdist = { url = "https://files.pythonhosted.org/packages/6c/d9/c5bc5edaeea1a3a5da6e7f93a5c0bdd49e0740d8c4a1e7ea9515fd4da2ed/faker-35.2.0.tar.gz", hash = "sha256:28c24061780f83b45d9cb15a72b8f143b09d276c9ff52eb557744b7a89e8ba19", size = 1874908 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/fe/40452fb1730b10afa34dfe016097b28baa070ad74a1c1a3512ebed438c08/Faker-35.0.0-py3-none-any.whl", hash = "sha256:926d2301787220e0554c2e39afc4dc535ce4b0a8d0a089657137999f66334ef4", size = 1894841 }, + { url = "https://files.pythonhosted.org/packages/4e/db/bab82efcf241dabc93ad65cebaf0f2332cb2827b55a5d3a6ef1d52fa2c29/Faker-35.2.0-py3-none-any.whl", hash = "sha256:609abe555761ff31b0e5e16f958696e9b65c9224a7ac612ac96bfc2b8f09fe35", size = 1917786 }, ] [[package]] name = "fastapi" -version = "0.115.7" +version = "0.115.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/f5/3f921e59f189e513adb9aef826e2841672d50a399fead4e69afdeb808ff4/fastapi-0.115.7.tar.gz", hash = "sha256:0f106da6c01d88a6786b3248fb4d7a940d071f6f488488898ad5d354b25ed015", size = 293177 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/b2/5a5dc4affdb6661dea100324e19a7721d5dc524b464fe8e366c093fd7d87/fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9", size = 295403 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/7f/bbd4dcf0faf61bc68a01939256e2ed02d681e9334c1a3cef24d5f77aba9f/fastapi-0.115.7-py3-none-any.whl", hash = "sha256:eb6a8c8bf7f26009e8147111ff15b5177a0e19bb4a45bc3486ab14804539d21e", size = 94777 }, + { url = "https://files.pythonhosted.org/packages/8f/7d/2d6ce181d7a5f51dedb8c06206cbf0ec026a99bf145edd309f9e17c3282f/fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf", size = 94814 }, ] [[package]] @@ -1097,14 +1098,14 @@ wheels = [ [[package]] name = "mako" -version = "1.3.8" +version = "1.3.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/d9/8518279534ed7dace1795d5a47e49d5299dd0994eed1053996402a8902f9/mako-1.3.8.tar.gz", hash = "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8", size = 392069 } +sdist = { url = "https://files.pythonhosted.org/packages/62/4f/ddb1965901bc388958db9f0c991255b2c469349a741ae8c9cd8a562d70a6/mako-1.3.9.tar.gz", hash = "sha256:b5d65ff3462870feec922dbccf38f6efb44e5714d7b593a656be86663d8600ac", size = 392195 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/bf/7a6a36ce2e4cafdfb202752be68850e22607fccd692847c45c1ae3c17ba6/Mako-1.3.8-py3-none-any.whl", hash = "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627", size = 78569 }, + { url = "https://files.pythonhosted.org/packages/cd/83/de0a49e7de540513f53ab5d2e105321dedeb08a8f5850f0208decf4390ec/Mako-1.3.9-py3-none-any.whl", hash = "sha256:95920acccb578427a9aa38e37a186b1e43156c87260d7ba18ca63aa4c7cbd3a1", size = 78456 }, ] [[package]] @@ -1305,7 +1306,7 @@ wheels = [ [[package]] name = "mkdocs-material" -version = "9.5.50" +version = "9.6.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "babel" }, @@ -1320,9 +1321,9 @@ dependencies = [ { name = "regex" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/16/c48d5a28bc4a67c49808180b6009d4d1b4c0753739ffee3cc37046ab29d7/mkdocs_material-9.5.50.tar.gz", hash = "sha256:ae5fe16f3d7c9ccd05bb6916a7da7420cf99a9ce5e33debd9d40403a090d5825", size = 3923354 } +sdist = { url = "https://files.pythonhosted.org/packages/c9/75/fb8f772d4acf5439a446aedbe6e49b4c42a4bc4f8c866c930a7b0c3be2f8/mkdocs_material-9.6.2.tar.gz", hash = "sha256:a3de1c5d4c745f10afa78b1a02f917b9dce0808fb206adc0f5bb48b58c1ca21f", size = 3942567 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/b5/1bf29cd744896ae83bd38c72970782c843ba13e0240b1a85277bd3928637/mkdocs_material-9.5.50-py3-none-any.whl", hash = "sha256:f24100f234741f4d423a9d672a909d859668a4f404796be3cf035f10d6050385", size = 8645274 }, + { url = "https://files.pythonhosted.org/packages/d1/17/b97aa245d43933acd416361d4f34612baec8ad4a6337339d45448cde728d/mkdocs_material-9.6.2-py3-none-any.whl", hash = "sha256:71d90dbd63b393ad11a4d90151dfe3dcbfcd802c0f29ce80bebd9bbac6abc753", size = 8688648 }, ] [[package]] @@ -1353,40 +1354,40 @@ wheels = [ [[package]] name = "mypy" -version = "1.14.1" +version = "1.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002 }, - { url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400 }, - { url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172 }, - { url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732 }, - { url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197 }, - { url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836 }, - { url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432 }, - { url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515 }, - { url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791 }, - { url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203 }, - { url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900 }, - { url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869 }, - { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 }, - { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 }, - { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 }, - { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 }, - { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 }, - { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 }, - { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 }, - { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 }, - { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 }, - { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 }, - { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 }, - { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 }, - { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 }, +sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433 }, + { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472 }, + { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424 }, + { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450 }, + { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765 }, + { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701 }, + { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338 }, + { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540 }, + { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051 }, + { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751 }, + { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783 }, + { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618 }, + { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 }, + { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 }, + { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 }, + { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 }, + { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 }, + { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 }, + { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 }, + { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 }, + { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 }, + { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 }, + { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 }, + { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 }, + { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 }, ] [[package]] @@ -1422,29 +1423,29 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "deprecated" }, { name = "importlib-metadata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/8e/b886a5e9861afa188d1fe671fb96ff9a1d90a23d57799331e137cc95d573/opentelemetry_api-1.29.0.tar.gz", hash = "sha256:d04a6cf78aad09614f52964ecb38021e248f5714dc32c2e0d8fd99517b4d69cf", size = 62900 } +sdist = { url = "https://files.pythonhosted.org/packages/2b/6d/bbbf879826b7f3c89a45252010b5796fb1f1a0d45d9dc4709db0ef9a06c8/opentelemetry_api-1.30.0.tar.gz", hash = "sha256:375893400c1435bf623f7dfb3bcd44825fe6b56c34d0667c542ea8257b1a1240", size = 63703 } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/53/5249ea860d417a26a3a6f1bdedfc0748c4f081a3adaec3d398bc0f7c6a71/opentelemetry_api-1.29.0-py3-none-any.whl", hash = "sha256:5fcd94c4141cc49c736271f3e1efb777bebe9cc535759c54c936cca4f1b312b8", size = 64304 }, + { url = "https://files.pythonhosted.org/packages/36/0a/eea862fae6413d8181b23acf8e13489c90a45f17986ee9cf4eab8a0b9ad9/opentelemetry_api-1.30.0-py3-none-any.whl", hash = "sha256:d5f5284890d73fdf47f843dda3210edf37a38d66f44f2b5aedc1e89ed455dc09", size = 64955 }, ] [[package]] name = "opentelemetry-distro" -version = "0.50b0" +version = "0.51b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/38/30ad58042eba064796a8c01cf723f587320e23aa2677c69dfd9ee29435d8/opentelemetry_distro-0.50b0.tar.gz", hash = "sha256:3e059e00f53553ebd646d1162d1d3edf5d7c6d3ceafd54a49e74c90dc1c39a7d", size = 2564 } +sdist = { url = "https://files.pythonhosted.org/packages/16/d9/f0d18b7df18ce4ec151530c30f8281b0b3d3f7fcba9f5a30c87c179b0102/opentelemetry_distro-0.51b0.tar.gz", hash = "sha256:4890d285f2f6bfc84e260aa5f8243b2b32aafcc896b0d9290007d9f306246011", size = 2598 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/e9/aeed7a8ffbe01cf09dd41eae8fcbe078547fb27a06cd9fc14fb08f95e439/opentelemetry_distro-0.50b0-py3-none-any.whl", hash = "sha256:5fa2e2a99a047ea477fab53e73fb8088b907bda141e8440745b92eb2a84d74aa", size = 3328 }, + { url = "https://files.pythonhosted.org/packages/e7/71/51ec46f69dfb4cf7e3fd66c6404c2216b75aa096ec1f066fe497a00a1096/opentelemetry_distro-0.51b0-py3-none-any.whl", hash = "sha256:60b5d18449ef6c1fc5bc71e4d48ba20b3711f42f4a84b455ecc66a8cf4d9ad80", size = 3349 }, ] [package.optional-dependencies] @@ -1454,32 +1455,32 @@ otlp = [ [[package]] name = "opentelemetry-exporter-otlp" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-exporter-otlp-proto-grpc" }, { name = "opentelemetry-exporter-otlp-proto-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/23/824e71822969cd3018897f5b0246baf8305bf7635f20df1ce5dfc423c32d/opentelemetry_exporter_otlp-1.29.0.tar.gz", hash = "sha256:ee7dfcccbb5e87ad9b389908452e10b7beeab55f70a83f41ce5b8c4efbde6544", size = 6159 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/5f/612208105a64018bdf1eae09f2bdddb2fb1ab292bb5e5aae8a3f567ddd3f/opentelemetry_exporter_otlp-1.30.0.tar.gz", hash = "sha256:da7769f95cd5be5b09dd4188ac153a33709eda652217f2d10aed6518c8e60f0d", size = 6188 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/54/2a84533f39bb240958d691bb3ddf1c3fb6a92356654fb2e02a210f65ce6b/opentelemetry_exporter_otlp-1.29.0-py3-none-any.whl", hash = "sha256:b8da6e20f5b0ffe604154b1e16a407eade17ce310c42fb85bb4e1246fc3688ad", size = 7011 }, + { url = "https://files.pythonhosted.org/packages/e2/9a/beb4d395cd581d3738c3b3942f2c2487991b6066a143e4e42b8e84e4ba3a/opentelemetry_exporter_otlp-1.30.0-py3-none-any.whl", hash = "sha256:44e11054ec571ccfed73a83c6429dee5d334d061d0e0572e3160d6de97156dbc", size = 7042 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/58/f7fd7eaf592b2521999a4271ab3ce1c82fe37fe9b0dc25c348398d95d66a/opentelemetry_exporter_otlp_proto_common-1.29.0.tar.gz", hash = "sha256:e7c39b5dbd1b78fe199e40ddfe477e6983cb61aa74ba836df09c3869a3e3e163", size = 19133 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/d7/44098bf1ef89fc5810cdbda05faa2ae9322a0dbda4921cdc965dc68a9856/opentelemetry_exporter_otlp_proto_common-1.30.0.tar.gz", hash = "sha256:ddbfbf797e518411857d0ca062c957080279320d6235a279f7b64ced73c13897", size = 19640 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/75/7609bda3d72bf307839570b226180513e854c01443ebe265ed732a4980fc/opentelemetry_exporter_otlp_proto_common-1.29.0-py3-none-any.whl", hash = "sha256:a9d7376c06b4da9cf350677bcddb9618ed4b8255c3f6476975f5e38274ecd3aa", size = 18459 }, + { url = "https://files.pythonhosted.org/packages/ee/54/f4b3de49f8d7d3a78fd6e6e1a6fd27dd342eb4d82c088b9078c6a32c3808/opentelemetry_exporter_otlp_proto_common-1.30.0-py3-none-any.whl", hash = "sha256:5468007c81aa9c44dc961ab2cf368a29d3475977df83b4e30aeed42aa7bc3b38", size = 18747 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "deprecated" }, @@ -1490,14 +1491,14 @@ dependencies = [ { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/aa/b3f2190613141f35fe15145bf438334fdd1eac8aeeee4f7ecbc887999443/opentelemetry_exporter_otlp_proto_grpc-1.29.0.tar.gz", hash = "sha256:3d324d07d64574d72ed178698de3d717f62a059a93b6b7685ee3e303384e73ea", size = 26224 } +sdist = { url = "https://files.pythonhosted.org/packages/86/3e/c7246df92c25e6ce95c349ad21597b4471b01ec9471e95d5261f1629fe92/opentelemetry_exporter_otlp_proto_grpc-1.30.0.tar.gz", hash = "sha256:d0f10f0b9b9a383b7d04a144d01cb280e70362cccc613987e234183fd1f01177", size = 26256 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/de/4b4127a25d1594851d99032f3a9acb09cb512d11edec713410fb906607f4/opentelemetry_exporter_otlp_proto_grpc-1.29.0-py3-none-any.whl", hash = "sha256:5a2a3a741a2543ed162676cf3eefc2b4150e6f4f0a193187afb0d0e65039c69c", size = 18520 }, + { url = "https://files.pythonhosted.org/packages/5e/35/d9f63fd84c2ed8dbd407bcbb933db4ed6e1b08e7fbdaca080b9ac309b927/opentelemetry_exporter_otlp_proto_grpc-1.30.0-py3-none-any.whl", hash = "sha256:2906bcae3d80acc54fd1ffcb9e44d324e8631058b502ebe4643ca71d1ff30830", size = 18550 }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "deprecated" }, @@ -1508,14 +1509,14 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/88/e70a2e9fbb1bddb1ab7b6d74fb02c68601bff5948292ce33464c84ee082e/opentelemetry_exporter_otlp_proto_http-1.29.0.tar.gz", hash = "sha256:b10d174e3189716f49d386d66361fbcf6f2b9ad81e05404acdee3f65c8214204", size = 15041 } +sdist = { url = "https://files.pythonhosted.org/packages/04/f9/abb9191d536e6a2e2b7903f8053bf859a76bf784e3ca19a5749550ef19e4/opentelemetry_exporter_otlp_proto_http-1.30.0.tar.gz", hash = "sha256:c3ae75d4181b1e34a60662a6814d0b94dd33b628bee5588a878bed92cee6abdc", size = 15073 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/49/a1c3d24e8fe73b5f422e21b46c24aed3db7fd9427371c06442e7bdfe4d3b/opentelemetry_exporter_otlp_proto_http-1.29.0-py3-none-any.whl", hash = "sha256:b228bdc0f0cfab82eeea834a7f0ffdd2a258b26aa33d89fb426c29e8e934d9d0", size = 17217 }, + { url = "https://files.pythonhosted.org/packages/e1/3c/cdf34bc459613f2275aff9b258f35acdc4c4938dad161d17437de5d4c034/opentelemetry_exporter_otlp_proto_http-1.30.0-py3-none-any.whl", hash = "sha256:9578e790e579931c5ffd50f1e6975cbdefb6a0a0a5dea127a6ae87df10e0a589", size = 17245 }, ] [[package]] name = "opentelemetry-instrumentation" -version = "0.50b0" +version = "0.51b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -1523,14 +1524,14 @@ dependencies = [ { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/2e/2e59a7cb636dc394bd7cf1758ada5e8ed87590458ca6bb2f9c26e0243847/opentelemetry_instrumentation-0.50b0.tar.gz", hash = "sha256:7d98af72de8dec5323e5202e46122e5f908592b22c6d24733aad619f07d82979", size = 26539 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/5a/4c7f02235ac1269b48f3855f6be1afc641f31d4888d28b90b732fbce7141/opentelemetry_instrumentation-0.51b0.tar.gz", hash = "sha256:4ca266875e02f3988536982467f7ef8c32a38b8895490ddce9ad9604649424fa", size = 27760 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/b1/55a77152a83ec8998e520a3a575f44af1020cfe4bdc000b7538583293b85/opentelemetry_instrumentation-0.50b0-py3-none-any.whl", hash = "sha256:b8f9fc8812de36e1c6dffa5bfc6224df258841fb387b6dfe5df15099daa10630", size = 30728 }, + { url = "https://files.pythonhosted.org/packages/40/2c/48fa93f1acca9f79a06da0df7bfe916632ecc7fce1971067b3e46bcae55b/opentelemetry_instrumentation-0.51b0-py3-none-any.whl", hash = "sha256:c6de8bd26b75ec8b0e54dff59e198946e29de6a10ec65488c357d4b34aa5bdcf", size = 30923 }, ] [[package]] name = "opentelemetry-instrumentation-asgi" -version = "0.50b0" +version = "0.51b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiref" }, @@ -1539,14 +1540,14 @@ dependencies = [ { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/cc/a7b2fd243c6d2621803092eba62e450071b6752dfe4f64f530bbfd91a328/opentelemetry_instrumentation_asgi-0.50b0.tar.gz", hash = "sha256:3ca4cb5616ae6a3e8ce86e7d5c360a8d8cc8ed722cf3dc8a5e44300774e87d49", size = 24105 } +sdist = { url = "https://files.pythonhosted.org/packages/9e/67/8aa6e1129f641f0f3f8786e6c5d18c1f2bbe490bd4b0e91a6879e85154d2/opentelemetry_instrumentation_asgi-0.51b0.tar.gz", hash = "sha256:b3fe97c00f0bfa934371a69674981d76591c68d937b6422a5716ca21081b4148", size = 24201 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/81/0899c6b56b1023835f266d909250d439174afa0c34ed5944c5021d3da263/opentelemetry_instrumentation_asgi-0.50b0-py3-none-any.whl", hash = "sha256:2ba1297f746e55dec5a17fe825689da0613662fb25c004c3965a6c54b1d5be22", size = 16304 }, + { url = "https://files.pythonhosted.org/packages/54/7e/0a95ab37302729543631a789ba8e71dea75c520495739dbbbdfdc580b401/opentelemetry_instrumentation_asgi-0.51b0-py3-none-any.whl", hash = "sha256:e8072993db47303b633c6ec1bc74726ba4d32bd0c46c28dfadf99f79521a324c", size = 16340 }, ] [[package]] name = "opentelemetry-instrumentation-httpx" -version = "0.50b0" +version = "0.51b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -1555,14 +1556,14 @@ dependencies = [ { name = "opentelemetry-util-http" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/9c/4a6b0af28d579690fbab7ffd0560802e4384cd56c8d637f99641f44a7291/opentelemetry_instrumentation_httpx-0.50b0.tar.gz", hash = "sha256:0072d1d39552449c08a45a7a0db0cd6af32c85205bd97267b2a272fc56a9b438", size = 17611 } +sdist = { url = "https://files.pythonhosted.org/packages/b7/d5/4a3990c461ae7e55212115e0f8f3aa412b5ce6493579e85c292245ac69ea/opentelemetry_instrumentation_httpx-0.51b0.tar.gz", hash = "sha256:061d426a04bf5215a859fea46662e5074f920e5cbde7e6ad6825a0a1b595802c", size = 17700 } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/25/6a0edd3d161d5a15cf4538e32a1289f1f6fd7a52651afd32bb856ede4e99/opentelemetry_instrumentation_httpx-0.50b0-py3-none-any.whl", hash = "sha256:27acd41a9e70384d0978d58f492e5c16fc7a1b2363d5992b5bd0a27a3df7b68e", size = 13838 }, + { url = "https://files.pythonhosted.org/packages/c3/ba/23d4ab6402408c01f1c3f32e0c04ea6dae575bf19bcb9a0049c9e768c983/opentelemetry_instrumentation_httpx-0.51b0-py3-none-any.whl", hash = "sha256:2e3fdf755ba6ead6ab43031497c3d55d4c796d0368eccc0ce48d304b7ec6486a", size = 14109 }, ] [[package]] name = "opentelemetry-instrumentation-sqlalchemy" -version = "0.50b0" +version = "0.51b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -1571,9 +1572,9 @@ dependencies = [ { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/ac/0cc668bb74b3646447936307bc0a56756568602e46be7a53a770cadab5f3/opentelemetry_instrumentation_sqlalchemy-0.50b0.tar.gz", hash = "sha256:8560fe2375d973746907599f360199ba0f658189ef6feba73c1702e8d832bb6e", size = 13632 } +sdist = { url = "https://files.pythonhosted.org/packages/ab/b2/970b1b46576b663bba64503486afe266c064c2bfd1862876420714ce29d9/opentelemetry_instrumentation_sqlalchemy-0.51b0.tar.gz", hash = "sha256:dbfe95b69006017f903dda194606be458d54789e6b3419d37161fb8861bb98a5", size = 14582 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/86/08880581575628870d3c052398b8e764bd0cb18f5d17b3620aefa791592e/opentelemetry_instrumentation_sqlalchemy-0.50b0-py3-none-any.whl", hash = "sha256:7385c380d3567f28a5a6e9b453400900f4b095230a244a13e3f54b4f3d36bd19", size = 13503 }, + { url = "https://files.pythonhosted.org/packages/12/d4/b68c3b3388dd5107f3ed532747e112249c152ba44af71a1f96673d66e3ee/opentelemetry_instrumentation_sqlalchemy-0.51b0-py3-none-any.whl", hash = "sha256:5ff4816228b496aef1511149e2b17a25e0faacec4d5eb65bf18a9964af40f1af", size = 14132 }, ] [[package]] @@ -1593,50 +1594,50 @@ wheels = [ [[package]] name = "opentelemetry-proto" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/52/fd3b3d79e1b00ad2dcac92db6885e49bedbf7a6828647954e4952d653132/opentelemetry_proto-1.29.0.tar.gz", hash = "sha256:3c136aa293782e9b44978c738fff72877a4b78b5d21a64e879898db7b2d93e5d", size = 34320 } +sdist = { url = "https://files.pythonhosted.org/packages/31/6e/c1ff2e3b0cd3a189a6be03fd4d63441d73d7addd9117ab5454e667b9b6c7/opentelemetry_proto-1.30.0.tar.gz", hash = "sha256:afe5c9c15e8b68d7c469596e5b32e8fc085eb9febdd6fb4e20924a93a0389179", size = 34362 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/66/a500e38ee322d89fce61c74bd7769c8ef3bebc6c2f43fda5f3fc3441286d/opentelemetry_proto-1.29.0-py3-none-any.whl", hash = "sha256:495069c6f5495cbf732501cdcd3b7f60fda2b9d3d4255706ca99b7ca8dec53ff", size = 55818 }, + { url = "https://files.pythonhosted.org/packages/56/d7/85de6501f7216995295f7ec11e470142e6a6e080baacec1753bbf272e007/opentelemetry_proto-1.30.0-py3-none-any.whl", hash = "sha256:c6290958ff3ddacc826ca5abbeb377a31c2334387352a259ba0df37c243adc11", size = 55854 }, ] [[package]] name = "opentelemetry-sdk" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/5a/1ed4c3cf6c09f80565fc085f7e8efa0c222712fd2a9412d07424705dcf72/opentelemetry_sdk-1.29.0.tar.gz", hash = "sha256:b0787ce6aade6ab84315302e72bd7a7f2f014b0fb1b7c3295b88afe014ed0643", size = 157229 } +sdist = { url = "https://files.pythonhosted.org/packages/93/ee/d710062e8a862433d1be0b85920d0c653abe318878fef2d14dfe2c62ff7b/opentelemetry_sdk-1.30.0.tar.gz", hash = "sha256:c9287a9e4a7614b9946e933a67168450b9ab35f08797eb9bc77d998fa480fa18", size = 158633 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/1d/512b86af21795fb463726665e2f61db77d384e8779fdcf4cb0ceec47866d/opentelemetry_sdk-1.29.0-py3-none-any.whl", hash = "sha256:173be3b5d3f8f7d671f20ea37056710217959e774e2749d984355d1f9391a30a", size = 118078 }, + { url = "https://files.pythonhosted.org/packages/97/28/64d781d6adc6bda2260067ce2902bd030cf45aec657e02e28c5b4480b976/opentelemetry_sdk-1.30.0-py3-none-any.whl", hash = "sha256:14fe7afc090caad881addb6926cec967129bd9260c4d33ae6a217359f6b61091", size = 118717 }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.50b0" +version = "0.51b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "deprecated" }, { name = "opentelemetry-api" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/4e/d7c7c91ff47cd96fe4095dd7231701aec7347426fd66872ff320d6cd1fcc/opentelemetry_semantic_conventions-0.50b0.tar.gz", hash = "sha256:02dc6dbcb62f082de9b877ff19a3f1ffaa3c306300fa53bfac761c4567c83d38", size = 100459 } +sdist = { url = "https://files.pythonhosted.org/packages/1e/c0/0f9ef4605fea7f2b83d55dd0b0d7aebe8feead247cd6facd232b30907b4f/opentelemetry_semantic_conventions-0.51b0.tar.gz", hash = "sha256:3fabf47f35d1fd9aebcdca7e6802d86bd5ebc3bc3408b7e3248dde6e87a18c47", size = 107191 } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/fb/dc15fad105450a015e913cfa4f5c27b6a5f1bea8fb649f8cae11e699c8af/opentelemetry_semantic_conventions-0.50b0-py3-none-any.whl", hash = "sha256:e87efba8fdb67fb38113efea6a349531e75ed7ffc01562f65b802fcecb5e115e", size = 166602 }, + { url = "https://files.pythonhosted.org/packages/2e/75/d7bdbb6fd8630b4cafb883482b75c4fc276b6426619539d266e32ac53266/opentelemetry_semantic_conventions-0.51b0-py3-none-any.whl", hash = "sha256:fdc777359418e8d06c86012c3dc92c88a6453ba662e941593adb062e48c2eeae", size = 177416 }, ] [[package]] name = "opentelemetry-util-http" -version = "0.50b0" +version = "0.51b0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/10/ce3f0d1157cedbd819194f0b27a6bbb7c19a8bceb3941e4a4775014076cf/opentelemetry_util_http-0.50b0.tar.gz", hash = "sha256:dc4606027e1bc02aabb9533cc330dd43f874fca492e4175c31d7154f341754af", size = 7859 } +sdist = { url = "https://files.pythonhosted.org/packages/58/64/32510c0a803465eb6ef1f5bd514d0f5627f8abc9444ed94f7240faf6fcaa/opentelemetry_util_http-0.51b0.tar.gz", hash = "sha256:05edd19ca1cc3be3968b1e502fd94816901a365adbeaab6b6ddb974384d3a0b9", size = 8043 } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8a/9e1b54f50d1fddebbeac9a9b0632f8db6ece7add904fb593ee2e268ee4de/opentelemetry_util_http-0.50b0-py3-none-any.whl", hash = "sha256:21f8aedac861ffa3b850f8c0a6c373026189eb8630ac6e14a2bf8c55695cc090", size = 6942 }, + { url = "https://files.pythonhosted.org/packages/48/dd/c371eeb9cc78abbdad231a27ce1a196a37ef96328d876ccbb381dea4c8ee/opentelemetry_util_http-0.51b0-py3-none-any.whl", hash = "sha256:0561d7a6e9c422b9ef9ae6e77eafcfcd32a2ab689f5e801475cbb67f189efa20", size = 7304 }, ] [[package]] @@ -1910,15 +1911,15 @@ wheels = [ [[package]] name = "pymdown-extensions" -version = "10.14.2" +version = "10.14.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/7b/de388047c577e43dc45ce35c23b9b349ec3df8c7023c3e3c4d413a850982/pymdown_extensions-10.14.2.tar.gz", hash = "sha256:7a77b8116dc04193f2c01143760a43387bd9dc4aa05efacb7d838885a7791253", size = 846777 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/44/e6de2fdc880ad0ec7547ca2e087212be815efbc9a425a8d5ba9ede602cbb/pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b", size = 846846 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/a3/61527d80d84e9fd4d97649322e83bd7efde8200fc07fe34469c8c2bd0d91/pymdown_extensions-10.14.2-py3-none-any.whl", hash = "sha256:f45bc5892410e54fd738ab8ccd736098b7ff0cb27fdb4bf24d0a0c6584bc90e1", size = 264459 }, + { url = "https://files.pythonhosted.org/packages/eb/f5/b9e2a42aa8f9e34d52d66de87941ecd236570c7ed2e87775ed23bbe4e224/pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9", size = 264467 }, ] [[package]] @@ -2187,27 +2188,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.9.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/7f/60fda2eec81f23f8aa7cbbfdf6ec2ca11eb11c273827933fb2541c2ce9d8/ruff-0.9.3.tar.gz", hash = "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a", size = 3586740 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/77/4fb790596d5d52c87fd55b7160c557c400e90f6116a56d82d76e95d9374a/ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624", size = 11656815 }, - { url = "https://files.pythonhosted.org/packages/a2/a8/3338ecb97573eafe74505f28431df3842c1933c5f8eae615427c1de32858/ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c", size = 11594821 }, - { url = "https://files.pythonhosted.org/packages/8e/89/320223c3421962762531a6b2dd58579b858ca9916fb2674874df5e97d628/ruff-0.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4", size = 11040475 }, - { url = "https://files.pythonhosted.org/packages/b2/bd/1d775eac5e51409535804a3a888a9623e87a8f4b53e2491580858a083692/ruff-0.9.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439", size = 11856207 }, - { url = "https://files.pythonhosted.org/packages/7f/c6/3e14e09be29587393d188454064a4aa85174910d16644051a80444e4fd88/ruff-0.9.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5", size = 11420460 }, - { url = "https://files.pythonhosted.org/packages/ef/42/b7ca38ffd568ae9b128a2fa76353e9a9a3c80ef19746408d4ce99217ecc1/ruff-0.9.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4", size = 12605472 }, - { url = "https://files.pythonhosted.org/packages/a6/a1/3167023f23e3530fde899497ccfe239e4523854cb874458ac082992d206c/ruff-0.9.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1", size = 13243123 }, - { url = "https://files.pythonhosted.org/packages/d0/b4/3c600758e320f5bf7de16858502e849f4216cb0151f819fa0d1154874802/ruff-0.9.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5", size = 12744650 }, - { url = "https://files.pythonhosted.org/packages/be/38/266fbcbb3d0088862c9bafa8b1b99486691d2945a90b9a7316336a0d9a1b/ruff-0.9.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4", size = 14458585 }, - { url = "https://files.pythonhosted.org/packages/63/a6/47fd0e96990ee9b7a4abda62de26d291bd3f7647218d05b7d6d38af47c30/ruff-0.9.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6", size = 12419624 }, - { url = "https://files.pythonhosted.org/packages/84/5d/de0b7652e09f7dda49e1a3825a164a65f4998175b6486603c7601279baad/ruff-0.9.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730", size = 11843238 }, - { url = "https://files.pythonhosted.org/packages/9e/be/3f341ceb1c62b565ec1fb6fd2139cc40b60ae6eff4b6fb8f94b1bb37c7a9/ruff-0.9.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2", size = 11484012 }, - { url = "https://files.pythonhosted.org/packages/a3/c8/ff8acbd33addc7e797e702cf00bfde352ab469723720c5607b964491d5cf/ruff-0.9.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519", size = 12038494 }, - { url = "https://files.pythonhosted.org/packages/73/b1/8d9a2c0efbbabe848b55f877bc10c5001a37ab10aca13c711431673414e5/ruff-0.9.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b", size = 12473639 }, - { url = "https://files.pythonhosted.org/packages/cb/44/a673647105b1ba6da9824a928634fe23186ab19f9d526d7bdf278cd27bc3/ruff-0.9.3-py3-none-win32.whl", hash = "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c", size = 9834353 }, - { url = "https://files.pythonhosted.org/packages/c3/01/65cadb59bf8d4fbe33d1a750103e6883d9ef302f60c28b73b773092fbde5/ruff-0.9.3-py3-none-win_amd64.whl", hash = "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4", size = 10821444 }, - { url = "https://files.pythonhosted.org/packages/69/cb/b3fe58a136a27d981911cba2f18e4b29f15010623b79f0f2510fd0d31fd3/ruff-0.9.3-py3-none-win_arm64.whl", hash = "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b", size = 10038168 }, +version = "0.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/17/529e78f49fc6f8076f50d985edd9a2cf011d1dbadb1cdeacc1d12afc1d26/ruff-0.9.4.tar.gz", hash = "sha256:6907ee3529244bb0ed066683e075f09285b38dd5b4039370df6ff06041ca19e7", size = 3599458 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/f8/3fafb7804d82e0699a122101b5bee5f0d6e17c3a806dcbc527bb7d3f5b7a/ruff-0.9.4-py3-none-linux_armv6l.whl", hash = "sha256:64e73d25b954f71ff100bb70f39f1ee09e880728efb4250c632ceed4e4cdf706", size = 11668400 }, + { url = "https://files.pythonhosted.org/packages/2e/a6/2efa772d335da48a70ab2c6bb41a096c8517ca43c086ea672d51079e3d1f/ruff-0.9.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6ce6743ed64d9afab4fafeaea70d3631b4d4b28b592db21a5c2d1f0ef52934bf", size = 11628395 }, + { url = "https://files.pythonhosted.org/packages/dc/d7/cd822437561082f1c9d7225cc0d0fbb4bad117ad7ac3c41cd5d7f0fa948c/ruff-0.9.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54499fb08408e32b57360f6f9de7157a5fec24ad79cb3f42ef2c3f3f728dfe2b", size = 11090052 }, + { url = "https://files.pythonhosted.org/packages/9e/67/3660d58e893d470abb9a13f679223368ff1684a4ef40f254a0157f51b448/ruff-0.9.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37c892540108314a6f01f105040b5106aeb829fa5fb0561d2dcaf71485021137", size = 11882221 }, + { url = "https://files.pythonhosted.org/packages/79/d1/757559995c8ba5f14dfec4459ef2dd3fcea82ac43bc4e7c7bf47484180c0/ruff-0.9.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de9edf2ce4b9ddf43fd93e20ef635a900e25f622f87ed6e3047a664d0e8f810e", size = 11424862 }, + { url = "https://files.pythonhosted.org/packages/c0/96/7915a7c6877bb734caa6a2af424045baf6419f685632469643dbd8eb2958/ruff-0.9.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87c90c32357c74f11deb7fbb065126d91771b207bf9bfaaee01277ca59b574ec", size = 12626735 }, + { url = "https://files.pythonhosted.org/packages/0e/cc/dadb9b35473d7cb17c7ffe4737b4377aeec519a446ee8514123ff4a26091/ruff-0.9.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56acd6c694da3695a7461cc55775f3a409c3815ac467279dfa126061d84b314b", size = 13255976 }, + { url = "https://files.pythonhosted.org/packages/5f/c3/ad2dd59d3cabbc12df308cced780f9c14367f0321e7800ca0fe52849da4c/ruff-0.9.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0c93e7d47ed951b9394cf352d6695b31498e68fd5782d6cbc282425655f687a", size = 12752262 }, + { url = "https://files.pythonhosted.org/packages/c7/17/5f1971e54bd71604da6788efd84d66d789362b1105e17e5ccc53bba0289b/ruff-0.9.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4c8772670aecf037d1bf7a07c39106574d143b26cfe5ed1787d2f31e800214", size = 14401648 }, + { url = "https://files.pythonhosted.org/packages/30/24/6200b13ea611b83260501b6955b764bb320e23b2b75884c60ee7d3f0b68e/ruff-0.9.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfc5f1d7afeda8d5d37660eeca6d389b142d7f2b5a1ab659d9214ebd0e025231", size = 12414702 }, + { url = "https://files.pythonhosted.org/packages/34/cb/f5d50d0c4ecdcc7670e348bd0b11878154bc4617f3fdd1e8ad5297c0d0ba/ruff-0.9.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faa935fc00ae854d8b638c16a5f1ce881bc3f67446957dd6f2af440a5fc8526b", size = 11859608 }, + { url = "https://files.pythonhosted.org/packages/d6/f4/9c8499ae8426da48363bbb78d081b817b0f64a9305f9b7f87eab2a8fb2c1/ruff-0.9.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a6c634fc6f5a0ceae1ab3e13c58183978185d131a29c425e4eaa9f40afe1e6d6", size = 11485702 }, + { url = "https://files.pythonhosted.org/packages/18/59/30490e483e804ccaa8147dd78c52e44ff96e1c30b5a95d69a63163cdb15b/ruff-0.9.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:433dedf6ddfdec7f1ac7575ec1eb9844fa60c4c8c2f8887a070672b8d353d34c", size = 12067782 }, + { url = "https://files.pythonhosted.org/packages/3d/8c/893fa9551760b2f8eb2a351b603e96f15af167ceaf27e27ad873570bc04c/ruff-0.9.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d612dbd0f3a919a8cc1d12037168bfa536862066808960e0cc901404b77968f0", size = 12483087 }, + { url = "https://files.pythonhosted.org/packages/23/15/f6751c07c21ca10e3f4a51ea495ca975ad936d780c347d9808bcedbd7182/ruff-0.9.4-py3-none-win32.whl", hash = "sha256:db1192ddda2200671f9ef61d9597fcef89d934f5d1705e571a93a67fb13a4402", size = 9852302 }, + { url = "https://files.pythonhosted.org/packages/12/41/2d2d2c6a72e62566f730e49254f602dfed23019c33b5b21ea8f8917315a1/ruff-0.9.4-py3-none-win_amd64.whl", hash = "sha256:05bebf4cdbe3ef75430d26c375773978950bbf4ee3c95ccb5448940dc092408e", size = 10850051 }, + { url = "https://files.pythonhosted.org/packages/c6/e6/3d6ec3bc3d254e7f005c543a661a41c3e788976d0e52a1ada195bd664344/ruff-0.9.4-py3-none-win_arm64.whl", hash = "sha256:585792f1e81509e38ac5123492f8875fbc36f3ede8185af0a26df348e5154f41", size = 10078251 }, ] [[package]] From 1db967cce7374a11a715be0e459c440da5c3a0a9 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:32:40 +0000 Subject: [PATCH 13/19] Fix edge case for messages without $defs --- src/common/asyncapi.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/common/asyncapi.py b/src/common/asyncapi.py index 011522fe..35ab323c 100644 --- a/src/common/asyncapi.py +++ b/src/common/asyncapi.py @@ -155,16 +155,19 @@ def register_channel_operation( _operation_message_refs = [] for message in messages: # TODO: Check for overlapping model schemas, if they are different log a warning! - _components_schemas[message.__name__] = message.model_json_schema( + _message_json_schema = message.model_json_schema( mode="validation" if operation_type == "receive" else "serialization", ref_template="#/components/schemas/{model}", ) - _components_schemas.update( - message.model_json_schema(mode="serialization", ref_template="#/components/schemas/{model}")["$defs"] - ) + + _components_schemas[message.__name__] = _message_json_schema + + if _message_json_schema.get("$defs"): + _components_schemas.update(_message_json_schema["$defs"]) _channels[channel_id].messages[message.__name__] = pa.Message( # type: ignore payload=pa.Reference(ref=f"#/components/schemas/{message.__name__}") ) + # Cannot point to the /components path _operation_message_refs.append(pa.Reference(ref=f"#/channels/{channel_id}/messages/{message.__name__}")) From 3679c94e79a6dbd2ac944bb39ffee6846d5ca043 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:33:23 +0000 Subject: [PATCH 14/19] Code style --- src/http_app/routes/ws/chat.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/http_app/routes/ws/chat.py b/src/http_app/routes/ws/chat.py index 01a66716..ef53e44b 100644 --- a/src/http_app/routes/ws/chat.py +++ b/src/http_app/routes/ws/chat.py @@ -32,6 +32,20 @@ async def broadcast(self, message: str): manager = ConnectionManager() + +@router.websocket("/{client_id}") +async def websocket_endpoint(websocket: WebSocket, client_id: int): + await manager.connect(websocket) + try: + while True: + data = await websocket.receive_text() + await manager.send_personal_message(f"You wrote: {data}", websocket) + await manager.broadcast(f"Client #{client_id} says: {data}") + except WebSocketDisconnect: + manager.disconnect(websocket) + await manager.broadcast(f"Client #{client_id} left the chat") + + """ In websocket case we create a server per route. If we create other routes we can create more servers @@ -61,16 +75,3 @@ async def broadcast(self, message: str): messages=[BookUpdatedV1], operation_name="ReceiveMessage", ) - - -@router.websocket("/{client_id}") -async def websocket_endpoint(websocket: WebSocket, client_id: int): - await manager.connect(websocket) - try: - while True: - data = await websocket.receive_text() - await manager.send_personal_message(f"You wrote: {data}", websocket) - await manager.broadcast(f"Client #{client_id} says: {data}") - except WebSocketDisconnect: - manager.disconnect(websocket) - await manager.broadcast(f"Client #{client_id} left the chat") From 7f7211ef326142cbf679178391902658e18b958e Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:33:55 +0000 Subject: [PATCH 15/19] Get application name from config, rename config dependency --- src/http_app/dependencies.py | 2 +- src/http_app/routes/auth.py | 6 +++--- src/http_app/routes/docs_ws.py | 8 ++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/http_app/dependencies.py b/src/http_app/dependencies.py index d576c5c3..2ab79af6 100644 --- a/src/http_app/dependencies.py +++ b/src/http_app/dependencies.py @@ -2,5 +2,5 @@ from http_app import context -def app_config() -> AppConfig: +def get_app_config() -> AppConfig: return context.app_config.get() diff --git a/src/http_app/routes/auth.py b/src/http_app/routes/auth.py index f7ee29e1..d6b5804f 100644 --- a/src/http_app/routes/auth.py +++ b/src/http_app/routes/auth.py @@ -5,7 +5,7 @@ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer, SecurityScopes from common import AppConfig -from http_app.dependencies import app_config +from http_app.dependencies import get_app_config class MissingAuthorizationServerException(HTTPException): @@ -26,7 +26,7 @@ def __init__(self): super().__init__(status_code=status.HTTP_401_UNAUTHORIZED, detail="Requires authentication") -def _jwks_client(config: Annotated[AppConfig, Depends(app_config)]) -> jwt.PyJWKClient: +def _jwks_client(config: Annotated[AppConfig, Depends(get_app_config)]) -> jwt.PyJWKClient: if not config.AUTH.JWKS_URL: raise MissingAuthorizationServerException() return jwt.PyJWKClient(config.AUTH.JWKS_URL) @@ -34,7 +34,7 @@ def _jwks_client(config: Annotated[AppConfig, Depends(app_config)]) -> jwt.PyJWK async def decode_jwt( security_scopes: SecurityScopes, - config: AppConfig = Depends(app_config), + config: AppConfig = Depends(get_app_config), jwks_client: jwt.PyJWKClient = Depends(_jwks_client), token: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer()), ): diff --git a/src/http_app/routes/docs_ws.py b/src/http_app/routes/docs_ws.py index 3ceefee2..9ccedefd 100644 --- a/src/http_app/routes/docs_ws.py +++ b/src/http_app/routes/docs_ws.py @@ -1,10 +1,14 @@ import json +from typing import Annotated import pydantic_asyncapi as pa from fastapi import APIRouter +from fastapi.params import Depends from starlette.responses import HTMLResponse +from common import AppConfig from common.asyncapi import get_schema +from http_app.dependencies import get_app_config router = APIRouter(prefix="/docs/ws") @@ -32,6 +36,7 @@ def asyncapi_raw() -> pa.v3.AsyncAPI: # https://github.com/asyncapi/asyncapi-react/blob/v2.5.0/docs/usage/standalone-bundle.md @router.get("", include_in_schema=False) async def get_asyncapi_html( + app_config: Annotated[AppConfig, Depends(get_app_config)], sidebar: bool = True, info: bool = True, servers: bool = True, @@ -40,7 +45,6 @@ async def get_asyncapi_html( schemas: bool = True, errors: bool = True, expand_message_examples: bool = False, - title: str = "Websocket", ) -> HTMLResponse: """Generate HTML for displaying an AsyncAPI document.""" config = { @@ -74,7 +78,7 @@ async def get_asyncapi_html( """ f""" - {title} AsyncAPI + {app_config.APP_NAME} AsyncAPI """ """ From d49d2cc80f6ac22a7f4d6281ee527446c658725e Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:34:07 +0000 Subject: [PATCH 16/19] Add tests --- tests/common/test_asyncapi.py | 133 ++++++++++++++++++++++++++ tests/conftest.py | 2 +- tests/http_app/conftest.py | 2 +- tests/http_app/routes/test_docs_ws.py | 92 ++++++++++++++++++ tests/http_app/routes/ws/__init__.py | 0 tests/http_app/routes/ws/test_chat.py | 85 ++++++++++++++++ tests/http_app/test_dependencies.py | 4 +- 7 files changed, 314 insertions(+), 4 deletions(-) create mode 100644 tests/common/test_asyncapi.py create mode 100644 tests/http_app/routes/test_docs_ws.py create mode 100644 tests/http_app/routes/ws/__init__.py create mode 100644 tests/http_app/routes/ws/test_chat.py diff --git a/tests/common/test_asyncapi.py b/tests/common/test_asyncapi.py new file mode 100644 index 00000000..fdb7bc96 --- /dev/null +++ b/tests/common/test_asyncapi.py @@ -0,0 +1,133 @@ +import pytest +from pydantic import BaseModel + +from common.asyncapi import ( + get_schema, + init_asyncapi_info, + register_channel, + register_channel_operation, + register_server, +) + + +# Test fixtures +@pytest.fixture +def reset_asyncapi_state(): + """Reset all global state between tests""" + from common.asyncapi import _channels, _components_schemas, _operations, _servers + + _servers.clear() + _channels.clear() + _operations.clear() + _components_schemas.clear() + yield + _servers.clear() + _channels.clear() + _operations.clear() + _components_schemas.clear() + + +# Test message models +class TestMessage(BaseModel): + content: str + timestamp: int + + +class AnotherTestMessage(BaseModel): + status: bool + code: int + + +# Test cases +def test_init_asyncapi_info(): + """Test initialization of AsyncAPI info""" + title = "Test API" + version = "2.0.0" + + init_asyncapi_info(title=title, version=version) + schema = get_schema() + + assert schema.info.title == title + assert schema.info.version == version + + +def test_register_server(reset_asyncapi_state): + """Test server registration""" + server_id = "test-server" + host = "localhost" + protocol = "ws" + pathname = "/ws" + + register_server(id=server_id, host=host, protocol=protocol, pathname=pathname) + + schema = get_schema() + assert server_id in schema.servers + assert schema.servers[server_id].host == host + assert schema.servers[server_id].protocol == protocol + assert schema.servers[server_id].pathname == pathname + + +def test_register_channel(reset_asyncapi_state): + """Test channel registration""" + channel_id = "test-channel" + address = "test/topic" + description = "Test channel" + title = "Test Channel" + + register_channel(address=address, id=channel_id, description=description, title=title) + + schema = get_schema() + assert channel_id in schema.channels + assert schema.channels[channel_id].address == address + assert schema.channels[channel_id].description == description + assert schema.channels[channel_id].title == title + + +def test_register_channel_with_server(reset_asyncapi_state): + """Test channel registration with server reference""" + server_id = "test-server" + channel_id = "test-channel" + + register_server(id=server_id, host="localhost", protocol="ws") + register_channel(address="test/topic", id=channel_id, server_id=server_id) + + schema = get_schema() + assert len(schema.channels[channel_id].servers) == 1 + assert schema.channels[channel_id].servers[0].ref == f"#/servers/{server_id}" + + +def test_register_channel_operation(reset_asyncapi_state): + """Test channel operation registration""" + channel_id = "test-channel" + operation_type = "receive" + + register_channel(address="test/topic", id=channel_id) + register_channel_operation( + channel_id=channel_id, operation_type=operation_type, messages=[TestMessage], operation_name="test-operation" + ) + + schema = get_schema() + assert "test-operation" in schema.operations + assert schema.operations["test-operation"].action == operation_type + assert schema.operations["test-operation"].channel.ref == f"#/channels/{channel_id}" + assert TestMessage.__name__ in schema.components.schemas + + +def test_register_channel_operation_invalid_channel(reset_asyncapi_state): + """Test channel operation registration with invalid channel""" + with pytest.raises(ValueError, match="Channel non-existent does not exist"): + register_channel_operation(channel_id="non-existent", operation_type="receive", messages=[TestMessage]) + + +def test_multiple_messages_registration(reset_asyncapi_state): + """Test registration of multiple messages for an operation""" + channel_id = "test-channel" + + register_channel(address="test/topic", id=channel_id) + register_channel_operation(channel_id=channel_id, operation_type="send", messages=[TestMessage, AnotherTestMessage]) + + schema = get_schema() + assert TestMessage.__name__ in schema.components.schemas + assert AnotherTestMessage.__name__ in schema.components.schemas + assert TestMessage.__name__ in schema.channels[channel_id].messages + assert AnotherTestMessage.__name__ in schema.channels[channel_id].messages diff --git a/tests/conftest.py b/tests/conftest.py index 67219f23..d3c3e28b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,7 @@ def anyio_backend(): return "asyncio" -@pytest.fixture(scope="function") +@pytest.fixture(scope="session") def test_config() -> AppConfig: return AppConfig( SQLALCHEMY_CONFIG={}, diff --git a/tests/http_app/conftest.py b/tests/http_app/conftest.py index a6cafd7b..19c220a4 100644 --- a/tests/http_app/conftest.py +++ b/tests/http_app/conftest.py @@ -7,7 +7,7 @@ from http_app import create_app -@pytest.fixture(scope="function") +@pytest.fixture(scope="session") def testapp(test_config) -> Iterator[FastAPI]: # We don't need the storage to test the HTTP app with patch("common.bootstrap.init_storage", return_value=None): diff --git a/tests/http_app/routes/test_docs_ws.py b/tests/http_app/routes/test_docs_ws.py new file mode 100644 index 00000000..7cf62525 --- /dev/null +++ b/tests/http_app/routes/test_docs_ws.py @@ -0,0 +1,92 @@ +import json +from unittest.mock import MagicMock, patch + +import pytest +from fastapi import FastAPI, status +from fastapi.testclient import TestClient +from pydantic_asyncapi.v3 import AsyncAPI, Info + +fake_schema = AsyncAPI( + asyncapi="3.0.0", + info=Info( + title="Some fake schema", + version="1.2.3", + ), +) + + +@patch("http_app.routes.docs_ws.get_schema", return_value=fake_schema) +async def test_asyncapi_json_is_whatever_returned_by_schema( + mock_schema: MagicMock, + testapp: FastAPI, +): + ac = TestClient(app=testapp, base_url="http://test") + response = ac.get( + "/docs/ws/asyncapi.json", + ) + + assert response.status_code == status.HTTP_200_OK + assert response.text == fake_schema.model_dump_json(exclude_unset=True) + + +@pytest.mark.parametrize("sidebar", (True, False)) +@pytest.mark.parametrize("info", (True, False)) +@pytest.mark.parametrize("servers", (True, False)) +@pytest.mark.parametrize("operations", (True, False)) +@pytest.mark.parametrize("messages", (True, False)) +@pytest.mark.parametrize("schema", (True, False)) +@pytest.mark.parametrize("errors", (True, False)) +@pytest.mark.parametrize("expand_message_examples", (True, False)) +async def test_ws_docs_renders_config_based_on_params( + sidebar: bool, + info: bool, + servers: bool, + operations: bool, + messages: bool, + schema: bool, + errors: bool, + expand_message_examples: bool, + testapp: FastAPI, +): + config = json.dumps( + { + "schema": { + "url": "/docs/ws/asyncapi.json", + }, + "config": { + "show": { + "sidebar": sidebar, + "info": info, + "servers": servers, + "operations": operations, + "messages": messages, + "schemas": schema, + "errors": errors, + }, + "expand": { + "messageExamples": expand_message_examples, + }, + "sidebar": { + "showServers": "byDefault", + "showOperations": "byDefault", + }, + }, + } + ) + + ac = TestClient(app=testapp, base_url="http://test") + response = ac.get( + "/docs/ws", + params={ + "sidebar": sidebar, + "info": info, + "servers": servers, + "operations": operations, + "messages": messages, + "schemas": schema, + "errors": errors, + "expand_message_examples": expand_message_examples, + }, + ) + assert response.status_code == status.HTTP_200_OK + assert config in response.text diff --git a/tests/http_app/routes/ws/__init__.py b/tests/http_app/routes/ws/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/http_app/routes/ws/test_chat.py b/tests/http_app/routes/ws/test_chat.py new file mode 100644 index 00000000..209a31e1 --- /dev/null +++ b/tests/http_app/routes/ws/test_chat.py @@ -0,0 +1,85 @@ +import pytest +from fastapi.testclient import TestClient + +from http_app.routes.ws.chat import ConnectionManager + + +@pytest.fixture +def test_client(testapp): + return TestClient(testapp) + + +@pytest.fixture +def connection_manager(): + return ConnectionManager() + + +def test_websocket_connection(test_client): + with test_client.websocket_connect("/ws/chat/1") as websocket: + websocket.send_text("Hello!") + data = websocket.receive_text() + + assert data == "You wrote: Hello!" + broadcast = websocket.receive_text() + assert broadcast == "Client #1 says: Hello!" + + +def test_multiple_clients(test_client): + with test_client.websocket_connect("/ws/chat/1") as websocket1: + with test_client.websocket_connect("/ws/chat/2") as websocket2: + # Client 1 sends message + websocket1.send_text("Hello from client 1") + + # Client 1 receives personal message + data1 = websocket1.receive_text() + assert data1 == "You wrote: Hello from client 1" + + # Both clients receive broadcast + broadcast1 = websocket1.receive_text() + broadcast2 = websocket2.receive_text() + assert broadcast1 == "Client #1 says: Hello from client 1" + assert broadcast2 == "Client #1 says: Hello from client 1" + + +def test_client_disconnect(test_client): + with test_client.websocket_connect("/ws/chat/1") as websocket1: + with test_client.websocket_connect("/ws/chat/2") as websocket2: + # Close first client + websocket1.close() + + # Second client should receive disconnect message + disconnect_message = websocket2.receive_text() + assert disconnect_message == "Client #1 left the chat" + + +@pytest.mark.asyncio +async def test_connection_manager(): + manager = ConnectionManager() + + # Mock WebSocket for testing + class MockWebSocket: + def __init__(self): + self.received_messages = [] + + async def accept(self): + pass + + async def send_text(self, message: str): + self.received_messages.append(message) + + # Test connect + websocket = MockWebSocket() + await manager.connect(websocket) + assert len(manager.active_connections) == 1 + + # Test personal message + await manager.send_personal_message("test message", websocket) + assert websocket.received_messages[-1] == "test message" + + # Test broadcast + await manager.broadcast("broadcast message") + assert websocket.received_messages[-1] == "broadcast message" + + # Test disconnect + manager.disconnect(websocket) + assert len(manager.active_connections) == 0 diff --git a/tests/http_app/test_dependencies.py b/tests/http_app/test_dependencies.py index 2a52322d..34bb5da2 100644 --- a/tests/http_app/test_dependencies.py +++ b/tests/http_app/test_dependencies.py @@ -1,9 +1,9 @@ from common import AppConfig from http_app import context -from http_app.dependencies import app_config +from http_app.dependencies import get_app_config def test_app_config_return_context_variable(): config = AppConfig(APP_NAME="SomeOtherAppName") context.app_config.set(config) - assert app_config() is config + assert get_app_config() is config From fdcc28fb8597dc08c3901dcb2b64640a8c1937d4 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 5 Feb 2025 20:43:04 +0000 Subject: [PATCH 17/19] Include docs in schema --- src/http_app/routes/docs_ws.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http_app/routes/docs_ws.py b/src/http_app/routes/docs_ws.py index 9ccedefd..90213c3e 100644 --- a/src/http_app/routes/docs_ws.py +++ b/src/http_app/routes/docs_ws.py @@ -34,7 +34,7 @@ def asyncapi_raw() -> pa.v3.AsyncAPI: # https://github.com/asyncapi/asyncapi-react/blob/v2.5.0/docs/usage/standalone-bundle.md -@router.get("", include_in_schema=False) +@router.get("") async def get_asyncapi_html( app_config: Annotated[AppConfig, Depends(get_app_config)], sidebar: bool = True, From 72ed197f7aaa4ed4dcd100775470aac5f2124fc1 Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 5 Feb 2025 20:52:04 +0000 Subject: [PATCH 18/19] Reduce cognitive complexity --- src/common/asyncapi.py | 109 +++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/src/common/asyncapi.py b/src/common/asyncapi.py index 35ab323c..f142afb6 100644 --- a/src/common/asyncapi.py +++ b/src/common/asyncapi.py @@ -105,6 +105,29 @@ def register_server( _servers[id].pathname = pathname +def _create_base_channel(address: str, channel_id: str) -> pa.Channel: + """Create a basic channel with minimum required parameters.""" + return pa.Channel( + address=address, + servers=[], + messages={}, + ) + + +def _add_channel_metadata(channel: pa.Channel, description: Optional[str], title: Optional[str]) -> None: + """Add optional metadata to the channel.""" + if description is not None: + channel.description = description + if title is not None: + channel.title = title + + +def _add_server_reference(channel: pa.Channel, server_id: Optional[str]) -> None: + """Add server reference to the channel if server exists.""" + if server_id is not None and server_id in _servers: + channel.servers.append(pa.Reference(ref=f"#/servers/{server_id}")) # type: ignore + + def register_channel( address: str, id: Optional[str] = None, @@ -113,34 +136,44 @@ def register_channel( server_id: Optional[str] = None, ) -> None: """ - Registers a communication channel with the specified parameters and updates the - internal dictionary holding channel metadata. The function allows optional - parameters to set additional properties such as description and title, and - optionally associates the channel with a predefined server. + Registers a communication channel with the specified parameters. Args: address (str): The address of the channel. id (Optional[str]): Unique identifier for the channel. Defaults to None. description (Optional[str]): Description of the channel. Defaults to None. title (Optional[str]): Title to be associated with the channel. Defaults to None. - server_id (Optional[str]): Server identifier to link this channel to. - Must exist in the internal server registry. Defaults to None. + server_id (Optional[str]): Server identifier to link this channel to. Defaults to None. Returns: None """ - # TODO: Define channel metadata in decorator - _channels[id or address] = pa.Channel( - address=address, - servers=[], - messages={}, + channel_id = id or address + channel = _create_base_channel(address, channel_id) + _add_channel_metadata(channel, description, title) + _add_server_reference(channel, server_id) + _channels[channel_id] = channel + + +def _register_message_schema(message: Type[BaseModel], operation_type: Literal["receive", "send"]) -> None: + """Register message schema in components schemas.""" + message_json_schema = message.model_json_schema( + mode="validation" if operation_type == "receive" else "serialization", + ref_template="#/components/schemas/{model}", ) - if description is not None: - _channels[id or address].description = description - if title is not None: - _channels[id or address].title = title - if server_id is not None and server_id in _servers: - _channels[id or address].servers.append(pa.Reference(ref=f"#/servers/{server_id}")) # type: ignore + + _components_schemas[message.__name__] = message_json_schema + + if message_json_schema.get("$defs"): + _components_schemas.update(message_json_schema["$defs"]) + + +def _create_channel_message(channel_id: str, message: Type[BaseModel]) -> pa.Reference: + """Create channel message and return reference to it.""" + _channels[channel_id].messages[message.__name__] = pa.Message( # type: ignore + payload=pa.Reference(ref=f"#/components/schemas/{message.__name__}") + ) + return pa.Reference(ref=f"#/channels/{channel_id}/messages/{message.__name__}") def register_channel_operation( @@ -148,35 +181,37 @@ def register_channel_operation( operation_type: Literal["receive", "send"], messages: List[Type[BaseModel]], operation_name: Optional[str] = None, -): - if not _channels.get(channel_id): - raise ValueError(f"Channel {channel_id} does not exist.") +) -> None: + """ + Registerm a channel operation with associated messages. - _operation_message_refs = [] - for message in messages: - # TODO: Check for overlapping model schemas, if they are different log a warning! - _message_json_schema = message.model_json_schema( - mode="validation" if operation_type == "receive" else "serialization", - ref_template="#/components/schemas/{model}", - ) + Args: + channel_id: Channel identifier + operation_type: Type of operation ("receive" or "send") + messages: List of message models + operation_name: Optional operation name - _components_schemas[message.__name__] = _message_json_schema + Raises: + ValueError: If channel_id doesn't exist + """ + if not _channels.get(channel_id): + raise ValueError(f"Channel {channel_id} does not exist.") - if _message_json_schema.get("$defs"): - _components_schemas.update(_message_json_schema["$defs"]) - _channels[channel_id].messages[message.__name__] = pa.Message( # type: ignore - payload=pa.Reference(ref=f"#/components/schemas/{message.__name__}") - ) + operation_message_refs = [] - # Cannot point to the /components path - _operation_message_refs.append(pa.Reference(ref=f"#/channels/{channel_id}/messages/{message.__name__}")) + for message in messages: + _register_message_schema(message, operation_type) + message_ref = _create_channel_message(channel_id, message) + operation_message_refs.append(message_ref) - _operations[operation_name or f"{channel_id}-{operation_type}"] = pa.Operation( + operation_id = operation_name or f"{channel_id}-{operation_type}" + _operations[operation_id] = pa.Operation( action=operation_type, channel=pa.Reference(ref=f"#/channels/{channel_id}"), - messages=_operation_message_refs, + messages=operation_message_refs, traits=[], ) + # TODO: Define operation traits # if operation_name is not None: # _operations[operation_name or f"{channel_id}-{operation_type}"].traits.append( From 6199961e15363f60eaaabc5cc2e5f343c28358df Mon Sep 17 00:00:00 2001 From: Federico Busetti <729029+febus982@users.noreply.github.com> Date: Wed, 5 Feb 2025 21:06:01 +0000 Subject: [PATCH 19/19] Add details on documentation for AsyncAPI docs --- README.md | 2 +- docs/api-documentation.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e3c29f5c..46b36741 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ and [SOLID principles](https://en.wikipedia.org/wiki/SOLID). This template provides out of the box some commonly used functionalities: -* API Documentation using [FastAPI](https://fastapi.tiangolo.com/) +* Sync and Async API Documentation using [FastAPI](https://fastapi.tiangolo.com/) and [AsyncAPI](https://www.asyncapi.com/en) * Async tasks execution using [Dramatiq](https://dramatiq.io/index.html) * Repository pattern for databases using [SQLAlchemy](https://www.sqlalchemy.org/) and [SQLAlchemy bind manager](https://febus982.github.io/sqlalchemy-bind-manager/stable/) * Database migrations using [Alembic](https://alembic.sqlalchemy.org/en/latest/) (configured supporting both sync and async SQLAlchemy engines) diff --git a/docs/api-documentation.md b/docs/api-documentation.md index 4c2d13d2..c26367cd 100644 --- a/docs/api-documentation.md +++ b/docs/api-documentation.md @@ -3,6 +3,10 @@ API documentation is rendered by [FastAPI](https://fastapi.tiangolo.com/features/) on `/docs` and `/redoc` paths using OpenAPI format. +AsyncAPI documentation is rendered using the +[AsyncAPI react components](https://github.com/asyncapi/asyncapi-react). +It is available on `/docs/ws` path. + ## API versioning Versioning an API at resource level provides a much more