* chore(lint): add ruff rules for logging, performance, exceptions, and print detection
Add wave 2 lint rules: G, PERF, RET, TRY, T20, C4, ERA. All existing
violations are suppressed via ignore/per-file-ignores so this config
change is merge-safe. Follow-up PRs will fix violations and remove the
ignore entries incrementally.
* fix(lint): exempt pre-commit hooks from T201 print rule (#3270)
Pre-commit hooks are CLI scripts where print is the intended output
interface, same as scripts/ and cli/ directories already exempted.
* fix(lint): fix all low-count ruff violations instead of suppressing them (#3275)
* fix(lint): replace manual dict-building loops with dict comprehensions (PERF403)
* fix(lint): replace bare Exception raises with specific built-in types (TRY002)
Replace all `raise Exception(...)` in production code with appropriate
built-in exception types: RuntimeError for operational/state failures,
ValueError for invalid data, and ConnectionError for HTTP errors.
* fix(lint): resolve TRY004 and PERF402 ruff violations
Use TypeError instead of ValueError for isinstance/issubclass type
checks (TRY004), and replace manual for-loop list copies with
list.extend() (PERF402).
* fix(lint): fix all low-count ruff violations instead of suppressing them
Fix all violations for 15 ruff rules that had ≤10 occurrences each,
rather than suppressing them with ignore directives:
- TRY002: raise-vanilla-class → use specific built-in exceptions
- TRY004: type-check-without-type-error → use TypeError
- C408: unnecessary-collection-call → use dict/list literals
- C401: unnecessary-generator-set → use set comprehensions
- C416: unnecessary-comprehension → use list()/set()
- C414: unnecessary-double-cast-or-process → simplify
- PERF403: manual-dict-comprehension → use dict comprehensions
- PERF102: incorrect-dict-iterator → use .values()/.keys()
- PERF402: manual-list-copy → use list.extend()
- RET503/RET506/RET507/RET508: superfluous else after return/raise/continue/break
- RET501/RET502: unnecessary/implicit return None
Adds per-file-ignores for tests/ and examples/ where these patterns
are acceptable (e.g. bare Exception in tests, dict() calls in fixtures).
* fix(lint): enforce E722, ERA001, RET505 and fix pre-commit RET503 gap (#3276)
Remove three rules from the global ignore list by fixing all violations:
E722 (bare except) — 6 violations in tests:
Replace `except:` with `except Exception:` to avoid swallowing
KeyboardInterrupt and SystemExit.
ERA001 (commented-out code) — 25 violations:
Delete 18 true positives (dead variables, disabled debug logs,
commented-out imports). Add `# noqa: ERA001` to 7 false positives
(template instructions, type annotations, documentation comments).
RET505 (superfluous else after return) — 413 violations:
Auto-fix all occurrences. Also fixes 5 cascading RET506/RET507
violations exposed by the RET505 removals.
Pre-commit hooks gap:
Add RET503 to `.pre-commit-hooks/**` per-file-ignores alongside T201.
* fix(lint): enforce RET504 and TRY301 — fix all violations (#3279)
* fix(lint): enforce RET504 — collapse unnecessary assign-before-return
Auto-fix all 46 RET504 violations via ruff unsafe-fixes: collapse
`result = expr; return result` into `return expr`.
Remove RET504 from global ignore list. Add to tests/examples
per-file-ignores where intermediate variables aid test clarity.
Also removes TRY301 from global ignore (violations fixed in next commit).
* fix(lint): enforce TRY301 — fix raises inside broad try/except blocks
Structural fixes for 65 TRY301 violations:
Security-critical fixes:
- url_validator.py: move 6 validation raises before try block,
replace isinstance-based re-raise with specific except clause
- path_validator.py: move validation outside try block
- env_settings.py: separate parsing (try) from validation (outside)
Route/service fixes:
- research_routes.py: replace raise-then-catch with direct error return
- mcp/server.py: move all 7 tool validations before try blocks
- news/api.py: move validation before try, noqa for db-session raises
- notifications: move rate limit and URL validation before try blocks
- iterative_refinement_strategy.py: move JSON validation after try
Added noqa for intentional patterns: re-raise in except handlers,
nested function definitions, db-session-dependent checks, rate limit
re-raises for base class retry logic.
* merge: resolve conflicts between wave2 lint branch and main
Resolve 14 merge conflicts by always starting from main's version
and re-applying lint fixes on top:
- mcp_strategy.py, ollama.py, security_settings.py, delete_routes.py:
Take main's code, re-apply RET505 (remove else: after return)
- mcp/server.py (3 conflicts): Take main's ValidationError handlers
and set_settings_context, re-apply TRY301 fixes, fix sensitive
data logging
- research_routes.py: Take main, fix duplicate block (merge artifact)
- settings_routes.py: Take main's default-settings fallback feature
- meta_search_engine.py, parallel_search_engine.py: Take main's
get_available_engines delegation, delete unreachable code
- search_engine_ddg.py, search_engine_google_pse.py: Take main's
sanitization, re-apply RET506 (if not elif after raise)
- rag_routes.py: Accept main's deletion (route moved to delete_routes)
- encryption_check.py: Accept main's deletion (dead code)
- test_storage_coverage.py: Remove broken test classes referencing
undefined stubs
- pre-commit hooks: extend per-file-ignores for ERA001, RET504
* fix: revert ValueError→TypeError changes that break tests and API contracts
Revert TRY004 fixes in 3 files where changing ValueError to TypeError
would break existing tests and HTTP status code contracts:
- card_factory.py: 5 tests assert pytest.raises(ValueError)
- base_rater.py: flask_api.py catches ValueError for HTTP 400 responses;
TypeError would fall through to HTTP 500
- full_search.py: test asserts pytest.raises(ValueError)
Add # noqa: TRY004 to suppress the lint rule on these lines.
* fix: move benchmark_data check back inside try block
The ValueError for missing benchmark_data must be inside the try/except
so the except handler can mark the run as FAILED in the database.
Without this, the exception propagates unhandled in a daemon thread,
leaving the benchmark run stuck in RUNNING state permanently.
* chore(lint): remove ERA rule and suppress TRY004 globally
Remove ERA (eradicate — commented-out code detection) from ruff select:
- 28% false positive rate in our codebase (7 of 25 violations)
- No major Python project enables it (Django, FastAPI, Pydantic, Airflow)
- Ruff itself doesn't use it; autofix was demoted to manual-only
- 172 noqa suppressions provided zero enforcement value
Suppress TRY004 (type-check-without-type-error) globally:
- Ruff maintainer agreed the autofix "can change functionality"
- We already had to revert 3 TypeError changes that broke tests
and HTTP 400→500 API contracts
- Django, Flask, pandas all use isinstance + ValueError routinely
- Pylint has no equivalent rule; near-zero PyPI adoption
Remove all 173 # noqa: ERA001 and 49 # noqa: TRY004 comments
from the codebase — no longer needed with rules disabled/suppressed.
* fix: resolve mypy errors, failing MCP test, and TRY301 noqa
- search_engine_factory.py: restore typed intermediate variable to fix
mypy no-any-return (RET504 collapse lost the type annotation)
- search_engine_pubchem.py: add explicit list[str] type annotation
- test_edge_cases.py: fix assertion that expected engine name in
sanitized error message
- mcp/server.py: add noqa: TRY301 to validation raises inside try
blocks (from main's new merge code)
* feat: extend resource leak hook to detect database session leaks
The pre-commit hook now detects unsafe usage of get_auth_db_session()
and suggests using the auth_db_session() context manager instead. This
prevents database session leaks when exceptions occur.
Changes:
- Add FUNCTIONS_REQUIRING_CONTEXT to detect function calls that return
resources needing cleanup
- Fix nested try/finally detection for close() calls
- Update user_exists() in encrypted_db.py to use context manager
- Update example files to use auth_db_session() context manager
* fix: prevent session use after close and add search engine cleanup
- Move config dict creation inside with block in api_routes.py to prevent
using SettingsManager after database session is closed (was causing errors)
- Remove redundant session.close() call that was after context manager exit
- Add close() method and context manager support to BaseSearchEngine so
search engines with HTTP sessions can be properly cleaned up
Implement dynamic cookie security that allows localhost HTTP connections
to work out of the box while maintaining security for production:
- Add WSGI middleware (SecureCookieMiddleware) for dynamic Secure flag
- Localhost HTTP (127.0.0.1, ::1): No Secure flag (local traffic is safe)
- Proxied requests (X-Forwarded-For): Always add Secure flag (production)
- Non-localhost HTTP: Add Secure flag (requires HTTPS by design)
- TESTING mode: Never add Secure flag (for CI/development)
Security: Prevents X-Forwarded-For spoofing by checking for header
presence rather than value - any proxy header triggers Secure flag.
Also includes:
- Update HTTP examples with clear "LOCALHOST ONLY" documentation
- Add helpful CSRF error message explaining the security model
- Add comprehensive cookie security tests (9 tests)
- Add cookie security tests to CI workflow
- Remove curl_examples.sh as authentication is too complex for simple curl commands
- Move complex examples to advanced/ subfolder for better organization
- Keep simple_working_example.py prominent as the recommended starting point
- Add comprehensive CI test for HTTP examples
- Update documentation to highlight the working example and learning path
- Improve user experience by focusing on Python examples with automatic auth