xcpcio 0.63.5__tar.gz → 0.63.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xcpcio might be problematic. Click here for more details.

Files changed (47) hide show
  1. {xcpcio-0.63.5 → xcpcio-0.63.7}/.gitignore +2 -0
  2. {xcpcio-0.63.5 → xcpcio-0.63.7}/PKG-INFO +4 -1
  3. xcpcio-0.63.7/app/contest_api_server.py +77 -0
  4. {xcpcio-0.63.5 → xcpcio-0.63.7}/cli/ccs_archiver_cli.py +2 -0
  5. {xcpcio-0.63.5 → xcpcio-0.63.7}/pyproject.toml +11 -1
  6. xcpcio-0.63.7/task.md +22 -0
  7. {xcpcio-0.63.5 → xcpcio-0.63.7}/uv.lock +100 -46
  8. xcpcio-0.63.7/xcpcio/__version__.py +4 -0
  9. xcpcio-0.63.7/xcpcio/ccs/__init__.py +3 -0
  10. xcpcio-0.63.7/xcpcio/ccs/api_server/__init__.py +9 -0
  11. xcpcio-0.63.7/xcpcio/ccs/api_server/dependencies.py +48 -0
  12. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/__init__.py +52 -0
  13. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/access.py +18 -0
  14. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/accounts.py +35 -0
  15. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/awards.py +33 -0
  16. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/clarifications.py +34 -0
  17. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/contests.py +119 -0
  18. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/general.py +33 -0
  19. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/groups.py +34 -0
  20. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/judgement_types.py +34 -0
  21. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/judgements.py +35 -0
  22. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/languages.py +34 -0
  23. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/organizations.py +71 -0
  24. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/problems.py +69 -0
  25. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/runs.py +35 -0
  26. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/submissions.py +71 -0
  27. xcpcio-0.63.7/xcpcio/ccs/api_server/routes/teams.py +69 -0
  28. xcpcio-0.63.7/xcpcio/ccs/api_server/server.py +83 -0
  29. xcpcio-0.63.7/xcpcio/ccs/api_server/services/__init__.py +9 -0
  30. xcpcio-0.63.7/xcpcio/ccs/api_server/services/contest_service.py +325 -0
  31. {xcpcio-0.63.5 → xcpcio-0.63.7}/xcpcio/ccs/contest_archiver.py +2 -3
  32. xcpcio-0.63.5/xcpcio/__version__.py +0 -1
  33. xcpcio-0.63.5/xcpcio/ccs/__init__.py +0 -3
  34. {xcpcio-0.63.5 → xcpcio-0.63.7}/.python-version +0 -0
  35. {xcpcio-0.63.5 → xcpcio-0.63.7}/README.md +0 -0
  36. {xcpcio-0.63.5 → xcpcio-0.63.7}/scripts/generate_ccs_models.sh +0 -0
  37. {xcpcio-0.63.5 → xcpcio-0.63.7}/tests/__init__.py +0 -0
  38. {xcpcio-0.63.5 → xcpcio-0.63.7}/tests/test_contest.py +0 -0
  39. {xcpcio-0.63.5 → xcpcio-0.63.7}/tests/test_submission.py +0 -0
  40. {xcpcio-0.63.5 → xcpcio-0.63.7}/tests/test_team.py +0 -0
  41. {xcpcio-0.63.5 → xcpcio-0.63.7}/tests/test_types.py +0 -0
  42. {xcpcio-0.63.5 → xcpcio-0.63.7}/xcpcio/__init__.py +0 -0
  43. {xcpcio-0.63.5 → xcpcio-0.63.7}/xcpcio/ccs/model/__init__.py +0 -0
  44. {xcpcio-0.63.5 → xcpcio-0.63.7}/xcpcio/ccs/model/model_2023_06/__init__.py +0 -0
  45. {xcpcio-0.63.5 → xcpcio-0.63.7}/xcpcio/ccs/model/model_2023_06/model.py +0 -0
  46. {xcpcio-0.63.5 → xcpcio-0.63.7}/xcpcio/constants.py +0 -0
  47. {xcpcio-0.63.5 → xcpcio-0.63.7}/xcpcio/types.py +0 -0
@@ -6,3 +6,5 @@ ccs-specs/
6
6
  output/
7
7
 
8
8
  .env
9
+
10
+ xcpcio/__version__.py
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xcpcio
3
- Version: 0.63.5
3
+ Version: 0.63.7
4
4
  Summary: xcpcio python lib
5
5
  Project-URL: homepage, https://github.com/xcpcio/xcpcio
6
6
  Project-URL: documentation, https://github.com/xcpcio/xcpcio
7
7
  Project-URL: repository, https://github.com/xcpcio/xcpcio
8
8
  Author-email: Dup4 <hi@dup4.com>
9
+ Maintainer-email: Dup4 <hi@dup4.com>, cubercsl <hi@cubercsl.site>
9
10
  License-Expression: MIT
10
11
  Keywords: xcpcio
11
12
  Classifier: Programming Language :: Python :: 3
@@ -18,10 +19,12 @@ Requires-Python: >=3.11
18
19
  Requires-Dist: aiofiles>=23.0.0
19
20
  Requires-Dist: aiohttp>=3.8.0
