feat: LightAPI v2 — annotation-driven REST framework with async support#22
Merged
feat: LightAPI v2 — annotation-driven REST framework with async support#22
Conversation
… framework Unifies SQLAlchemy ORM model, Pydantic v2 schema, and REST endpoint into a single annotated class (RestEndpoint). All features are v2-native: Core engine - RestEndpointMeta metaclass with SQLAlchemy 2.0 imperative mapping - PEP-563 string annotation resolution via typing.get_type_hints() - ORM-style insert (session.add/flush/refresh) to correctly populate created_at, updated_at, version in responses - Dynamic Optional patch schema for PATCH partial updates - Re-apply serializer field projection after model_dump() to prevent null bleed-through for non-serialized Optional fields - Read schema uses Optional[T] for all user fields (output, not input) - Replace deprecated datetime.utcnow() with timezone-aware equivalent - Optimistic locking: PUT/PATCH require version, mismatch → 409 - Global SQLAlchemy registry singleton (_registry.py) New modules - lightapi/fields.py — custom Field() wrapper strips LightAPI kwargs - lightapi/methods.py — HttpMethod marker mixins (GET/POST/PUT/PATCH/DELETE) - lightapi/schema.py — SchemaFactory, _row_to_dict, _apply_fields - lightapi/_registry.py — global registry/metadata/engine singleton Refactored modules - lightapi/rest.py — RestEndpointMeta + RestEndpoint CRUD methods - lightapi/lightapi.py — Starlette/Uvicorn-native LightApi, YAML from_config - lightapi/auth.py — AllowAny, IsAuthenticated, IsAdminUser permissions - lightapi/filters.py — FieldFilter, SearchFilter, OrderingFilter - lightapi/pagination.py — PageNumberPaginator, CursorPaginator - lightapi/cache.py — Redis utility functions - lightapi/config.py — Authentication, Filtering, Pagination, Serializer, Cache Tests (98 passing) - tests/test_crud.py, test_auth.py, test_filtering.py, test_http_methods.py - tests/test_middleware.py, test_queryset.py, test_reflection.py - tests/test_schema.py, test_serializer.py, test_pipeline.py, test_yaml_config.py - conftest.py uses StaticPool for in-memory SQLite stability Documentation - README.md fully rewritten for v2 API - docs/index.md, quickstart.md, first-steps.md, installation.md updated - All remaining v1 docs pages marked with deprecation notice Example - examples/v2_full_demo.py — full-feature PostgreSQL demo covering CRUD, optimistic locking, filtering, pagination, serializer, JWT auth + permissions, HttpMethod mixins, custom queryset, cursor pagination, middleware
Remove 65+ deprecated v1 example files (aiohttp-based numbered scripts, YAML configs, duplicate files) and replace with 4 focused v2 examples: v2_quickstart.py, v2_full_demo.py, smoke_async.py, postgres_full.py. Rewrite every stale documentation page to reflect the v2 API: - getting-started: introduction (remove aiohttp), configuration (LightApi constructor, YAML schema), first-steps, quickstart - tutorial: basic-api, endpoints, database, requests, responses — all updated from v1 patterns (request.data, lightapi.database, Base) to v2 (RestEndpoint metaclass, Starlette Request, async sessions) - advanced: authentication, filtering, pagination, caching, validation — all rewritten for Meta-based config API - api-reference: core, auth, database, models, filters, pagination, cache, validation, exceptions, swagger/openapi — all rewritten for v2 - docs/.pages nav: remove broken examples/ section and technical-reference/ Also fix minor v1 references in installation.md and troubleshooting.md.
|
Important Review skippedToo many files! This PR contains 161 files, which is 11 over the limit of 150. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (161)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
v2 uses PEP 604 union syntax (X | Y) and built-in generics (list[str], dict[str, type]) that require Python >= 3.10. Drop 3.8 and 3.9 from the test matrix to match requires-python = ">=3.10" in pyproject.toml. Also fix the install command from `.[test,dev]` to `.[dev,async]` so the async test suite (pytest-asyncio, aiosqlite, asyncpg) installs correctly in CI.
F821 undefined names in legacy v1 code: - cache.py: add missing Optional/Dict imports - filters.py: replace Query/List annotations with Any - pagination.py: replace Query/List annotations with Any/list[Any] F401 unused imports: - auth.py: remove unused Any from typing import - core.py: remove unused hashlib import - core.py: remove unused swagger route imports (imported but never called) - database.py: remove duplicate/unused os import - handlers.py: remove inspect import (only used for the now-removed mapper line) - swagger.py: remove unused List and sql_inspect imports F811 duplicate definition: - filters.py: remove duplicate no-op ParameterFilter (shadowed by the real one) F841 unused local variable: - handlers.py: remove unused `mapper = inspect(self.model)` assignment F402 shadowed import in loop: - lightapi.py: remove inline `import importlib` inside for loop (already imported at top)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR delivers LightAPI v2 — a ground-up redesign of the framework — and adds opt-in asynchronous I/O support on top.
v2 Core Refactor (
feat: implement LightAPI v2 full refactor)RestEndpointsubclass acts as the SQLAlchemy ORM model, the Pydantic v2 schema, and the HTTP handler simultaneously — noBasemixin, no separate model filesRestEndpointMetametaclass: Processes type annotations at class definition time to create SQLAlchemyTableobjects and two Pydantic v2 schemas (create / read)id,created_at,updated_at,version(optimistic locking) with no declaration neededField(**kwargs): Re-exportspydantic.Fieldwith extra kwargs (unique,index,foreign_key,decimal_places,exclude) processed by the metaclassHttpMethodmixins:HttpMethod.GET,.POST,.PUT,.PATCH,.DELETErestrict allowed verbs at class definition timeMetainner class: Per-endpoint config forauthentication,filtering,pagination,serializer,cache,reflect,tableAuthentication:JWTAuthentication+IsAuthenticated/IsAdminUser/AllowAnypermission classes with per-method permission dictsFiltering:FieldFilter,SearchFilter,OrderingFilterbackends configured viaFiltering(backends=..., fields=..., search=..., ordering=...)Pagination:page_numberandcursorstyles viaPagination(style=..., page_size=...)Serializer: Read/write field projection viaSerializer(read=[...], write=[...])orSerializer(fields=[...])Cache: Redis-backed response caching viaCache(ttl=..., vary_on=[...])Meta.reflect = True+Meta.table = "name"to map an endpoint to an existing database tableLightApi.from_config("lightapi.yaml")for zero-code bootstrappingAsync Support (
docs: overhaul all documentation and examples for v2)create_engineforcreate_async_engine— LightAPI detectsAsyncEngineautomatically and routes all built-in CRUD operations through async sessionslightapi/session.py: Newget_sync_sessionandget_async_sessioncontext managers with automatic commit/rollback_list_async,_retrieve_async,_create_async,_update_async,_destroy_asyncon everyRestEndpointasync def queryset: Detected and awaited automatically; syncqueryset()still works on async enginesasync defmethod overrides:async def post,get, etc. are detected viaasyncio.iscoroutinefunctionand awaitedself.background(fn, *args)schedules StarletteBackgroundTasksfire-and-forget after the responseasync def process()inMiddlewaresubclasses is awaited; sync and async middleware coexistMeta.reflect = Trueworks withAsyncEngineviaconn.run_sync(metadata.reflect)sqlalchemy[asyncio],asyncpg/aiosqlite) raiseConfigurationErrorwith clear install instructions_coerce_filter_valueconverts string query params to correct Python types (bool/int/float) for strict databases like PostgreSQLmodel_rebuild(): Fixes Pydantic forward-reference resolution forOptionaltypes in generated schemaspyproject.toml: New[project.optional-dependencies.async]group (sqlalchemy[asyncio],asyncpg,aiosqlite,greenlet);pytest-asyncioadded to dev deps;asyncio_mode = "auto"configuredtest_async_crud,test_async_queryset,test_async_middleware,test_async_reflection,test_async_session,test_background_tasks,test_mixed_sync_asyncDocumentation & Examples
getting-started/,tutorial/,advanced/, andapi-reference/— removed all v1 (aiohttp,lightapi.models,lightapi.database,RESTEndpoint,Configuration) referencesdocs/advanced/async.md: Comprehensive async reference pagev2_quickstart.py,v2_full_demo.py,smoke_async.py,postgres_full.py)docs/examples/snippet-include section fromdocs/.pagesTest plan
uv run pytest— all 135 tests passuv run python examples/v2_quickstart.py— SQLite CRUD worksuv run python examples/smoke_async.py— async smoke test passes end-to-enduv run python examples/postgres_full.py— PostgreSQL async demo runs (requires Docker PostgreSQL)