feat!: adopt Python 3.15 and support BattlesnakeArena

- Require Python 3.15 in project metadata, runtime pin, and Docker stages.
- Use lazy imports for database backends and type-only dependencies.
- Defer benchmark dependencies until their code paths are used.
- Rely on deferred annotations and use Path.info for file discovery.
- Accept BattlesnakeArena alongside BattlesnakeEngine on game endpoints.
- Advance quart_common for Python 3.15 and multi-agent validation.
- Add pytest development dependencies and refresh the uv lockfile.
- Test lazy backend loading, import order, and fresh file discovery.
- Document the runtime requirements and locked uv workflow.

BREAKING CHANGE: Python 3.15 or newer is required; earlier runtimes cannot parse the lazy import syntax.
This commit is contained in:
2026-11-03 10:02:40 +01:00
parent f14d780f29
commit 5efab77248
35 changed files with 226 additions and 202 deletions
+5 -6
View File
@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, cast
from typing import cast
import asyncio, json, time, os
from quart import Blueprint, request, jsonify
@@ -10,8 +10,7 @@ from snakes import DEFAULT_SNAKE_CONFIG
from server.GameBoard import GameBoard
from server.Files import read_file
if TYPE_CHECKING:
from server.Server import Server
lazy from server.Server import Server
def create_battlesnake_blueprint(server:'Server') -> Blueprint:
blueprint = Blueprint('battlesnake', __name__)
@@ -45,7 +44,7 @@ def create_battlesnake_blueprint(server:'Server') -> Blueprint:
return snake_json
@blueprint.post('/start')
@require_user_agent("BattlesnakeEngine", abort_code=404)
@require_user_agent(("BattlesnakeEngine", "BattlesnakeArena"), abort_code=404)
async def on_start():
server.metrics_collector.record_http_request('start')
await server.game_runtime.prune_stale_games()
@@ -59,7 +58,7 @@ def create_battlesnake_blueprint(server:'Server') -> Blueprint:
return 'ok'
@blueprint.post('/move')
@require_user_agent("BattlesnakeEngine", abort_code=404)
@require_user_agent(("BattlesnakeEngine", "BattlesnakeArena"), abort_code=404)
async def on_move():
server.metrics_collector.record_http_request('move')
game_state = await request.get_json()
@@ -91,7 +90,7 @@ def create_battlesnake_blueprint(server:'Server') -> Blueprint:
return {'move': next_move}
@blueprint.post('/end')
@require_user_agent("BattlesnakeEngine", abort_code=404)
@require_user_agent(("BattlesnakeEngine", "BattlesnakeArena"), abort_code=404)
async def on_end():
server.metrics_collector.record_http_request('end')
await server.game_runtime.prune_stale_games()