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:
+12
-6
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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")
|
||||||
|
|||||||
@@ -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__"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user