Skip to content

Phase 2.2–2.5 docs + Phase 3 feature parity (openapi_extra, Enum coercion, CLI)#27

Merged
EshwarCVS merged 3 commits intomasterfrom
claude/tasks-2.2-2.5-42472
Apr 22, 2026
Merged

Phase 2.2–2.5 docs + Phase 3 feature parity (openapi_extra, Enum coercion, CLI)#27
EshwarCVS merged 3 commits intomasterfrom
claude/tasks-2.2-2.5-42472

Conversation

@EshwarCVS
Copy link
Copy Markdown
Collaborator

@EshwarCVS EshwarCVS commented Apr 22, 2026

Summary

This PR completes Phase 2.2–2.5 (docs expansion + site UX) and Phase 3 (feature parity + CLI) from the project roadmap.


Phase 2.2 — Advanced User Guide

  • docs/advanced/additional-responses.md (new) — Documents the responses= parameter: multiple status codes with schemas, shared error models (COMMON_ERRORS dict pattern), response headers, multiple content types, and the "default" catch-all key.

Phase 2.3 — Deployment Guides

  • docs/deployment/systemd.md (new) — Full systemd unit file with security hardening (PrivateTmp, ProtectSystem, NoNewPrivileges), EnvironmentFile for secrets, socket activation, journald log commands, zero-downtime SIGHUP reload.
  • docs/deployment/gunicorn.md (new) — Gunicorn + UvicornWorker setup, gunicorn.conf.py with max_requests jitter and preload_app caveats, worker sizing formula, Docker and systemd integration.
  • docs/deployment/https.md (new) — Let's Encrypt + Certbot walkthrough, hardened Nginx TLS config (TLS 1.2/1.3, HSTS, security headers), auto-renewal deploy hook, TLS verification commands.

Phase 2.4 — Conceptual Docs

  • docs/concepts/msgspec-vs-pydantic.md (new) — Philosophy table, performance benchmark table (~5× across encode/decode/construction), full API side-by-side comparisons (decode, encode, field opts, validators), coercion and mutability differences, migration table, step-by-step migration guide.
  • docs/concepts/radix-tree-routing.md (new) — O(k) complexity proof vs linear/regex routers, visual tree diagram, annotated iterative _walk algorithm, __slots__ rationale, static-before-param priority, routing benchmark table, documented limitations (no regex constraints, no catch-all).

Phase 2.5 — Docs Site UX (mkdocs.yml)

  • Prev/next navigation: navigation.footer feature
  • Edit-on-GitHub + View source: content.action.edit / content.action.view
  • OpenGraph + Twitter card meta tags: docs/overrides/main.html injects og:* and twitter:* on every page via custom_dir
  • mike versioning: mike plugin + extra.version.provider: mike for navbar version selector
  • Search enhancements: search.highlight, search.suggest, search.share
  • Code annotations: content.code.annotate
  • New markdown extensions: pymdownx.snippets, pymdownx.emoji, footnotes
  • mike>=2.0.0 added to docs optional deps

Phase 3.2 — Core Feature Gaps

openapi_extra= on route decorators (app.py, router.py, openapi/generator.py)

  • All route decorators now accept openapi_extra: dict | None
  • The OpenAPI generator merges it into the operation object; the responses key is deep-merged so the primary 200 response is preserved alongside any extra ones
  • Works on both Faster app decorators and FasterRouter decorators
@app.get("/items", openapi_extra={"x-internal": True, "externalDocs": {"url": "..."}})
async def list_items(): ...

Enum path parameter coercion (dependencies.py)

  • New _coerce_path_value() helper called from both explicit Path markers and the implicit fallback
  • String-valued enums: passes raw string to annotation() directly
  • Numeric enums (IntEnum): coerces string → value type first, so "2"Priority(2)
  • Invalid values return 422 with a clear "permitted: [...]" message
class Color(enum.Enum):
    RED = "red"

@app.get("/colors/{color}")
async def get_color(color: Color):   # "red" → Color.RED automatically
    return {"value": color.value}

Phase 3.3 — CLI Tool (FasterAPI/cli.py + pyproject.toml)

New fasterapi entrypoint with four sub-commands:

Command Description
fasterapi run [app] Wraps uvicorn, production mode; workers default to 2×cores+1
fasterapi dev [app] Same with --reload; no workers flag
fasterapi new <name> Scaffolds a complete project (lifespan, CRUD router, pyproject, Dockerfile, .gitignore)
fasterapi migrate-from-fastapi <path> [--dry-run] Rewrites fastapi imports to FasterAPI across a file/directory tree

Test plan

  • tests/test_phase3.py — 29 new tests covering openapi_extra (5 cases), Enum coercion (6 cases including IntEnum numeric coercion), and all CLI commands (18 cases including scaffold file creation, migration rewriting, dry-run, edge cases)
  • Full suite: 291/291 tests pass on rebased branch
  • Branch rebased cleanly onto current master (no stale benchmark-sync commits)

claude added 2 commits April 22, 2026 22:51
…s UX

2.2 Advanced User Guide:
- docs/advanced/additional-responses.md: document extra OpenAPI status codes,
  response headers, multiple content types, and the "default" response key

2.3 Deployment Guides:
- docs/deployment/systemd.md: systemd unit file, EnvironmentFile, socket
  activation, journald log commands, zero-downtime reload via SIGHUP
- docs/deployment/gunicorn.md: Gunicorn + UvicornWorker setup, gunicorn.conf.py,
  worker sizing formula, preload_app caveats, systemd integration
- docs/deployment/https.md: Let's Encrypt + Certbot, hardened Nginx TLS config,
  HSTS, auto-renewal deploy hook, TLS verification commands

