refactor: remove import-time side effects
Импорт модулей больше не мутирует процесс:
- config.py: module-level load_dotenv(override=False) мутировал os.environ
при любом импорте. Вызов перенесён в Config.load_yaml() — точку явной
загрузки конфигурации, семантика override=False и путь по умолчанию
сохранены. --config PATH (load_dotenv(path, override=True)) не затронут.
- mailer.py: глобальная регистрация email.charset.add_charset('utf-8', ...)
меняла кодировку для всего процесса. Заменена на точечное применение
8bit UTF-8 к телу письма в _utf8_text_part() — payload в UTF-8
(surrogateescape) + Content-Transfer-Encoding: 8bit на конкретной части.
Проверено: сериализация в байты (BytesGenerator, как в send_message)
побайтово совпадает со старым поведением.
Тест test_env_var_takes_priority_over_dotenv адаптирован к новой точке
вызова load_dotenv; test_body_substitution переведён с as_string() на
декодированный payload (str-сериализация не-ASCII части без глобальной
мутации реестра теперь даёт base64; wire-формат 8bit сохранён и
зафиксирован test_wire_format_is_8bit_utf8).
Closes #64
This commit is contained in:
50
tests/test_import_side_effects.py
Normal file
50
tests/test_import_side_effects.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""#64: импорт модулей не должен давать side effects в процессе."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def _run_in_subprocess(code: str, cwd: Path) -> str:
|
||||
"""Выполняет code в чистом интерпретаторе с доступом к пакету."""
|
||||
env = {**os.environ, "PYTHONPATH": str(REPO_ROOT)}
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-c", code],
|
||||
cwd=cwd,
|
||||
env=env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def test_config_import_does_not_mutate_os_environ(tmp_path):
|
||||
"""import redmine_reporter.config не подгружает .env из текущей директории."""
|
||||
(tmp_path / ".env").write_text("RR_IMPORT_PROBE=1\n")
|
||||
|
||||
output = _run_in_subprocess(
|
||||
"import os\n"
|
||||
"import redmine_reporter.config\n"
|
||||
"print('RR_IMPORT_PROBE' in os.environ)\n",
|
||||
cwd=tmp_path,
|
||||
)
|
||||
|
||||
assert output == "False"
|
||||
|
||||
|
||||
def test_mailer_import_does_not_mutate_global_charset_registry(tmp_path):
|
||||
"""import redmine_reporter.mailer не меняет email.charset для всего процесса."""
|
||||
output = _run_in_subprocess(
|
||||
"from email.charset import BASE64, Charset\n"
|
||||
"before = Charset('utf-8').body_encoding\n"
|
||||
"import redmine_reporter.mailer\n"
|
||||
"after = Charset('utf-8').body_encoding\n"
|
||||
"print(before == BASE64, after == BASE64)\n",
|
||||
cwd=tmp_path,
|
||||
)
|
||||
|
||||
assert output == "True True"
|
||||
Reference in New Issue
Block a user