mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 13:07:49 +01:00
The last unit over the cap is now github.com/kuhyx/digital-wellbeing, cloned at ~/digital-wellbeing, and `--warn` is gone from the directory-depth-cap hook. `check_directory_depth.sh --all` exits 0 with zero violations, down from 46 at the start of the session. That was the point of the campaign. History was filtered across all three paths the unit has lived at (63 commits); the current path alone holds 2. The six suites that test this code lived in linux_configuration/tests/ rather than beside it, so they were filtered separately (24 commits) and merged in as a second root. A path filter of the unit alone would have left them here, testing code that had left the repo -- and their names would still have been in shell-tests.yml's hardcoded arrays, failing CI on missing files. linux_configuration/lib/common.sh is vendored into vendor/ rather than trimmed to the four functions actually used: set_actual_user_vars calls two of the others, so a trim breaks silently. It is in vendor/ and not lib/ because test_music_parallelism.sh copies the unit's own lib/ into a scratch worktree while stubbing the shared lib; in one directory the real common.sh overwrote the stub, and the suite silently measured the wrong library. Running the callers caught what shellcheck and grep both missed: check_and_enable_services.sh died with "DW_REPO: unbound variable" because the definition sat below its first use. It now runs clean and reports ok for every extracted-unit check. Two long-standing live defects fixed as a consequence. The pacman-wrapper drift manifest embeds ABSOLUTE paths and had named deleted scripts/ paths since the flatten, so the check reported a permanent false "stale or tampered"; it now verifies, with unchanged hashes proving the extracted bytes are identical. And the last two stale live references are gone: 8 -> 0. The two pre-commit excludes for setup_midnight_shutdown.sh are dropped rather than moved. lsattr shows nothing in the unit is chattr +i any more, and the fixer hooks leave the file alone when allowed to run. Removing 80 shell files also shrank the jscpd denominator enough that the virtualenv/tqdm clones inside the GENERATED .ci-mirror-venv/ tipped the repo to 2.09% and blocked this commit, which adds no duplication. That directory is the same vendored third-party shell the ignore list already excludes as .venv/, just under another name; jscpd walks the working tree rather than the index, so being untracked did not spare it. Excluded by name: 1.74%. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
390 lines
14 KiB
TOML
390 lines
14 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/misc/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', 'linux_configuration/utils', 'linux_configuration/misc/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
|