Ctrl K
ComunidadGuías…

pyproject.toml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
testpaths = ["tests", "management/tests"]
# management/tests/ has __init__.py but management/ itself doesn't (implicit
# namespace package) — pytest's default rootdir walk-up stops at management/
# instead of reaching the repo root, breaking `from management.api import ...`
# under bare `pytest` (phase-808). pythonpath sidesteps that without touching
# application packaging.
pythonpath = ["."]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]

# --tb=short: concise tracebacks
# --timeout=60: per-test hard limit (requires pytest-timeout)
# -q: quiet mode by default; run-local-tests.sh adds -v when writing to log
addopts = "--tb=short --timeout=60"

markers = [
    "live_services: requires live Go/Python proxy and Redis; excluded from make test (use make test-live)",
    "integration: requires live external IdP (Okta, Entra ID, Keycloak); set OKTA_METADATA_URL or ENTRA_OIDC_DISCOVERY_URL",
    "unit: marks a unit test",
    "chaos: chaos/resilience test",
    "adversarial: adversarial/fuzz test",
]

# Warning policy:
#   - Third-party library internals: ignore (too noisy, nothing we can fix)
#   - asyncio cleanup noise from mocks: ignore specific patterns
#   - Everything else: default behaviour (shown but not fatal)
#   - Our own code: any unexpected RuntimeWarning will surface naturally
filterwarnings = [
    # pytest-asyncio internal deprecation notices
    "ignore::DeprecationWarning:pytest_asyncio",
    "ignore::DeprecationWarning:_pytest",
    # Third-party library internals — nothing actionable for us
    "ignore::DeprecationWarning:pkg_resources",
    "ignore::DeprecationWarning:google",
    "ignore::DeprecationWarning:aiohttp",
    "ignore::DeprecationWarning:redis",
    # urllib3 / requests noise in test environments
    "ignore::urllib3.exceptions.InsecureRequestWarning",
    "ignore::urllib3.exceptions.SecurityWarning",
    # asyncio teardown with mocked tasks — not a real resource leak
    "ignore:.*Enable tracemalloc.*:pytest.PytestUnraisableExceptionWarning",
    "ignore:coroutine .* was never awaited:RuntimeWarning",
    # aiohttp connector cleanup in unit tests (no real connections, just mocks)
    "ignore:.*Unclosed client session.*:ResourceWarning",
    "ignore:.*Unclosed connector.*:ResourceWarning",
    # ssl.OP_NO_TLS* deprecated in Python 3.10+ (security/validation.py — third-party pattern)
    "ignore:ssl.OP_NO_SSL.*:DeprecationWarning",
    "ignore:ssl.OP_NO_TLS.*:DeprecationWarning",
    # pycares async DNS callbacks arrive after asyncio loop closes during test teardown.
    # This is a known interaction between pycares, Python 3.10 asyncio, and pytest-asyncio.
    # Nothing actionable in our code — pycares teardown is outside our control.
    # Approved Exception #001: see docs/security/EXCEPTIONS.md
    "ignore::pytest.PytestUnraisableExceptionWarning",
    # Google API Python 3.10 end-of-support notice — not actionable until we upgrade
    "ignore::FutureWarning:google.api_core",
    # Strict warning enforcement on first-party management and src modules
    "error::DeprecationWarning:management.*",
    "error::DeprecationWarning:src.*",
    # Upstream python3-saml (onelogin.saml2) datetime & port deprecations (Python 3.14 upstream debt)
    "ignore:datetime.datetime.utcnow\\(\\) is deprecated:DeprecationWarning:onelogin.saml2.*",
    "ignore:datetime.datetime.utcfromtimestamp\\(\\) is deprecated:DeprecationWarning:onelogin.saml2.*",
    "ignore:The server_port key in request data is deprecated:DeprecationWarning:onelogin.saml2.*",
    # Upstream Authlib / Jose deprecation warnings (scheduled for Authlib 2.0.0)
    "ignore::authlib.deprecate.AuthlibDeprecationWarning",
    # Upstream Starlette / FastAPI testclient httpx warning
    "ignore::starlette.exceptions.StarletteDeprecationWarning",
    # HTTPX per-request cookie deprecation in integration test clients
    "ignore:Setting per-request cookies=.* is being deprecated:DeprecationWarning:httpx.*",
    # Intentional short HMAC key in test_protected_route_wrong_secret negative test
    "ignore:The HMAC key is 12 bytes long:UserWarning:jwt.api_jwt",
]

