Initial commit — gin-config strict-pattern coding standard

Code-style guide and reference patterns for DL/CV research at the
ЛИСАД laboratory (NADEZHDA / SOFIA CVGL projects).

Files:
- Стандарт написания кода для DL CV исследований (CVGL).md
- Правила написания Python-кода (Gin-Config Strict Pattern).md
- REQUIREMENTS_GIN_STYLE.md
- Gin-Config Strict Pattern Reference Examples.md
- Переход от argparse и dataclass к gin-config.md
- gin-parse.md
- Рекомендуемые gin-config категории.md
- config_loader_reference.py
- README.md (this commit)
- .gitignore (Python artifacts)
This commit is contained in:
2026-04-27 17:12:38 +03:00
commit 3278322a17
10 changed files with 1970 additions and 0 deletions

101
gin-parse.md Normal file
View File

@@ -0,0 +1,101 @@
Хороший вопрос. Вижу проблему в текущем паттерне — каждый `get_*_cfg()` вызывает `gin.parse_config_file()` отдельно, а gin имеет **глобальное состояние**. Если вызвать два загрузчика подряд — второй `gin.parse_config_file()` **добавляет** параметры к первому, не очищая.
## Проблемы текущего подхода
```python
# Текущий паттерн — каждый загрузчик парсит свой .gin:
fdir_conf = get_fdir_cfg(path2cfg) # gin.parse_config_file("file_dir.gin")
input_conf = get_inputval_cfg(path2cfg) # gin.parse_config_file("input.gin")
train_conf = get_train_cfg(path2cfg) # gin.parse_config_file("train.gin")
```
|Проблема|Почему|
|:--|:--|
|**Gin state accumulates**|Второй `parse_config_file` не очищает первый — параметры накапливаются|
|**Порядок зависимость**|Если `input.gin` случайно переопределит параметр из `file_dir.gin` — тихая ошибка|
|**Нет единой точки загрузки**|Разбросанные `gin.parse_config_file()` в каждом файле conf/|
|**Дублирование кода**|Каждый `get_*_cfg()` — одинаковые 3 строки|
## Рекомендуемое решение
**Один раз загрузить все .gin файлы, один раз создать все конфиги:**
```python
from __future__ import annotations
import gin
from pathlib import Path
def load_all_configs(path2cfg: str) -> dict[str, object]:
"""Load all gin configs at once and return all config objects.
Args:
path2cfg: Path to config directory (with trailing slash).
Returns:
Dict with config objects keyed by name.
"""
gin.clear_config() # Reset global state — critical!
# Parse ALL .gin files in one call:
gin_files = sorted(Path(path2cfg).glob("*.gin"))
gin.parse_config_files_and_bindings(
[str(f) for f in gin_files],
bindings=[], # No CLI overrides
)
# Instantiate all configs (gin state is now fully populated):
return {
"pipeline": PipelineConfig(),
"hardware": HardwareConfig(),
"models": ModelsConfig(),
"input": InputConfig(),
"seg": SegConfig(),
}
```
**Использование в main:**
```python
def main() -> None:
proj_dir = get_proj_dir()
path2cfg = f"{proj_dir}in/config_files/"
configs = load_all_configs(path2cfg)
run_pipeline(
configs["pipeline"],
configs["hardware"],
configs["models"],
configs["input"],
configs["seg"],
)
```
## Сохранить ли отдельные `get_*_cfg()` ?
**Да, но как обёртки для тестирования** (когда нужен один конфиг в изоляции):
```python
def get_hardware_cfg(path2cfg: str) -> HardwareConfig:
"""Load ONLY hardware config (for testing/debugging)."""
gin.clear_config()
gin.parse_config_file(f"{path2cfg}hardware.gin")
return HardwareConfig()
```
Ключевое: добавить `gin.clear_config()` перед каждым `parse_config_file()`.
## Итого: что изменить
|Было|Стало|
|:--|:--|
|Каждый `get_*_cfg()` парсит свой .gin|**`load_all_configs()`** парсит все .gin за один вызов|
|Нет `gin.clear_config()`|**Обязательный** `gin.clear_config()` перед загрузкой|
|5 отдельных вызовов в main|**1 вызов** `load_all_configs()`|
|Отдельные `get_*_cfg()` для продакшена|Оставить **только для тестирования**|
Хотите, чтобы я создал файл `src/conf/config_loader.py` с этим паттерном и обновил `REQUIREMENTS_GIN_STYLE.md`?