testsAndMisc/meta/pyproject.toml
Krzysztof kuhy Rudnicki a1f8044a4a Extract system-maintenance, the isolated subsystem
github.com/kuhyx/system-maintenance, cloned to ~/system-maintenance

First of the four live subsystems, and the one with no cross-unit source
edges, so it establishes the pattern for the harder three. Passed in
vmbox first -- the installer ran from an extracted-layout copy in the
guest and detected its distro without any monorepo path -- then on the
host.

Its 21 Python and shell test files lived in linux_configuration/tests/,
reaching bin/ through a shared conftest. Twenty moved with the code and
got their own conftest; the shared one stays, since it serves several
script directories that are not going anywhere.

test_shutdown_timer_monitor.sh stays here. It is a pair test: it checks
that digital_wellbeing's installer template and the monitor it installs
still agree, so it belongs to whichever half owns the installer. It now
reads the monitor from ~/system-maintenance and SKIPs when that checkout
is absent.

An intermediate version of that test fell back to /usr/local/bin, which
HUNG -- the installed copy is stale and differs from source, so the test
was comparing an installer template against a binary it never generated.
Falling back to it would have been worse than skipping, so the fallback
is gone and the comment says why.

Also removed test_hosts_file_monitor.sh from shell-tests.yml's explicit
list: it left with the subsystem, and CI would have failed on the missing
file rather than quietly skipping it.

Verified: 136 tests pass in the new repo, 202 still pass here, the pair
test passes against the checkout and skips without it, and both the shell
coverage gate and shellcheck are clean. Depth 294 -> 267.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
2026-08-23 17:55:29 +02:00

390 lines
15 KiB
TOML