20
21
  Requires-Dist: click>=8.0.0
22
+ Requires-Dist: fastapi>=0.117.1
21
23
  Requires-Dist: pydantic>=2.11.7
22
24
  Requires-Dist: pyyaml>=6.0.0
23
25
  Requires-Dist: semver>=3.0.0
24
26
  Requires-Dist: tenacity>=8.0.0
27
+ Requires-Dist: uvicorn>=0.36.0
25
28
  Description-Content-Type: text/markdown
26
29
 
27
30
  # xcpcio-python
@@ -0,0 +1,77 @@
1
+ import logging
2
+ from pathlib import Path
3
+
4
+ import click
5
+
6
+ from xcpcio import __version__
7
+ from xcpcio.ccs.api_server.server import ContestAPIServer
8
+
9
+
10
+ def setup_logging(level: str = "INFO"):
11
+ """Setup logging configuration"""
12
+ logging.basicConfig(
13
+ level=getattr(logging, level.upper()),
14
+ format="%(asctime)s [%(name)s] %(filename)s:%(lineno)d %(levelname)s: %(message)s",
15
+ )
16
+
17
+
18
+ @click.command()
19
+ @click.version_option(__version__)
20
+ @click.option(
21
+ "--contest-dir",
22
+ "-d",
23
+ required=True,
24
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
25
+ help="Contest package directory path",
26
+ )
27
+ @click.option("--host", default="0.0.0.0", help="Host to bind to")
28
+ @click.option("--port", default=8000, type=int, help="Port to bind to")
29
+ @click.option("--reload", is_flag=True, help="Enable auto-reload for development")
30
+ @click.option(
31
+ "--log-level",
32
+ default="info",
33
+ type=click.Choice(["debug", "info", "warning", "error", "critical"], case_sensitive=False),
34
+ help="Log level",
35
+ )
36
+ @click.option("--verbose", "-v", is_flag=True, help="Enable verbose logging (same as --log-level debug)")
37
+ def main(contest_dir: Path, host: str, port: int, reload: bool, log_level: str, verbose: bool):
38
+ """
39
+ Start the Contest API Server.
40
+
41
+ Examples:
42
+
43
+ # Start server with contest directory
44
+ contest-api-server -d /path/to/contest
45
+
46
+ # Custom host and port
47
+ contest-api-server -d /path/to/contest --host 127.0.0.1 --port 9000
48
+
49
+ # Enable reload for development
50
+ contest-api-server -d /path/to/contest --reload
51
+ """
52
+ # Setup logging
53
+ if verbose:
54
+ log_level = "debug"
55
+ setup_logging(log_level.upper())
56
+
57
+ # Display configuration
58
+ click.echo("Starting Contest API Server...")
59
+ click.echo(f"Contest directory: {contest_dir}")
60
+ click.echo(f"Host: {host}")
61
+ click.echo(f"Port: {port}")
62
+ click.echo(f"Reload: {reload}")
63
+ click.echo(f"Log level: {log_level}")
64
+
65
+ # Create and run server
66
+ try:
67
+ server = ContestAPIServer(contest_dir)
68
+ server.run(host=host, port=port, reload=reload, log_level=log_level.lower())
69
+ except KeyboardInterrupt:
70
+ click.echo("\nServer stopped by user")
71
+ except Exception as e:
72
+ click.echo(f"Error starting server: {e}", err=True)
73
+ raise click.Abort()
74
+
75
+
76
+ if __name__ == "__main__":
77
+ main()
@@ -5,6 +5,7 @@ from typing import Optional
5
5
 
6
6
  import click
7
7
 
8
+ from xcpcio import __version__
8
9
  from xcpcio.ccs.contest_archiver import APICredentials, ArchiveConfig, ContestArchiver
9
10
 
10
11
 
@@ -17,6 +18,7 @@ def setup_logging(level: str = "INFO"):
17
18
 
18
19
 
19
20
  @click.command()
21
+ @click.version_option(__version__)
20
22
  @click.option("--base-url", required=True, help="Base URL of the CCS API (e.g., https://example.com/api)")
21
23
  @click.option("--contest-id", required=True, help="Contest ID to dump")
