[build-system] requires = ["setuptools>=68", "wheel"] build-backend = "setuptools.build_meta" [project] name = "webmail-backend" version = "0.1.0" description = "Webmail client backend (sync-first, see webmail-client-spec.md)" requires-python = "==3.11.*" # Pinned per SPEC §3. Do not swap libraries or add deps without a `# DEPENDENCY:` comment. dependencies = [ "fastapi~=0.115", "uvicorn[standard]~=0.30", "sqlalchemy[asyncio]~=2.0", "asyncpg~=0.29", "alembic~=1.13", "pydantic~=2.8", "pydantic-settings~=2.4", "arq~=0.26", "redis~=5.0", "aioimaplib~=1.1", "imapclient~=3.0", "aiosmtplib~=3.0", "argon2-cffi~=23.1", "pyotp~=2.9", "dnspython~=2.6", "httpx~=0.27", "nh3~=0.2", "cryptography~=43.0", "mail-parser~=4.0", "meilisearch-python-sdk==2.7.0", # last release that fits the pinned Meilisearch v1.8 "python-multipart~=0.0.9", # DEPENDENCY: SPEC §6.2 mandates accounts/presets.yaml (config, not code); the # stdlib has no YAML parser, so PyYAML is required to load it. "pyyaml~=6.0", # DEPENDENCY: autoconfig/ISPDB return XML from untrusted servers; defusedxml # blocks XXE/billion-laughs that stdlib ElementTree is vulnerable to (§10 hostile input). "defusedxml~=0.7", ] [project.optional-dependencies] dev = [ "ruff~=0.6", "mypy~=1.11", "pytest~=8.3", "pytest-asyncio~=0.24", "respx~=0.21", "aiosqlite~=0.20", ] [tool.setuptools.packages.find] where = ["."] include = ["app*", "workers*"] # Non-Python files that a *non-editable* install (the Docker image) must carry: # the account presets config (SPEC §6.2) and the shared Alembic env used by the # per-plugin migration runner (app/plugins/migrate.py). Without these, a # `pip install ./backend` produces a package that fails at runtime. [tool.setuptools.package-data] app = ["accounts/presets.yaml", "plugins/alembic/*"] [tool.ruff] line-length = 100 target-version = "py311" [tool.ruff.lint] select = ["E", "F", "I", "UP", "B", "ASYNC", "C4", "SIM"] ignore = ["B008"] # FastAPI Depends() in defaults is idiomatic [tool.mypy] python_version = "3.11" strict = true plugins = ["pydantic.mypy"] mypy_path = "." explicit_package_bases = true namespace_packages = true [[tool.mypy.overrides]] module = ["aioimaplib.*", "mailparser.*", "imapclient.*", "arq.*", "yaml", "defusedxml.*"] ignore_missing_imports = true [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] pythonpath = ["."]