diff --git a/velocix/__init__.py b/velocix/__init__.py index 41ebe03..d7e3f9d 100644 --- a/velocix/__init__.py +++ b/velocix/__init__.py @@ -57,9 +57,8 @@ from velocix.http.multipart import MultipartForm, UploadFile # OpenAPI and Documentation -from velocix.openapi.auto_docs import AutoDocRouter, enable_auto_docs +from velocix.openapi.auto_docs import enable_auto_docs from velocix.openapi.decorators import operation, parameter, response -from velocix.openapi.decorators_style import Body, Path, delete, get, patch, post, put from velocix.openapi.generator import OpenAPIGenerator from velocix.security.cors import CORSMiddleware @@ -139,20 +138,11 @@ def create_app( "PlainTextResponse", "RedirectResponse", # OpenAPI & Documentation - "AutoDocRouter", "enable_auto_docs", "OpenAPIGenerator", "operation", "parameter", "response", - # Decorator-style syntax - "get", - "post", - "put", - "delete", - "patch", - "Path", - "Body", # Parameter markers "Query", "Header", diff --git a/velocix/core/depends.py b/velocix/core/depends.py index e8dbba5..3bfc262 100644 --- a/velocix/core/depends.py +++ b/velocix/core/depends.py @@ -7,7 +7,7 @@ import inspect import types from collections.abc import Callable -from typing import Any, TypeVar, Union, get_args, get_origin, get_type_hints +from typing import Any, Union, get_args, get_origin, get_type_hints import msgspec import orjson @@ -84,8 +84,6 @@ def _extract_marker(annotation: Any) -> tuple[Any, Any] | None: ] _plan_cache: dict[int, tuple[Callable[..., Any], PlanEntry]] = {} -T = TypeVar("T") - class Depends: """ @@ -656,69 +654,3 @@ async def resolve_dependencies( return kwargs - -class DependencyCache: - """ - Request-scoped dependency cache (FastAPI pattern). - Automatically managed by resolve_dependencies. - """ - - __slots__ = ("_cache",) - - def __init__(self): - self._cache: dict[str, Any] = {} - - def get(self, key: str, default: Any = None) -> Any: - """Get cached dependency""" - return self._cache.get(key, default) - - def set(self, key: str, value: Any) -> None: - """Cache dependency""" - self._cache[key] = value - - def clear(self) -> None: - """Clear all cached dependencies""" - self._cache.clear() - - def __contains__(self, key: str) -> bool: - return key in self._cache - - def __len__(self) -> int: - return len(self._cache) - - -def inject(dependency: Callable[..., T]) -> T: - """ - Type-safe dependency injection helper. - - Usage: - async def get_db() -> Database: - return Database() - - @app.get("/users") - async def get_users(db: Database = inject(get_db)): - return await db.fetch_all() - - This is a type-safe alternative to Depends() that works better - with type checkers like mypy. - """ - return Depends(dependency) # type: ignore - - -# Cleanup old cache entries to prevent memory leaks -def cleanup_caches(max_size: int = 1000) -> None: - """Clean up signature and type hints caches""" - global _sig_cache, _type_hints_cache, _plan_cache - - if len(_sig_cache) > max_size: - # Keep most recent entries - sig_items = list(_sig_cache.items()) - _sig_cache = dict(sig_items[-max_size:]) - - if len(_type_hints_cache) > max_size: - hints_items = list(_type_hints_cache.items()) - _type_hints_cache = dict(hints_items[-max_size:]) - - if len(_plan_cache) > max_size: - plan_items = list(_plan_cache.items()) - _plan_cache = dict(plan_items[-max_size:]) diff --git a/velocix/core/router.py b/velocix/core/router.py index 87e4f61..6876462 100644 --- a/velocix/core/router.py +++ b/velocix/core/router.py @@ -6,17 +6,11 @@ from collections import defaultdict from collections.abc import Callable from dataclasses import dataclass, field -from typing import Any, Optional, Protocol +from typing import Any, Optional from .exceptions import MethodNotAllowed, NotFound -class HandlerProtocol(Protocol): - """Protocol for route handlers""" - - def __call__(self, *args: Any, **kwargs: Any) -> Any: ... - - @dataclass class RouteMetrics: """Per-route hit tracking, only attached when Router(metrics_enabled=True)""" @@ -77,7 +71,6 @@ def __init__(self, *, metrics_enabled: bool = False): "WEBSOCKET": RouteNode(), } self.static_routes: dict[str, dict[str, Callable]] = defaultdict(dict) - self.dynamic_patterns: list[tuple] = [] self.middleware_stack: list[Callable] = [] # Flat (method, path, handler, name) registration log for include_router self._registered: list[tuple[str, str, Callable, str | None]] = [] diff --git a/velocix/openapi/__init__.py b/velocix/openapi/__init__.py index b5141e8..730cc28 100644 --- a/velocix/openapi/__init__.py +++ b/velocix/openapi/__init__.py @@ -1,9 +1,7 @@ """OpenAPI support for Velocix""" from .auto_docs import ( - AutoDocRouter, auto_document_function, - create_auto_router, enable_auto_docs, generate_operation_from_function, ) @@ -21,23 +19,9 @@ string_schema, tag, ) -from .decorators_style import ( - Body, - Path, - Query, - VelocixStyleDocs, - create_docs, - delete, - get, - post, - put, - responses, - tags, -) from .generator import ( OpenAPIGenerator, ReDocHandler, - SwaggerUIHandler, create_openapi_generator, setup_docs_routes, ) @@ -86,26 +70,11 @@ "object_schema", # Generator "OpenAPIGenerator", - "SwaggerUIHandler", "ReDocHandler", "create_openapi_generator", "setup_docs_routes", - # Velocix-style (recommended) - "get", - "post", - "put", - "delete", - "Path", - "Query", - "Body", - "responses", - "tags", - "VelocixStyleDocs", - "create_docs", # Auto-documentation (zero decorators!) - "AutoDocRouter", "enable_auto_docs", - "create_auto_router", "auto_document_function", "generate_operation_from_function", ] diff --git a/velocix/openapi/auto_docs.py b/velocix/openapi/auto_docs.py index 65d9e8f..395e7db 100644 --- a/velocix/openapi/auto_docs.py +++ b/velocix/openapi/auto_docs.py @@ -1,7 +1,6 @@ """Automatic OpenAPI generation from function signatures""" import inspect -from collections.abc import Callable from typing import Any, get_origin, get_type_hints from ..core.depends import Depends @@ -505,65 +504,6 @@ def auto_document_function(func: Any, path: str, method: str) -> Any: return func -class AutoDocRouter: - """Router that automatically generates OpenAPI documentation""" - - def __init__(self, auto_docs: bool = True, auto_tags: bool = True): - self.routes: list[Any] = [] - self.auto_docs = auto_docs - self.auto_tags = auto_tags - - def _add_route(self, path: str, method: str, handler: Any) -> Any: - """Add route with automatic documentation""" - if self.auto_docs: - handler = auto_document_function(handler, path, method) - - route = type("Route", (), {"path": path, "method": method.upper(), "handler": handler})() - - self.routes.append(route) - return handler - - def get(self, path: str) -> Callable: - """GET route with auto-docs""" - - def decorator(func: Any) -> Any: - return self._add_route(path, "GET", func) - - return decorator - - def post(self, path: str) -> Callable: - """POST route with auto-docs""" - - def decorator(func: Any) -> Any: - return self._add_route(path, "POST", func) - - return decorator - - def put(self, path: str) -> Callable: - """PUT route with auto-docs""" - - def decorator(func: Any) -> Any: - return self._add_route(path, "PUT", func) - - return decorator - - def delete(self, path: str) -> Callable: - """DELETE route with auto-docs""" - - def decorator(func: Any) -> Any: - return self._add_route(path, "DELETE", func) - - return decorator - - def patch(self, path: str) -> Callable: - """PATCH route with auto-docs""" - - def decorator(func: Any) -> Any: - return self._add_route(path, "PATCH", func) - - return decorator - - # Integration with existing Velocix router def enable_auto_docs( app: Any, @@ -644,8 +584,3 @@ async def get_redoc(): return app - -# Convenience function -def create_auto_router(auto_docs: bool = True, auto_tags: bool = True) -> AutoDocRouter: - """Create a router with automatic OpenAPI documentation""" - return AutoDocRouter(auto_docs=auto_docs, auto_tags=auto_tags) diff --git a/velocix/openapi/decorators_style.py b/velocix/openapi/decorators_style.py deleted file mode 100644 index d930657..0000000 --- a/velocix/openapi/decorators_style.py +++ /dev/null @@ -1,267 +0,0 @@ -"""Simplified OpenAPI interface with decorator-style syntax""" - -from typing import Any - -from .decorators import operation as _operation -from .decorators import parameter as _parameter -from .decorators import response as _response -from .generator import OpenAPIGenerator -from .models import ParameterIn - - -# Simplified decorators with automatic defaults -def get( - path: str, - summary: str | None = None, - tags: list[str] | None = None, - response_model: type[Any] | None = None, -): - """GET decorator with automatic OpenAPI generation""" - - def decorator(func): - # Auto-generate summary from function name if not provided - if not summary: - func_summary = func.__name__.replace("_", " ").title() - else: - func_summary = summary - - # Apply operation decorator - decorated = _operation(summary=func_summary, tags=tags or [])(func) - - # Add default 200 response - if response_model: - decorated = _response(200, "Success", schema={"type": "object"})(decorated) - else: - decorated = _response(200, "Success")(decorated) - - return decorated - - return decorator - - -def patch( - path: str, - summary: str | None = None, - tags: list[str] | None = None, - response_model: type[Any] | None = None, -): - """PATCH decorator with automatic OpenAPI generation""" - - def decorator(func): - # Auto-generate summary from function name if not provided - if not summary: - func_summary = func.__name__.replace("_", " ").title() - else: - func_summary = summary - - # Apply operation decorator - decorated = _operation(summary=func_summary, tags=tags or [])(func) - - # Add default 200 response - if response_model: - decorated = _response(200, "Success", schema={"type": "object"})(decorated) - - return decorated - - return decorator - - -def post( - path: str, - summary: str | None = None, - tags: list[str] | None = None, - response_model: type[Any] | None = None, -): - """Velocix-style POST decorator""" - - def decorator(func): - if not summary: - func_summary = func.__name__.replace("_", " ").title() - else: - func_summary = summary - - decorated = _operation(summary=func_summary, tags=tags or [])(func) - - if response_model: - decorated = _response(201, "Created", schema={"type": "object"})(decorated) - else: - decorated = _response(201, "Created")(decorated) - - return decorated - - return decorator - - -def put( - path: str, - summary: str | None = None, - tags: list[str] | None = None, - response_model: type[Any] | None = None, -): - """Velocix-style PUT decorator""" - - def decorator(func): - if not summary: - func_summary = func.__name__.replace("_", " ").title() - else: - func_summary = summary - - decorated = _operation(summary=func_summary, tags=tags or [])(func) - - decorated = _response(200, "Success")(decorated) - return decorated - - return decorator - - -def delete(path: str, summary: str | None = None, tags: list[str] | None = None): - """Velocix-style DELETE decorator""" - - def decorator(func): - if not summary: - func_summary = func.__name__.replace("_", " ").title() - else: - func_summary = summary - - decorated = _operation(summary=func_summary, tags=tags or [])(func) - - decorated = _response(204, "Deleted")(decorated) - return decorated - - return decorator - - -# Path parameter helper -def Path(description: str = "", example: Any = None): - """Velocix-style Path parameter""" - - def wrapper(func): - import inspect - - sig = inspect.signature(func) - for param_name in sig.parameters: - func = _parameter( - param_name, - ParameterIn.PATH, - description=description or f"Path parameter {param_name}", - required=True, - example=example, - )(func) - return func - - return wrapper - - -# Query parameter helper -def Query(default: Any = None, description: str = "", example: Any = None): - """Velocix-style Query parameter""" - - def wrapper(func): - import inspect - - sig = inspect.signature(func) - for param_name in sig.parameters: - func = _parameter( - param_name, - ParameterIn.QUERY, - description=description or f"Query parameter {param_name}", - required=default is None, - example=example or default, - )(func) - return func - - return wrapper - - -# Body parameter helper -def Body(description: str = "Request body", example: Any = None): - """Velocix-style Body parameter""" - - def wrapper(func): - from .decorators import request_body - - return request_body( - description=description, - schema={"type": "object", "example": example} if example else {"type": "object"}, - )(func) - - return wrapper - - -# Response helper -def responses(**status_responses): - """Velocix-style multiple responses""" - - def wrapper(func): - for status_code, response_data in status_responses.items(): - if isinstance(response_data, str): - func = _response(status_code, response_data)(func) - elif isinstance(response_data, dict): - func = _response( - status_code, - response_data.get("description", "Response"), - schema=response_data.get("model"), - )(func) - return func - - return wrapper - - -# Tags helper -def tags(*tag_names: str): - """Velocix-style tags""" - - def wrapper(func): - return _operation(tags=list(tag_names))(func) - - return wrapper - - -# Complete Velocix-style app setup -class VelocixStyleDocs: - """Velocix-style documentation setup""" - - def __init__( - self, - title: str = "Velocix", - description: str | None = None, - version: str = "0.1.0", - openapi_url: str = "/openapi.json", - docs_url: str = "/docs", - redoc_url: str = "/redoc", - ): - self.generator = OpenAPIGenerator(title=title, version=version, description=description) - self.openapi_url = openapi_url - self.docs_url = docs_url - self.redoc_url = redoc_url - - def setup_docs(self, router): - """Setup documentation routes on router""" - from .generator import setup_docs_routes - - setup_docs_routes(router, self.generator) - - -# Convenience function for quick setup -def create_docs( - title: str = "API", description: str | None = None, version: str = "1.0.0" -) -> VelocixStyleDocs: - """Create Velocix-style docs with minimal setup""" - return VelocixStyleDocs(title=title, description=description, version=version) - - -# Export commonly used items -__all__ = [ - "get", - "post", - "put", - "delete", - "patch", - "Path", - "Query", - "Body", - "responses", - "tags", - "VelocixStyleDocs", - "create_docs", -] diff --git a/velocix/validation/models.py b/velocix/validation/models.py index 00c42e2..19d87ee 100644 --- a/velocix/validation/models.py +++ b/velocix/validation/models.py @@ -153,19 +153,3 @@ def copy(self: T, **changes: Any) -> T: current.update(changes) return self.from_dict(current) - -def create_model(name: str, **field_definitions: Any) -> type[msgspec.Struct]: - """Dynamically create a msgspec Struct model""" - annotations = {} - defaults = {} - - for field_name, field_type in field_definitions.items(): - if isinstance(field_type, tuple): - annotations[field_name] = field_type[0] - defaults[field_name] = field_type[1] - else: - annotations[field_name] = field_type - - namespace = {"__annotations__": annotations, **defaults} - - return type(name, (msgspec.Struct,), namespace)