22
24
  @click.option(
@@ -3,6 +3,10 @@ name = "xcpcio"
3
3
  description = "xcpcio python lib"
4
4
  readme = "README.md"
5
5
  authors = [ { name = "Dup4", email = "hi@dup4.com" } ]
6
+ maintainers = [
7
+ { name = "Dup4", email = "hi@dup4.com" },
8
+ { name = "cubercsl", email = "hi@cubercsl.site" },
9
+ ]
6
10
  license = "MIT"
7
11
  keywords = [ "xcpcio" ]
8
12
  classifiers = [
@@ -22,6 +26,8 @@ dependencies = [
22
26
  "pyyaml>=6.0.0",
23
27
  "semver>=3.0.0",
24
28
  "tenacity>=8.0.0",
29
+ "fastapi>=0.117.1",
30
+ "uvicorn>=0.36.0",
25
31
  ]
26
32
  dynamic = ["version"]
27
33
 
@@ -37,10 +43,14 @@ ccs-archiver = "cli.ccs_archiver_cli:main"
37
43
  line-length = 120
38
44
 
39
45
  [build-system]
40
- requires = ["hatchling"]
46
+ requires = ["hatchling", "hatch-nodejs-version"]
41
47
  build-backend = "hatchling.build"
42
48
 
43
49
  [tool.hatch.version]
50
+ source = "nodejs"
51
+ path = "../package.json"
52
+
53
+ [tool.hatch.build.hooks.version]
44
54
  path = "xcpcio/__version__.py"
45
55
 
46
56
  [dependency-groups]
xcpcio-0.63.7/task.md ADDED
@@ -0,0 +1,22 @@
1
+ # Task
2
+
3
+ <import>
4
+
5
+ 你的活动范围仅限于 ./ 之下,请务必不要超出此范围
6
+
7
+ </import>
8
+
9
+ 您需要写一个 api server:
10
+
11
+ * 使用 python3,基于 fastapi 和 asyncio
12
+ * 用一个单文件写出来即可,不用拆文件,并且需要将所有功能封装成一个 class,class 命名为 ContestAPIServer
13
+ * 写入 ./xcpcio/ccs/contest_api_server.py 中
14
+ * 这个 APIServer 需要基于 fastapi 提供 swagger api docs,api docs 中的 response_model 请使用 ./xcpcio/ccs/model/__init__.py 中的东西
15
+ * 在本地有一个 contest package dir,你需要基于这个 dir,提供 contest api,但是 contest package api 需要作为一个配置项可配置,而不是写死
16
+ * contest package dir 的文件结构可以查阅 [Contest_Package.md][Contest_Package.md]
17
+ * contest api 的相关接口你可以查阅 [Contest_API.md][Contest_API.md]
18
+ * 本地的 contest package 你可以使用参考这个路径: ./output/2024hk
19
+ * 关于 [Contest_API.md][Contest_API.md] 中定义的那些数据结构,已经生成好了,你可以查阅 ./xcpcio/ccs/model/__init__.py,你可以直接使用
20
+
21
+ [Contest_Package.md]: ./ccs-specs/Contest_Package.md
22
+ [Contest_API.md]: ./ccs-specs/Contest_API.md
@@ -112,16 +112,16 @@ wheels = [
112
112
 
113
113
  [[package]]
114
114
  name = "anyio"
115
- version = "4.10.0"
115
+ version = "4.11.0"
116
116
  source = { registry = "https://pypi.org/simple" }
117
117
  dependencies = [
118
118
  { name = "idna" },
119
119
  { name = "sniffio" },
120
120
  { name = "typing-extensions", marker = "python_full_version < '3.13'" },
121
121
  ]
122
- sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" }
122
+ sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" }
123
123
  wheels = [
124
- { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" },
124
+ { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" },
125
125
  ]
126
126
 
127
127
  [[package]]
@@ -153,7 +153,7 @@ wheels = [
153
153
 
154
154
  [[package]]
155
155
  name = "black"
156
- version = "25.1.0"
156
+ version = "25.9.0"
157
157
  source = { registry = "https://pypi.org/simple" }
158
158
  dependencies = [
159
159
  { name = "click" },
@@ -161,22 +161,23 @@ dependencies = [
161
161
  { name = "packaging" },
162
162
  { name = "pathspec" },
163
163
  { name = "platformdirs" },
164
+ { name = "pytokens" },
164
165
  ]
165
- sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" }
166
+ sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" }
166
167
  wheels = [
167
- { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372, upload-time = "2025-01-29T05:37:11.71Z" },
168
- { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865, upload-time = "2025-01-29T05:37:14.309Z" },
169
- { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699, upload-time = "2025-01-29T04:18:17.688Z" },
170
- { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028, upload-time = "2025-01-29T04:18:51.711Z" },
171
- { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988, upload-time = "2025-01-29T05:37:16.707Z" },
172
- { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985, upload-time = "2025-01-29T05:37:18.273Z" },
173
- { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816, upload-time = "2025-01-29T04:18:33.823Z" },
174
- { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860, upload-time = "2025-01-29T04:19:12.944Z" },
175
- { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" },
176
- { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" },
177
- { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" },
178
- { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" },
179
- { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" },
168
+ { url = "https://files.pythonhosted.org/packages/b7/f4/7531d4a336d2d4ac6cc101662184c8e7d068b548d35d874415ed9f4116ef/black-25.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:456386fe87bad41b806d53c062e2974615825c7a52159cde7ccaeb0695fa28fa", size = 1698727, upload-time = "2025-09-19T00:31:14.264Z" },
169
+ { url = "https://files.pythonhosted.org/packages/28/f9/66f26bfbbf84b949cc77a41a43e138d83b109502cd9c52dfc94070ca51f2/black-25.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a16b14a44c1af60a210d8da28e108e13e75a284bf21a9afa6b4571f96ab8bb9d", size = 1555679, upload-time = "2025-09-19T00:31:29.265Z" },
170
+ { url = "https://files.pythonhosted.org/packages/bf/59/61475115906052f415f518a648a9ac679d7afbc8da1c16f8fdf68a8cebed/black-25.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aaf319612536d502fdd0e88ce52d8f1352b2c0a955cc2798f79eeca9d3af0608", size = 1617453, upload-time = "2025-09-19T00:30:42.24Z" },
171
+ { url = "https://files.pythonhosted.org/packages/7f/5b/20fd5c884d14550c911e4fb1b0dae00d4abb60a4f3876b449c4d3a9141d5/black-25.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:c0372a93e16b3954208417bfe448e09b0de5cc721d521866cd9e0acac3c04a1f", size = 1333655, upload-time = "2025-09-19T00:30:56.715Z" },
172
+ { url = "https://files.pythonhosted.org/packages/fb/8e/319cfe6c82f7e2d5bfb4d3353c6cc85b523d677ff59edc61fdb9ee275234/black-25.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1b9dc70c21ef8b43248f1d86aedd2aaf75ae110b958a7909ad8463c4aa0880b0", size = 1742012, upload-time = "2025-09-19T00:33:08.678Z" },
173
+ { url = "https://files.pythonhosted.org/packages/94/cc/f562fe5d0a40cd2a4e6ae3f685e4c36e365b1f7e494af99c26ff7f28117f/black-25.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e46eecf65a095fa62e53245ae2795c90bdecabd53b50c448d0a8bcd0d2e74c4", size = 1581421, upload-time = "2025-09-19T00:35:25.937Z" },
174
+ { url = "https://files.pythonhosted.org/packages/84/67/6db6dff1ebc8965fd7661498aea0da5d7301074b85bba8606a28f47ede4d/black-25.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9101ee58ddc2442199a25cb648d46ba22cd580b00ca4b44234a324e3ec7a0f7e", size = 1655619, upload-time = "2025-09-19T00:30:49.241Z" },
175
+ { url = "https://files.pythonhosted.org/packages/10/10/3faef9aa2a730306cf469d76f7f155a8cc1f66e74781298df0ba31f8b4c8/black-25.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:77e7060a00c5ec4b3367c55f39cf9b06e68965a4f2e61cecacd6d0d9b7ec945a", size = 1342481, upload-time = "2025-09-19T00:31:29.625Z" },
176
+ { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" },
177
+ { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" },
178
+ { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" },
179
+ { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" },
180
+ { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" },
180
181
  ]
181
182
 
182
183
  [[package]]
@@ -235,14 +236,14 @@ wheels = [
235
236
 
236
237
  [[package]]
237
238
  name = "click"
238
- version = "8.2.1"
239
+ version = "8.3.0"
239
240
  source = { registry = "https://pypi.org/simple" }
240
241
  dependencies = [
241
242
  { name = "colorama", marker = "sys_platform == 'win32'" },
242
243
  ]
243
- sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
244
+ sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" }
244
245
  wheels = [
245
- { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
246
+ { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" },
246
247
  ]
247
248
 
248
249
  [[package]]
@@ -337,6 +338,20 @@ wheels = [
337
338
  { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
338
339
  ]
339
340
 
341
+ [[package]]
342
+ name = "fastapi"
343
+ version = "0.117.1"
344
+ source = { registry = "https://pypi.org/simple" }
345
+ dependencies = [
346
+ { name = "pydantic" },
347
+ { name = "starlette" },
348
+ { name = "typing-extensions" },
349
+ ]
350
+ sdist = { url = "https://files.pythonhosted.org/packages/7e/7e/d9788300deaf416178f61fb3c2ceb16b7d0dc9f82a08fdb87a5e64ee3cc7/fastapi-0.117.1.tar.gz", hash = "sha256:fb2d42082d22b185f904ca0ecad2e195b851030bd6c5e4c032d1c981240c631a", size = 307155, upload-time = "2025-09-20T20:16:56.663Z" }
351
+ wheels = [
352
+ { url = "https://files.pythonhosted.org/packages/6d/45/d9d3e8eeefbe93be1c50060a9d9a9f366dba66f288bb518a9566a23a8631/fastapi-0.117.1-py3-none-any.whl", hash = "sha256:33c51a0d21cab2b9722d4e56dbb9316f3687155be6b276191790d8da03507552", size = 95959, upload-time = "2025-09-20T20:16:53.661Z" },
353
+ ]
354
+
340
355
  [[package]]
341
356
  name = "filelock"
342
357
  version = "3.19.1"
@@ -959,7 +974,7 @@ wheels = [
959
974
 
960
975
  [[package]]
961
976
  name = "pydantic"
962
- version = "2.11.7"
977
+ version = "2.11.9"
963
978
  source = { registry = "https://pypi.org/simple" }
964
979
  dependencies = [
965
980
  { name = "annotated-types" },
@@ -967,9 +982,9 @@ dependencies = [
967
982
  { name = "typing-extensions" },
968
983
  { name = "typing-inspection" },
969
984
  ]
970
- sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" }
985
+ sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" }
971
986
  wheels = [
972
- { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" },
987
+ { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" },
973
988
  ]
974
989
 
975
990
  [[package]]
@@ -1062,6 +1077,15 @@ wheels = [
1062
1077
  { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
1063
1078
  ]
1064
1079
 
1080
+ [[package]]
1081
+ name = "pytokens"
1082
+ version = "0.1.10"
1083
+ source = { registry = "https://pypi.org/simple" }
1084
+ sdist = { url = "https://files.pythonhosted.org/packages/30/5f/e959a442435e24f6fb5a01aec6c657079ceaca1b3baf18561c3728d681da/pytokens-0.1.10.tar.gz", hash = "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044", size = 12171, upload-time = "2025-02-19T14:51:22.001Z" }
1085
+ wheels = [
1086
+ { url = "https://files.pythonhosted.org/packages/60/e5/63bed382f6a7a5ba70e7e132b8b7b8abbcf4888ffa6be4877698dcfbed7d/pytokens-0.1.10-py3-none-any.whl", hash = "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b", size = 12046, upload-time = "2025-02-19T14:51:18.694Z" },
1087
+ ]
1088
+
1065
1089
  [[package]]
1066
1090
  name = "pywin32-ctypes"
1067
1091
  version = "0.2.3"
@@ -1121,28 +1145,28 @@ wheels = [
1121
1145
 
1122
1146
  [[package]]
1123
1147
  name = "ruff"
1124
- version = "0.12.12"
1125
- source = { registry = "https://pypi.org/simple" }
1126
- sdist = { url = "https://files.pythonhosted.org/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6", size = 5359915, upload-time = "2025-09-04T16:50:18.273Z" }
1127
- wheels = [
1128
- { url = "https://files.pythonhosted.org/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc", size = 12116602, upload-time = "2025-09-04T16:49:18.892Z" },
1129
- { url = "https://files.pythonhosted.org/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727", size = 12868393, upload-time = "2025-09-04T16:49:23.043Z" },
1130
- { url = "https://files.pythonhosted.org/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb", size = 12036967, upload-time = "2025-09-04T16:49:26.04Z" },
1131
- { url = "https://files.pythonhosted.org/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577", size = 12276038, upload-time = "2025-09-04T16:49:29.056Z" },
1132
- { url = "https://files.pythonhosted.org/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e", size = 11901110, upload-time = "2025-09-04T16:49:32.07Z" },
1133
- { url = "https://files.pythonhosted.org/packages/9d/3d/f8b1038f4b9822e26ec3d5b49cf2bc313e3c1564cceb4c1a42820bf74853/ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e", size = 13668352, upload-time = "2025-09-04T16:49:35.148Z" },
1134
- { url = "https://files.pythonhosted.org/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8", size = 14638365, upload-time = "2025-09-04T16:49:38.892Z" },
1135
- { url = "https://files.pythonhosted.org/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5", size = 14060812, upload-time = "2025-09-04T16:49:42.732Z" },
1136
- { url = "https://files.pythonhosted.org/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92", size = 13050208, upload-time = "2025-09-04T16:49:46.434Z" },
1137
- { url = "https://files.pythonhosted.org/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45", size = 13311444, upload-time = "2025-09-04T16:49:49.931Z" },
1138
- { url = "https://files.pythonhosted.org/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5", size = 13279474, upload-time = "2025-09-04T16:49:53.465Z" },
1139
- { url = "https://files.pythonhosted.org/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4", size = 12070204, upload-time = "2025-09-04T16:49:56.882Z" },
1140
- { url = "https://files.pythonhosted.org/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23", size = 11880347, upload-time = "2025-09-04T16:49:59.729Z" },
1141
- { url = "https://files.pythonhosted.org/packages/df/58/30185fcb0e89f05e7ea82e5817b47798f7fa7179863f9d9ba6fd4fe1b098/ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489", size = 12891844, upload-time = "2025-09-04T16:50:02.591Z" },
1142
- { url = "https://files.pythonhosted.org/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee", size = 13360687, upload-time = "2025-09-04T16:50:05.8Z" },
1143
- { url = "https://files.pythonhosted.org/packages/c8/fa/05b6428a008e60f79546c943e54068316f32ec8ab5c4f73e4563934fbdc7/ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1", size = 12052870, upload-time = "2025-09-04T16:50:09.121Z" },
1144
- { url = "https://files.pythonhosted.org/packages/85/60/d1e335417804df452589271818749d061b22772b87efda88354cf35cdb7a/ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d", size = 13178016, upload-time = "2025-09-04T16:50:12.559Z" },
1145
- { url = "https://files.pythonhosted.org/packages/28/7e/61c42657f6e4614a4258f1c3b0c5b93adc4d1f8575f5229d1906b483099b/ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093", size = 12256762, upload-time = "2025-09-04T16:50:15.737Z" },
1148
+ version = "0.13.1"
1149
+ source = { registry = "https://pypi.org/simple" }
1150
+ sdist = { url = "https://files.pythonhosted.org/packages/ab/33/c8e89216845615d14d2d42ba2bee404e7206a8db782f33400754f3799f05/ruff-0.13.1.tar.gz", hash = "sha256:88074c3849087f153d4bb22e92243ad4c1b366d7055f98726bc19aa08dc12d51", size = 5397987, upload-time = "2025-09-18T19:52:44.33Z" }
1151
+ wheels = [
1152
+ { url = "https://files.pythonhosted.org/packages/f3/41/ca37e340938f45cfb8557a97a5c347e718ef34702546b174e5300dbb1f28/ruff-0.13.1-py3-none-linux_armv6l.whl", hash = "sha256:b2abff595cc3cbfa55e509d89439b5a09a6ee3c252d92020bd2de240836cf45b", size = 12304308, upload-time = "2025-09-18T19:51:56.253Z" },
1153
+ { url = "https://files.pythonhosted.org/packages/ff/84/ba378ef4129415066c3e1c80d84e539a0d52feb250685091f874804f28af/ruff-0.13.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4ee9f4249bf7f8bb3984c41bfaf6a658162cdb1b22e3103eabc7dd1dc5579334", size = 12937258, upload-time = "2025-09-18T19:52:00.184Z" },
1154
+ { url = "https://files.pythonhosted.org/packages/8d/b6/ec5e4559ae0ad955515c176910d6d7c93edcbc0ed1a3195a41179c58431d/ruff-0.13.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c5da4af5f6418c07d75e6f3224e08147441f5d1eac2e6ce10dcce5e616a3bae", size = 12214554, upload-time = "2025-09-18T19:52:02.753Z" },
1155
+ { url = "https://files.pythonhosted.org/packages/70/d6/cb3e3b4f03b9b0c4d4d8f06126d34b3394f6b4d764912fe80a1300696ef6/ruff-0.13.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80524f84a01355a59a93cef98d804e2137639823bcee2931f5028e71134a954e", size = 12448181, upload-time = "2025-09-18T19:52:05.279Z" },
1156
+ { url = "https://files.pythonhosted.org/packages/d2/ea/bf60cb46d7ade706a246cd3fb99e4cfe854efa3dfbe530d049c684da24ff/ruff-0.13.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff7f5ce8d7988767dd46a148192a14d0f48d1baea733f055d9064875c7d50389", size = 12104599, upload-time = "2025-09-18T19:52:07.497Z" },
1157
+ { url = "https://files.pythonhosted.org/packages/2d/3e/05f72f4c3d3a69e65d55a13e1dd1ade76c106d8546e7e54501d31f1dc54a/ruff-0.13.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c55d84715061f8b05469cdc9a446aa6c7294cd4bd55e86a89e572dba14374f8c", size = 13791178, upload-time = "2025-09-18T19:52:10.189Z" },
1158
+ { url = "https://files.pythonhosted.org/packages/81/e7/01b1fc403dd45d6cfe600725270ecc6a8f8a48a55bc6521ad820ed3ceaf8/ruff-0.13.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ac57fed932d90fa1624c946dc67a0a3388d65a7edc7d2d8e4ca7bddaa789b3b0", size = 14814474, upload-time = "2025-09-18T19:52:12.866Z" },
1159
+ { url = "https://files.pythonhosted.org/packages/fa/92/d9e183d4ed6185a8df2ce9faa3f22e80e95b5f88d9cc3d86a6d94331da3f/ruff-0.13.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c366a71d5b4f41f86a008694f7a0d75fe409ec298685ff72dc882f882d532e36", size = 14217531, upload-time = "2025-09-18T19:52:15.245Z" },
1160
+ { url = "https://files.pythonhosted.org/packages/3b/4a/6ddb1b11d60888be224d721e01bdd2d81faaf1720592858ab8bac3600466/ruff-0.13.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4ea9d1b5ad3e7a83ee8ebb1229c33e5fe771e833d6d3dcfca7b77d95b060d38", size = 13265267, upload-time = "2025-09-18T19:52:17.649Z" },
1161
+ { url = "https://files.pythonhosted.org/packages/81/98/3f1d18a8d9ea33ef2ad508f0417fcb182c99b23258ec5e53d15db8289809/ruff-0.13.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f70202996055b555d3d74b626406476cc692f37b13bac8828acff058c9966a", size = 13243120, upload-time = "2025-09-18T19:52:20.332Z" },
1162
+ { url = "https://files.pythonhosted.org/packages/8d/86/b6ce62ce9c12765fa6c65078d1938d2490b2b1d9273d0de384952b43c490/ruff-0.13.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f8cff7a105dad631085d9505b491db33848007d6b487c3c1979dd8d9b2963783", size = 13443084, upload-time = "2025-09-18T19:52:23.032Z" },
1163
+ { url = "https://files.pythonhosted.org/packages/a1/6e/af7943466a41338d04503fb5a81b2fd07251bd272f546622e5b1599a7976/ruff-0.13.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9761e84255443316a258dd7dfbd9bfb59c756e52237ed42494917b2577697c6a", size = 12295105, upload-time = "2025-09-18T19:52:25.263Z" },
1164
+ { url = "https://files.pythonhosted.org/packages/3f/97/0249b9a24f0f3ebd12f007e81c87cec6d311de566885e9309fcbac5b24cc/ruff-0.13.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3d376a88c3102ef228b102211ef4a6d13df330cb0f5ca56fdac04ccec2a99700", size = 12072284, upload-time = "2025-09-18T19:52:27.478Z" },
1165
+ { url = "https://files.pythonhosted.org/packages/f6/85/0b64693b2c99d62ae65236ef74508ba39c3febd01466ef7f354885e5050c/ruff-0.13.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cbefd60082b517a82c6ec8836989775ac05f8991715d228b3c1d86ccc7df7dae", size = 12970314, upload-time = "2025-09-18T19:52:30.212Z" },
1166
+ { url = "https://files.pythonhosted.org/packages/96/fc/342e9f28179915d28b3747b7654f932ca472afbf7090fc0c4011e802f494/ruff-0.13.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd16b9a5a499fe73f3c2ef09a7885cb1d97058614d601809d37c422ed1525317", size = 13422360, upload-time = "2025-09-18T19:52:32.676Z" },
1167
+ { url = "https://files.pythonhosted.org/packages/37/54/6177a0dc10bce6f43e392a2192e6018755473283d0cf43cc7e6afc182aea/ruff-0.13.1-py3-none-win32.whl", hash = "sha256:55e9efa692d7cb18580279f1fbb525146adc401f40735edf0aaeabd93099f9a0", size = 12178448, upload-time = "2025-09-18T19:52:35.545Z" },
1168
+ { url = "https://files.pythonhosted.org/packages/64/51/c6a3a33d9938007b8bdc8ca852ecc8d810a407fb513ab08e34af12dc7c24/ruff-0.13.1-py3-none-win_amd64.whl", hash = "sha256:3a3fb595287ee556de947183489f636b9f76a72f0fa9c028bdcabf5bab2cc5e5", size = 13286458, upload-time = "2025-09-18T19:52:38.198Z" },
1169
+ { url = "https://files.pythonhosted.org/packages/fd/04/afc078a12cf68592345b1e2d6ecdff837d286bac023d7a22c54c7a698c5b/ruff-0.13.1-py3-none-win_arm64.whl", hash = "sha256:c0bae9ffd92d54e03c2bf266f466da0a65e145f298ee5b5846ed435f6a00518a", size = 12437893, upload-time = "2025-09-18T19:52:41.283Z" },
1146
1170
  ]
1147
1171
 
1148
1172
  [[package]]
@@ -1185,6 +1209,19 @@ wheels = [
1185
1209
  { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
1186
1210
  ]
1187
1211
 
1212
+ [[package]]
1213
+ name = "starlette"
1214
+ version = "0.48.0"
1215
+ source = { registry = "https://pypi.org/simple" }
1216
+ dependencies = [
1217
+ { name = "anyio" },
1218
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
1219
+ ]
1220
+ sdist = { url = "https://files.pythonhosted.org/packages/a7/a5/d6f429d43394057b67a6b5bbe6eae2f77a6bf7459d961fdb224bf206eee6/starlette-0.48.0.tar.gz", hash = "sha256:7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46", size = 2652949, upload-time = "2025-09-13T08:41:05.699Z" }
1221
+ wheels = [
1222
+ { url = "https://files.pythonhosted.org/packages/be/72/2db2f49247d0a18b4f1bb9a5a39a0162869acf235f3a96418363947b3d46/starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659", size = 73736, upload-time = "2025-09-13T08:41:03.869Z" },
1223
+ ]
1224
+
1188
1225
  [[package]]
1189
1226
  name = "tenacity"
1190
1227
  version = "9.1.2"
@@ -1331,6 +1368,19 @@ wheels = [
1331
1368
  { url = "https://files.pythonhosted.org/packages/f9/00/2c7a93bbe93b74dc0496a8e875bac11027cb30c29636c106c6e49038b95f/uv-0.8.22-py3-none-win_arm64.whl", hash = "sha256:2a436b941b6e79fe1e1065b705a5689d72210f4367cbe885e19910cbcde2e4a1", size = 19778983, upload-time = "2025-09-23T20:35:12.188Z" },
1332
1369
  ]
1333
1370
 
1371
+ [[package]]
1372
+ name = "uvicorn"
1373
+ version = "0.37.0"
1374
+ source = { registry = "https://pypi.org/simple" }
1375
+ dependencies = [
1376
+ { name = "click" },
1377
+ { name = "h11" },
1378
+ ]
1379
+ sdist = { url = "https://files.pythonhosted.org/packages/71/57/1616c8274c3442d802621abf5deb230771c7a0fec9414cb6763900eb3868/uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13", size = 80367, upload-time = "2025-09-23T13:33:47.486Z" }
1380
+ wheels = [
1381
+ { url = "https://files.pythonhosted.org/packages/85/cd/584a2ceb5532af99dd09e50919e3615ba99aa127e9850eafe5f31ddfdb9a/uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c", size = 67976, upload-time = "2025-09-23T13:33:45.842Z" },
1382
+ ]
1383
+
1334
1384
  [[package]]
1335
1385
  name = "virtualenv"
1336
1386
  version = "20.34.0"
@@ -1352,10 +1402,12 @@ dependencies = [
1352
1402
  { name = "aiofiles" },
1353
1403
  { name = "aiohttp" },
1354
1404
  { name = "click" },
1405
+ { name = "fastapi" },
1355
1406
  { name = "pydantic" },
1356
1407
  { name = "pyyaml" },
1357
1408
  { name = "semver" },
1358
1409
  { name = "tenacity" },
1410
+ { name = "uvicorn" },
1359
1411
  ]
1360
1412
 
1361
1413
  [package.dev-dependencies]
@@ -1371,10 +1423,12 @@ requires-dist = [
1371
1423
  { name = "aiofiles", specifier = ">=23.0.0" },
1372
1424
  { name = "aiohttp", specifier = ">=3.8.0" },
1373
1425
  { name = "click", specifier = ">=8.0.0" },
1426
+ { name = "fastapi", specifier = ">=0.117.1" },
1374
1427
  { name = "pydantic", specifier = ">=2.11.7" },
1375
1428
  { name = "pyyaml", specifier = ">=6.0.0" },
1376
1429
  { name = "semver", specifier = ">=3.0.0" },
1377
1430
  { name = "tenacity", specifier = ">=8.0.0" },
1431
+ { name = "uvicorn", specifier = ">=0.36.0" },
1378
1432
  ]
1379
1433
 
1380
1434
  [package.metadata.requires-dev]
@@ -0,0 +1,4 @@
1
+ # This file is auto-generated by Hatchling. As such, do not:
2
+ # - modify
3
+ # - track in version control e.g. be sure to add to .gitignore
4
+ __version__ = VERSION = '0.63.7'
@@ -0,0 +1,3 @@
1
+ from . import api_server, contest_archiver, model
2
+
3
+ __all__ = [model, contest_archiver, api_server]
@@ -0,0 +1,9 @@
1
+ """
2
+ Contest API Server Package
3
+
4
+ Modern FastAPI-based implementation of the Contest API specification.
5
+ """
6
+
7
+ from .server import ContestAPIServer
8
+
9
+ __all__ = [ContestAPIServer]
@@ -0,0 +1,48 @@
1
+ """
2
+ FastAPI Dependencies
3
+
4
+ Dependency injection system for Contest API Server.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Annotated
9
+
10
+ from fastapi import Depends
11
+
12
+ from .services.contest_service import ContestService
13
+
14
+ # Global contest service instance cache
15
+ _contest_service_instance = None
16
+
17
+
18
+ def get_contest_service() -> ContestService:
19
+ """
20
+ Dependency that provides ContestService instance.
21
+
22
+ This is configured by the main server class and cached globally.
23
+
24
+ Returns:
25
+ ContestService instance
26
+
27
+ Raises:
28
+ RuntimeError: If service not configured
29
+ """
30
+ global _contest_service_instance
31
+ if _contest_service_instance is None:
32
+ raise RuntimeError("ContestService not configured. Call configure_dependencies() first.")
33
+ return _contest_service_instance
34
+
35
+
36
+ def configure_dependencies(contest_package_dir: Path) -> None:
37
+ """
38
+ Configure the dependency injection system.
39
+
40
+ Args:
41
+ contest_package_dir: Path to contest package directory
42
+ """
43
+ global _contest_service_instance
44
+ _contest_service_instance = ContestService(contest_package_dir)
45
+
46
+
47
+ # Type alias for dependency injection
48
+ ContestServiceDep = Annotated[ContestService, Depends(get_contest_service)]
@@ -0,0 +1,52 @@
1
+ """
2
+ Routes package for Contest API Server
3
+
4
+ Contains all API route definitions organized by functionality.
5
+ """
6
+
7
+ from fastapi import APIRouter
8
+
9
+ from . import (
10
+ access,
11
+ accounts,
12
+ awards,
13
+ clarifications,
14
+ contests,
15
+ general,
16
+ groups,
17
+ judgement_types,
18
+ judgements,
19
+ languages,
20
+ organizations,
21
+ problems,
22
+ runs,
23
+ submissions,
24
+ teams,
25
+ )
26
+
27
+
28
+ def create_router() -> APIRouter:
29
+ """Create and configure the main API router"""
30
+ router = APIRouter(prefix="/api")
31
+
32
+ # Include all route modules
33
+ router.include_router(access.router, tags=["Access"])
34
+ router.include_router(accounts.router, tags=["Accounts"])
35
+ router.include_router(awards.router, tags=["Awards"])
36
+ router.include_router(clarifications.router, tags=["Clarifications"])
37
+ router.include_router(contests.router, tags=["Contests"])
38
+ router.include_router(general.router, tags=["General"])
39
+ router.include_router(groups.router, tags=["Groups"])
40
+ router.include_router(judgements.router, tags=["Judgements"])
41
+ router.include_router(judgement_types.router, tags=["Judgement types"])
42
+ router.include_router(languages.router, tags=["Languages"])
43
+ router.include_router(organizations.router, tags=["Organizations"])
44
+ router.include_router(problems.router, tags=["Problems"])
45
+ router.include_router(runs.router, tags=["Runs"])
46
+ router.include_router(submissions.router, tags=["Submissions"])
47
+ router.include_router(teams.router, tags=["Teams"])
48
+
49
+ return router
50
+
51
+
52
+ __all__ = ["create_router"]
@@ -0,0 +1,18 @@
1
+ import logging
2
+ from typing import Any, Dict
3
+
4
+ from fastapi import APIRouter
5
+
6
+ from ..dependencies import ContestServiceDep
7
+
8
+ router = APIRouter()
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ @router.get(
13
+ "/contests/{contest_id}/access",
14
+ summary="Get access information",
15
+ response_model=Dict[str, Any],
16
+ )
17
+ async def get_access(contest_id: str, service: ContestServiceDep) -> Dict[str, Any]:
18
+ return service.get_access(contest_id)