[tool.black] line-length = 120 [tool.isort] profile = "black" line_length = 120 skip = [".eggs", ".git", ".hg", ".mypy_cache", ".tox", ".venv", "venv", "_build", "buck-out", "build", "dist", "migrations"] [tool.pylint.messages_control] disable = [ "C0111", # missing-docstring "C0103", # invalid-name "R0903", # too-few-public-methods "R0913", # too-many-arguments ] [tool.pylint.format] max-line-length = 120 [tool.bandit] exclude_dirs = ["tests", "migrations", "venv", ".venv"] skips = ["B101"] # Skip assert_used test [tool.coverage.run] source = ["app"] omit = [ "*/tests/*", "*/test_*.py", "*/__pycache__/*", "*/venv/*", "*/env/*", "*/migrations/*", "app/utils/pdf_generator.py", "app/utils/pdf_generator_fallback.py", ] [tool.coverage.report] precision = 2 show_missing = true skip_covered = false exclude_lines = [ "pragma: no cover", "def __repr__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "@abstractmethod", ] [tool.mypy] python_version = "3.11" # warn_return_any is intentionally OFF: SQLAlchemy 1.x-style ``Model.query`` and # ``.first()`` / ``.all()`` return ``Any``, so wrapping them in a typed return # yields hundreds of false positives until/unless the codebase migrates to # SQLAlchemy 2.x ``Mapped[X]`` annotations. warn_return_any = false warn_unused_configs = true disallow_untyped_defs = false ignore_missing_imports = true # Restore pre-mypy-0.991 implicit Optional so ``def foo(x: str = None)`` is not # flagged. The codebase uses this Flask-style idiom in many places. implicit_optional = true disable_error_code = [ # ``if SomeClass:`` truthy-class checks are used as import-guards across # routes/services; their meaning is intentional. "truthy-function", "truthy-bool", ] exclude = [ "migrations/", "tests/", "venv/", ".venv/", ] # Flask-SQLAlchemy models defined as ``class Foo(db.Model)`` are not understood # by mypy without a dedicated plugin. Skip them rather than chase ~150 false # positives of "Name 'db.Model' is not defined". [[tool.mypy.overrides]] module = "app.models.*" ignore_errors = true # Repositories use ``TypeVar("ModelType")`` which mypy cannot bind to the # Flask-SQLAlchemy ``.query`` / ``joinedload(rel)`` runtime API. The patterns # are correct at runtime; suppress the documented limitation. [[tool.mypy.overrides]] module = "app.repositories.*" disable_error_code = ["arg-type", "attr-defined"] # Generic base service uses ``RepositoryType`` TypeVar; same limitation as # repositories — methods exist at runtime via the bound class. [[tool.mypy.overrides]] module = "app.services.base_crud_service" disable_error_code = ["attr-defined"] # ``Query.paginate`` is added by Flask-SQLAlchemy and is missing from the # SQLAlchemy stubs. Suppress this single attr-defined case in helpers that # wrap pagination. [[tool.mypy.overrides]] module = ["app.utils.pagination", "app.services.project_service"] disable_error_code = ["attr-defined"] # SQLAlchemy joinedload(rel) accepts ``RelationshipProperty`` at runtime but # is typed as ``QueryableAttribute``. This pattern is used pervasively in # services and is correct. [[tool.mypy.overrides]] module = ["app.services.task_service", "app.services.quote_service", "app.services.invoice_service", "app.services.expense_service", "app.services.scheduled_report_service"] disable_error_code = ["arg-type"] # Pytest config is in pytest.ini (single source of truth to avoid duplicate config warning)