# ── mypy configuration (consolidated from mypy.ini) ──────────────────────────
[tool.mypy]
exclude = ["^archive/", "^vendor/", "^node_modules/"]
# Phase 16f baseline — --ignore-missing-imports --no-strict-optional
# Goal: zero errors in new code; suppress known legacy patterns.
# Phase 16g will tighten to --strict once Redis typing is resolved.
python_version = "3.15"
ignore_missing_imports = true
no_strict_optional = true
warn_return_any = false
warn_unused_ignores = false
# Suppress "Library stubs not installed" (import-untyped) raised by mypy ≥1.19
# when third-party stub packages (e.g. types-PyYAML) are absent in CI Docker.
disable_error_code = "import-untyped"

[[tool.mypy.overrides]]
module = [
    "proxy",
    "src.security.validation",
    "src.analytics.detection",
    "src.analytics.stream_consumer",
    "src.security.rdap_enrichment",
    "src.security.tcp_analyzer",
    "src.security.abuseipdb",
    "src.security.beaconing_detector",
    "src.security.sni_analyzer",
    "src.security.tls_enforcer",
    "src.security.dns_enrichment",
    "src.security.pipeline",
    "src.security.asn_classifier",
    "src.security.gdpr_storage",
    "src.cache.bloom",
    "src.analytics.monitoring",
    "src.analytics.shadow_scoring",
    "src.analytics.baseline_monitor",
    "src.pubsub",
    "src.analytics.security_hardening",
    "src.analytics.ml_detector",
]
ignore_errors = true

# ── flake8 configuration (consolidated from .flake8) ─────────────────────────
# Note: flake8 does not natively read pyproject.toml; requires Flake8-pyproject
# or use ruff instead. Kept here for reference alongside ruff config.
[tool.flake8]
max-line-length = 88
extend-ignore = ["E203", "W503", "E501", "E402", "F841"]

[tool.ruff]
exclude = ["archive", "vendor", "node_modules", "deploy/secrets"]
# Phase 16f baseline — check src/ and proxy.py; tests are advisory only.
target-version = "py313"
line-length = 120

[tool.ruff.lint]
# Enable: pyflakes (F), pycodestyle errors (E), isort (I)
# Skip: W (warnings — cosmetic), C (complexity), ANN (annotations — covered by mypy)
select = ["E", "F", "I"]
ignore = [
    "E501",   # line-too-long — handled by black
    "E402",   # module-level import not at top — some files have conditional imports
    "F401",   # unused import — too noisy in tests; addressed separately in src/
]

[tool.ruff.lint.per-file-ignores]
# Tests: allow unused imports (often imported for re-export or side effects)
"tests/**" = ["F401", "F811", "F841", "E711", "E712", "E721", "E741"]
# Admin CLI: allow broad exception handling
"scripts/ja4proxy_admin.py" = ["E722"]

# ── pylint configuration ─────────────────────────────────────────────────────
# `make lint-pylint` runs errors-only over src/ and management/. Two classes of
# false positive were drowning out real findings; both are silenced here rather
# than at each call site so the reason is recorded once.
[tool.pylint.main]
# `management/` is a PEP 420 namespace package (no __init__.py) while
# `management/api/` has one, so pylint sometimes anchors on `api` as the top
# level and then reports `from ..tasks.dial_revert` as an import beyond it —
# intermittently, depending on walk order. At runtime the module is always
# imported as `management.api.main` and the import is fine. Pinning the source
# root makes pylint resolve packages the same way the application does.
source-roots = ["."]

[tool.pylint.typecheck]
# weasyprint is an OPTIONAL dependency — it is deliberately absent from
# requirements.txt because it needs system libraries (pango, cairo, fontconfig)
# that the lint and CI images do not carry. Every import of it in this repo sits
# inside a try/except that degrades to HTML output, so `import-error` here
# reports a supported configuration as a failure. Removing this entry is only
# correct if weasyprint becomes a hard dependency.
ignored-modules = ["weasyprint"]