mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-11-10 20:27:03 +01:00
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
200 lines
6.3 KiB
TOML
200 lines
6.3 KiB
TOML
[project]
|
|
name = "steam-backlog-enforcer"
|
|
version = "1.0.0"
|
|
description = "Enforce your Steam backlog: one game at a time, tracked with HowLongToBeat data"
|
|
# 3.11 floor: websockets 17 (and its successors) drop 3.10, and this repo
|
|
# tracks newest stable rather than the oldest interpreter still alive.
|
|
requires-python = ">=3.14"
|
|
dependencies = [
|
|
"requests==2.34.2",
|
|
"howlongtobeatpy==1.0.23",
|
|
# Directly imported by the package (aiohttp: _hltb_* async lookups;
|
|
# websockets: library_hider). aiohttp was previously satisfied only
|
|
# transitively via howlongtobeatpy and websockets was undeclared — both
|
|
# are declared here so a clean install imports the package.
|
|
"aiohttp==3.14.3",
|
|
"websockets==17.1",
|
|
]
|
|
|
|
# Optional extras. The `mcp` group is ONLY needed by the MCP server
|
|
# (steam_backlog_enforcer._mcp), which Claude Code spawns via a dedicated
|
|
# venv (see scripts/setup_mcp.sh / .mcp.json). It is deliberately NOT a core
|
|
# dependency so the systemd/CLI system-python path stays dependency-light and
|
|
# never has to import the MCP SDK.
|
|
[project.optional-dependencies]
|
|
mcp = ["mcp==2.1.1"]
|
|
|
|
# Explicit package discovery. Without this, setuptools' flat-layout
|
|
# auto-discovery aborts because the repo root holds two top-level dirs
|
|
# (`steam_backlog_enforcer/` and the React `web/` frontend) and it refuses
|
|
# to guess. Scoping discovery to the Python package fixes `pip install -e .`
|
|
# for the CLI, systemd, AND the dedicated MCP venv.
|
|
[tool.setuptools.packages.find]
|
|
include = ["steam_backlog_enforcer*"]
|
|
|
|
# ============================================================================
|
|
# RUFF - Fast Python linter and formatter
|
|
# ============================================================================
|
|
[tool.ruff]
|
|
target-version = "py314"
|
|
include = ["*.py", "**/*.py"]
|
|
# scripts/file_length is a VENDORED copy of ~/utils/file_length and must stay
|
|
# byte-identical to upstream; ruff --unsafe-fixes otherwise strips its print()
|
|
# calls (T201) and silently guts the gate's reporting.
|
|
exclude = [".git", ".venv", "__pycache__", "build", "dist", ".eggs", "scripts/file_length"]
|
|
|
|
[tool.ruff.lint]
|
|
select = ["ALL"]
|
|
ignore = [
|
|
"D203", # conflicts with D211
|
|
"D213", # conflicts with D212
|
|
"COM812", # formatter handles this
|
|
"ISC001", # formatter may create these
|
|
"S603", # prone to false positives
|
|
# Conflicts with SIM117. A `pytest.raises` that needs patches in scope has
|
|
# no legal form: combine them into one `with` and PT012 objects to the
|
|
# extra context managers; nest `raises` inside and SIM117 objects to the
|
|
# nested `with`. SIM117's combined form is the style used across this repo,
|
|
# so PT012 is the one that loses.
|
|
"PT012",
|
|
]
|
|
fixable = ["ALL"]
|
|
unfixable = []
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"**/tests/**/*.py" = ["ARG", "D", "PLC0415", "PLR2004", "S101", "SLF001"]
|
|
"**/test_*.py" = ["ARG", "D", "PLC0415", "PLR2004", "S101", "SLF001"]
|
|
|
|
[tool.ruff.lint.pydocstyle]
|
|
convention = "google"
|
|
|
|
[tool.ruff.lint.isort]
|
|
force-single-line = false
|
|
force-sort-within-sections = true
|
|
known-first-party = ["steam_backlog_enforcer"]
|
|
|
|
[tool.ruff.lint.flake8-quotes]
|
|
docstring-quotes = "double"
|
|
inline-quotes = "double"
|
|
|
|
[tool.ruff.lint.flake8-tidy-imports]
|
|
ban-relative-imports = "all"
|
|
|
|
[tool.ruff.format]
|
|
quote-style = "double"
|
|
indent-style = "space"
|
|
skip-magic-trailing-comma = false
|
|
line-ending = "auto"
|
|
docstring-code-format = true
|
|
|
|
# ============================================================================
|
|
# MYPY - Static type checker
|
|
# ============================================================================
|
|
[tool.mypy]
|
|
python_version = "3.14"
|
|
strict = true
|
|
warn_return_any = true
|
|
warn_unused_configs = true
|
|
disallow_untyped_defs = true
|
|
disallow_incomplete_defs = true
|
|
check_untyped_defs = true
|
|
disallow_untyped_decorators = true
|
|
no_implicit_optional = true
|
|
warn_redundant_casts = true
|
|
warn_unused_ignores = true
|
|
warn_no_return = true
|
|
warn_unreachable = true
|
|
disallow_any_unimported = true
|
|
disallow_any_explicit = false
|
|
disallow_any_generics = true
|
|
disallow_subclassing_any = true
|
|
strict_equality = true
|
|
extra_checks = true
|
|
ignore_missing_imports = true
|
|
show_error_codes = true
|
|
color_output = true
|
|
exclude = [".venv/"]
|
|
|
|
# ============================================================================
|
|
# PYLINT
|
|
# ============================================================================
|
|
[tool.pylint.main]
|
|
analyse-fallback-blocks = true
|
|
persistent = true
|
|
jobs = 0
|
|
py-version = "3.14"
|
|
ignore = [".venv", "__pycache__"]
|
|
ignore-patterns = [".*\\.pyi$"]
|
|
|
|
[tool.pylint.messages_control]
|
|
enable = "all"
|
|
disable = []
|
|
|
|
[tool.pylint.design]
|
|
min-public-methods = 0
|
|
max-module-lines = 250
|
|
max-attributes = 10
|
|
|
|
[tool.pylint.typecheck]
|
|
generated-members = [
|
|
".*\\.assert_called_once_with",
|
|
".*\\.assert_called_once",
|
|
".*\\.assert_called",
|
|
".*\\.assert_not_called",
|
|
".*\\.assert_any_call",
|
|
".*\\.call_args",
|
|
".*\\.call_args_list",
|
|
".*\\.call_count",
|
|
]
|
|
|
|
# ============================================================================
|
|
# BANDIT - Security linter
|
|
# ============================================================================
|
|
[tool.bandit]
|
|
exclude_dirs = ["tests", ".venv"]
|
|
|
|
# ============================================================================
|
|
# PYTEST
|
|
# ============================================================================
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["steam_backlog_enforcer/tests"]
|
|
python_files = ["test_*.py", "*_test.py"]
|
|
python_classes = ["Test*"]
|
|
python_functions = ["test_*"]
|
|
addopts = [
|
|
"-v",
|
|
"--strict-markers",
|
|
"--strict-config",
|
|
"-ra",
|
|
"--cov=steam_backlog_enforcer",
|
|
"--cov-branch",
|
|
"--cov-report=term-missing",
|
|
"--cov-report=lcov",
|
|
]
|
|
filterwarnings = [
|
|
"error",
|
|
"ignore::DeprecationWarning",
|
|
"default::pytest.PytestUnraisableExceptionWarning",
|
|
]
|
|
|
|
# ============================================================================
|
|
# COVERAGE
|
|
# ============================================================================
|
|
[tool.coverage.run]
|
|
source = ["steam_backlog_enforcer"]
|
|
branch = true
|
|
omit = ["*/__pycache__/*", "*/tests/*", "*/.venv/*"]
|
|
|
|
[tool.coverage.report]
|
|
fail_under = 100
|
|
show_missing = true
|
|
skip_covered = false
|
|
exclude_lines = [
|
|
"pragma: no cover",
|
|
"raise NotImplementedError",
|
|
"raise AssertionError",
|
|
"if TYPE_CHECKING:",
|
|
'if __name__ == "__main__":',
|
|
]
|
|
partial_branches = ["pragma: no branch"]
|