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.
Files changed (72) hide show
  1. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/PKG-INFO +1 -1
  2. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/__init__.py +1 -1
  3. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/cli.py +1 -1
  4. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/entities/__init__.py +5 -0
  5. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/app.py +106 -15
  6. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/router.py +111 -4
  7. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/engine.py +5 -2
  8. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/pyproject.toml +1 -1
  9. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/.gitignore +0 -0
  10. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/LICENSE +0 -0
  11. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/README.md +0 -0
  12. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/__init__.py +0 -0
  13. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/agent.py +0 -0
  14. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/decorator.py +0 -0
  15. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/llm.py +0 -0
  16. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/memory.py +0 -0
  17. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/prompt.py +0 -0
  18. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/rag.py +0 -0
  19. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/ai/vector.py +0 -0
  20. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/config/__init__.py +0 -0
  21. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/config/settings.py +0 -0
  22. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/__init__.py +0 -0
  23. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/exceptions/__init__.py +0 -0
  24. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/interfaces/__init__.py +0 -0
  25. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/core/jwt.py +0 -0
  26. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/__init__.py +0 -0
  27. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/frame.py +0 -0
  28. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/loader.py +0 -0
  29. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/pipeline.py +0 -0
  30. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/sink.py +0 -0
  31. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/data/testing.py +0 -0
  32. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/__init__.py +0 -0
  33. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/auto.py +0 -0
  34. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/expert.py +0 -0
  35. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/magic.py +0 -0
  36. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/model.py +0 -0
  37. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/session.py +0 -0
  38. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/db/sync.py +0 -0
  39. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/deployment.py +0 -0
  40. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/logging/__init__.py +0 -0
  41. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/logging/logger.py +0 -0
  42. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/magic/__init__.py +0 -0
  43. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/magic/injector.py +0 -0
  44. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/magic/introspect.py +0 -0
  45. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/observability.py +0 -0
  46. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/plugins/__init__.py +0 -0
  47. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/py.typed +0 -0
  48. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/scaffolding.py +0 -0
  49. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/testing.py +0 -0
  50. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/__init__.py +0 -0
  51. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/asgi.py +0 -0
  52. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/auth.py +0 -0
  53. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/background.py +0 -0
  54. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/cookies.py +0 -0
  55. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/debugger.py +0 -0
  56. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/idempotency.py +0 -0
  57. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/lifespan.py +0 -0
  58. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/middlewares.py +0 -0
  59. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/openapi.py +0 -0
  60. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/pagination.py +0 -0
  61. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/security.py +0 -0
  62. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/session.py +0 -0
  63. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/sse.py +0 -0
  64. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/staticfiles.py +0 -0
  65. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/__init__.py +0 -0
  66. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/compiler.py +0 -0
  67. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/filters.py +0 -0
  68. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/lexer.py +0 -0
  69. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/template/parser.py +0 -0
  70. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/upload.py +0 -0
  71. {nexus_framework-0.2.1 → nexus_framework-0.2.2}/nexus/web/validation.py +0 -0
  72. {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.1
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.1"
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.1)
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, name=name, auth=auth, permissions=permissions, roles=roles, cache=cache
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(path, name=name, auth=auth, permissions=permissions, roles=roles)
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(path, name=name, auth=auth, permissions=permissions, roles=roles)
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(path, name=name, auth=auth, permissions=permissions, roles=roles)
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(path, name=name, auth=auth, permissions=permissions, roles=roles)
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 = 200
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__(self, prefix: str = "", name: str | None = None) -> None:
338
- self.prefix = "/" + prefix.strip("/") if prefix else ""
339
- self.name = name
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 = "./templates",
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
- self.templates_dir = Path(templates_dir).resolve()
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.1"
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