cello-framework 1.2.4__tar.gz → 1.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cello_framework-1.4.0/CLAUDE.md +473 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/Cargo.lock +539 -555
- {cello_framework-1.2.4 → cello_framework-1.4.0}/Cargo.toml +24 -5
- {cello_framework-1.2.4 → cello_framework-1.4.0}/ENTERPRISE_ROADMAP.md +59 -104
- {cello_framework-1.2.4 → cello_framework-1.4.0}/PKG-INFO +104 -35
- {cello_framework-1.2.4 → cello_framework-1.4.0}/README.md +103 -34
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/README.md +27 -34
- cello_framework-1.4.0/benchmarks/orm/README.md +128 -0
- cello_framework-1.4.0/benchmarks/orm/RESULTS.md +220 -0
- cello_framework-1.4.0/benchmarks/orm/app.py +276 -0
- cello_framework-1.4.0/benchmarks/orm/paired.py +445 -0
- cello_framework-1.4.0/benchmarks/orm/profile_stages.py +102 -0
- cello_framework-1.4.0/benchmarks/orm/results-v130-vs-v140.json +364 -0
- cello_framework-1.4.0/benchmarks/orm/seed.py +92 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/cello.md +14 -15
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/community/index.md +0 -9
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/community/support.md +3 -3
- cello_framework-1.4.0/docs/data-layer.md +579 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/index.md +6 -9
- cello_framework-1.4.0/docs/enterprise/integration/database.md +128 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/roadmap.md +3 -8
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/basic/hello-world.md +3 -3
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/basic/rest-api.md +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/index.md +77 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/advanced-patterns.md +6 -6
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/all-features.md +9 -9
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/api-protocols.md +3 -3
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/auto-openapi.md +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/blueprints-advanced.md +3 -3
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/cluster-demo.md +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/complete-showcase.md +4 -4
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/comprehensive-demo.md +17 -23
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/database-demo.md +6 -2
- cello_framework-1.4.0/docs/examples/real/database-orm.md +374 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/enterprise-config.md +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/hello-world.md +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/middleware-demo.md +3 -3
- cello_framework-1.4.0/docs/examples/real/migrations.md +347 -0
- cello_framework-1.4.0/docs/examples/real/native-schema.md +119 -0
- cello_framework-1.4.0/docs/examples/real/orm-advanced.md +383 -0
- cello_framework-1.4.0/docs/examples/real/postgres-tls.md +214 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/security.md +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/streaming-sse.md +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/advanced/dependency-injection.md +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/advanced/dto-validation.md +89 -1
- cello_framework-1.4.0/docs/features/advanced/migrations.md +206 -0
- cello_framework-1.4.0/docs/features/advanced/threadpool.md +126 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/core/async.md +12 -6
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/core/blueprints.md +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/core/responses.md +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/index.md +33 -36
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/getting-started/configuration.md +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/getting-started/first-app.md +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/getting-started/index.md +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/index.md +46 -52
- cello_framework-1.4.0/docs/issue-5-answer.md +162 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/javascripts/extra.js +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/guides/performance.md +2 -3
- cello_framework-1.4.0/docs/middleware.md +125 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/overrides/main.html +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/api/app.md +8 -7
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/api/blueprint.md +1 -1
- cello_framework-1.4.0/docs/reference/api/database.md +390 -0
- cello_framework-1.4.0/docs/reference/api/orm.md +730 -0
- cello_framework-1.4.0/docs/reference/api/plugins.md +199 -0
- cello_framework-1.4.0/docs/reference/cli.md +277 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/config/server.md +37 -0
- cello_framework-1.4.0/docs/reference/errors.md +346 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/index.md +3 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/changelog.md +101 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/index.md +60 -13
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v0.3.0.md +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v0.5.0.md +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v1.0.0.md +8 -49
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v1.0.1.md +8 -49
- cello_framework-1.4.0/docs/releases/v1.3.0.md +258 -0
- cello_framework-1.4.0/docs/releases/v1.4.0-plan.md +479 -0
- cello_framework-1.4.0/docs/releases/v1.4.0.md +1501 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/advanced.py +3 -3
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/advanced_middleware.py +6 -7
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/advanced_patterns_demo.py +6 -6
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/all_features_demo.py +9 -9
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/api_protocols_demo.py +3 -3
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/auto_openapi_demo.py +2 -2
- cello_framework-1.4.0/examples/blocking_handlers_demo.py +79 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/cluster_demo.py +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/complete_showcase.py +4 -4
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/comprehensive_demo.py +17 -23
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/database_demo.py +3 -3
- cello_framework-1.4.0/examples/database_orm_demo.py +293 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/enterprise.py +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/hello.py +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/middleware_demo.py +3 -3
- cello_framework-1.4.0/examples/middleware_full_demo.py +74 -0
- cello_framework-1.4.0/examples/migrations_demo.py +130 -0
- cello_framework-1.4.0/examples/migrations_demo_models.py +46 -0
- cello_framework-1.4.0/examples/native_schema_demo.py +51 -0
- cello_framework-1.4.0/examples/orm_advanced_demo.py +253 -0
- cello_framework-1.4.0/examples/plugins_demo.py +93 -0
- cello_framework-1.4.0/examples/postgres_tls_demo.py +132 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/security.py +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/simple_api.py +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/streaming_demo.py +2 -2
- cello_framework-1.4.0/examples/three_pillars_demo.py +69 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/mkdocs.yml +14 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/pyproject.toml +4 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/python/cello/__init__.py +356 -72
- cello_framework-1.4.0/python/cello/__main__.py +32 -0
- cello_framework-1.4.0/python/cello/database.py +210 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/python/cello/middleware.py +4 -0
- cello_framework-1.4.0/python/cello/migrate.py +643 -0
- cello_framework-1.4.0/python/cello/orm.py +2263 -0
- cello_framework-1.4.0/python/cello/validation.py +219 -0
- cello_framework-1.4.0/src/async_bridge.rs +417 -0
- cello_framework-1.4.0/src/async_loop.rs +179 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/background.rs +1 -1
- cello_framework-1.4.0/src/db/error.rs +360 -0
- cello_framework-1.4.0/src/db/mod.rs +17 -0
- cello_framework-1.4.0/src/db/postgres.rs +744 -0
- cello_framework-1.4.0/src/db/redis_client.rs +419 -0
- cello_framework-1.4.0/src/db/rows.rs +428 -0
- cello_framework-1.4.0/src/db/tls.rs +810 -0
- cello_framework-1.4.0/src/db/value.rs +1563 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/dependency.rs +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/dto.rs +1 -1
- cello_framework-1.4.0/src/handler.rs +576 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/http_client.rs +12 -14
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/json.rs +267 -78
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/lib.rs +537 -141
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/lifecycle.rs +3 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/auth.rs +40 -9
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/body_limit.rs +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/cache.rs +49 -8
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/cors.rs +8 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/csrf.rs +67 -18
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/etag.rs +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/exception_handler.rs +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/graphql.rs +6 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/guards.rs +1 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/health.rs +9 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/mod.rs +90 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/prometheus.rs +20 -3
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/static_files.rs +1 -3
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/telemetry.rs +2 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/minijinja_engine.rs +10 -12
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/openapi.rs +81 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/request/mod.rs +32 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/response/mod.rs +22 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/response/streaming.rs +8 -2
- cello_framework-1.4.0/src/schema/ident.rs +85 -0
- cello_framework-1.4.0/src/schema/migrate.rs +1273 -0
- cello_framework-1.4.0/src/schema/migrate_conn.rs +455 -0
- cello_framework-1.4.0/src/schema/mod.rs +132 -0
- cello_framework-1.4.0/src/schema/native.rs +324 -0
- cello_framework-1.4.0/src/schema/python.rs +594 -0
- cello_framework-1.4.0/src/schema/runtime.rs +104 -0
- cello_framework-1.4.0/src/schema/spec.rs +626 -0
- cello_framework-1.4.0/src/schema/validate.rs +971 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/server/cluster.rs +3 -1
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/server/mod.rs +294 -42
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/sse.rs +40 -2
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/template.rs +1 -1
- cello_framework-1.4.0/tests/orm_compat_support.py +155 -0
- cello_framework-1.4.0/tests/test_async_bridge.py +349 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/tests/test_cello.py +45 -403
- cello_framework-1.4.0/tests/test_db_tls.py +472 -0
- cello_framework-1.4.0/tests/test_db_values_v140.py +641 -0
- cello_framework-1.4.0/tests/test_error_exposure.py +123 -0
- cello_framework-1.4.0/tests/test_middleware.py +149 -0
- cello_framework-1.4.0/tests/test_migrations.py +587 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/tests/test_minijinja.py +1 -1
- cello_framework-1.4.0/tests/test_native_db.py +246 -0
- cello_framework-1.4.0/tests/test_native_validation.py +721 -0
- cello_framework-1.4.0/tests/test_orm_advanced.py +914 -0
- cello_framework-1.4.0/tests/test_orm_compat_model.py +779 -0
- cello_framework-1.4.0/tests/test_orm_compat_queryset.py +662 -0
- cello_framework-1.4.0/tests/test_orm_compat_tx.py +245 -0
- cello_framework-1.4.0/tests/test_orm_compat_values.py +650 -0
- cello_framework-1.4.0/tests/test_orm_fastpath.py +719 -0
- cello_framework-1.4.0/tests/test_orm_fixes_v140.py +734 -0
- cello_framework-1.4.0/tests/test_orm_injection.py +337 -0
- cello_framework-1.4.0/tests/test_plugins.py +291 -0
- cello_framework-1.4.0/tests/test_schema_registry.py +520 -0
- cello_framework-1.4.0/tests/test_shutdown_hooks.py +104 -0
- cello_framework-1.4.0/tests/test_threadpool.py +269 -0
- cello_framework-1.4.0/tests/test_upgrades.py +197 -0
- cello_framework-1.4.0/tests/test_v130_fixes.py +269 -0
- cello_framework-1.2.4/CLAUDE.md +0 -426
- cello_framework-1.2.4/docs/enterprise/integration/database.md +0 -144
- cello_framework-1.2.4/docs/reference/cli.md +0 -134
- cello_framework-1.2.4/docs/reference/errors.md +0 -213
- cello_framework-1.2.4/python/cello/database.py +0 -422
- cello_framework-1.2.4/python/cello/validation.py +0 -90
- cello_framework-1.2.4/src/handler.rs +0 -312
- {cello_framework-1.2.4 → cello_framework-1.4.0}/.github/workflows/ci.yml +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/.github/workflows/docs.yml +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/.github/workflows/publish.yml +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/.gitignore +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/CONTRIBUTING.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/LICENSE +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/PUBLISHING.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/benchmark.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/compare/README.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/compare/apps/__init__.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/compare/apps/blacksheep_app.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/compare/apps/cello_app.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/compare/apps/fastapi_app.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/compare/apps/robyn_app.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/compare/requirements.txt +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/compare/run_benchmarks.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/benchmarks/quick_bench.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/community/code-of-conduct.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/community/contributing.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/deployment/docker.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/deployment/kubernetes.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/deployment/service-mesh.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/integration/graphql.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/integration/grpc.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/integration/message-queues.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/observability/health-checks.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/observability/metrics.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/observability/opentelemetry.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/enterprise/observability/tracing.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/advanced/background-tasks.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/advanced/file-storage.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/advanced/fullstack.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/advanced/graphql.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/advanced/microservices.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/advanced/realtime-dashboard.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/advanced/redis-caching.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/basic/database.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/basic/forms.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/basic/jwt-auth.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/basic/query-params.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/enterprise/api-gateway.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/enterprise/event-sourcing.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/enterprise/health-checks.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/enterprise/multi-tenant.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/enterprise/oauth2.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/enterprise/rate-limiting.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/adaptive-rate-limiting.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/advanced-middleware.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/async-handlers.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/circuit-breaker.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/dto-validation.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/guards.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/lifecycle-hooks.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/minijinja-advanced.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/minijinja-basic.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/minijinja-blog.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/minijinja-emails.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/minijinja-forms.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/minijinja-macros.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/simple-api.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/examples/real/smart-caching.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/advanced/background-tasks.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/advanced/file-uploads.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/advanced/static-files.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/advanced/templates.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/core/requests.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/core/routing.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/middleware/caching.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/middleware/circuit-breaker.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/middleware/compression.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/middleware/cors.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/middleware/logging.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/middleware/overview.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/middleware/rate-limiting.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/minijinja-templates.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/realtime/sse.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/realtime/websocket.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/security/authentication.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/security/csrf.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/security/guards.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/security/headers.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/security/jwt.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/security/overview.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/security/sessions.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/features/templates.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/getting-started/installation.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/getting-started/project-structure.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/getting-started/quickstart.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/includes/abbreviations.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/guides/best-practices.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/guides/deployment.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/guides/error-handling.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/guides/testing.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/index.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/patterns/cqrs.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/patterns/event-driven.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/patterns/repository.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/patterns/service-layer.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/tutorials/auth-system.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/tutorials/chat-app.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/tutorials/microservices.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/learn/tutorials/rest-api.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/logo-full.png +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/logo-icon.svg +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/logo.jpg +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/logo.svg +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/api/context.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/api/guards.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/api/middleware.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/api/request.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/api/response.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/config/middleware.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/reference/config/security.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/migration.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v0.10.0.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v0.4.0.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v0.6.0.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v0.7.0.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v0.8.0.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v0.9.0.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v1.1.0.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v1.2.0.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v1.2.1.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v1.2.2.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v1.2.3.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/releases/v1.2.4.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/requirements.txt +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/stylesheets/extra.css +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/docs/tags.md +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/adaptive_rate_limit.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/async_demo.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/circuit_breaker.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/dto_validation.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/guards.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/lifecycle_hooks.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/minijinja_advanced.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/minijinja_basic.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/minijinja_blog.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/minijinja_emails.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/minijinja_forms.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/minijinja_macros.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/examples/smart_caching.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/python/cello/cqrs.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/python/cello/eventsourcing.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/python/cello/graphql.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/python/cello/grpc.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/python/cello/guards.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/python/cello/messaging.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/python/cello/saga.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/arena.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/blueprint.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/context.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/error.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/circuit_breaker.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/cqrs.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/database.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/eventsourcing.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/grpc.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/messaging.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/rate_limit.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/redis.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/request_id.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/saga.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/security.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/middleware/session.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/multipart.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/request/multipart_streaming.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/request/parsing.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/response/xml.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/router.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/routing/constraints.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/routing/mod.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/server/protocols.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/timeout.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/src/websocket.rs +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/tests/verify_adaptive.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/tests/verify_async_client.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/tests/verify_caching.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/tests/verify_circuit_breaker.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/tests/verify_dto.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/tests/verify_guards_impl.py +0 -0
- {cello_framework-1.2.4 → cello_framework-1.4.0}/tests/verify_lifecycle.py +0 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
# CLAUDE.md - Cello Framework Project Intelligence
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
**Cello** is an ultra-fast, Rust-powered Python async web framework designed to achieve C-level performance on the hot path while maintaining Python's developer experience. It combines a modern feature set with a pure Rust implementation for maximum performance.
|
|
6
|
+
|
|
7
|
+
**Version:** 1.4.0
|
|
8
|
+
**License:** MIT
|
|
9
|
+
**Python Requirement:** 3.12+
|
|
10
|
+
**Author:** Jagadeesh Katla
|
|
11
|
+
|
|
12
|
+
## Architecture Philosophy
|
|
13
|
+
|
|
14
|
+
### Core Principle: Rust Owns the Hot Path
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Request → Rust HTTP Engine → Python Handler → Rust Response
|
|
18
|
+
│ │
|
|
19
|
+
├─ SIMD JSON ├─ Return dict or Response
|
|
20
|
+
├─ Radix routing └─ Python business logic only
|
|
21
|
+
└─ Middleware (Rust)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Key Rules:**
|
|
25
|
+
- Python = Developer Experience (DX) / DSL
|
|
26
|
+
- Rust = Runtime & Execution Engine
|
|
27
|
+
- Async-first design
|
|
28
|
+
- Zero-copy data flow
|
|
29
|
+
- Minimal Python involvement per request
|
|
30
|
+
|
|
31
|
+
### What Rust Owns (MUST stay in Rust)
|
|
32
|
+
- TCP accept loop
|
|
33
|
+
- HTTP parsing
|
|
34
|
+
- Routing (radix tree)
|
|
35
|
+
- All middleware
|
|
36
|
+
- JSON serialization (SIMD)
|
|
37
|
+
- Response building
|
|
38
|
+
|
|
39
|
+
### What Python Does (ONLY)
|
|
40
|
+
- Route registration
|
|
41
|
+
- Handler function pointers
|
|
42
|
+
- Business logic
|
|
43
|
+
- Returns minimal data structures
|
|
44
|
+
|
|
45
|
+
## Project Structure
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
/home/vrinda/cello/
|
|
49
|
+
├── src/ # Rust source (23K+ lines, 45 files)
|
|
50
|
+
│ ├── lib.rs # PyO3 module entry point
|
|
51
|
+
│ ├── router.rs # Radix-tree routing (matchit)
|
|
52
|
+
│ ├── handler.rs # Handler registry & caching
|
|
53
|
+
│ ├── request/ # HTTP request handling
|
|
54
|
+
│ │ ├── mod.rs # Request struct
|
|
55
|
+
│ │ ├── body.rs # Lazy body parsing
|
|
56
|
+
│ │ └── multipart.rs # Multipart form handling
|
|
57
|
+
│ ├── response/ # Response types
|
|
58
|
+
│ │ ├── mod.rs # Response struct
|
|
59
|
+
│ │ ├── streaming.rs # Streaming responses
|
|
60
|
+
│ │ └── xml.rs # XML responses
|
|
61
|
+
│ ├── middleware/ # Middleware suite (16 files)
|
|
62
|
+
│ │ ├── mod.rs # Middleware chain & traits
|
|
63
|
+
│ │ ├── auth.rs # JWT, Basic, API Key auth
|
|
64
|
+
│ │ ├── rate_limit.rs # Token bucket, sliding window
|
|
65
|
+
│ │ ├── cache.rs # Smart caching with TTL
|
|
66
|
+
│ │ ├── session.rs # Secure cookie sessions
|
|
67
|
+
│ │ ├── security.rs # CSP, HSTS, security headers
|
|
68
|
+
│ │ ├── guards.rs # RBAC with composable guards
|
|
69
|
+
│ │ ├── cors.rs # CORS handling
|
|
70
|
+
│ │ ├── csrf.rs # CSRF protection
|
|
71
|
+
│ │ ├── etag.rs # ETag caching
|
|
72
|
+
│ │ ├── body_limit.rs # Request size limits
|
|
73
|
+
│ │ ├── static_files.rs # Static file serving
|
|
74
|
+
│ │ ├── request_id.rs # UUID request tracing
|
|
75
|
+
│ │ ├── prometheus.rs # Metrics collection
|
|
76
|
+
│ │ ├── circuit_breaker.rs # Fault tolerance
|
|
77
|
+
│ │ ├── exception_handler.rs # Global error handling
|
|
78
|
+
│ │ └── redis.rs # Redis integration (v0.8.0)
|
|
79
|
+
│ ├── routing/ # Route constraints
|
|
80
|
+
│ ├── server/ # Server modes (cluster, TLS)
|
|
81
|
+
│ ├── blueprint.rs # Modular route grouping
|
|
82
|
+
│ ├── websocket.rs # WebSocket support
|
|
83
|
+
│ ├── sse.rs # Server-Sent Events
|
|
84
|
+
│ ├── json.rs # SIMD JSON parsing
|
|
85
|
+
│ ├── arena.rs # Arena allocators
|
|
86
|
+
│ ├── context.rs # Request context & DI
|
|
87
|
+
│ ├── dependency.rs # Dependency injection
|
|
88
|
+
│ ├── error.rs # RFC 7807 errors
|
|
89
|
+
│ ├── lifecycle.rs # Startup/shutdown hooks
|
|
90
|
+
│ ├── timeout.rs # Timeout config
|
|
91
|
+
│ ├── dto.rs # Data Transfer Objects
|
|
92
|
+
│ ├── openapi.rs # OpenAPI generation
|
|
93
|
+
│ ├── background.rs # Background tasks
|
|
94
|
+
│ └── template.rs # Jinja2 templates
|
|
95
|
+
│
|
|
96
|
+
├── python/cello/ # Python API wrapper
|
|
97
|
+
│ ├── __init__.py # Public Python API
|
|
98
|
+
│ ├── database.py # Database & Redis wrappers (v0.8.0)
|
|
99
|
+
│ ├── guards.py # RBAC guard classes
|
|
100
|
+
│ └── validation.py # DTO validation
|
|
101
|
+
│
|
|
102
|
+
├── tests/ # Test suite
|
|
103
|
+
│ ├── test_cello.py # Main integration tests
|
|
104
|
+
│ └── verify_*.py # Feature verification tests
|
|
105
|
+
│
|
|
106
|
+
├── examples/ # 20 example applications
|
|
107
|
+
│ ├── hello.py # Basic hello world
|
|
108
|
+
│ ├── simple_api.py # REST API with OpenAPI
|
|
109
|
+
│ ├── comprehensive_demo.py # All v0.7.0 features
|
|
110
|
+
│ ├── database_demo.py # Database & Redis (v0.8.0)
|
|
111
|
+
│ ├── guards.py # RBAC examples
|
|
112
|
+
│ └── ...
|
|
113
|
+
│
|
|
114
|
+
├── docs/ # Documentation
|
|
115
|
+
│ ├── README.md # Doc index
|
|
116
|
+
│ ├── getting-started.md # Installation & basics
|
|
117
|
+
│ ├── api-reference.md # Complete API docs
|
|
118
|
+
│ └── ...
|
|
119
|
+
│
|
|
120
|
+
├── Cargo.toml # Rust dependencies
|
|
121
|
+
├── pyproject.toml # Python packaging
|
|
122
|
+
└── maturin build config
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Technology Stack
|
|
126
|
+
|
|
127
|
+
### Rust Dependencies (Critical)
|
|
128
|
+
|
|
129
|
+
| Component | Crate | Purpose |
|
|
130
|
+
|-----------|-------|---------|
|
|
131
|
+
| Python Bindings | `pyo3 0.20` | Python-Rust FFI (abi3-py312) |
|
|
132
|
+
| Async Runtime | `tokio 1.x` | Full-featured async runtime |
|
|
133
|
+
| HTTP Server | `hyper 1.x` | HTTP/1.1 server |
|
|
134
|
+
| HTTP/2 | `h2 0.4` | HTTP/2 support |
|
|
135
|
+
| HTTP/3 | `quinn 0.10` | QUIC protocol |
|
|
136
|
+
| TLS | `rustls 0.22` | TLS implementation |
|
|
137
|
+
| JSON | `simd-json 0.13` | SIMD-accelerated parsing |
|
|
138
|
+
| Serialization | `serde 1` | Rust serialization |
|
|
139
|
+
| Routing | `matchit 0.7` | Radix tree routing |
|
|
140
|
+
| Concurrency | `dashmap 5` | Lock-free HashMaps |
|
|
141
|
+
| Memory | `bumpalo 3` | Arena allocators |
|
|
142
|
+
| JWT | `jsonwebtoken 9` | JWT authentication |
|
|
143
|
+
| Security | `subtle 2` | Constant-time comparison |
|
|
144
|
+
| Metrics | `prometheus 0.13` | Prometheus metrics |
|
|
145
|
+
| WebSocket | `tokio-tungstenite 0.21` | WebSocket support |
|
|
146
|
+
| Multipart | `multer 3` | Form parsing |
|
|
147
|
+
|
|
148
|
+
## Coding Conventions
|
|
149
|
+
|
|
150
|
+
### Rust Code Style
|
|
151
|
+
|
|
152
|
+
1. **Error Handling**: Use `thiserror` for custom errors, return `Result<T, CelloError>`
|
|
153
|
+
2. **Async**: All I/O operations must be async using Tokio
|
|
154
|
+
3. **Memory**: Prefer zero-copy operations, use `Bytes` for buffers
|
|
155
|
+
4. **Concurrency**: Use `DashMap` for concurrent access, `parking_lot` for locks
|
|
156
|
+
5. **Traits**: Implement `Send + Sync` for all middleware and handlers
|
|
157
|
+
|
|
158
|
+
```rust
|
|
159
|
+
// Good: Async with proper error handling
|
|
160
|
+
pub async fn handle_request(&self, req: Request) -> Result<Response, CelloError> {
|
|
161
|
+
let body = req.body().await?;
|
|
162
|
+
let json: Value = simd_json::from_slice(&body)?;
|
|
163
|
+
Ok(Response::json(json))
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Bad: Blocking I/O in async context
|
|
167
|
+
pub async fn bad_handler(&self, req: Request) -> Result<Response, CelloError> {
|
|
168
|
+
let data = std::fs::read_to_string("file.txt")?; // BLOCKING!
|
|
169
|
+
Ok(Response::text(data))
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Python Code Style
|
|
174
|
+
|
|
175
|
+
1. **Type Hints**: Always use type hints for public APIs
|
|
176
|
+
2. **Decorators**: Route decorators should be clean and intuitive
|
|
177
|
+
3. **Returns**: Handlers return `dict`, `Response`, or async equivalents
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
# Good: Clean, typed handler
|
|
181
|
+
@app.get("/users/{id}")
|
|
182
|
+
def get_user(request: Request) -> dict:
|
|
183
|
+
user_id = request.params["id"]
|
|
184
|
+
return {"id": user_id, "name": "John"}
|
|
185
|
+
|
|
186
|
+
# Good: Explicit Response with status
|
|
187
|
+
@app.post("/users")
|
|
188
|
+
def create_user(request: Request) -> Response:
|
|
189
|
+
data = request.json()
|
|
190
|
+
return Response.json({"created": True, **data}, status=201)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Middleware Pattern
|
|
194
|
+
|
|
195
|
+
All middleware must implement the `Middleware` trait:
|
|
196
|
+
|
|
197
|
+
```rust
|
|
198
|
+
#[async_trait]
|
|
199
|
+
pub trait Middleware: Send + Sync {
|
|
200
|
+
async fn process(
|
|
201
|
+
&self,
|
|
202
|
+
request: &mut Request,
|
|
203
|
+
response: &mut Response,
|
|
204
|
+
context: &mut Context,
|
|
205
|
+
) -> Result<MiddlewareResult, CelloError>;
|
|
206
|
+
|
|
207
|
+
fn priority(&self) -> i32 { 0 }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
pub enum MiddlewareResult {
|
|
211
|
+
Continue, // Proceed to next middleware/handler
|
|
212
|
+
Stop, // Stop processing, return current response
|
|
213
|
+
Error(CelloError), // Return error response
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Building & Testing
|
|
218
|
+
|
|
219
|
+
### Development Setup
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# Clone and setup
|
|
223
|
+
git clone https://github.com/jagadeesh32/cello.git
|
|
224
|
+
cd cello
|
|
225
|
+
python -m venv .venv
|
|
226
|
+
source .venv/bin/activate
|
|
227
|
+
pip install maturin pytest requests
|
|
228
|
+
|
|
229
|
+
# Build Rust extensions
|
|
230
|
+
maturin develop
|
|
231
|
+
|
|
232
|
+
# Run tests
|
|
233
|
+
pytest tests/ -v
|
|
234
|
+
|
|
235
|
+
# Rust checks
|
|
236
|
+
cargo clippy --all-targets
|
|
237
|
+
cargo fmt --check
|
|
238
|
+
cargo test
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Running Examples
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Basic example
|
|
245
|
+
python examples/hello.py
|
|
246
|
+
|
|
247
|
+
# Full feature demo
|
|
248
|
+
python examples/comprehensive_demo.py
|
|
249
|
+
|
|
250
|
+
# With options
|
|
251
|
+
python examples/simple_api.py --port 8080 --workers 4
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Key Design Decisions
|
|
255
|
+
|
|
256
|
+
### 1. Why Rust for Hot Path?
|
|
257
|
+
- Python's GIL limits concurrency
|
|
258
|
+
- SIMD JSON is 10x faster than Python JSON (with serde_json fallback on ARM)
|
|
259
|
+
- Zero-copy routing eliminates allocations
|
|
260
|
+
- Async I/O without Python overhead
|
|
261
|
+
- Cross-platform: Linux (fork + SO_REUSEPORT), Windows (subprocess re-execution)
|
|
262
|
+
|
|
263
|
+
### 2. Why PyO3 with abi3?
|
|
264
|
+
- Single binary works across Python versions
|
|
265
|
+
- Minimal FFI overhead
|
|
266
|
+
- Native async support via `pyo3-asyncio`
|
|
267
|
+
|
|
268
|
+
### 3. Why matchit for Routing?
|
|
269
|
+
- O(log n) radix tree lookup
|
|
270
|
+
- Compile-time route optimization
|
|
271
|
+
- Support for path parameters and wildcards
|
|
272
|
+
|
|
273
|
+
### 4. Why DashMap over RwLock<HashMap>?
|
|
274
|
+
- Lock-free concurrent reads
|
|
275
|
+
- Fine-grained locking for writes
|
|
276
|
+
- Better performance under contention
|
|
277
|
+
|
|
278
|
+
## Performance Guidelines
|
|
279
|
+
|
|
280
|
+
### DO:
|
|
281
|
+
- Return `dict` directly (Rust handles JSON serialization)
|
|
282
|
+
- Use path parameters over query parameters (cached in router)
|
|
283
|
+
- Enable compression for responses > 1KB
|
|
284
|
+
- Use connection pooling for external services
|
|
285
|
+
- Leverage lazy body parsing
|
|
286
|
+
|
|
287
|
+
### DON'T:
|
|
288
|
+
- Parse JSON in Python (use `request.json()` from Rust)
|
|
289
|
+
- Use Python middleware on hot paths
|
|
290
|
+
- Block async handlers with sync I/O
|
|
291
|
+
- Create Response objects unnecessarily
|
|
292
|
+
- Hold references across await points
|
|
293
|
+
|
|
294
|
+
## Common Patterns
|
|
295
|
+
|
|
296
|
+
### Dependency Injection
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
from cello import App, Depends
|
|
300
|
+
|
|
301
|
+
def get_db():
|
|
302
|
+
return DatabaseConnection()
|
|
303
|
+
|
|
304
|
+
def get_current_user(request, db=Depends(get_db)):
|
|
305
|
+
token = request.get_header("Authorization")
|
|
306
|
+
return db.get_user_by_token(token)
|
|
307
|
+
|
|
308
|
+
@app.get("/profile")
|
|
309
|
+
def profile(request, user=Depends(get_current_user)):
|
|
310
|
+
return {"user": user.name}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Guards (RBAC)
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
from cello import App
|
|
317
|
+
from cello.guards import RoleGuard, PermissionGuard
|
|
318
|
+
|
|
319
|
+
admin_only = RoleGuard(["admin"])
|
|
320
|
+
can_write = PermissionGuard(["write"])
|
|
321
|
+
|
|
322
|
+
@app.get("/admin", guards=[admin_only])
|
|
323
|
+
def admin_panel(request):
|
|
324
|
+
return {"admin": True}
|
|
325
|
+
|
|
326
|
+
@app.post("/data", guards=[can_write])
|
|
327
|
+
def write_data(request):
|
|
328
|
+
return {"written": True}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Error Handling (RFC 7807)
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
from cello import App, ProblemDetails
|
|
335
|
+
|
|
336
|
+
@app.exception_handler(ValueError)
|
|
337
|
+
def handle_value_error(request, exc):
|
|
338
|
+
return ProblemDetails(
|
|
339
|
+
type_uri="/errors/validation",
|
|
340
|
+
title="Validation Error",
|
|
341
|
+
status=400,
|
|
342
|
+
detail=str(exc),
|
|
343
|
+
instance=request.path
|
|
344
|
+
)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Version History
|
|
348
|
+
|
|
349
|
+
- **v1.4.0**: The Native Core — one model declaration compiled once into a Rust schema registry drives the ORM, validation, serialization and OpenAPI. Full notes: `docs/releases/v1.4.0.md` (design: `v1.4.0-plan.md`). Verification: full suite **1300 pass** (`pytest --asyncio-mode=auto`); perf numbers come only from `benchmarks/orm/paired.py` (paired, simultaneous servers).
|
|
350
|
+
- **Speed**: prepared-statement cache (`prepare_cached`, DDL-change retry); new `src/async_bridge.rs` replaces `pyo3_asyncio::future_into_py` (single `cello-io` current-thread runtime, never takes the GIL, pipe-wakeup of the asyncio loop) and async handlers are submitted to the persistent loop directly (no parked blocking thread) — fixed the interpreter-exit "panic in a function that cannot unwind" and lost Ctrl-C; response serializer (`src/json.rs`) dispatches on concrete types first (the old extract chain raised ~4 Python exceptions per string; ~5.5× on a 200-row response); Phase 02 row pipeline — a returned queryset (`return {"users": qs}`) or `await qs.json()` / `db.fetch_json()` is encoded straight from Postgres wire rows to JSON on the I/O thread (zero per-row Python objects, byte-identical to `to_dict()`), native `to_dict()` and native hydration. Native query compiler deliberately **skipped**: query building measured <3% of request time.
|
|
351
|
+
- **Security**: identifier injection in `filter/exclude/order_by/update` closed — every identifier resolves through the registry (undeclared → `FieldError`); LIKE wildcards escaped; int range checks; `write_only`/`read_only` fields; `body=Model` rejects U+0000 and ignores client-sent pk/read-only fields (no mass assignment); Postgres TLS with libpq `sslmode` (default `prefer`; `verify-full` recommended) for pool and migrations (`src/db/tls.rs`). Production-safe 500s: with `debug` off (incl. `env="production"`) an unhandled handler error returns a generic `Internal Server Error` and logs the details (global `EXPOSE_ERROR_DETAILS` in `src/server/mod.rs`, set by `App.run()` via `set_expose_errors`) — database messages carry constraint names/row values and must never reach clients. `async def` shutdown hooks now run on Ctrl-C (pending `KeyboardInterrupt` consumed via `py.check_signals()` before hooks; `tests/test_shutdown_hooks.py`).
|
|
352
|
+
- **Simplicity / correctness**: Rust schema registry (`src/schema/`); value layer rewrite (`src/db/value.rs`: NUMERIC exact decimal strings, arrays, interval/inet/…, ISO/datetime/UUID binding, `DatabaseError` hierarchy in `src/db/error.rs` subclassing `RuntimeError`); ORM semantics fixes documented as deliberate breaks (defaults, create/save, FK assignment, `=None`→IS NULL, null-safe exclude, ambient transactions + `TransactionAborted`); native `body=Model` validation (`src/schema/validate.rs`, 400 `{"detail": [...]}` frozen); OpenAPI request schemas from the registry; Phase 04 Q objects, aggregates/annotate, `select_related`/`prefetch_related`/reverse accessors, `bulk_create`/`bulk_update`; Phase 05 experimental `python -m cello migrate --experimental {make,apply,status,sql}`.
|
|
353
|
+
- **Tests**: ORM compatibility suite `tests/test_orm_compat_*.py` pins observable behaviour (changed pins carry a "1.4.0 deliberate break" comment); `test_orm_fastpath.py` (byte parity), `test_native_validation.py`, `test_orm_advanced.py`, `test_migrations.py`, `test_db_tls.py` (throwaway TLS cluster), `test_async_bridge.py`.
|
|
354
|
+
- **Known follow-ups**: lookups across relations (`filter(author__name=…)`), `HAVING`, `annotate()` without `values()`; migrations don't handle PK/identity changes, CHECK constraints or indexes; returned querysets run after the handler returns (outside its transaction — use `await qs.json()` inside one).
|
|
355
|
+
- **v1.3.0**: The Speed/Simplicity/Security release — async rework (below), native data layer + ORM, a full `enable_*` plugin audit, and three-pillar upgrades. Native data layer + ORM resolves issue #5 ("how do I use the database pool and Redis client?"); the previous DB/Redis layer was **mock scaffolding** (methods returned `[]`/`None`, never connected) and is now real.
|
|
356
|
+
- **Native Postgres pool** (`src/db/postgres.rs`, `PyDatabase`): backed by `deadpool-postgres` + `tokio-postgres`. `request.database` / `app.database` expose `fetch` (→`list[dict]`), `fetchrow` (→`dict|None`), `fetchval`, `execute` (→rows affected), and `transaction()`. Positional `$1` params via a `SqlParam`/`ToSql` bridge (`src/db/value.rs`) that dispatches on the target column type; rows decode common pg types incl. `jsonb`→nested Python.
|
|
357
|
+
- **Native Redis client** (`src/db/redis_client.rs`, `PyRedis`): `redis` crate `aio::ConnectionManager`. `request.redis` / `app.redis` expose get/set/del/expire/incr/hget/hset/lpush/lrange/sadd/publish/eval/evalsha/script_load/ping/… (real, verified against a live server).
|
|
358
|
+
- **Transactions**: `async with request.database.transaction() as tx` (explicit BEGIN/COMMIT/ROLLBACK on a held pooled connection), plus a rewritten `@transactional` decorator (async-only; injects a `tx` argument; auto commit/rollback).
|
|
359
|
+
- **Built-in ORM** (`python/cello/orm.py`): `Model` + typed fields (`AutoField/IntegerField/CharField/TextField/BooleanField/FloatField/JSONField/DateTimeField/ForeignKey`), a chainable async `QuerySet` (`filter/exclude/order_by/limit/offset` with field lookups; `get/first/all/count/exists/values/create/update/delete`), `create_table`/`drop_table`. Intentionally lightweight (no migration diffing, lazy reverse relations, `select_related`, signals/admin) — documented as such.
|
|
360
|
+
- **Async bridge**: all methods return `pyo3_asyncio::tokio::future_into_py` awaitables (same proven pattern as `AsyncClient`); verified to resolve on the persistent asyncio loop.
|
|
361
|
+
- **Wiring**: `lib.rs` `enable_database`/`enable_redis` build real pools; `app.state` namespace added; `request.database`/`request.db`/`request.redis` injected per request. Stub `Database`/`Redis`/`Transaction` in `python/cello/database.py` removed — native classes exported from `cello`.
|
|
362
|
+
- **Deps**: `deadpool-postgres`/`tokio-postgres` made non-optional (with `with-chrono-0_4`/`with-uuid-1`/`with-serde_json-1`); added `redis = 0.25`. `postgres` feature kept as an empty alias.
|
|
363
|
+
- **Tests**: `tests/test_native_db.py` — live-server integration for raw queries/types, transaction commit+rollback, Redis commands, ORM CRUD/filters/FK (auto-skips when PG/Redis unreachable). Obsolete mock-based unit tests removed. Docs: `docs/data-layer.md`, `docs/issue-5-answer.md`, `examples/database_orm_demo.py`.
|
|
364
|
+
- **Middleware verification + Prometheus fix**: verified the 7 core `enable_*` middleware end-to-end (cors, logging, compression, caching, rate_limit, circuit_breaker, prometheus). Six worked; **`enable_prometheus` was broken** — `/metrics` returned 404 because `handle_request` (`src/server/mod.rs`) does "route match FIRST, fast-return 404" before the prometheus middleware runs, and `/metrics` isn't a registered route. Fixed via `PrometheusMiddleware::try_serve(path)` (`src/middleware/prometheus.rs`), called in the routing-miss branch before returning 404. Tests: `tests/test_middleware.py`; docs: `docs/middleware.md`; example: `examples/middleware_full_demo.py`. Known middleware notes (documented): CORS preflight `OPTIONS` to an *unrouted* path still 404s (same fast-404 cause; register an `OPTIONS` route); a cache HIT serves the stored body uncompressed (bypasses compression); rate-limit + circuit-breaker are global.
|
|
365
|
+
- **Full `enable_*` plugin audit (all 27)**: verified every plugin end-to-end over live HTTP (extends the 7-core pass above). Bugs found & fixed:
|
|
366
|
+
- **Health checks & GraphQL returned 404** — identical root cause to the prometheus bug (fast-404 runs before the middleware chain; `/health*` and `/graphql` aren't registered routes). Fixed generally: added a `serves_unrouted(method, path)` hook to the `Middleware`/`AsyncMiddleware` traits (default `false`; overridden in `src/middleware/health.rs` and `graphql.rs`), plus `MiddlewareChain::has_unrouted` / `execute_before_unrouted` / `execute_before_async_unrouted` (`src/middleware/mod.rs`) and a `serve_unrouted()` helper in the router-miss branch (`src/server/mod.rs`). **Only** path-owning middleware run on a miss, so unknown paths under auth still 404 (no 404→401 route-existence leak).
|
|
367
|
+
- **BasicAuth 401 lacked `WWW-Authenticate`** — `before` returned `Err`, so the server short-circuited and the `after` hook that set the header never ran. Fixed by returning `Stop(challenge())` with the header set inline (`src/middleware/auth.rs`).
|
|
368
|
+
- **JWT rejected tokens without `iat`** — a standard `sub`+`exp` token failed with "missing field iat". Fixed with `#[serde(default)]` on `JwtClaims.iat`. `exp` stays required (secure default).
|
|
369
|
+
- **`enable_security_headers` only accepted a `bool`** while docs/examples pass a `SecurityHeadersConfig` (TypeError). Now accepts `None` | `bool` | `SecurityHeadersConfig` via `Option<&PyAny>` + `build_security_headers_mw()` (`src/lib.rs`).
|
|
370
|
+
- **`set_timeouts` panicked** — hyper 1.10 requires `builder.timer(TokioTimer::new())` when `header_read_timeout` is set ("timeout set, but no timer set"); was missing. Fixed in `src/server/mod.rs` (unblocks the 3 timeout/limit tests in `tests/test_v130_fixes.py`).
|
|
371
|
+
- **Announce-only plugins** (documented, not "broken"): `enable_grpc`, `enable_messaging`, `enable_rabbitmq`, `enable_sqs`, `enable_event_sourcing`, `enable_cqrs`, `enable_saga` print config but do not wire runtime behaviour into the HTTP app — real functionality is in `cello.grpc/messaging/cqrs/saga/eventsourcing`.
|
|
372
|
+
- Tests: `tests/test_plugins.py` (20 live-HTTP checks); example: `examples/plugins_demo.py`; docs: `docs/reference/api/plugins.md` (+ mkdocs nav). Full suite: **433 pass** with `pytest --asyncio-mode=auto` (`pytest-asyncio` IS required for `test_cello.py`'s `async def` tests).
|
|
373
|
+
- **Three-pillar upgrades (Speed / Simplicity / Security)**:
|
|
374
|
+
- **Security — full headers**: `SecurityHeadersConfig` now exposes `csp` (a `CSP` builder), `permissions_policy` (`{feature: [origins]}`), and `coep`/`coop`/`corp` (string values); `build_security_headers_mw` in `src/lib.rs` bridges them onto the Rust `SecurityHeadersMiddleware` (which already emitted them). `SecurityHeadersConfig.secure()` now includes cross-origin isolation (`require-corp` / `same-origin`).
|
|
375
|
+
- **Security — Redis TLS**: added rustls features (`tls-rustls`, `tls-rustls-webpki-roots`, `tokio-rustls-comp`) to the `redis` crate; `rediss://` URLs now work (`src/db/redis_client.rs` unchanged — `Client::open` handles the scheme). Resolves the v1.4.0 "TLS for Redis" follow-up.
|
|
376
|
+
- **Speed — compressed cache**: a cache HIT short-circuits the pipeline so the compression middleware never ran on it (was served uncompressed). `CacheMiddleware` now gzips the HIT inline for `Accept-Encoding: gzip` clients (`compress`/`compress_min_size` on `CacheConfig`, default on, min 1 KB), sets `Vary: Accept-Encoding`, and serves identity to non-gzip clients. `enable_caching(..., compress=True)` toggles it. Cache stores identity because async-`after` (store) runs before sync-`after` (compress).
|
|
377
|
+
- **Simplicity — honest stubs**: the 7 announce-only `enable_*` methods (`grpc`, `messaging`, `rabbitmq`, `sqs`, `event_sourcing`, `cqrs`, `saga`) no longer print a misleading "enabled" banner — they emit a clear "records config only — use the `cello.X` module" note (Rust + Python docstrings). No behavior change; just honest.
|
|
378
|
+
- **DX + Security — request validation**: `@app.{get,post,...}(path, body=DTO)` (App + Blueprint) parses/validates the JSON body and returns **400 `{"detail": [...]}`** before the handler runs, injecting the validated instance (`wrap_handler_with_body` in `python/cello/validation.py`). Works with Pydantic, dataclasses, and plain classes. Complements the pre-existing type-hint validation (returns 422).
|
|
379
|
+
- Tests: `tests/test_upgrades.py` (11 checks). Example: `examples/three_pillars_demo.py`. Docs: `docs/reference/api/plugins.md`, `docs/features/advanced/dto-validation.md`. Full suite: **464 pass** (`pytest --asyncio-mode=auto`).
|
|
380
|
+
- **Blocking-handler threadpool (Speed)**: sync `def` handlers were called **inline, under the GIL, on the single-threaded Tokio runtime** (`src/handler.rs` Phase 1), so one blocking handler (`time.sleep`, sync DB driver, `requests`) pinned both the GIL *and* the thread that accepts connections and parses HTTP — throughput collapsed to the one-at-a-time ceiling (**94 rps** measured on a 10 ms handler). Only the coroutine path escaped to `spawn_blocking`. Fixed with **adaptive offload**: sync handlers are timed *inside the GIL* (wall-clock around `Python::with_gil` also counts GIL-acquisition wait and misclassifies cheap handlers under concurrency), and after **two consecutive** calls over `offload_threshold_ms` the handler is stickily promoted to the Tokio blocking pool. Two samples are required because a handler's first call pays one-time warmup that alone exceeds the threshold. Paired benchmark (both builds running simultaneously, alternating `wrk` runs — sequential runs on this box are too noisy to compare): **94 → 3,735 rps, 39.9×**, with the trivial-handler inline path unchanged (**+0.4%**, within noise).
|
|
381
|
+
- `src/handler.rs`: `HandlerMeta.offload`/`slow_streak`/`offload_policy`; Phase 1 and Phase 3 extracted to `call_handler()` / `serialize()` so the offloaded path does call+drive+serialize in **one** `spawn_blocking`; sync handlers serialize inline without awaiting `drive_and_serialize` (no extra state machine on the hot path).
|
|
382
|
+
- `src/lib.rs`: `ThreadPoolConfig` (`size=64`, `offload_threshold_ms=1`, `adaptive=True`) + `App.set_threadpool()`; `.max_blocking_threads()` on the runtime builder.
|
|
383
|
+
- `blocking=True|False` kwarg on every `App`/`Blueprint` verb decorator (incl. `options`/`head`/`route`), plumbed via a `__cello_blocking__` attribute read in `HandlerRegistry::register` — no Rust signature churn.
|
|
384
|
+
- Caveats (documented): only GIL-releasing work benefits (CPU-bound Python still serialises — use `workers=N`); the pool is shared with async waits and background tasks; `handler_timeout` returns 504 but does not reclaim a stuck pool thread.
|
|
385
|
+
- Tests: `tests/test_threadpool.py` (10 live-HTTP checks, inline-vs-pooled asserted via `threading.get_ident()`). Docs: `docs/features/advanced/threadpool.md` (+ mkdocs nav). Example: `examples/blocking_handlers_demo.py`. Full suite: **474 pass**.
|
|
386
|
+
- **DI fix (pre-existing, found during the above)**: `HandlerRegistry::set_has_dependencies()` was **never called anywhere**, so `has_dependencies` stayed `false` and `Depends(...)` parameters were never resolved — handlers received the raw `Depends` marker object (`TypeError: 'Depends' object is not subscriptable`, or a 500 on serialization). `register_singleton` (`src/lib.rs`) now sets the flag. Unrelated to the threadpool work; covered by `test_dependency_injection_survives_offload`.
|
|
387
|
+
- **Known follow-up**: `numeric`/`timestamptz` params need an explicit `$1::type` cast; `cargo test --lib` can't link libpython (pre-existing pyo3 `extension-module` limitation) — Python integration tests are the verification path.
|
|
388
|
+
- **Async runtime rework, security hardening & DoS protection** (the async-rework portion of v1.3.0):
|
|
389
|
+
- **Async runtime rework** (`src/async_loop.rs`, `handler.rs`, `lib.rs`): async `def` handlers now run on a single **persistent asyncio loop** (dedicated daemon thread) via `run_coroutine_threadsafe` instead of a fresh `asyncio.run()` per request. Loop-bound resources (aiohttp/asyncpg pools, `asyncio.Lock`/`Queue`) survive across requests, and the GIL is released while a coroutine awaits I/O (async handlers no longer serialize on the GIL). Async startup/shutdown hooks now actually run (previously the `pyo3_asyncio::into_future` path failed silently).
|
|
390
|
+
- **Security**: CSRF Origin/Referer validation rewritten to exact-authority matching (`example.com.evil.com` no longer bypasses); all middleware skip/exclude paths use `path_matches_skip` (no prefix bypass); CORS adds `Vary: Origin` on reflected responses; BasicAuth uses non-short-circuit comparison (no username-timing oracle); SSE `id`/`event` strip CR/LF (no stream injection).
|
|
391
|
+
- **DoS hardening**: `max_body_size` enforced (413, default 100 MB, `Limited`-capped streaming) via `App.set_limits()`; `App.set_timeouts()` wires header/body read timeouts (Slowloris) and a handler timeout (504). Previously `LimitsConfig`/`TimeoutConfig` were inert.
|
|
392
|
+
- **Correctness**: large Python ints (`> i64::MAX`, up to `u64::MAX`) serialize exactly instead of becoming lossy floats; `Range` header no longer underflows on 0-byte files; DELETE/OPTIONS request bodies are read; query `+` decodes to space in keys as well as values.
|
|
393
|
+
- **Python API**: `CsrfConfig` passed to `app.use()` is now honored (`cookie_name`/`header_name`/`allowed_origins`); `options()`/`head()`/`route()` apply the same validation + redis-injection wrapping as other verbs.
|
|
394
|
+
- **Tests**: new Rust `#[cfg(test)]` units (CSRF authority, `path_matches_skip`, Range, SSE) + `tests/test_v130_fixes.py` integration suite.
|
|
395
|
+
- **Known follow-up**: per-process Tokio runtime remains current-thread (multicore via the existing multi-process model); `AsyncClient` still uses `pyo3_asyncio::future_into_py`.
|
|
396
|
+
- **v1.2.4**: Critical fix — async handlers broken since v1.2.1; `pyo3_asyncio::tokio::into_future` failed silently after server startup switched to `py.allow_threads + block_on` (v1.2.1), causing all `async def` handlers to return 500; fixed by driving coroutines via `tokio::task::spawn_blocking + asyncio.run()` (`handler.rs`)
|
|
397
|
+
- **v1.2.3**: Full middleware Python API — `cello.middleware` module with `JwtAuth`, `BasicAuth`, `ApiKeyAuth`, `CsrfConfig`, `AdaptiveRateLimitConfig`; `app.use()` dispatcher; 6 new `enable_*` methods on App (`enable_jwt`, `enable_session`, `enable_security_headers`, `enable_csrf`, `enable_basic_auth`, `enable_api_key`); all docs import paths corrected (`from cello import RoleGuard` not `cello.guards`)
|
|
398
|
+
- **v1.2.2**: Security & bug fixes — CSRF `HttpOnly` on double-submit cookie (critical, broke all AJAX CSRF); all middleware `skip_path` prefix bypass fixed via `path_matches_skip()` helper; `FixedWindowStore` window_start never updated after reset; unused `mut` cleaned in minijinja tests
|
|
399
|
+
- **v1.2.1**: Bug fixes — server port never bound (`pyo3_asyncio` replaced with native `py.allow_threads` + `tokio::block_on`); `ProblemDetails` was missing from Python module export; `And`/`Or` guards now accept both `*args` and list styles; CSRF `HttpOnly` removed from double-submit cookie (JS must read it); `FixedWindowStore` window_start never updated after reset; all middleware `skip_path` used raw `starts_with` allowing prefix bypass; doc corrections (`type_uri` not `type_url`)
|
|
400
|
+
- **v1.2.0**: Bug fixes (shutdown coroutine never awaited, KeyboardInterrupt in shutdown handler, `request.redis` AttributeError); Redis Lua scripting (`eval`, `evalsha`, `script_load`); Rust-native `AsyncClient` backed by `reqwest + Tokio` — GIL never held during HTTP I/O, HTTP/2, gzip, rustls
|
|
401
|
+
- **v1.1.0**: MiniJinja Jinja2-compatible template engine (`MiniJinjaEngine`, `App.enable_templates()`, `App.render()`, `App.render_string()`); minijinja 2 Rust crate; HTML auto-escaping; globals; 47 new tests; 6 examples
|
|
402
|
+
- **v1.0.1**: Cross-platform fixes (Windows multi-worker, signal handling, UNC paths; ARM JSON fallback; Linux-only CPU affinity), async compatibility fixes (handler validation, guards, cache decorator, blueprints), guards and database exports in `__all__`
|
|
403
|
+
- **v1.0.0**: Production-ready stable release, performance optimizations, API stability guarantees
|
|
404
|
+
- **v0.10.0**: Advanced patterns (Event Sourcing, CQRS, Saga Pattern)
|
|
405
|
+
- **v0.9.0**: API protocols (GraphQL, gRPC), message queue adapters (Kafka, RabbitMQ)
|
|
406
|
+
- **v0.8.0**: Database connection pooling (enhanced), Redis integration, transaction support
|
|
407
|
+
- **v0.7.0**: OpenTelemetry, health checks, GraphQL support, structured logging
|
|
408
|
+
- **v0.6.0**: Smart caching, adaptive rate limiting, DTO validation, circuit breaker
|
|
409
|
+
- **v0.5.0**: Dependency injection, guards (RBAC), Prometheus metrics, OpenAPI
|
|
410
|
+
- **v0.4.0**: JWT auth, rate limiting, sessions, security headers, cluster mode
|
|
411
|
+
- **v0.3.0**: WebSocket, SSE, multipart, blueprints
|
|
412
|
+
- **v0.2.0**: Middleware system, CORS, logging, compression
|
|
413
|
+
- **v0.1.0**: Initial release with basic HTTP routing
|
|
414
|
+
|
|
415
|
+
## Roadmap (Post-1.0.1 Features)
|
|
416
|
+
|
|
417
|
+
### Planned for v1.1.0+
|
|
418
|
+
- OAuth2/OIDC Provider
|
|
419
|
+
- Service mesh integration (Istio/Envoy)
|
|
420
|
+
- Admin dashboard (real-time monitoring UI)
|
|
421
|
+
- Multi-tenancy support
|
|
422
|
+
|
|
423
|
+
## Troubleshooting
|
|
424
|
+
|
|
425
|
+
### Build Issues
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
# Missing Rust toolchain
|
|
429
|
+
rustup default stable
|
|
430
|
+
|
|
431
|
+
# PyO3 version mismatch
|
|
432
|
+
pip install --upgrade maturin
|
|
433
|
+
maturin develop --release
|
|
434
|
+
|
|
435
|
+
# Linker errors on Linux
|
|
436
|
+
sudo apt install build-essential pkg-config libssl-dev
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Runtime Issues
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
# Import errors
|
|
443
|
+
maturin develop # Rebuild extensions
|
|
444
|
+
|
|
445
|
+
# Performance issues
|
|
446
|
+
python app.py --env production --workers $(nproc)
|
|
447
|
+
|
|
448
|
+
# Debug mode
|
|
449
|
+
python app.py --debug --env development
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Cross-Platform Notes (v1.0.1)
|
|
453
|
+
|
|
454
|
+
- **Windows multi-worker**: Uses subprocess re-execution (`CELLO_WORKER=1` env var) instead of `os.fork()`
|
|
455
|
+
- **Windows signals**: `SIGTERM` is not available; Cello handles this gracefully with try/except
|
|
456
|
+
- **Windows static files**: UNC paths are normalized automatically
|
|
457
|
+
- **CPU affinity**: Only supported on Linux (`os.sched_setaffinity`); a warning is emitted on other platforms
|
|
458
|
+
- **ARM/non-SIMD**: JSON falls back to `serde_json` when SIMD instructions are unavailable
|
|
459
|
+
|
|
460
|
+
## Contributing Guidelines
|
|
461
|
+
|
|
462
|
+
1. **Rust Changes**: Run `cargo clippy` and `cargo fmt` before committing
|
|
463
|
+
2. **Python Changes**: Follow PEP 8, use type hints
|
|
464
|
+
3. **Tests**: Add tests for new features in `tests/`
|
|
465
|
+
4. **Docs**: Update relevant documentation
|
|
466
|
+
5. **Examples**: Add example if feature is user-facing
|
|
467
|
+
|
|
468
|
+
## Contact & Resources
|
|
469
|
+
|
|
470
|
+
- **Repository**: https://github.com/jagadeesh32/cello
|
|
471
|
+
- **Documentation**: See `docs/` directory
|
|
472
|
+
- **Issues**: GitHub Issues
|
|
473
|
+
- **License**: MIT
|