Auto merge dev → main (#1225)

* fix BOT.data_parser import in render_recap.py

render_recap.py runs as a subprocess (Path(__file__).parent.parent on
sys.path) and used `from BOT.data_parser import ...`. After the SHARED
move, data_parser is no longer in the BOT package. Add BOTS/SHARED to
sys.path and switch to the absolute `from data_parser import ...`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* centralize SHARED sys.path bootstrap in BOT/__init__.py

Drop per-file `sys.path.insert(SHARED)` bandaids from BOT/scoreboard.py,
gob.py, utils.py, and game_api.py. The bootstrap now happens exactly
once when the BOT package is imported (via BOT/__init__.py), which is
implicit for any `from BOT.X import …` / `import BOT.X` and any
`python -m BOT.x` invocation.

`SHARED_DIR` is exposed as a public name on the BOT package; siblings
import it via `from . import SHARED_DIR` for building asset paths
(MAPS, ICONS, FONTS, vromfs) instead of recomputing the location.

render_recap.py is the one subprocess entry point that runs as
__main__, so it keeps a minimal bootstrap: add SREBOT to sys.path
then `import BOT` to fire the package init once.

Also move pyrightconfig.json to the BOTS monorepo root so pyright
resolves data_parser and third-party imports regardless of which
subproject the editor opens from.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
NotSoToothless
2026-05-13 23:42:47 -07:00
committed by GitHub
parent ff420e131f
commit 39ef90b3fd
7 changed files with 35 additions and 56 deletions
+12 -6
View File
@@ -1,12 +1,18 @@
"""SREBOT bot package. """SREBOT bot package.
Bootstraps the sibling BOTS/SHARED directory onto sys.path so that Single source of truth for the BOTS/SHARED bootstrap. Importing this
`from data_parser import ...` (and other SHARED-only modules) resolve package (which happens automatically for any `from BOT.X import ...`
regardless of which submodule gets imported first. or `import BOT.X`) puts the sibling `BOTS/SHARED/` directory on
sys.path, so submodules can simply `from data_parser import ...` etc.
`SHARED_DIR` is also re-exported so consumers can compute asset paths
(MAPS, ICONS, FONTS, vromfs) without re-deriving the location.
""" """
import sys import sys
from pathlib import Path from pathlib import Path
_SHARED_DIR = Path(__file__).resolve().parents[2] / "SHARED" SHARED_DIR: Path = Path(__file__).resolve().parents[2] / "SHARED"
if str(_SHARED_DIR) not in sys.path: if str(SHARED_DIR) not in sys.path:
sys.path.insert(0, str(_SHARED_DIR)) sys.path.insert(0, str(SHARED_DIR))
__all__ = ["SHARED_DIR"]
+1 -6
View File
@@ -12,7 +12,6 @@ import json
import logging import logging
import os import os
import sqlite3 import sqlite3
import sys
import time import time
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Tuple from typing import Any, Dict, Tuple
@@ -23,11 +22,7 @@ import aiohttp
import aiosqlite import aiosqlite
from dotenv import load_dotenv from dotenv import load_dotenv
# Ensure project root is in path for DAGOR_FILES imports # DAGOR_FILES lives in BOTS/SHARED; BOT/__init__.py already put it on sys.path.
_project_root = Path(__file__).resolve().parent.parent
if str(_project_root) not in sys.path:
sys.path.insert(0, str(_project_root))
from DAGOR_FILES.WtFileUtils.blk.BlkParser import BlkDecoder from DAGOR_FILES.WtFileUtils.blk.BlkParser import BlkDecoder
# Local Module Imports # Local Module Imports
+5 -9
View File
@@ -33,14 +33,10 @@ import numpy as np
import pygob import pygob
import zstandard as zstd import zstandard as zstd
from . import SHARED_DIR
from .utils import REPLAYS_DIR from .utils import REPLAYS_DIR
from PIL import Image, ImageDraw, ImageFilter, ImageFont from PIL import Image, ImageDraw, ImageFilter, ImageFont
# Make SHARED (sibling of SREBOT under BOTS/) importable
_SHARED_DIR = Path(__file__).resolve().parents[2] / "SHARED"
if str(_SHARED_DIR) not in sys.path:
sys.path.insert(0, str(_SHARED_DIR))
try: try:
from data_parser import ( from data_parser import (
LangTableReader as _LangTableReader, LangTableReader as _LangTableReader,
@@ -114,9 +110,9 @@ N_WORKERS = min(8, os.cpu_count() or 4) # Thread pool size for parallel frame
WIN_COLOR = (0, 200, 0) # green WIN_COLOR = (0, 200, 0) # green
LOSE_COLOR = (220, 30, 30) # red LOSE_COLOR = (220, 30, 30) # red
MINIMAPS_DIR = _SHARED_DIR / "MAPS" / "MINIMAPS" MINIMAPS_DIR = SHARED_DIR / "MAPS" / "MINIMAPS"
LEVELS_DIR = _SHARED_DIR / "MAPS" / "LEVELS" LEVELS_DIR = SHARED_DIR / "MAPS" / "LEVELS"
ICONS_DIR = _SHARED_DIR / "ICONS" ICONS_DIR = SHARED_DIR / "ICONS"
_tl = threading.local() _tl = threading.local()
@@ -221,7 +217,7 @@ def _get_unit_tags(internal_name: str) -> list[str] | None:
return UnitTags.get()._get_tags(internal_name) return UnitTags.get()._get_tags(internal_name)
MINIS_DIR = _SHARED_DIR / "ICONS" / "MINIS" MINIS_DIR = SHARED_DIR / "ICONS" / "MINIS"
_AIRCRAFT_TAGS = {"air", "aircraft", "helicopter"} _AIRCRAFT_TAGS = {"air", "aircraft", "helicopter"}
+6 -4
View File
@@ -69,11 +69,13 @@ def _sanitize_render_text(s: str) -> str:
) )
_repo_root = Path(__file__).resolve().parent.parent # This script runs as a subprocess entry point (python BOT/render_recap.py …),
if str(_repo_root) not in sys.path: # so BOT/__init__.py is not invoked automatically. Put SREBOT on sys.path,
sys.path.insert(0, str(_repo_root)) # then `import BOT` to trigger the package's SHARED bootstrap once.
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import BOT # noqa: F401, E402 — side effect: adds BOTS/SHARED to sys.path
from BOT.data_parser import apply_vehicle_name_filters # noqa: E402 from data_parser import apply_vehicle_name_filters # noqa: E402
LOCALES_DIR = Path(__file__).resolve().parent.parent / "web" / "locales" LOCALES_DIR = Path(__file__).resolve().parent.parent / "web" / "locales"
DEFAULT_LANG = "en" DEFAULT_LANG = "en"
+5 -9
View File
@@ -14,7 +14,6 @@ import logging
import os import os
import pstats import pstats
import re import re
import sys
from datetime import datetime, timezone from datetime import datetime, timezone
from functools import lru_cache from functools import lru_cache
from pathlib import Path from pathlib import Path
@@ -24,12 +23,9 @@ import numpy as np
from PIL import Image, ImageDraw, ImageFilter, ImageFont from PIL import Image, ImageDraw, ImageFilter, ImageFont
from PIL.Image import Resampling from PIL.Image import Resampling
# Make SHARED (sibling of SREBOT under BOTS/) importable
_SHARED_DIR = Path(__file__).resolve().parents[2] / "SHARED"
if str(_SHARED_DIR) not in sys.path:
sys.path.insert(0, str(_SHARED_DIR))
# Local Module Imports # Local Module Imports
from . import SHARED_DIR
# Toggle for data_parser dependency # Toggle for data_parser dependency
# Set to False to avoid importing data_parser and show all vehicles as "?" in team composition # Set to False to avoid importing data_parser and show all vehicles as "?" in team composition
USE_DATA_PARSER = True USE_DATA_PARSER = True
@@ -45,9 +41,9 @@ else:
return name return name
BASE_DIR = Path(__file__).resolve().parent BASE_DIR = Path(__file__).resolve().parent
MAPS_DIR = _SHARED_DIR / "MAPS" MAPS_DIR = SHARED_DIR / "MAPS"
ICON_BASE_DIR = _SHARED_DIR / "ICONS" ICON_BASE_DIR = SHARED_DIR / "ICONS"
TEXT_FONT_PATH = _SHARED_DIR / "FONTS" / "arial_unicode_ms.otf" TEXT_FONT_PATH = SHARED_DIR / "FONTS" / "arial_unicode_ms.otf"
def _normalize_squad_key(value: str | None) -> str: def _normalize_squad_key(value: str | None) -> str:
+6 -8
View File
@@ -30,12 +30,10 @@ from discord.utils import escape_markdown, escape_mentions
from dotenv import load_dotenv from dotenv import load_dotenv
from wcwidth import wcswidth from wcwidth import wcswidth
# Make SHARED (sibling of SREBOT under BOTS/) importable
_SHARED_DIR = Path(__file__).resolve().parents[2] / "SHARED"
if str(_SHARED_DIR) not in sys.path:
sys.path.insert(0, str(_SHARED_DIR))
# Local Module Imports # Local Module Imports
# BOT/__init__.py has already put BOTS/SHARED on sys.path; re-export it
# under a public name so peer modules can use it for asset paths.
from . import SHARED_DIR # noqa: F401 — re-exported for siblings
from data_parser import ( from data_parser import (
LangTableReader, LangTableReader,
UnitTags, UnitTags,
@@ -64,7 +62,7 @@ def esc(text: str) -> str:
# Base storage paths # Base storage paths
STORAGE_DIR = require_storage_dir() STORAGE_DIR = require_storage_dir()
ICONS_DIR = _SHARED_DIR / "ICONS" ICONS_DIR = SHARED_DIR / "ICONS"
# Cache and Auth directories # Cache and Auth directories
CACHE_DIR = STORAGE_DIR / "CACHE" CACHE_DIR = STORAGE_DIR / "CACHE"
@@ -1411,7 +1409,7 @@ async def init_game_cache():
all_names = unit_tags.all_names all_names = unit_tags.all_names
logging.info(f"[GAMES] TOTAL VEHICLES: {len(all_names)}") logging.info(f"[GAMES] TOTAL VEHICLES: {len(all_names)}")
icons_dir = _SHARED_DIR / "ICONS" / "VEHICLES" icons_dir = SHARED_DIR / "ICONS" / "VEHICLES"
# Case-insensitive lookup: unittags.blk uses CDKs like "ussr_su_122P" while the # Case-insensitive lookup: unittags.blk uses CDKs like "ussr_su_122P" while the
# icon file on disk is "ussr_su_122p.png". On case-sensitive filesystems an exact # icon file on disk is "ussr_su_122p.png". On case-sensitive filesystems an exact
# match silently drops these vehicles from the cache. # match silently drops these vehicles from the cache.
@@ -1445,7 +1443,7 @@ async def init_game_cache_all():
all_names = unit_tags.all_names all_names = unit_tags.all_names
logging.info(f"[GAMES] TOTAL VEHICLES (ALL): {len(all_names)}") logging.info(f"[GAMES] TOTAL VEHICLES (ALL): {len(all_names)}")
icons_dir = _SHARED_DIR / "ICONS" / "VEHICLES" icons_dir = SHARED_DIR / "ICONS" / "VEHICLES"
icons_on_disk = {p.name.lower(): p.name for p in icons_dir.iterdir() if p.suffix == ".png"} if icons_dir.is_dir() else {} icons_on_disk = {p.name.lower(): p.name for p in icons_dir.iterdir() if p.suffix == ".png"} if icons_dir.is_dir() else {}
translate = LangTableReader("English") translate = LangTableReader("English")
-14
View File
@@ -1,14 +0,0 @@
{
"pythonVersion": "3.14",
"extraPaths": [
".venv/lib/python3.14/site-packages",
"../SHARED"
],
"exclude": [
".venv",
"../SHARED/DAGOR_FILES",
"web/node_modules",
"node_modules",
"**/__pycache__"
]
}