[project] name = "lectio" version = "0.1.0" description = "Browser-based RSS and Atom reader" readme = "README.md" requires-python = ">=3.15" dependencies = [ "aiosqlite", "fastapi", "feedparser>=6.0.14", "httpx", "pillow>=12.3.0", "smartcrop>=0.5", "itsdangerous", "jinja2", "python-multipart", "reader>=3.27", "beautifulsoup4", "readability-lxml", "sqlalchemy", "resend", "uvicorn[standard]", "markdown>=3.10.2", "pysocks>=1.7.1", # httpx's own SOCKS proxy support needs socksio specifically — pysocks # above is a separate library that only requests/reader's session knows # how to use, not something httpx's proxy= kwarg can consume. "socksio>=1.0.0", ] [project.optional-dependencies] # Tooling-only: drives scripts/refresh_screenshots.py. Not needed at runtime. # Install with `uv sync --extra screenshots && uv run playwright install chromium`. screenshots = [ "playwright>=1.44", ] [dependency-groups] dev = [ "pytest>=8.4.0", "ruff", "ty", "uv", "uvicorn[standard]>=0.44.0", # starlette's TestClient prefers httpx2; without it the suite emits a # StarletteDeprecationWarning. Test-only — production code still uses httpx. "httpx2", # Drives the browser verification of UI work (launch the app, click through # it, assert on the real DOM). Also in the `screenshots` extra, which exists # as a no-dev install path — but a plain `uv sync` prunes anything outside # the default groups, so leaving it only there silently removed the tooling # mid-session. The wheel alone is cheap; browsers are a separate # `playwright install chromium` and are NOT fetched by syncing. "playwright>=1.44", ] [tool.ruff] line-length = 140 [tool.ruff.lint] extend-ignore = ["E203"] select = ["E", "F", "W", "I", "B"] [tool.ruff.lint.per-file-ignores] # main.py registers a feedparser date handler *before* its remaining imports, so # the handler is in place before `reader` (which imports feedparser) parses # anything. The late import block is that deliberate ordering, not drift — E402 # flags all 55 of them and none will ever be "fixed", so the rule is noise here # and would block every change that adds an import. # # B008 flags FastAPI's `Query(...)`/`File(...)`/`Form(...)` route defaults as # "function call in argument default" — that's the framework's required idiom # (FastAPI inspects the default at import time to build request parsing), not # a mutable-default bug, so the rule is a false positive here specifically. "main.py" = ["E402", "B008"] # Extracted route modules (Plan.md's main.py/index.html breakup) hit the same # FastAPI-default false positive as main.py above, for routes that took a # File(...)/Form(...)/Query(...) default with them when they moved out. "routes/*.py" = ["B008"] # Ruff is the canonical linter/formatter for this project. The `select` list # enables pycodestyle (`E`), pyflakes (`F`), warnings (`W`), isort-style # import ordering (`I`) and bugbear (`B`). Add or remove checks here if # you want stricter or looser linting.