pyproject.toml
Rama master · réplica de lectura del repositorio original.
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "torrcast"
dynamic = ["version"]
description = "Поиск торрент-релиза и каст его на ТВ без скачивания и без облака"
readme = "README.md"
# Debian 12 везёт python3.11 и никакого python3.12 в репозиториях у неё нет. Нижней
# границе это больше не указ: где системного 3.12 нет, установщик приносит его сам
# через uv (install.sh, pick_python), и установка остаётся теми же двумя командами.
requires-python = ">=3.12"
license = { text = "MIT" }
authors = [{ name = "anysda", email = "anysdaw@gmail.com" }]
dependencies = [
"requests>=2.32",
"PySocks>=1.7",
# Не catt: он гонит любой URL через yt-dlp и не умеет подсказки формата HLS,
# без которых Default Media Receiver отвечает LOAD ERROR.
"PyChromecast>=14",
# Мост Home Assistant объявляет себя по mDNS (hass/announce.py). Библиотека и так
# приезжает вместе с PyChromecast - он ищет приёмники ею же, - но зависимость
# названа прямо: транзитивная держится чужим решением, а мост зовёт её сам.
"zeroconf>=0.130",
]
[project.scripts]
cast = "torrcast.runtime.main:main"
torrcast-bot = "tgbot.bot:_bot"
torrcast-ha = "hass.main:main"
[dependency-groups]
dev = [
"grimp>=3.15",
"mypy>=1.11",
"pyte>=0.8",
"pytest>=8.3",
"pytest-deadfixtures>=3.1",
"pytest-testmon>=2.1",
"pytest-xdist>=3.8",
"ruff==0.16.1",
"types-requests>=2.32",
"vulture>=2.16",
]
# Единственное ручное место с номером версии - torrcast/domain/version.py. Отсюда
# hatchling берёт версию собранного пакета сам; остальные производные места (JSON-
# манифест интеграции, install.sh, uv.lock) читать python на лету не умеют и разносятся
# командой scripts/set-version.py (сторож - tests/test_version_places.py).
[tool.hatch.version]
path = "torrcast/domain/version.py"
[tool.hatch.build.targets.wheel]
packages = ["torrcast", "tgbot", "hass"]
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
# ARG - параметр, который никто не читает: это мёртвый код в малом, и он же первый
# признак договора, который разъехался с телом. ERA - закомментированный код: по дереву
# он даёт ноль, и правило стоит здесь ровно затем, чтобы так и осталось.
select = ["E", "F", "W", "I", "N", "UP", "B", "C4", "SIM", "RUF", "ARG", "ERA"]
# RUF001-003: кириллица в строках/докстрингах/комментариях — не «неоднозначные символы».
ignore = ["RUF001", "RUF002", "RUF003"]
[tool.ruff.lint.per-file-ignores]
# Подделка порта обязана держать сигнатуру договора целиком, даже когда телу подделки
# половина параметров не нужна: сценарий зовёт её по договору, а не по телу. Убрать
# «лишний» параметр здесь значит сломать подделку молча. То же у обвязки в scripts:
# колбэки чужих библиотек принимают то, что им передают.
"tests/**" = ["ARG"]
"scripts/**" = ["ARG"]
# Белый список vulture - не код, а перечень имён: они здесь намеренно не определены
# (F821) и стоят голыми выражениями (B018), потому что читает файл только vulture.
"scripts/vulture-whitelist.py" = ["F821", "B018"]
[tool.ruff.lint.isort]
known-first-party = ["torrcast", "tgbot", "hass", "custom_components"]
[tool.mypy]
python_version = "3.12"
strict = true
incremental = false
files = ["torrcast", "tgbot", "hass", "custom_components", "tests", "scripts"]
# Белый список vulture переехал в `scripts`, то есть в охват mypy, но кодом он не
# является: имена в нём намеренно нигде не определены, их читает только vulture.
exclude = ['scripts/vulture-whitelist\.py$']
warn_unreachable = true
# Без этих двух строк `custom_components/torrcast/` читается как модуль `torrcast` и
# сталкивается с настоящим пакетом: у каталога `custom_components/` нет и не будет
# `__init__.py` (его форму задаёт Home Assistant, а не мы). Корень пакетов назван
# явно - имя модуля берётся от него, и интеграция зовётся `custom_components.torrcast`,
# ровно как её зовут тесты и сам Home Assistant. `scripts` в том же списке потому, что
# явный корень отменяет прежний неявный: прогонялки сценария зовут друг друга по голому
# имени (`import poolreplay`), и без этой записи их импорты перестают находиться.
mypy_path = [".", "scripts"]
explicit_package_bases = true
warn_unused_configs = true
disallow_any_explicit = false
pretty = true
# Home Assistant в зависимостях torrcast не значится и значиться не будет: интеграция
# живёт внутри чужого процесса и типы берёт оттуда. В венве гейта его нет, поэтому
# договор соседа приезжает сюда как `Any` - и подписи наших собственных функций это не
# ослабляет, они проверяются как раньше.
[[tool.mypy.overrides]]
# `aiohttp` тут в той же роли: его в венв тянет Home Assistant, а не torrcast.
module = [
"aiohttp.*",
"homeassistant.*",
"pytest_homeassistant_custom_component.*",
"voluptuous.*",
]
ignore_missing_imports = true
# Наследование от `Any` тут не небрежность, а единственный способ написать сущность
# Home Assistant: `MediaPlayerEntity` и `ConfigFlow` без установленного Home Assistant
# ничем иным быть не могут.
[[tool.mypy.overrides]]
module = ["custom_components.*", "tests.hass_integration.*"]
disallow_subclassing_any = false
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--strict-markers"
markers = [
"ffmpeg: интеграционный тест с настоящими ffmpeg/ffprobe (гоняется последовательно)",
"machine: тест с настоящими часами, сокетом или подпроцессом (гоняется последовательно)",
]