[project]
name = "testsandmisc"
version = "0.1.0"
description = "Collection of miscellaneous tests and scripts"
requires-python = ">=3.10"
# ============================================================================
# RUFF - Extremely fast Python linter and formatter (written in Rust)
# ============================================================================
[tool.ruff]
target-version = "py310"
# Include all Python files
include = ["*.py", "**/*.py"]
# Exclude vendored/build directories
exclude = [
".git",
".venv",
"__pycache__",
"build",
"dist",
".eggs",
"Bash/ffmpeg-build", # Vendored FFmpeg tools
]
[tool.ruff.lint]
# AGGRESSIVE: Select ALL rules from all categories
select = ["ALL"]
# Ignores for rules that are too strict for this mixed script repository
ignore = [
# D203 vs D211 conflict - we use D211 (no blank line before class docstring)
"D203", # 1 blank line required before class docstring (conflicts with D211)
# D212 vs D213 conflict - we use D212 (summary on first line after """)
"D213", # Multi-line docstring summary should start at second line (conflicts with D212)
# Formatter conflicts - recommended to disable when using ruff format
# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
"COM812", # Trailing comma missing - formatter handles this automatically
"ISC001", # Implicit string concatenation - formatter may create these when wrapping
# Security audit - prone to false positives with validated input
# https://github.com/astral-sh/ruff/issues/4045
"S603", # subprocess call without shell - prone to false positives as it is
# difficult to determine whether the passed arguments have been validated
]
# Allow ALL rules to be auto-fixed
fixable = ["ALL"]
unfixable = []
# Per-file ignores — only rules that FUNDAMENTALLY conflict with test code remain.
# Every other rule was fixed in source. See justifications below.
[tool.ruff.lint.per-file-ignores]
"**/code_tutor/cli.py" = [
"B008", # typer.Argument(...) as a parameter default is the standard Typer
# dependency-injection pattern, not a mutable-default bug.
]
"**/tests/**/*.py" = [
"ARG", # @patch decorators inject mock params that aren't always referenced;
# the patch side-effect is needed, not the mock object itself.
"D", # Test names like test_sub_cards_no_answer_text are self-documenting;
# docstrings would be redundant noise on every test method.
"PLC0415", # Test isolation requires importing AFTER mocking sys.modules;
# top-level imports would bypass the mocks entirely.
"PLR2004", # assert count == 5 is clearer than assert count == EXPECTED_COUNT;
# named constants for test expectations add indirection without value.
"S101", # assert IS what tests do — every Python test suite suppresses this.
"SLF001", # Unit tests must exercise private internals (_method, _attr) to reach
# 100% branch coverage; only integration tests can avoid this.
"TC002", # pytest resolves fixture parameter annotations at RUNTIME, so moving
"TC003", # pytest/Path into a TYPE_CHECKING block breaks collection. Verified
# empirically: guarding them failed 11 tests with UnboundLocalError
# and NameError, not a hypothetical concern.
]
"**/test_*.py" = [
"ARG",
"D",
"PLC0415",
"PLR2004",
"S101",
"SLF001",
]
[tool.ruff.lint.pylint]
max-args = 6 # code_tutor challenge flows genuinely need 6 params
max-returns = 8 # challenge state machines have multiple exit paths
[tool.ruff.lint.pydocstyle]
convention = "google" # Use Google docstring convention
[tool.ruff.lint.isort]
force-single-line = false
force-sort-within-sections = true
known-first-party = ["python_pkg"]
[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 (most aggressive settings)
# ============================================================================
[tool.mypy]
python_version = "3.10"
# Strict mode enables most checks
strict = true
# Additional aggressive settings
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
# Extra strict settings
disallow_any_unimported = true
disallow_any_explicit = false # Too aggressive for practical use
disallow_any_generics = true
disallow_subclassing_any = true
strict_equality = true
extra_checks = true
# Allow missing imports for third-party packages
ignore_missing_imports = true
# Show error codes
show_error_codes = true
# Enable colored output
color_output = true
# Exclude vendored directories
exclude = [
"Bash/ffmpeg-build/",
".venv/",
"linux_configuration/scripts/single_use/misc/testsAndMisc-bash/tools/", # Avoid duplicate module named 'tools'
]
# Standalone tool scripts outside python_pkg/ are not held to strict mypy rules
[[tool.mypy.overrides]]
module = "linux_configuration.*"
ignore_errors = true
# External libraries without bundled stubs - suppress import-untyped errors
[[tool.mypy.overrides]]
module = ["requests.*", "rich.*", "anthropic.*", "typer.*"]
ignore_missing_imports = true
# code_tutor uses TYPE_CHECKING-only imports for rich/typer; types propagate as
# Any at call sites because those libs lack complete stubs in this env.
# untyped-decorator fires for @app.command() which is a Typer pattern.
[[tool.mypy.overrides]]
module = "python_pkg.code_tutor.*"
disallow_any_unimported = false
disallow_untyped_decorators = false
# ============================================================================
# PYLINT - Comprehensive Python linter
# ============================================================================
[tool.pylint.main]
# Make standalone script dirs importable so tests that import their modules by
# bare name (the same dirs linux_configuration/tests/conftest.py adds to
# sys.path at runtime) resolve under static analysis instead of raising E0401.
# Paths are relative to the repo root, which is pre-commit's working directory.
init-hook = "import sys; sys.path[:0] = ['meta/scripts', 'python_pkg', 'phone_focus_mode', 'phone_focus_mode/lib', 'linux_configuration/scripts/single_use/utils', 'linux_configuration/scripts/periodic_background/digital_wellbeing', 'linux_configuration/scripts/single_use/misc/testsAndMisc-bash/tools']"
# Analyse import fallback blocks
analyse-fallback-blocks = true
# Pickle collected data for later comparisons
persistent = true
# Jobs to use for parallel execution (0 = auto)
jobs = 0
# Minimum Python version
py-version = "3.10"
# Ignore vendored directories. "tests" and "conftest.py" are basename
# matches: test suites and pytest fixtures intentionally use patterns
# (protected-access, missing docstrings, fixture-arg shadowing) that don't
# apply to source code, so they are linted separately (not by this hook).
ignore = ["Bash", ".venv", "__pycache__", "tests", "conftest.py"]
# Ignore patterns
ignore-patterns = [".*\\.pyi$"]
# Allow C extension modules to be introspected
extension-pkg-allow-list = ["cv2", "pygame", "lxml"]
[tool.pylint.messages_control]
# Enable all checks by disabling disable
enable = "all"
# Globally disabled checks. Each is either a stylistic preference that conflicts
# with deliberate, clearer code, or a structural false positive that cannot be
# rewritten without harming readability. Everything else stays at max strictness.
disable = [
# use-implicit-booleaness family (C1803/C1804/C1805): pylint wants
# `not x` / `not s` instead of `x == 0` / `s == ""`. Explicit comparisons
# against 0 and "" state numeric/string intent more clearly than truthiness
# (and are not equivalent when the value may be None), so we keep them.
"use-implicit-booleaness-not-comparison",
"use-implicit-booleaness-not-comparison-to-string",
"use-implicit-booleaness-not-comparison-to-zero",
# consider-using-with (R1732): several subprocess.Popen calls are
# intentionally fire-and-forget — the process must outlive the calling
# scope and is polled/killed later, so a `with` block is wrong here.
"consider-using-with",
# unnecessary-ellipsis (W2301): Protocol abstract method bodies use `...`
# by Python convention (PEP 544); `pass` is equivalent but less idiomatic.
"unnecessary-ellipsis",
]
[tool.pylint.design]
# Mixins and single-entry-point classes may have zero public methods
min-public-methods = 0
# Test modules can be large
max-module-lines = 1000
# UI/mixin classes accumulate attributes across multiple mixins
max-attributes = 12
# code_tutor challenge flows genuinely need 6 params (matches ruff max-args)
max-args = 6
max-positional-arguments = 6
# Complex lesson/challenge functions need up to 20 locals
max-locals = 20
[tool.pylint.basic]
# Allow standard snake_case methods AND AST NodeVisitor visit_CapWords methods
method-rgx = "[a-z_][a-z0-9_]{2,}$|visit_[A-Za-z][A-Za-z0-9_]*"
[tool.pylint.similarities]
# 6 lines: CodeItem and ItemData share id/file/type/name/start_line/end_line fields
# intentionally (mirror the same domain schema). Raise the threshold so pylint
# doesn't flag this intentional structural duplication.
min-similarity-lines = 10
[tool.pylint.spelling]
# No spelling dictionary to avoid false positives
spelling-dict = ""
[tool.pylint.typecheck]
# cv2 (OpenCV) dynamically loads members from C extension at runtime.
# unittest.mock.MagicMock generates assertion/introspection methods at runtime.
# wave.open(path, "wb") returns a Wave_write, but pylint's stdlib stub infers the
# read-mode Wave_read overload and wrongly reports its setter/writer methods as
# missing — list them so the write API is recognised.
generated-members = [
"cv2.*",
".*\\.assert_called_once_with",
".*\\.assert_called_once",
".*\\.assert_called",
".*\\.assert_not_called",
".*\\.assert_any_call",
".*\\.call_args",
".*\\.call_args_list",
".*\\.call_count",
".*\\.setnchannels",
".*\\.setsampwidth",
".*\\.setframerate",
".*\\.writeframes",
".*\\.writeframesraw",
]
# ============================================================================
# BANDIT - Security linter
# ============================================================================
[tool.bandit]
# Exclude test directories and vendored code
exclude_dirs = ["tests", ".venv", "Bash/ffmpeg-build"]
# ============================================================================
# BLACK & ISORT - Removed (ruff handles formatting and import sorting)
# ============================================================================
# ============================================================================
# PYTEST - Testing framework configuration
# ============================================================================
[tool.pytest.ini_options]
# linux_configuration/tests covers the standalone usage_report scripts. It adds
# no python_pkg coverage (those tests don't import python_pkg), so running it
# alongside leaves the 100%-on-python_pkg gate untouched.
testpaths = ["python_pkg", "linux_configuration/tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-v",
"--strict-markers",
"--strict-config",
"-ra",
"--cov=python_pkg",
"--cov-branch",
"--cov-report=term-missing",
"--cov-report=lcov",
]
filterwarnings = [
"error",
"ignore::DeprecationWarning",
"default::pytest.PytestUnraisableExceptionWarning",
]
# ============================================================================
# COVERAGE - Code coverage configuration
# ============================================================================
[tool.coverage.run]
source = ["python_pkg"]
branch = true
omit = [
"*/__pycache__/*",
"*/tests/*",
"*/.venv/*",
]
[tool.coverage.report]
# Fail under this percentage
fail_under = 100
show_missing = true
skip_covered = false
exclude_lines = [
# Standard exclusions
"pragma: no cover",
# Unreachable defensive code
"raise NotImplementedError",
"raise AssertionError",
# Type checking imports
"if TYPE_CHECKING:",
# Main script entry point
'if __name__ == "__main__":',
]
# Partial branch exclusions for unreachable branches
partial_branches = [
"pragma: no branch",
]
# ============================================================================
# VULTURE - Dead code detection
# ============================================================================
# Note: Vulture uses command-line args, but we can document settings here
# vulture --min-confidence 80 --exclude ".venv,Bash" .
# ============================================================================
# FLAKE8 - Python linter (via Flake8-pyproject for pyproject.toml support)
# ============================================================================
[tool.flake8]
# Maximum line length (matches ruff/black)
max-line-length = 88
# Maximum McCabe complexity (matches ruff C901 threshold)
max-complexity = 10
# Maximum cognitive complexity (flake8-cognitive-complexity)
max-cognitive-complexity = 12
# Maximum function length (flake8-functions)
max-function-length = 20
# Maximum returns/arguments per function
max-returns-amount = 6
max-arguments = 5
# Docstring convention (matches ruff)
docstring-convention = "google"
# Select all error codes
select = ["E", "F", "W", "C", "B", "B950"]
# Extend with plugin codes
extend-select = ["B", "B9", "C4", "SIM", "PT", "TC", "ANN"]
# Ignore rules that conflict with ruff-format or are duplicated
extend-ignore = [
"E501", # Line too long - B950 from bugbear is smarter (allows 10% overflow)
"W503", # Line break before binary operator - contradicts PEP 8 update
"ANN101", # Missing type annotation for self
"ANN102", # Missing type annotation for cls
]
# Exclude directories
exclude = [
".git",
".venv",
"__pycache__",
"build",
"dist",
".eggs",
"Bash/ffmpeg-build",
]
# Per-file ignores
per-file-ignores = [
"**/tests/**/*.py:S101,ANN",
"**/test_*.py:S101,ANN",
]
# ============================================================================
# PYDOCSTYLE - Docstring style checker (ruff handles this, but for standalone)
# ============================================================================
# Configured in ruff.lint.pydocstyle above