2.4 Conceptual Docs:
- docs/concepts/msgspec-vs-pydantic.md: philosophy comparison, perf table,
  API side-by-side (decode, encode, validators, field opts), coercion
  differences, migration table and step-by-step guide
- docs/concepts/radix-tree-routing.md: O(k) complexity proof, iterative walk
  algorithm, __slots__ rationale, static-before-param priority, benchmark
  table, limitations (no regex constraints, no catch-all wildcards)

2.5 Docs Site UX (mkdocs.yml):
- navigation.footer: prev/next page links at bottom of every page
- content.action.edit / content.action.view: edit-on-GitHub and view-source
  buttons (backed by existing edit_uri)
- content.code.annotate, search.highlight/suggest/share: enhanced search UX
- custom_dir: docs/overrides with main.html for OpenGraph + Twitter card meta
  tags on every page
- mike plugin: version selector in navbar with alias_type=symlink,
  canonical_version=latest and extra.version.provider=mike
- pymdownx.snippets, pymdownx.emoji, footnotes markdown extensions added

https://claude.ai/code/session_01JXqCtpvUCzfGyHNbcxe9D9
3.2 Core Feature Gaps — resolved:

openapi_extra= on route decorators
- app.py _add_route and _route_decorator: accept and store openapi_extra
- router.py _add_route and _route_kw: same treatment
- openapi/generator.py _build_operation: deep-merge openapi_extra into the
  operation dict; responses key is merged rather than replaced so the primary
  200 response is preserved alongside any extra ones

Enum path parameter coercion
- dependencies.py: add enum import and _coerce_path_value() helper that:
  * Is a no-op for non-Enum annotations (strings, ints, etc.)
  * For string-valued Enums: passes raw string directly to annotation()
  * For numeric Enums (IntEnum, etc.): coerces string → value_type first
    to handle "2" → Priority(2) correctly
  * Returns 422 with clear permitted-values message on invalid input
- _resolve_path() and the implicit-path fallback in _resolve_from_specs
  both route through _coerce_path_value()

3.3 CLI Tool — new FasterAPI/cli.py:

fasterapi run  [app] [--host] [--port] [--workers] [--log-level]
  Wraps uvicorn in production mode; auto-expands bare module names to
  module:app; worker count defaults to 2×cores+1

fasterapi dev  [app] [--host] [--port] [--log-level]
  Same as run but passes --reload; no --workers flag

fasterapi new  <name>
  Scaffolds a complete project: app/main.py with lifespan, app/routers/items.py
  with CRUD routes, pyproject.toml, Dockerfile, .gitignore, .env

fasterapi migrate-from-fastapi  <path> [--dry-run]
  Rewrites fastapi imports to FasterAPI across a file or directory tree using
  ordered regex substitutions covering: FastAPI→Faster, APIRouter→FasterRouter,
  fastapi.responses, fastapi.middleware, fastapi.security, fastapi.staticfiles,
  fastapi.templating, fastapi.websockets, fastapi.testclient

pyproject.toml:
- Added [project.scripts] fasterapi = "FasterAPI.cli:main"
- Added mike>=2.0.0 to docs extras

tests/test_phase3.py: 29 new tests covering all three feature areas;
full test suite 291/291 green.

https://claude.ai/code/session_01JXqCtpvUCzfGyHNbcxe9D9
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 22, 2026

Benchmark results

Ubuntu runner, Python 3.13. HTTP table uses the same httpx load against uvicorn (Python) and Fiber (Go). Direct ASGI (below) is Python-only and excludes network I/O.

🟢 Benchmark status: improvement detected.

HTTP throughput (FasterAPI vs FastAPI vs Fiber)

Endpoint FasterAPI FastAPI Fiber (Go) F / Fast
GET /health 472/s 471/s 476/s 1.00x
GET /users/{id} 493/s 487/s 500/s 1.01x
POST /users 439/s 450/s 446/s 0.98x

Direct ASGI (no HTTP; 50,000 iterations)

Endpoint FasterAPI FastAPI Speedup vs README ASGI ratio
GET /health 193,310/s 19,546/s 9.89x +44.4%
GET /users/{id} 160,591/s 13,376/s 12.01x +37.5%
POST /users 102,583/s 10,705/s 9.58x +34.0%

Routing (radix vs regex, 1,500,000 lookups)

Router Ops/s Speedup vs README
Radix 946,751 10.1x +32.6%
Regex 93,963 1.0x
How to read this
  • F / Fast = FasterAPI req/s ÷ FastAPI req/s on the same HTTP harness (higher is better).
  • Fiber uses the Go app in benchmarks/fiber (same routes). Go is often several times faster than Python here; the important guard for regressions is check_regressions.py (ASGI + routing floors), which must pass in this workflow.
  • vs README compares combined speedups to documented reference numbers (local machine); CI absolute req/s differs by hardware.

…ests

- ruff format: reformat cli.py, dependencies.py, test_phase3.py to match
  project style (line length, blank lines, import ordering)
- ruff I001: fix import block ordering in cli.py and test_phase3.py
- ruff F401: remove unused imports (sys, Path, Annotated, pytest) from test_phase3.py
- ruff B904: add `from None` to raise-in-except in _coerce_path_value so the
  chained exception context is suppressed intentionally
- mypy no-any-return: annotate the result of args.func(args) in cli.main()
  explicitly as `int` to satisfy strict return-type checking

All 291 tests pass; ruff and mypy both clean.

https://claude.ai/code/session_01JXqCtpvUCzfGyHNbcxe9D9
@EshwarCVS EshwarCVS merged commit 779a265 into master Apr 22, 2026
6 checks passed
@EshwarCVS EshwarCVS deleted the claude/tasks-2.2-2.5-42472 branch April 22, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants