bustapi 0.13.9__tar.gz → 0.14.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.
- {bustapi-0.13.9 → bustapi-0.14.0}/CHANGELOG.md +7 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/Cargo.lock +29 -33
- {bustapi-0.13.9 → bustapi-0.14.0}/Cargo.toml +1 -1
- {bustapi-0.13.9 → bustapi-0.14.0}/PKG-INFO +1 -1
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/changelog.md +7 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/pyproject.toml +1 -1
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/__init__.py +1 -1
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/app.py +34 -15
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/auth/login.py +39 -3
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/core/helpers.py +11 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/dispatch.py +42 -12
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/http/request.py +8 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/routing/blueprints.py +31 -16
- bustapi-0.14.0/tests/test_issue_29.py +119 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_login_manager.py +51 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/- +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/.github/FUNDING.yml +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/.github/workflows/ci-multiplatform.yml +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/.github/workflows/ci.yml +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/.github/workflows/docs.yml +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/.github/workflows/manual-publish.yml +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/.gitignore +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/.python-version +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/AGENT_KNOWLEDGE.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/Dockerfile +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/LICENSE +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/README.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/CACHE_BENCHMARK.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/README.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/app.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/benchmark_all.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/benchmark_avg.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/benchmark_cache.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/benchmark_combined.png +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/bustapi.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/bustapi_bench.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/bustapi_internal.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/comparison_bench.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/comprehensive_benchmark.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/gen_graph.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/quick_bench.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/resource_bench.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/rps_comparison.png +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/run_comparison_auto.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/benchmarks/ws_benchmark.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/advanced/async.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/advanced/blueprints.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/advanced/dependency_injection.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/advanced/deployment.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/advanced/middleware.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/advanced/security.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/advanced/validation.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/api-reference.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/assets/logo.png +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/complete-api-reference.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/deployment-performance-guide.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/deployment.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/examples-guide.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/index.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/installation.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/overrides/.gitkeep +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/quickstart.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/release_track.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/advanced/async_support.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/advanced/blueprints.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/advanced/dependency_injection.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/advanced/middleware.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/advanced/validation.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/api/all.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/api/app.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/api/context.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/api/utilities.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/assets/logo.png +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/changelog.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/core_concepts/request_data.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/core_concepts/responses.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/core_concepts/routing.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/core_concepts/templates.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/examples/index.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/getting_started/index.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/getting_started/quickstart.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/index.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/script.js +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/site/style.css +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/stylesheets/extra.css +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/authentication.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/caching.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/cli.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/database.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/documentation.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/error_handling.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/jwt.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/logging.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/multiprocessing.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/request_data.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/responses.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/routing.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/sessions.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/static_files.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/templates.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/turbo-routes.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/user-guide/video-streaming.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/docs/websockets.md +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/08_auto_docs.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/12_test_modes.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/13_error_handling.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/14_middleware.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/16_middleware_session.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/20_file_uploads.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/24_body_and_depends.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/27_video_stream.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/28_websocket.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/advanced/29_turbo_websocket.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/basics/01_hello_world.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/basics/02_parameters.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/basics/03_async.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/basics/04_request_data.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/basics/19_all_types.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/database/07_database_raw.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/database/10_database_sqlmodel.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/routing/06_blueprints.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/routing/09_complex_routing.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/routing/21_path_validation.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/routing/22_path_docs.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/routing/23_query_validation.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/routing/blueprint_with_jwt.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/security/10_rate_limit_demo.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/security/11_security_demo.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/security/17_jwt_auth.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/security/17_safe_features.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/security/18_jwt_cookies.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/security/18_safe_advanced.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/security/18_session_login.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/templates/05_templates.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/templates/26_complex_template.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/templates/templates/index.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/turbo/cache_test.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/turbo/fair_benchmark.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/turbo/typed_turbo_example.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/ws/app.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/ws/templates/index.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/ws/websockets_demo.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/examples/ws/ws_limit.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/mkdocs.yml +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/auth/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/auth/csrf.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/auth/decorators.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/auth/password.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/auth/tokens.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/auth/user.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/cli/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/cli/main.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/context.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/core/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/core/asgi.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/core/exceptions.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/core/logging.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/dependencies.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/documentation/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/documentation/generator.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/extraction.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/fastapi_compat.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/hooks.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/http/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/http/response.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/jwt.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/logging.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/middleware.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/multiprocess.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/params.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/py.typed +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/responses.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/routing/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/routing/decorators.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/safe/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/safe/concurrency.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/safe/types.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/security/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/security/extension.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/security/rate_limit.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/server/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/server/runner.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/server/wsgi.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/sessions.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/templating/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/templating/engine.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/templating/mixin.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/testing/__init__.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/testing/client.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/utils.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/python/bustapi/websocket.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/run_all_examples.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/scratch/test_abort.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/scratch/test_headers.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/bindings/app.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/bindings/converters.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/bindings/handlers.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/bindings/mod.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/bindings/request.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/bindings/typed_turbo.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/bindings/websocket.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/crypto.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/jwt.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/lib.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/logger.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/rate_limiter.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/request/methods.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/request/mod.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/request/tests.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/response/builders.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/response/methods.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/response/mod.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/router/handlers.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/router/matching.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/router/middleware.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/router/mod.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/router/tests.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/server/handlers.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/server/mod.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/server/startup.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/server/stream.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/static_files.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/templating.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/watcher.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/websocket/config.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/websocket/mod.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/websocket/session.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/src/websocket/turbo.rs +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/static/style.css +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/templates/base.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/templates/complex.html +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/docs_test_all.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/docs_test_api_reference.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/docs_test_installation.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/docs_test_quickstart.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/docs_test_simple.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_050_features.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_async_hooks.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_async_login.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_auth.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_discussion_21.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_discussion_22.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_discussion_28.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_examples.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_hot_reload.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_issue_18.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_issue_23.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_issue_24.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_jwt.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_jwt_cookies.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_middleware.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_multiprocess_stability.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_new_examples.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_orm_conflict.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_path_standalone.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_path_validation.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_rate_limit_rust.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_safe.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_security_unit.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_sessions.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/test_zero_copy.py +0 -0
- {bustapi-0.13.9 → bustapi-0.14.0}/tests/verify_router.py +0 -0
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented here.
|
|
4
4
|
|
|
5
|
+
## [0.14.0] - 2026-06-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Auth Login**: Enhanced `login_user()` to accept a raw `user_id` (string, integer, or UUID) directly as the user argument, resolving Discussion #27.
|
|
9
|
+
- **Blueprint Routing Stabilization**: Stabilized Blueprint routing structure (Issue #29) with support for relative `url_for()` endpoint resolution, nested lifecycle hooks (`before_request`, `after_request`, `teardown_request`), and blueprint-specific error handlers (`@bp.errorhandler`).
|
|
10
|
+
|
|
5
11
|
## [0.13.9] - 2026-06-12
|
|
6
12
|
|
|
7
13
|
### Fixed
|
|
14
|
+
- **Blueprint Routing**: Fixed an issue where routes registered via `app.register_blueprint()` were not added to the application's `url_map`. This ensures `url_for()` can correctly reverse-map Blueprint endpoints (Issue #29).
|
|
8
15
|
- **Session Permanence**: Fixed a bug in `SessionMixin` where `session.permanent` was not serialized into the session payload, causing it to drop across requests. It now functions correctly as a persistent session setting.
|
|
9
16
|
- **Login Manager Remember**: `login_user(user, remember=True)` now automatically sets `session.permanent = True` as expected, tying persistent logins to the application's permanent session lifetime configuration.
|
|
10
17
|
|
|
@@ -318,9 +318,9 @@ checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
|
|
|
318
318
|
|
|
319
319
|
[[package]]
|
|
320
320
|
name = "alloc-stdlib"
|
|
321
|
-
version = "0.2.
|
|
321
|
+
version = "0.2.4"
|
|
322
322
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
323
|
-
checksum = "
|
|
323
|
+
checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
|
|
324
324
|
dependencies = [
|
|
325
325
|
"alloc-no-stdlib",
|
|
326
326
|
]
|
|
@@ -405,9 +405,9 @@ dependencies = [
|
|
|
405
405
|
|
|
406
406
|
[[package]]
|
|
407
407
|
name = "brotli"
|
|
408
|
-
version = "8.0.
|
|
408
|
+
version = "8.0.4"
|
|
409
409
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
410
|
-
checksum = "
|
|
410
|
+
checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3"
|
|
411
411
|
dependencies = [
|
|
412
412
|
"alloc-no-stdlib",
|
|
413
413
|
"alloc-stdlib",
|
|
@@ -416,9 +416,9 @@ dependencies = [
|
|
|
416
416
|
|
|
417
417
|
[[package]]
|
|
418
418
|
name = "brotli-decompressor"
|
|
419
|
-
version = "5.0.
|
|
419
|
+
version = "5.0.3"
|
|
420
420
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
421
|
-
checksum = "
|
|
421
|
+
checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
|
|
422
422
|
dependencies = [
|
|
423
423
|
"alloc-no-stdlib",
|
|
424
424
|
"alloc-stdlib",
|
|
@@ -432,7 +432,7 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
|
|
432
432
|
|
|
433
433
|
[[package]]
|
|
434
434
|
name = "bustapi_core"
|
|
435
|
-
version = "0.
|
|
435
|
+
version = "0.14.0"
|
|
436
436
|
dependencies = [
|
|
437
437
|
"actix-files",
|
|
438
438
|
"actix-http",
|
|
@@ -491,9 +491,9 @@ dependencies = [
|
|
|
491
491
|
|
|
492
492
|
[[package]]
|
|
493
493
|
name = "cc"
|
|
494
|
-
version = "1.2.
|
|
494
|
+
version = "1.2.64"
|
|
495
495
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
496
|
-
checksum = "
|
|
496
|
+
checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f"
|
|
497
497
|
dependencies = [
|
|
498
498
|
"find-msvc-tools",
|
|
499
499
|
"jobserver",
|
|
@@ -722,9 +722,6 @@ name = "deranged"
|
|
|
722
722
|
version = "0.5.8"
|
|
723
723
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
724
724
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
|
725
|
-
dependencies = [
|
|
726
|
-
"powerfmt",
|
|
727
|
-
]
|
|
728
725
|
|
|
729
726
|
[[package]]
|
|
730
727
|
name = "derive_more"
|
|
@@ -1329,9 +1326,9 @@ dependencies = [
|
|
|
1329
1326
|
|
|
1330
1327
|
[[package]]
|
|
1331
1328
|
name = "js-sys"
|
|
1332
|
-
version = "0.3.
|
|
1329
|
+
version = "0.3.102"
|
|
1333
1330
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1334
|
-
checksum = "
|
|
1331
|
+
checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
|
|
1335
1332
|
dependencies = [
|
|
1336
1333
|
"cfg-if",
|
|
1337
1334
|
"futures-util",
|
|
@@ -2132,9 +2129,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|
|
2132
2129
|
|
|
2133
2130
|
[[package]]
|
|
2134
2131
|
name = "syn"
|
|
2135
|
-
version = "2.0.
|
|
2132
|
+
version = "2.0.118"
|
|
2136
2133
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2137
|
-
checksum = "
|
|
2134
|
+
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
|
2138
2135
|
dependencies = [
|
|
2139
2136
|
"proc-macro2",
|
|
2140
2137
|
"quote",
|
|
@@ -2202,12 +2199,11 @@ dependencies = [
|
|
|
2202
2199
|
|
|
2203
2200
|
[[package]]
|
|
2204
2201
|
name = "time"
|
|
2205
|
-
version = "0.3.
|
|
2202
|
+
version = "0.3.49"
|
|
2206
2203
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2207
|
-
checksum = "
|
|
2204
|
+
checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469"
|
|
2208
2205
|
dependencies = [
|
|
2209
2206
|
"deranged",
|
|
2210
|
-
"itoa",
|
|
2211
2207
|
"num-conv",
|
|
2212
2208
|
"powerfmt",
|
|
2213
2209
|
"serde_core",
|
|
@@ -2217,15 +2213,15 @@ dependencies = [
|
|
|
2217
2213
|
|
|
2218
2214
|
[[package]]
|
|
2219
2215
|
name = "time-core"
|
|
2220
|
-
version = "0.1.
|
|
2216
|
+
version = "0.1.9"
|
|
2221
2217
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2222
|
-
checksum = "
|
|
2218
|
+
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
|
|
2223
2219
|
|
|
2224
2220
|
[[package]]
|
|
2225
2221
|
name = "time-macros"
|
|
2226
|
-
version = "0.2.
|
|
2222
|
+
version = "0.2.29"
|
|
2227
2223
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2228
|
-
checksum = "
|
|
2224
|
+
checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d"
|
|
2229
2225
|
dependencies = [
|
|
2230
2226
|
"num-conv",
|
|
2231
2227
|
"time-core",
|
|
@@ -2438,9 +2434,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
|
|
2438
2434
|
|
|
2439
2435
|
[[package]]
|
|
2440
2436
|
name = "wasip2"
|
|
2441
|
-
version = "1.0.
|
|
2437
|
+
version = "1.0.4+wasi-0.2.12"
|
|
2442
2438
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2443
|
-
checksum = "
|
|
2439
|
+
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
|
2444
2440
|
dependencies = [
|
|
2445
2441
|
"wit-bindgen 0.57.1",
|
|
2446
2442
|
]
|
|
@@ -2456,9 +2452,9 @@ dependencies = [
|
|
|
2456
2452
|
|
|
2457
2453
|
[[package]]
|
|
2458
2454
|
name = "wasm-bindgen"
|
|
2459
|
-
version = "0.2.
|
|
2455
|
+
version = "0.2.125"
|
|
2460
2456
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2461
|
-
checksum = "
|
|
2457
|
+
checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
|
|
2462
2458
|
dependencies = [
|
|
2463
2459
|
"cfg-if",
|
|
2464
2460
|
"once_cell",
|
|
@@ -2469,9 +2465,9 @@ dependencies = [
|
|
|
2469
2465
|
|
|
2470
2466
|
[[package]]
|
|
2471
2467
|
name = "wasm-bindgen-macro"
|
|
2472
|
-
version = "0.2.
|
|
2468
|
+
version = "0.2.125"
|
|
2473
2469
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2474
|
-
checksum = "
|
|
2470
|
+
checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
|
|
2475
2471
|
dependencies = [
|
|
2476
2472
|
"quote",
|
|
2477
2473
|
"wasm-bindgen-macro-support",
|
|
@@ -2479,9 +2475,9 @@ dependencies = [
|
|
|
2479
2475
|
|
|
2480
2476
|
[[package]]
|
|
2481
2477
|
name = "wasm-bindgen-macro-support"
|
|
2482
|
-
version = "0.2.
|
|
2478
|
+
version = "0.2.125"
|
|
2483
2479
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2484
|
-
checksum = "
|
|
2480
|
+
checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
|
|
2485
2481
|
dependencies = [
|
|
2486
2482
|
"bumpalo",
|
|
2487
2483
|
"proc-macro2",
|
|
@@ -2492,9 +2488,9 @@ dependencies = [
|
|
|
2492
2488
|
|
|
2493
2489
|
[[package]]
|
|
2494
2490
|
name = "wasm-bindgen-shared"
|
|
2495
|
-
version = "0.2.
|
|
2491
|
+
version = "0.2.125"
|
|
2496
2492
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2497
|
-
checksum = "
|
|
2493
|
+
checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
|
|
2498
2494
|
dependencies = [
|
|
2499
2495
|
"unicode-ident",
|
|
2500
2496
|
]
|
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented here.
|
|
4
4
|
|
|
5
|
+
## [0.14.0] - 2026-06-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Auth Login**: Enhanced `login_user()` to accept a raw `user_id` (string, integer, or UUID) directly as the user argument, resolving Discussion #27.
|
|
9
|
+
- **Blueprint Routing Stabilization**: Stabilized Blueprint routing structure (Issue #29) with support for relative `url_for()` endpoint resolution, nested lifecycle hooks (`before_request`, `after_request`, `teardown_request`), and blueprint-specific error handlers (`@bp.errorhandler`).
|
|
10
|
+
|
|
5
11
|
## [0.13.9] - 2026-06-12
|
|
6
12
|
|
|
7
13
|
### Fixed
|
|
14
|
+
- **Blueprint Routing**: Fixed an issue where routes registered via `app.register_blueprint()` were not added to the application's `url_map`. This ensures `url_for()` can correctly reverse-map Blueprint endpoints (Issue #29).
|
|
8
15
|
- **Session Permanence**: Fixed a bug in `SessionMixin` where `session.permanent` was not serialized into the session payload, causing it to drop across requests. It now functions correctly as a persistent session setting.
|
|
9
16
|
- **Login Manager Remember**: `login_user(user, remember=True)` now automatically sets `session.permanent = True` as expected, tying persistent logins to the application's permanent session lifetime configuration.
|
|
10
17
|
|
|
@@ -144,7 +144,6 @@ class BustAPI(
|
|
|
144
144
|
raise RuntimeError(f"Failed to initialize Rust backend: {e}") from e
|
|
145
145
|
|
|
146
146
|
def register_blueprint(self, blueprint: Blueprint, **options) -> None:
|
|
147
|
-
url_prefix = options.get("url_prefix", blueprint.url_prefix)
|
|
148
147
|
self.blueprints[blueprint.name] = blueprint
|
|
149
148
|
|
|
150
149
|
# Register blueprint template context processors
|
|
@@ -155,20 +154,8 @@ class BustAPI(
|
|
|
155
154
|
blueprint.app_context_processor_funcs
|
|
156
155
|
)
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
rule = url_prefix.rstrip("/") + "/" + rule.lstrip("/")
|
|
161
|
-
full_endpoint = f"{blueprint.name}.{endpoint}"
|
|
162
|
-
self.view_functions[full_endpoint] = view_func
|
|
163
|
-
for method in methods:
|
|
164
|
-
if inspect.iscoroutinefunction(view_func):
|
|
165
|
-
self._rust_app.add_async_route(
|
|
166
|
-
method, rule, create_async_wrapper(self, view_func, rule)
|
|
167
|
-
)
|
|
168
|
-
else:
|
|
169
|
-
self._rust_app.add_route(
|
|
170
|
-
method, rule, create_sync_wrapper(self, view_func, rule)
|
|
171
|
-
)
|
|
157
|
+
# Register all deferred functions (routes, hooks, etc.)
|
|
158
|
+
blueprint.register(self, options)
|
|
172
159
|
|
|
173
160
|
def add_websocket_route(
|
|
174
161
|
self, path: str, handler: Any, config: Optional[Any] = None
|
|
@@ -184,6 +171,38 @@ class BustAPI(
|
|
|
184
171
|
return make_response(*args)
|
|
185
172
|
|
|
186
173
|
def _handle_exception(self, exception: Exception) -> Response:
|
|
174
|
+
from .http.request import request
|
|
175
|
+
|
|
176
|
+
# 1. Try blueprint-specific error handlers first
|
|
177
|
+
bp = (
|
|
178
|
+
self.blueprints.get(request.blueprint)
|
|
179
|
+
if (request and request.blueprint)
|
|
180
|
+
else None
|
|
181
|
+
)
|
|
182
|
+
if bp:
|
|
183
|
+
for exc_class_or_code, handler in bp.error_handler_spec.items():
|
|
184
|
+
if isinstance(exc_class_or_code, type) and isinstance(
|
|
185
|
+
exception, exc_class_or_code
|
|
186
|
+
):
|
|
187
|
+
rv = handler(exception)
|
|
188
|
+
return (
|
|
189
|
+
self._make_response(*rv)
|
|
190
|
+
if isinstance(rv, tuple)
|
|
191
|
+
else self._make_response(rv)
|
|
192
|
+
)
|
|
193
|
+
elif isinstance(exc_class_or_code, int):
|
|
194
|
+
if (
|
|
195
|
+
hasattr(exception, "code")
|
|
196
|
+
and exception.code == exc_class_or_code
|
|
197
|
+
):
|
|
198
|
+
rv = handler(exception)
|
|
199
|
+
return (
|
|
200
|
+
self._make_response(*rv)
|
|
201
|
+
if isinstance(rv, tuple)
|
|
202
|
+
else self._make_response(rv)
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# 2. Try app-wide error handlers
|
|
187
206
|
for exc_class_or_code, handler in self.error_handler_spec.items():
|
|
188
207
|
if isinstance(exc_class_or_code, type) and isinstance(
|
|
189
208
|
exception, exc_class_or_code
|
|
@@ -94,7 +94,7 @@ def login_user(user, remember: bool = False, fresh: bool = True) -> bool:
|
|
|
94
94
|
Log in a user.
|
|
95
95
|
|
|
96
96
|
Args:
|
|
97
|
-
user: User object (must have get_id() method)
|
|
97
|
+
user: User object (must have get_id() method) or user_id (str, int)
|
|
98
98
|
remember: Create persistent session
|
|
99
99
|
fresh: Mark session as fresh (from password login)
|
|
100
100
|
|
|
@@ -106,6 +106,8 @@ def login_user(user, remember: bool = False, fresh: bool = True) -> bool:
|
|
|
106
106
|
if not session:
|
|
107
107
|
return False
|
|
108
108
|
|
|
109
|
+
is_user_id = False
|
|
110
|
+
|
|
109
111
|
# Get user ID
|
|
110
112
|
# 1. Support custom conflict-free get_login_id() first
|
|
111
113
|
user_id = getattr(user, "get_login_id", lambda: None)()
|
|
@@ -122,7 +124,14 @@ def login_user(user, remember: bool = False, fresh: bool = True) -> bool:
|
|
|
122
124
|
try:
|
|
123
125
|
user_id = str(user.id)
|
|
124
126
|
except AttributeError:
|
|
125
|
-
|
|
127
|
+
# 5. Check if `user` itself is the user ID (string, integer, etc.)
|
|
128
|
+
import uuid
|
|
129
|
+
|
|
130
|
+
if isinstance(user, (str, int, uuid.UUID)):
|
|
131
|
+
user_id = str(user)
|
|
132
|
+
is_user_id = True
|
|
133
|
+
else:
|
|
134
|
+
return False
|
|
126
135
|
|
|
127
136
|
# Get login manager config
|
|
128
137
|
login_manager = getattr(request, "app", None)
|
|
@@ -146,7 +155,34 @@ def login_user(user, remember: bool = False, fresh: bool = True) -> bool:
|
|
|
146
155
|
session.permanent = True
|
|
147
156
|
|
|
148
157
|
# Update request
|
|
149
|
-
|
|
158
|
+
if is_user_id:
|
|
159
|
+
loaded_user = None
|
|
160
|
+
if login_manager and login_manager._user_loader_callback:
|
|
161
|
+
import inspect
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
res = login_manager._user_loader_callback(user_id)
|
|
165
|
+
if not inspect.isawaitable(res):
|
|
166
|
+
loaded_user = res
|
|
167
|
+
except Exception:
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
if loaded_user is not None:
|
|
171
|
+
request._login_user = loaded_user
|
|
172
|
+
else:
|
|
173
|
+
from .user import BaseUser
|
|
174
|
+
|
|
175
|
+
class SimpleUser(BaseUser):
|
|
176
|
+
def __init__(self, uid: str):
|
|
177
|
+
self.id = uid
|
|
178
|
+
|
|
179
|
+
def get_id(self) -> str:
|
|
180
|
+
return self.id
|
|
181
|
+
|
|
182
|
+
request._login_user = SimpleUser(user_id)
|
|
183
|
+
else:
|
|
184
|
+
request._login_user = user
|
|
185
|
+
|
|
150
186
|
request._login_fresh = fresh
|
|
151
187
|
|
|
152
188
|
return True
|
|
@@ -59,6 +59,17 @@ def url_for(endpoint: str, **values) -> str:
|
|
|
59
59
|
# Get current application to access url_map
|
|
60
60
|
app = _get_current_object()
|
|
61
61
|
|
|
62
|
+
# Handle relative endpoint (e.g. '.index' -> 'blueprint.index')
|
|
63
|
+
if endpoint.startswith("."):
|
|
64
|
+
from ..http.request import request
|
|
65
|
+
|
|
66
|
+
if request and request.blueprint:
|
|
67
|
+
endpoint = f"{request.blueprint}{endpoint}"
|
|
68
|
+
else:
|
|
69
|
+
raise RuntimeError(
|
|
70
|
+
"Working outside of request context or no blueprint active"
|
|
71
|
+
)
|
|
72
|
+
|
|
62
73
|
# Find rule for endpoint
|
|
63
74
|
rule = None
|
|
64
75
|
for r, options in app.url_map.items():
|
|
@@ -130,8 +130,12 @@ def create_sync_wrapper(app: "BustAPI", handler: Callable, rule: str) -> Callabl
|
|
|
130
130
|
request.session = session
|
|
131
131
|
|
|
132
132
|
# 3. Before Request Hooks
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
bp = app.blueprints.get(request.blueprint) if request.blueprint else None
|
|
134
|
+
before_funcs = (
|
|
135
|
+
bp.before_request_funcs if bp else []
|
|
136
|
+
) + app.before_request_funcs
|
|
137
|
+
if before_funcs:
|
|
138
|
+
for before_func in before_funcs:
|
|
135
139
|
res = before_func()
|
|
136
140
|
if inspect.isawaitable(res):
|
|
137
141
|
res = async_to_sync(res)
|
|
@@ -262,8 +266,12 @@ def create_sync_wrapper(app: "BustAPI", handler: Callable, rule: str) -> Callabl
|
|
|
262
266
|
# 5. Pipeline Cleanup and Hooks
|
|
263
267
|
if app.middleware_manager.middlewares:
|
|
264
268
|
response = app.middleware_manager.process_response(request, response)
|
|
265
|
-
|
|
266
|
-
|
|
269
|
+
bp = app.blueprints.get(request.blueprint) if request.blueprint else None
|
|
270
|
+
after_funcs = (
|
|
271
|
+
bp.after_request_funcs if bp else []
|
|
272
|
+
) + app.after_request_funcs
|
|
273
|
+
if after_funcs:
|
|
274
|
+
for after_func in after_funcs:
|
|
267
275
|
res = after_func(response)
|
|
268
276
|
if inspect.isawaitable(res):
|
|
269
277
|
res = async_to_sync(res)
|
|
@@ -276,8 +284,12 @@ def create_sync_wrapper(app: "BustAPI", handler: Callable, rule: str) -> Callabl
|
|
|
276
284
|
except Exception as e:
|
|
277
285
|
return app._response_to_rust_format(app._handle_exception(e))
|
|
278
286
|
finally:
|
|
279
|
-
|
|
280
|
-
|
|
287
|
+
bp = app.blueprints.get(request.blueprint) if request.blueprint else None
|
|
288
|
+
teardown_funcs = (
|
|
289
|
+
bp.teardown_request_funcs if bp else []
|
|
290
|
+
) + app.teardown_request_funcs
|
|
291
|
+
if teardown_funcs:
|
|
292
|
+
for f in teardown_funcs:
|
|
281
293
|
try:
|
|
282
294
|
res = f(None)
|
|
283
295
|
if inspect.isawaitable(res):
|
|
@@ -325,8 +337,14 @@ def create_async_wrapper(app: "BustAPI", handler: Callable, rule: str) -> Callab
|
|
|
325
337
|
session = app.session_interface.open_session(app, request)
|
|
326
338
|
request.session = session
|
|
327
339
|
|
|
328
|
-
|
|
329
|
-
|
|
340
|
+
bp = (
|
|
341
|
+
app.blueprints.get(request.blueprint) if request.blueprint else None
|
|
342
|
+
)
|
|
343
|
+
before_funcs = (
|
|
344
|
+
bp.before_request_funcs if bp else []
|
|
345
|
+
) + app.before_request_funcs
|
|
346
|
+
if before_funcs:
|
|
347
|
+
for before_func in before_funcs:
|
|
330
348
|
res = before_func()
|
|
331
349
|
if inspect.isawaitable(res):
|
|
332
350
|
res = await res
|
|
@@ -424,8 +442,14 @@ def create_async_wrapper(app: "BustAPI", handler: Callable, rule: str) -> Callab
|
|
|
424
442
|
response = app.middleware_manager.process_response(
|
|
425
443
|
request, response
|
|
426
444
|
)
|
|
427
|
-
|
|
428
|
-
|
|
445
|
+
bp = (
|
|
446
|
+
app.blueprints.get(request.blueprint) if request.blueprint else None
|
|
447
|
+
)
|
|
448
|
+
after_funcs = (
|
|
449
|
+
bp.after_request_funcs if bp else []
|
|
450
|
+
) + app.after_request_funcs
|
|
451
|
+
if after_funcs:
|
|
452
|
+
for after_func in after_funcs:
|
|
429
453
|
res = after_func(response)
|
|
430
454
|
if inspect.isawaitable(res):
|
|
431
455
|
res = await res
|
|
@@ -438,8 +462,14 @@ def create_async_wrapper(app: "BustAPI", handler: Callable, rule: str) -> Callab
|
|
|
438
462
|
except Exception as e:
|
|
439
463
|
return app._handle_exception(e)
|
|
440
464
|
finally:
|
|
441
|
-
|
|
442
|
-
|
|
465
|
+
bp = (
|
|
466
|
+
app.blueprints.get(request.blueprint) if request.blueprint else None
|
|
467
|
+
)
|
|
468
|
+
teardown_funcs = (
|
|
469
|
+
bp.teardown_request_funcs if bp else []
|
|
470
|
+
) + app.teardown_request_funcs
|
|
471
|
+
if teardown_funcs:
|
|
472
|
+
for f in teardown_funcs:
|
|
443
473
|
try:
|
|
444
474
|
res = f(None)
|
|
445
475
|
if inspect.isawaitable(res):
|
|
@@ -49,6 +49,7 @@ class Request:
|
|
|
49
49
|
self._user = None
|
|
50
50
|
self._login_user = None
|
|
51
51
|
self._login_fresh = None
|
|
52
|
+
self.endpoint = None
|
|
52
53
|
|
|
53
54
|
@classmethod
|
|
54
55
|
def _from_rust_request(cls, rust_request) -> "Request":
|
|
@@ -363,6 +364,13 @@ class Request:
|
|
|
363
364
|
"""Set the authenticated user."""
|
|
364
365
|
self._user = value
|
|
365
366
|
|
|
367
|
+
@property
|
|
368
|
+
def blueprint(self) -> Optional[str]:
|
|
369
|
+
"""The name of the current blueprint, if any."""
|
|
370
|
+
if self.endpoint and "." in self.endpoint:
|
|
371
|
+
return self.endpoint.split(".", 1)[0]
|
|
372
|
+
return None
|
|
373
|
+
|
|
366
374
|
|
|
367
375
|
class EnvironHeaders:
|
|
368
376
|
"""
|
|
@@ -62,7 +62,7 @@ class Blueprint:
|
|
|
62
62
|
self.cli_group = cli_group
|
|
63
63
|
|
|
64
64
|
# Deferred functions to be registered when blueprint is registered with app
|
|
65
|
-
self.deferred_functions: List[
|
|
65
|
+
self.deferred_functions: List[Callable[["BlueprintSetupState"], None]] = []
|
|
66
66
|
|
|
67
67
|
# Error handlers
|
|
68
68
|
self.error_handler_spec: Dict[Union[int, type], Callable] = {}
|
|
@@ -95,14 +95,23 @@ class Blueprint:
|
|
|
95
95
|
|
|
96
96
|
def decorator(f: Callable) -> Callable:
|
|
97
97
|
endpoint = options.pop("endpoint", f.__name__)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# Store route info for later registration
|
|
101
|
-
self.deferred_functions.append((rule, endpoint, f, methods))
|
|
98
|
+
self.add_url_rule(rule, endpoint, f, **options)
|
|
102
99
|
return f
|
|
103
100
|
|
|
104
101
|
return decorator
|
|
105
102
|
|
|
103
|
+
def add_url_rule(
|
|
104
|
+
self,
|
|
105
|
+
rule: str,
|
|
106
|
+
endpoint: Optional[str] = None,
|
|
107
|
+
view_func: Optional[Callable] = None,
|
|
108
|
+
**options,
|
|
109
|
+
) -> None:
|
|
110
|
+
"""
|
|
111
|
+
Register a URL rule with the blueprint.
|
|
112
|
+
"""
|
|
113
|
+
self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
|
|
114
|
+
|
|
106
115
|
def get(self, rule: str, **options) -> Callable:
|
|
107
116
|
"""Convenience decorator for GET routes."""
|
|
108
117
|
return self.route(rule, methods=["GET"], **options)
|
|
@@ -181,7 +190,7 @@ class Blueprint:
|
|
|
181
190
|
Returns:
|
|
182
191
|
The original function
|
|
183
192
|
"""
|
|
184
|
-
self.
|
|
193
|
+
self.record_once(lambda s: s.app.before_request_funcs.append(f))
|
|
185
194
|
return f
|
|
186
195
|
|
|
187
196
|
def after_app_request(self, f: Callable) -> Callable:
|
|
@@ -194,7 +203,7 @@ class Blueprint:
|
|
|
194
203
|
Returns:
|
|
195
204
|
The original function
|
|
196
205
|
"""
|
|
197
|
-
self.
|
|
206
|
+
self.record_once(lambda s: s.app.after_request_funcs.append(f))
|
|
198
207
|
return f
|
|
199
208
|
|
|
200
209
|
def teardown_app_request(self, f: Callable) -> Callable:
|
|
@@ -208,7 +217,7 @@ class Blueprint:
|
|
|
208
217
|
Returns:
|
|
209
218
|
The original function
|
|
210
219
|
"""
|
|
211
|
-
self.
|
|
220
|
+
self.record_once(lambda s: s.app.teardown_request_funcs.append(f))
|
|
212
221
|
return f
|
|
213
222
|
|
|
214
223
|
def errorhandler(self, code_or_exception: Union[int, type]) -> Callable:
|
|
@@ -240,8 +249,7 @@ class Blueprint:
|
|
|
240
249
|
"""
|
|
241
250
|
|
|
242
251
|
def decorator(f: Callable) -> Callable:
|
|
243
|
-
|
|
244
|
-
self.error_handler_spec[f"app_{code_or_exception}"] = f
|
|
252
|
+
self.record_once(lambda s: s.app.errorhandler(code_or_exception)(f))
|
|
245
253
|
return f
|
|
246
254
|
|
|
247
255
|
return decorator
|
|
@@ -331,8 +339,7 @@ class Blueprint:
|
|
|
331
339
|
Args:
|
|
332
340
|
func: Function to record
|
|
333
341
|
"""
|
|
334
|
-
|
|
335
|
-
pass
|
|
342
|
+
self.deferred_functions.append(func)
|
|
336
343
|
|
|
337
344
|
def record_once(self, func: Callable) -> None:
|
|
338
345
|
"""
|
|
@@ -341,8 +348,7 @@ class Blueprint:
|
|
|
341
348
|
Args:
|
|
342
349
|
func: Function to record
|
|
343
350
|
"""
|
|
344
|
-
|
|
345
|
-
pass
|
|
351
|
+
self.deferred_functions.append(func)
|
|
346
352
|
|
|
347
353
|
def make_setup_state(self, app, options, first_registration: bool = False):
|
|
348
354
|
"""
|
|
@@ -377,6 +383,9 @@ class Blueprint:
|
|
|
377
383
|
view_func=app.send_static_file,
|
|
378
384
|
)
|
|
379
385
|
|
|
386
|
+
for deferred in self.deferred_functions:
|
|
387
|
+
deferred(state)
|
|
388
|
+
|
|
380
389
|
@property
|
|
381
390
|
def has_static_folder(self) -> bool:
|
|
382
391
|
"""Check if blueprint has a static folder."""
|
|
@@ -455,6 +464,8 @@ class BlueprintSetupState:
|
|
|
455
464
|
self.first_registration = first_registration
|
|
456
465
|
|
|
457
466
|
self.url_prefix = options.get("url_prefix")
|
|
467
|
+
if self.url_prefix is None:
|
|
468
|
+
self.url_prefix = blueprint.url_prefix
|
|
458
469
|
self.subdomain = options.get("subdomain")
|
|
459
470
|
self.url_defaults = options.get("url_defaults")
|
|
460
471
|
|
|
@@ -475,10 +486,14 @@ class BlueprintSetupState:
|
|
|
475
486
|
**options: Additional options
|
|
476
487
|
"""
|
|
477
488
|
if self.url_prefix is not None:
|
|
489
|
+
prefix = self.url_prefix.rstrip("/")
|
|
490
|
+
if not prefix.startswith("/"):
|
|
491
|
+
prefix = f"/{prefix}"
|
|
492
|
+
|
|
478
493
|
if rule:
|
|
479
|
-
rule = f"{
|
|
494
|
+
rule = f"{prefix}/{rule.lstrip('/')}"
|
|
480
495
|
else:
|
|
481
|
-
rule =
|
|
496
|
+
rule = prefix
|
|
482
497
|
|
|
483
498
|
options.setdefault("subdomain", self.subdomain)
|
|
484
499
|
if endpoint is None:
|