nexus-framework 0.2.1__tar.gz → 0.2.2__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.
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/PKG-INFO +1 -1
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/__init__.py +1 -1
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/cli.py +1 -1
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/entities/__init__.py +5 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/app.py +106 -15
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/router.py +111 -4
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/engine.py +5 -2
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/pyproject.toml +1 -1
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/.gitignore +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/LICENSE +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/README.md +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/agent.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/decorator.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/llm.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/memory.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/prompt.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/rag.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/vector.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/config/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/config/settings.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/exceptions/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/interfaces/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/jwt.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/frame.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/loader.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/pipeline.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/sink.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/testing.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/auto.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/expert.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/magic.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/model.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/session.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/sync.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/deployment.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/logging/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/logging/logger.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/magic/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/magic/injector.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/magic/introspect.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/observability.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/plugins/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/py.typed +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/scaffolding.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/testing.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/asgi.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/auth.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/background.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/cookies.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/debugger.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/idempotency.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/lifespan.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/middlewares.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/openapi.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/pagination.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/security.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/session.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/sse.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/staticfiles.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/__init__.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/compiler.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/filters.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/lexer.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/parser.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/upload.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/validation.py +0 -0
- {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/websocket.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: nexus-framework
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Ultimate Simplicity, Uncompromising Power. Next-Gen Python Framework for Web, Data, and AI.
|
|
5
5
|
Project-URL: Homepage, https://github.com/nexus-framework/nexus
|
|
6
6
|
Project-URL: Documentation, https://github.com/nexus-framework/nexus#readme
|
|
@@ -110,7 +110,7 @@ from nexus.web.template.engine import NXEngine
|
|
|
110
110
|
from nexus.web.upload import UploadFile
|
|
111
111
|
from nexus.web.websocket import WebSocket, WebSocketDisconnect, WebSocketGroup
|
|
112
112
|
|
|
113
|
-
__version__ = "0.2.
|
|
113
|
+
__version__ = "0.2.2"
|
|
114
114
|
|
|
115
115
|
__all__ = [
|
|
116
116
|
"LLM",
|
|
@@ -234,7 +234,7 @@ def _handle_db(
|
|
|
234
234
|
NEXUS_BANNER_HELP = """
|
|
235
235
|
==============================================================================
|
|
236
236
|
NEXUS FRAMEWORK CLI
|
|
237
|
-
Ultimate Simplicity, Uncompromising Power (v0.2.
|
|
237
|
+
Ultimate Simplicity, Uncompromising Power (v0.2.2)
|
|
238
238
|
==============================================================================
|
|
239
239
|
|
|
240
240
|
Usage:
|
|
@@ -337,6 +337,11 @@ class Route:
|
|
|
337
337
|
permissions: str | list[str] | tuple[str, ...] | None = None
|
|
338
338
|
roles: str | list[str] | tuple[str, ...] | None = None
|
|
339
339
|
cache: int | None = None
|
|
340
|
+
status_code: int | None = None
|
|
341
|
+
tags: list[str] | None = None
|
|
342
|
+
is_system: bool = False
|
|
343
|
+
override: bool = False
|
|
344
|
+
extra: dict[str, Any] = field(default_factory=dict)
|
|
340
345
|
|
|
341
346
|
def __post_init__(self) -> None:
|
|
342
347
|
if self.roles is not None and self.auth is None:
|
|
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
from nexus.config.settings import NexusSettings, get_settings
|
|
25
|
-
from nexus.core.entities import NexusRequest, NexusResponse
|
|
25
|
+
from nexus.core.entities import HTTPMethod, NexusRequest, NexusResponse
|
|
26
26
|
from nexus.core.exceptions import HTTPException, InternalServerError, NotFoundError
|
|
27
27
|
from nexus.logging.logger import get_logger, setup_logging
|
|
28
28
|
from nexus.web.asgi import (
|
|
@@ -189,7 +189,7 @@ class NexusApp:
|
|
|
189
189
|
def _register_observability_routes(self) -> None:
|
|
190
190
|
"""Register built-in /metrics, /health, and /ready endpoints."""
|
|
191
191
|
|
|
192
|
-
@self.get("/metrics")
|
|
192
|
+
@self.get("/metrics", is_system=True)
|
|
193
193
|
def prometheus_metrics() -> NexusResponse:
|
|
194
194
|
"""Expose Prometheus-formatted metrics."""
|
|
195
195
|
text = self.metrics.render_prometheus_text()
|
|
@@ -199,7 +199,7 @@ class NexusApp:
|
|
|
199
199
|
content_type="text/plain; version=0.0.4; charset=utf-8",
|
|
200
200
|
)
|
|
201
201
|
|
|
202
|
-
@self.get("/health")
|
|
202
|
+
@self.get("/health", is_system=True)
|
|
203
203
|
async def liveness_probe() -> NexusResponse:
|
|
204
204
|
"""Component liveness probe for Kubernetes and load balancers."""
|
|
205
205
|
healthy, details = await self.health_manager.check_liveness()
|
|
@@ -213,7 +213,7 @@ class NexusApp:
|
|
|
213
213
|
content_type="application/json",
|
|
214
214
|
)
|
|
215
215
|
|
|
216
|
-
@self.get("/ready")
|
|
216
|
+
@self.get("/ready", is_system=True)
|
|
217
217
|
async def readiness_probe() -> NexusResponse:
|
|
218
218
|
"""Readiness probe checking backing services (DB, Redis, etc.)."""
|
|
219
219
|
ready, details = await self.health_manager.check_readiness()
|
|
@@ -232,12 +232,12 @@ class NexusApp:
|
|
|
232
232
|
from nexus.core.entities import HTMLResponse, JSONResponse
|
|
233
233
|
from nexus.web.openapi import get_redoc_html, get_swagger_ui_html
|
|
234
234
|
|
|
235
|
-
@self.get("/openapi.json")
|
|
235
|
+
@self.get("/openapi.json", is_system=True)
|
|
236
236
|
def openapi_schema() -> NexusResponse:
|
|
237
237
|
schema = self.openapi_generator.generate_schema(self.router)
|
|
238
238
|
return JSONResponse(schema)
|
|
239
239
|
|
|
240
|
-
@self.get("/docs")
|
|
240
|
+
@self.get("/docs", is_system=True)
|
|
241
241
|
def swagger_ui() -> NexusResponse:
|
|
242
242
|
html = get_swagger_ui_html(
|
|
243
243
|
title=f"{self.title} - Swagger UI",
|
|
@@ -245,7 +245,7 @@ class NexusApp:
|
|
|
245
245
|
)
|
|
246
246
|
return HTMLResponse(html)
|
|
247
247
|
|
|
248
|
-
@self.get("/redoc")
|
|
248
|
+
@self.get("/redoc", is_system=True)
|
|
249
249
|
def redoc_ui() -> NexusResponse:
|
|
250
250
|
html = get_redoc_html(
|
|
251
251
|
title=f"{self.title} - ReDoc",
|
|
@@ -396,6 +396,35 @@ class NexusApp:
|
|
|
396
396
|
# Level 1 (Magical): Route Registration Decorators
|
|
397
397
|
# =========================================================================
|
|
398
398
|
|
|
399
|
+
def route(
|
|
400
|
+
self,
|
|
401
|
+
path: str,
|
|
402
|
+
methods: tuple[HTTPMethod, ...] = (HTTPMethod.GET,),
|
|
403
|
+
name: str | None = None,
|
|
404
|
+
summary: str | None = None,
|
|
405
|
+
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
406
|
+
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
407
|
+
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
408
|
+
cache: int | None = None,
|
|
409
|
+
status_code: int | None = None,
|
|
410
|
+
tags: list[str] | None = None,
|
|
411
|
+
**kwargs: Any,
|
|
412
|
+
) -> Callable[[Any], Any]:
|
|
413
|
+
"""Register a generic HTTP route handler."""
|
|
414
|
+
return self.router.route(
|
|
415
|
+
path,
|
|
416
|
+
methods=methods,
|
|
417
|
+
name=name,
|
|
418
|
+
summary=summary,
|
|
419
|
+
auth=auth,
|
|
420
|
+
permissions=permissions,
|
|
421
|
+
roles=roles,
|
|
422
|
+
cache=cache,
|
|
423
|
+
status_code=status_code,
|
|
424
|
+
tags=tags,
|
|
425
|
+
**kwargs,
|
|
426
|
+
)
|
|
427
|
+
|
|
399
428
|
def get(
|
|
400
429
|
self,
|
|
401
430
|
path: str,
|
|
@@ -404,10 +433,21 @@ class NexusApp:
|
|
|
404
433
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
405
434
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
406
435
|
cache: int | None = None,
|
|
436
|
+
status_code: int | None = None,
|
|
437
|
+
tags: list[str] | None = None,
|
|
438
|
+
**kwargs: Any,
|
|
407
439
|
) -> Callable[[Any], Any]:
|
|
408
440
|
"""Register a GET route handler with optional response caching."""
|
|
409
441
|
return self.router.get(
|
|
410
|
-
path,
|
|
442
|
+
path,
|
|
443
|
+
name=name,
|
|
444
|
+
auth=auth,
|
|
445
|
+
permissions=permissions,
|
|
446
|
+
roles=roles,
|
|
447
|
+
cache=cache,
|
|
448
|
+
status_code=status_code,
|
|
449
|
+
tags=tags,
|
|
450
|
+
**kwargs,
|
|
411
451
|
)
|
|
412
452
|
|
|
413
453
|
def post(
|
|
@@ -417,9 +457,21 @@ class NexusApp:
|
|
|
417
457
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
418
458
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
419
459
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
460
|
+
status_code: int | None = None,
|
|
461
|
+
tags: list[str] | None = None,
|
|
462
|
+
**kwargs: Any,
|
|
420
463
|
) -> Callable[[Any], Any]:
|
|
421
464
|
"""Register a POST route handler."""
|
|
422
|
-
return self.router.post(
|
|
465
|
+
return self.router.post(
|
|
466
|
+
path,
|
|
467
|
+
name=name,
|
|
468
|
+
auth=auth,
|
|
469
|
+
permissions=permissions,
|
|
470
|
+
roles=roles,
|
|
471
|
+
status_code=status_code,
|
|
472
|
+
tags=tags,
|
|
473
|
+
**kwargs,
|
|
474
|
+
)
|
|
423
475
|
|
|
424
476
|
def put(
|
|
425
477
|
self,
|
|
@@ -428,9 +480,21 @@ class NexusApp:
|
|
|
428
480
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
429
481
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
430
482
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
483
|
+
status_code: int | None = None,
|
|
484
|
+
tags: list[str] | None = None,
|
|
485
|
+
**kwargs: Any,
|
|
431
486
|
) -> Callable[[Any], Any]:
|
|
432
487
|
"""Register a PUT route handler."""
|
|
433
|
-
return self.router.put(
|
|
488
|
+
return self.router.put(
|
|
489
|
+
path,
|
|
490
|
+
name=name,
|
|
491
|
+
auth=auth,
|
|
492
|
+
permissions=permissions,
|
|
493
|
+
roles=roles,
|
|
494
|
+
status_code=status_code,
|
|
495
|
+
tags=tags,
|
|
496
|
+
**kwargs,
|
|
497
|
+
)
|
|
434
498
|
|
|
435
499
|
def patch(
|
|
436
500
|
self,
|
|
@@ -439,9 +503,21 @@ class NexusApp:
|
|
|
439
503
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
440
504
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
441
505
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
506
|
+
status_code: int | None = None,
|
|
507
|
+
tags: list[str] | None = None,
|
|
508
|
+
**kwargs: Any,
|
|
442
509
|
) -> Callable[[Any], Any]:
|
|
443
510
|
"""Register a PATCH route handler."""
|
|
444
|
-
return self.router.patch(
|
|
511
|
+
return self.router.patch(
|
|
512
|
+
path,
|
|
513
|
+
name=name,
|
|
514
|
+
auth=auth,
|
|
515
|
+
permissions=permissions,
|
|
516
|
+
roles=roles,
|
|
517
|
+
status_code=status_code,
|
|
518
|
+
tags=tags,
|
|
519
|
+
**kwargs,
|
|
520
|
+
)
|
|
445
521
|
|
|
446
522
|
def delete(
|
|
447
523
|
self,
|
|
@@ -450,9 +526,21 @@ class NexusApp:
|
|
|
450
526
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
451
527
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
452
528
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
529
|
+
status_code: int | None = None,
|
|
530
|
+
tags: list[str] | None = None,
|
|
531
|
+
**kwargs: Any,
|
|
453
532
|
) -> Callable[[Any], Any]:
|
|
454
533
|
"""Register a DELETE route handler."""
|
|
455
|
-
return self.router.delete(
|
|
534
|
+
return self.router.delete(
|
|
535
|
+
path,
|
|
536
|
+
name=name,
|
|
537
|
+
auth=auth,
|
|
538
|
+
permissions=permissions,
|
|
539
|
+
roles=roles,
|
|
540
|
+
status_code=status_code,
|
|
541
|
+
tags=tags,
|
|
542
|
+
**kwargs,
|
|
543
|
+
)
|
|
456
544
|
|
|
457
545
|
def include_blueprint(self, blueprint: Any, prefix: str = "") -> None:
|
|
458
546
|
"""Mount a Blueprint / RouteGroup into the application router."""
|
|
@@ -473,6 +561,9 @@ class NexusApp:
|
|
|
473
561
|
permissions=route.permissions,
|
|
474
562
|
roles=route.roles,
|
|
475
563
|
cache=route.cache,
|
|
564
|
+
status_code=route.status_code,
|
|
565
|
+
tags=route.tags,
|
|
566
|
+
extra=route.extra,
|
|
476
567
|
)
|
|
477
568
|
self.router.add_route(routed)
|
|
478
569
|
else:
|
|
@@ -640,7 +731,7 @@ class NexusApp:
|
|
|
640
731
|
if inspect.isawaitable(result):
|
|
641
732
|
result = await result
|
|
642
733
|
|
|
643
|
-
response = self._normalize_response(result)
|
|
734
|
+
response = self._normalize_response(result, default_status_code=route.status_code or 200)
|
|
644
735
|
|
|
645
736
|
# Apply response caching & ETag computation
|
|
646
737
|
if (
|
|
@@ -667,13 +758,13 @@ class NexusApp:
|
|
|
667
758
|
|
|
668
759
|
return response
|
|
669
760
|
|
|
670
|
-
def _normalize_response(self, result: Any) -> NexusResponse:
|
|
761
|
+
def _normalize_response(self, result: Any, default_status_code: int = 200) -> NexusResponse:
|
|
671
762
|
"""Normalize handler return values into a NexusResponse."""
|
|
672
763
|
if isinstance(result, NexusResponse):
|
|
673
764
|
return result
|
|
674
765
|
|
|
675
766
|
# Handle (data, status_code) tuple return
|
|
676
|
-
status_code =
|
|
767
|
+
status_code = default_status_code
|
|
677
768
|
data = result
|
|
678
769
|
if isinstance(result, tuple) and len(result) == 2 and isinstance(result[1], int):
|
|
679
770
|
data, status_code = result
|
|
@@ -112,10 +112,14 @@ class Router:
|
|
|
112
112
|
current.children[part] = RadixNode(part)
|
|
113
113
|
current = current.children[part]
|
|
114
114
|
|
|
115
|
-
for r in self._all_routes[:-1]:
|
|
115
|
+
for r in list(self._all_routes[:-1]):
|
|
116
116
|
if r.path == route.path:
|
|
117
117
|
for method in route.methods:
|
|
118
118
|
if method in r.methods:
|
|
119
|
+
if getattr(r, "is_system", False) or getattr(route, "override", False):
|
|
120
|
+
if r in self._all_routes:
|
|
121
|
+
self._all_routes.remove(r)
|
|
122
|
+
continue
|
|
119
123
|
msg = f"Duplicate route: [{method.value}] {route.path} already registered."
|
|
120
124
|
raise ValueError(msg)
|
|
121
125
|
|
|
@@ -132,10 +136,15 @@ class Router:
|
|
|
132
136
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
133
137
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
134
138
|
cache: int | None = None,
|
|
139
|
+
status_code: int | None = None,
|
|
140
|
+
tags: list[str] | None = None,
|
|
141
|
+
**kwargs: Any,
|
|
135
142
|
) -> Callable[[Any], Any]:
|
|
136
143
|
"""Decorator to register a handler directly on this router."""
|
|
137
144
|
|
|
138
145
|
def decorator(handler: Any) -> Any:
|
|
146
|
+
is_sys = bool(kwargs.pop("is_system", False))
|
|
147
|
+
is_ovr = bool(kwargs.pop("override", False))
|
|
139
148
|
new_route = Route(
|
|
140
149
|
path=path,
|
|
141
150
|
methods=methods,
|
|
@@ -146,6 +155,11 @@ class Router:
|
|
|
146
155
|
permissions=permissions,
|
|
147
156
|
roles=roles,
|
|
148
157
|
cache=cache,
|
|
158
|
+
status_code=status_code,
|
|
159
|
+
tags=tags,
|
|
160
|
+
is_system=is_sys,
|
|
161
|
+
override=is_ovr,
|
|
162
|
+
extra=kwargs,
|
|
149
163
|
)
|
|
150
164
|
self.add_route(new_route)
|
|
151
165
|
return handler
|
|
@@ -160,6 +174,9 @@ class Router:
|
|
|
160
174
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
161
175
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
162
176
|
cache: int | None = None,
|
|
177
|
+
status_code: int | None = None,
|
|
178
|
+
tags: list[str] | None = None,
|
|
179
|
+
**kwargs: Any,
|
|
163
180
|
) -> Callable[[Any], Any]:
|
|
164
181
|
"""Decorator for GET routes."""
|
|
165
182
|
return self.route(
|
|
@@ -170,6 +187,9 @@ class Router:
|
|
|
170
187
|
permissions=permissions,
|
|
171
188
|
roles=roles,
|
|
172
189
|
cache=cache,
|
|
190
|
+
status_code=status_code,
|
|
191
|
+
tags=tags,
|
|
192
|
+
**kwargs,
|
|
173
193
|
)
|
|
174
194
|
|
|
175
195
|
def post(
|
|
@@ -179,6 +199,9 @@ class Router:
|
|
|
179
199
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
180
200
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
181
201
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
202
|
+
status_code: int | None = None,
|
|
203
|
+
tags: list[str] | None = None,
|
|
204
|
+
**kwargs: Any,
|
|
182
205
|
) -> Callable[[Any], Any]:
|
|
183
206
|
"""Decorator for POST routes."""
|
|
184
207
|
return self.route(
|
|
@@ -188,6 +211,9 @@ class Router:
|
|
|
188
211
|
auth=auth,
|
|
189
212
|
permissions=permissions,
|
|
190
213
|
roles=roles,
|
|
214
|
+
status_code=status_code,
|
|
215
|
+
tags=tags,
|
|
216
|
+
**kwargs,
|
|
191
217
|
)
|
|
192
218
|
|
|
193
219
|
def put(
|
|
@@ -197,6 +223,9 @@ class Router:
|
|
|
197
223
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
198
224
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
199
225
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
226
|
+
status_code: int | None = None,
|
|
227
|
+
tags: list[str] | None = None,
|
|
228
|
+
**kwargs: Any,
|
|
200
229
|
) -> Callable[[Any], Any]:
|
|
201
230
|
"""Decorator for PUT routes."""
|
|
202
231
|
return self.route(
|
|
@@ -206,6 +235,9 @@ class Router:
|
|
|
206
235
|
auth=auth,
|
|
207
236
|
permissions=permissions,
|
|
208
237
|
roles=roles,
|
|
238
|
+
status_code=status_code,
|
|
239
|
+
tags=tags,
|
|
240
|
+
**kwargs,
|
|
209
241
|
)
|
|
210
242
|
|
|
211
243
|
def patch(
|
|
@@ -215,6 +247,9 @@ class Router:
|
|
|
215
247
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
216
248
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
217
249
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
250
|
+
status_code: int | None = None,
|
|
251
|
+
tags: list[str] | None = None,
|
|
252
|
+
**kwargs: Any,
|
|
218
253
|
) -> Callable[[Any], Any]:
|
|
219
254
|
"""Decorator for PATCH routes."""
|
|
220
255
|
return self.route(
|
|
@@ -224,6 +259,9 @@ class Router:
|
|
|
224
259
|
auth=auth,
|
|
225
260
|
permissions=permissions,
|
|
226
261
|
roles=roles,
|
|
262
|
+
status_code=status_code,
|
|
263
|
+
tags=tags,
|
|
264
|
+
**kwargs,
|
|
227
265
|
)
|
|
228
266
|
|
|
229
267
|
def delete(
|
|
@@ -233,6 +271,9 @@ class Router:
|
|
|
233
271
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
234
272
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
235
273
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
274
|
+
status_code: int | None = None,
|
|
275
|
+
tags: list[str] | None = None,
|
|
276
|
+
**kwargs: Any,
|
|
236
277
|
) -> Callable[[Any], Any]:
|
|
237
278
|
"""Decorator for DELETE routes."""
|
|
238
279
|
return self.route(
|
|
@@ -242,6 +283,9 @@ class Router:
|
|
|
242
283
|
auth=auth,
|
|
243
284
|
permissions=permissions,
|
|
244
285
|
roles=roles,
|
|
286
|
+
status_code=status_code,
|
|
287
|
+
tags=tags,
|
|
288
|
+
**kwargs,
|
|
245
289
|
)
|
|
246
290
|
|
|
247
291
|
def match(self, path: str, method: str) -> tuple[Route, dict[str, Any]] | None:
|
|
@@ -334,9 +378,29 @@ class Router:
|
|
|
334
378
|
class Blueprint:
|
|
335
379
|
"""Modular Route Group with prefix and isolated/inherited routing rules."""
|
|
336
380
|
|
|
337
|
-
def __init__(
|
|
338
|
-
self
|
|
339
|
-
|
|
381
|
+
def __init__(
|
|
382
|
+
self,
|
|
383
|
+
name_or_prefix: str = "",
|
|
384
|
+
prefix: str | None = None,
|
|
385
|
+
name: str | None = None,
|
|
386
|
+
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
387
|
+
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
388
|
+
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
389
|
+
) -> None:
|
|
390
|
+
if prefix is not None:
|
|
391
|
+
raw_prefix = prefix
|
|
392
|
+
self.name = name or (name_or_prefix if name_or_prefix != prefix else None)
|
|
393
|
+
elif name_or_prefix.startswith("/"):
|
|
394
|
+
raw_prefix = name_or_prefix
|
|
395
|
+
self.name = name
|
|
396
|
+
else:
|
|
397
|
+
raw_prefix = ""
|
|
398
|
+
self.name = name or (name_or_prefix if name_or_prefix else None)
|
|
399
|
+
|
|
400
|
+
self.prefix = "/" + raw_prefix.strip("/") if raw_prefix else ""
|
|
401
|
+
self.auth = auth
|
|
402
|
+
self.permissions = permissions
|
|
403
|
+
self.roles = roles
|
|
340
404
|
self.routes: list[Route] = []
|
|
341
405
|
|
|
342
406
|
def add_route(self, route: Route) -> None:
|
|
@@ -353,6 +417,9 @@ class Blueprint:
|
|
|
353
417
|
permissions=route.permissions,
|
|
354
418
|
roles=route.roles,
|
|
355
419
|
cache=route.cache,
|
|
420
|
+
status_code=route.status_code,
|
|
421
|
+
tags=route.tags,
|
|
422
|
+
extra=route.extra,
|
|
356
423
|
)
|
|
357
424
|
self.routes.append(scoped_route)
|
|
358
425
|
|
|
@@ -366,8 +433,13 @@ class Blueprint:
|
|
|
366
433
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
367
434
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
368
435
|
cache: int | None = None,
|
|
436
|
+
status_code: int | None = None,
|
|
437
|
+
tags: list[str] | None = None,
|
|
438
|
+
**kwargs: Any,
|
|
369
439
|
) -> Callable[[Any], Any]:
|
|
370
440
|
def decorator(handler: Any) -> Any:
|
|
441
|
+
is_sys = bool(kwargs.pop("is_system", False))
|
|
442
|
+
is_ovr = bool(kwargs.pop("override", False))
|
|
371
443
|
new_route = Route(
|
|
372
444
|
path=path,
|
|
373
445
|
methods=methods,
|
|
@@ -378,6 +450,11 @@ class Blueprint:
|
|
|
378
450
|
permissions=permissions,
|
|
379
451
|
roles=roles,
|
|
380
452
|
cache=cache,
|
|
453
|
+
status_code=status_code,
|
|
454
|
+
tags=tags,
|
|
455
|
+
is_system=is_sys,
|
|
456
|
+
override=is_ovr,
|
|
457
|
+
extra=kwargs,
|
|
381
458
|
)
|
|
382
459
|
self.add_route(new_route)
|
|
383
460
|
return handler
|
|
@@ -392,6 +469,9 @@ class Blueprint:
|
|
|
392
469
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
393
470
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
394
471
|
cache: int | None = None,
|
|
472
|
+
status_code: int | None = None,
|
|
473
|
+
tags: list[str] | None = None,
|
|
474
|
+
**kwargs: Any,
|
|
395
475
|
) -> Callable[[Any], Any]:
|
|
396
476
|
return self.route(
|
|
397
477
|
path,
|
|
@@ -401,6 +481,9 @@ class Blueprint:
|
|
|
401
481
|
permissions=permissions,
|
|
402
482
|
roles=roles,
|
|
403
483
|
cache=cache,
|
|
484
|
+
status_code=status_code,
|
|
485
|
+
tags=tags,
|
|
486
|
+
**kwargs,
|
|
404
487
|
)
|
|
405
488
|
|
|
406
489
|
def post(
|
|
@@ -410,6 +493,9 @@ class Blueprint:
|
|
|
410
493
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
411
494
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
412
495
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
496
|
+
status_code: int | None = None,
|
|
497
|
+
tags: list[str] | None = None,
|
|
498
|
+
**kwargs: Any,
|
|
413
499
|
) -> Callable[[Any], Any]:
|
|
414
500
|
return self.route(
|
|
415
501
|
path,
|
|
@@ -418,6 +504,9 @@ class Blueprint:
|
|
|
418
504
|
auth=auth,
|
|
419
505
|
permissions=permissions,
|
|
420
506
|
roles=roles,
|
|
507
|
+
status_code=status_code,
|
|
508
|
+
tags=tags,
|
|
509
|
+
**kwargs,
|
|
421
510
|
)
|
|
422
511
|
|
|
423
512
|
def put(
|
|
@@ -427,6 +516,9 @@ class Blueprint:
|
|
|
427
516
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
428
517
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
429
518
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
519
|
+
status_code: int | None = None,
|
|
520
|
+
tags: list[str] | None = None,
|
|
521
|
+
**kwargs: Any,
|
|
430
522
|
) -> Callable[[Any], Any]:
|
|
431
523
|
return self.route(
|
|
432
524
|
path,
|
|
@@ -435,6 +527,9 @@ class Blueprint:
|
|
|
435
527
|
auth=auth,
|
|
436
528
|
permissions=permissions,
|
|
437
529
|
roles=roles,
|
|
530
|
+
status_code=status_code,
|
|
531
|
+
tags=tags,
|
|
532
|
+
**kwargs,
|
|
438
533
|
)
|
|
439
534
|
|
|
440
535
|
def patch(
|
|
@@ -444,6 +539,9 @@ class Blueprint:
|
|
|
444
539
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
445
540
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
446
541
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
542
|
+
status_code: int | None = None,
|
|
543
|
+
tags: list[str] | None = None,
|
|
544
|
+
**kwargs: Any,
|
|
447
545
|
) -> Callable[[Any], Any]:
|
|
448
546
|
return self.route(
|
|
449
547
|
path,
|
|
@@ -452,6 +550,9 @@ class Blueprint:
|
|
|
452
550
|
auth=auth,
|
|
453
551
|
permissions=permissions,
|
|
454
552
|
roles=roles,
|
|
553
|
+
status_code=status_code,
|
|
554
|
+
tags=tags,
|
|
555
|
+
**kwargs,
|
|
455
556
|
)
|
|
456
557
|
|
|
457
558
|
def delete(
|
|
@@ -461,6 +562,9 @@ class Blueprint:
|
|
|
461
562
|
auth: bool | str | list[str] | tuple[str, ...] | None = None,
|
|
462
563
|
permissions: str | list[str] | tuple[str, ...] | None = None,
|
|
463
564
|
roles: str | list[str] | tuple[str, ...] | None = None,
|
|
565
|
+
status_code: int | None = None,
|
|
566
|
+
tags: list[str] | None = None,
|
|
567
|
+
**kwargs: Any,
|
|
464
568
|
) -> Callable[[Any], Any]:
|
|
465
569
|
return self.route(
|
|
466
570
|
path,
|
|
@@ -469,4 +573,7 @@ class Blueprint:
|
|
|
469
573
|
auth=auth,
|
|
470
574
|
permissions=permissions,
|
|
471
575
|
roles=roles,
|
|
576
|
+
status_code=status_code,
|
|
577
|
+
tags=tags,
|
|
578
|
+
**kwargs,
|
|
472
579
|
)
|
|
@@ -25,12 +25,15 @@ class NXEngine:
|
|
|
25
25
|
|
|
26
26
|
def __init__(
|
|
27
27
|
self,
|
|
28
|
-
templates_dir: str | Path =
|
|
28
|
+
templates_dir: str | Path | None = None,
|
|
29
29
|
*,
|
|
30
|
+
template_dir: str | Path | None = None,
|
|
30
31
|
auto_reload: bool = True,
|
|
31
32
|
cache_size: int = 500,
|
|
32
33
|
) -> None:
|
|
33
|
-
|
|
34
|
+
target = templates_dir if templates_dir is not None else (template_dir or "./templates")
|
|
35
|
+
self.templates_dir = Path(target).resolve()
|
|
36
|
+
self.template_dir = self.templates_dir
|
|
34
37
|
self.auto_reload = auto_reload
|
|
35
38
|
self.cache_size = cache_size
|
|
36
39
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nexus-framework"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Ultimate Simplicity, Uncompromising Power. Next-Gen Python Framework for Web, Data, and AI."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|