orionis 0.651.0__py3-none-any.whl → 0.653.0__py3-none-any.whl

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.
@@ -11,6 +11,7 @@ from orionis.container.contracts.container import IContainer
11
11
  from orionis.container.entities.binding import Binding
12
12
  from orionis.container.enums.lifetimes import Lifetime
13
13
  from orionis.container.exceptions import OrionisContainerException
14
+ from orionis.container.exceptions.container import OrionisContainerTypeError
14
15
  from orionis.container.validators import (
15
16
  ImplementsAbstractMethods,
16
17
  IsAbstractClass,
@@ -28,6 +29,8 @@ from orionis.services.introspection.concretes.reflection import ReflectionConcre
28
29
  from orionis.services.introspection.dependencies.entities.argument import Argument
29
30
  from orionis.services.introspection.dependencies.entities.resolve_argument import ResolveArguments
30
31
  from orionis.services.introspection.dependencies.reflection import ReflectDependencies
32
+ from orionis.services.introspection.instances.reflection import ReflectionInstance
33
+ from orionis.services.introspection.objects.types import Type
31
34
 
32
35
  class Container(IContainer):
33
36
 
@@ -178,6 +181,7 @@ class Container(IContainer):
178
181
  return future.result()
179
182
 
180
183
  else:
184
+
181
185
  # If loop exists but not running, we can run the coroutine
182
186
  return loop.run_until_complete(result)
183
187
 
@@ -225,12 +229,8 @@ class Container(IContainer):
225
229
  total_dependencies = len(dependencies.resolved) + len(dependencies.unresolved)
226
230
 
227
231
  # If the callable does not require any dependencies, invoke directly
228
- if total_dependencies == 0:
229
- result = fn(*args, **kwargs)
230
- return self.__handleSyncAsyncResult(result)
231
-
232
232
  # If enough arguments are provided, invoke directly
233
- if total_provided_args >= total_dependencies:
233
+ if (total_dependencies == 0) or (total_provided_args >= total_dependencies):
234
234
  result = fn(*args, **kwargs)
235
235
  return self.__handleSyncAsyncResult(result)
236
236
 
@@ -298,6 +298,184 @@ class Container(IContainer):
298
298
  f"Expected function signature: {function_name}{signature}"
299
299
  ) from e
300
300
 
301
+ def __decouplingCheck(
302
+ self,
303
+ abstract: Callable[..., Any],
304
+ concrete: Callable[..., Any],
305
+ enforce_decoupling: bool
306
+ ) -> None:
307
+ """
308
+ Validates the decoupling relationship between abstract and concrete classes.
309
+
310
+ Parameters
311
+ ----------
312
+ abstract : Callable[..., Any]
313
+ The abstract base class.
314
+ concrete : Callable[..., Any]
315
+ The concrete implementation class.
316
+ enforce_decoupling : bool
317
+ Whether to enforce that concrete does NOT inherit from abstract.
318
+
319
+ Raises
320
+ ------
321
+ OrionisContainerException
322
+ If the decoupling check fails.
323
+ """
324
+
325
+ if enforce_decoupling:
326
+ if issubclass(concrete, abstract):
327
+ raise OrionisContainerException(
328
+ "The concrete class must NOT inherit from the provided abstract class. "
329
+ "Please ensure that the concrete class is not a subclass of the specified abstract class."
330
+ )
331
+ else:
332
+ if not issubclass(concrete, abstract):
333
+ raise OrionisContainerException(
334
+ "The concrete class must inherit from the provided abstract class. "
335
+ "Please ensure that the concrete class is a subclass of the specified abstract class."
336
+ )
337
+
338
+ def __implementsAbstractMethods(
339
+ self,
340
+ *,
341
+ abstract: Callable[..., Any] = None,
342
+ concrete: Callable[..., Any] = None,
343
+ instance: Any = None
344
+ ) -> None:
345
+ """
346
+ Validates that a concrete class or instance implements all abstract methods defined in an abstract class.
347
+
348
+ Parameters
349
+ ----------
350
+ abstract : Callable[..., Any]
351
+ The abstract base class.
352
+ concrete : Callable[..., Any], optional
353
+ The class expected to implement the abstract methods.
354
+ instance : Any, optional
355
+ The instance expected to implement the abstract methods.
356
+
357
+ Raises
358
+ ------
359
+ OrionisContainerException
360
+ If any abstract method is not implemented.
361
+ """
362
+
363
+ # Validate that the abstract class is provided
364
+ if abstract is None:
365
+ raise OrionisContainerException("Abstract class must be provided for implementation check.")
366
+
367
+ # Instantiation of ReflectionAbstract for potential future use
368
+ rf_abstract = ReflectionAbstract(abstract)
369
+
370
+ # Check if the abstract class has abstract methods
371
+ abstract_methods = rf_abstract.getMethods()
372
+ if not abstract_methods:
373
+ raise OrionisContainerException(
374
+ f"The abstract class '{abstract.__name__}' does not define any abstract methods. "
375
+ "An abstract class must have at least one abstract method."
376
+ )
377
+
378
+ # Determine the target class or instance to check
379
+ target = concrete if concrete is not None else instance
380
+ if target is None:
381
+ raise OrionisContainerException("Either concrete class or instance must be provided for implementation check.")
382
+
383
+ # Validate that the target is a class or instance
384
+ target_class = target if Type(target).isClass() else target.__class__
385
+
386
+ # Instantiation of ReflectionConcrete for potential future use
387
+ rf_class = ReflectionConcrete(target_class)
388
+
389
+ # Extract class names for error messages
390
+ target_name = rf_class.getClassName()
391
+ abstract_name = rf_abstract.getClassName()
392
+
393
+ # Extract methods implemented by the target class
394
+ implemented_methods = rf_class.getMethods()
395
+
396
+ # Check if the target class implements all abstract methods
397
+ not_implemented = []
398
+ for method in abstract_methods:
399
+ if method not in implemented_methods:
400
+ not_implemented.append(method)
401
+
402
+ # If any abstract methods are not implemented, raise an exception
403
+ if not_implemented:
404
+ formatted = "\n • " + "\n • ".join(not_implemented)
405
+ raise OrionisContainerException(
406
+ f"'{target_name}' does not implement the following abstract methods defined in '{abstract_name}':{formatted}\n"
407
+ "Please ensure that all abstract methods are implemented."
408
+ )
409
+
410
+ def __makeAliasKey(
411
+ self,
412
+ abstract: Callable[..., Any],
413
+ alias: str = None
414
+ ) -> str:
415
+ """
416
+ Generates a unique and valid key for an alias based on the abstract class and optional alias.
417
+
418
+ This method ensures that the alias used for service registration is valid and unique.
419
+ If an explicit alias is provided, it validates the alias for type, emptiness, and
420
+ forbidden characters. If no alias is provided, it generates a default alias using
421
+ the abstract class's module and name.
422
+
423
+ Parameters
424
+ ----------
425
+ abstract : Callable[..., Any]
426
+ The abstract base class or interface for which the alias is being generated.
427
+ alias : str, optional
428
+ An optional custom alias to use instead of the default generated alias.
429
+
430
+ Returns
431
+ -------
432
+ str
433
+ The validated or generated alias key. If a valid alias is provided, it is returned
434
+ directly. Otherwise, the default alias in the format 'module.ClassName' is returned.
435
+
436
+ Raises
437
+ ------
438
+ OrionisContainerTypeError
439
+ If the provided alias is None, empty, whitespace only, not a string, or contains
440
+ invalid characters.
441
+
442
+ Notes
443
+ -----
444
+ - The alias must not contain whitespace or special symbols.
445
+ - If no alias is provided, the default alias is generated using the abstract's module
446
+ and class name.
447
+ """
448
+
449
+ # Set of characters that are not allowed in aliases
450
+ invalid_chars = set(' \t\n\r\x0b\x0c!@#$%^&*()[]{};:,/<>?\\|`~"\'')
451
+
452
+ # If an alias is provided, validate and use it directly
453
+ if alias:
454
+ # Check for None, empty string, or whitespace-only alias
455
+ if alias is None or alias == "" or str(alias).isspace():
456
+ raise OrionisContainerTypeError(
457
+ "Alias cannot be None, empty, or whitespace only."
458
+ )
459
+
460
+ # Ensure the alias is a string
461
+ if not isinstance(alias, str):
462
+ raise OrionisContainerTypeError(
463
+ f"Expected a string type for alias, but got {type(alias).__name__} instead."
464
+ )
465
+
466
+ # Check for invalid characters in the alias
467
+ if any(char in invalid_chars for char in alias):
468
+ raise OrionisContainerTypeError(
469
+ f"Alias '{alias}' contains invalid characters. "
470
+ "Aliases must not contain whitespace or special symbols."
471
+ )
472
+
473
+ # Return the validated alias
474
+ return alias
475
+
476
+ # If no alias is provided, generate a default alias using module and class name
477
+ return f"{abstract.__module__}.{abstract.__name__}"
478
+
301
479
  def transient(
302
480
  self,
303
481
  abstract: Callable[..., Any],
@@ -309,79 +487,190 @@ class Container(IContainer):
309
487
  """
310
488
  Registers a service with a transient lifetime.
311
489
 
490
+ This method binds a concrete implementation to an abstract base type or interface
491
+ in the container, ensuring that a new instance of the concrete class is created
492
+ each time the service is requested. It validates the abstract and concrete types,
493
+ enforces decoupling rules if specified, checks that all abstract methods are implemented,
494
+ and manages service aliases.
495
+
312
496
  Parameters
313
497
  ----------
314
498
  abstract : Callable[..., Any]
315
- The abstract base type or interface to be bound.
499
+ The abstract base type or interface to be bound. Must be an abstract class or interface.
316
500
  concrete : Callable[..., Any]
317
- The concrete implementation to associate with the abstract type.
501
+ The concrete implementation to associate with the abstract type. Must be a concrete class.
318
502
  alias : str, optional
319
- An alternative name to register the service under. If not provided, the abstract's class name is used.
503
+ An alternative name to register the service under. If not provided, a default alias is generated
504
+ using the abstract's module and class name.
505
+ enforce_decoupling : bool, optional
506
+ If True, enforces that the concrete class does NOT inherit from the abstract class.
507
+ If False, requires that the concrete class is a subclass of the abstract.
320
508
 
321
509
  Returns
322
510
  -------
323
- bool
324
- True if the service was registered successfully.
511
+ bool or None
512
+ Returns True if the service was registered successfully.
513
+ Returns None if registration fails due to an exception.
325
514
 
326
515
  Raises
327
516
  ------
328
517
  OrionisContainerTypeError
329
- If the abstract or concrete class checks fail.
518
+ If the abstract or concrete class validation fails.
330
519
  OrionisContainerException
331
- If the concrete class inherits from the abstract class.
520
+ If the decoupling check fails or if an unexpected error occurs during registration.
332
521
 
333
522
  Notes
334
523
  -----
335
- Registers the given concrete implementation to the abstract type with a transient lifetime,
336
- meaning a new instance will be created each time the service is requested. Optionally, an alias
337
- can be provided for registration.
524
+ - Registers the given concrete implementation to the abstract type with a transient lifetime,
525
+ meaning a new instance will be created each time the service is requested.
526
+ - Validates the abstract and concrete types, checks decoupling rules, ensures all abstract methods
527
+ are implemented, and manages service aliases.
528
+ - If a service is already registered under the same abstract or alias, it is removed before registering
529
+ the new binding.
338
530
  """
339
531
 
340
- # Ensure that abstract is an abstract class
341
- IsAbstractClass(abstract, Lifetime.TRANSIENT)
532
+ try:
342
533
 
343
- # Ensure that concrete is a concrete class
344
- IsConcreteClass(concrete, Lifetime.TRANSIENT)
534
+ # Ensure that abstract is an abstract class
535
+ ReflectionAbstract.ensureIsAbstractClass(abstract)
345
536
 
346
- # Ensure that concrete is NOT a subclass of abstract
347
- if enforce_decoupling:
348
- IsNotSubclass(abstract, concrete)
537
+ # Ensure that concrete is a concrete class
538
+ ReflectionConcrete.ensureIsConcreteClass(concrete)
349
539
 
350
- # Validate that concrete is a subclass of abstract
351
- else:
352
- IsSubclass(abstract, concrete)
540
+ # Enforce decoupling or subclass relationship as specified
541
+ self.__decouplingCheck(abstract, concrete, enforce_decoupling)
353
542
 
354
- # Ensure implementation
355
- ImplementsAbstractMethods(
356
- abstract=abstract,
357
- concrete=concrete
358
- )
543
+ # Ensure all abstract methods are implemented by the concrete class
544
+ self.__implementsAbstractMethods(
545
+ abstract=abstract,
546
+ concrete=concrete
547
+ )
359
548
 
360
- # Ensure that the alias is a valid string if provided
361
- if alias:
362
- IsValidAlias(alias)
549
+ # Validate and generate the alias key (either provided or default)
550
+ alias = self.__makeAliasKey(abstract, alias)
363
551
 
364
- # Cretate a default alias if none provided
365
- else:
366
- alias = f"{abstract.__module__}.{abstract.__name__}"
552
+ # If the service is already registered, remove the existing binding
553
+ self.drop(abstract, alias)
367
554
 
368
- # If the service is already registered, drop it
369
- self.drop(abstract, alias)
555
+ # Register the service with transient lifetime
556
+ self.__bindings[abstract] = Binding(
557
+ contract = abstract,
558
+ concrete = concrete,
559
+ lifetime = Lifetime.TRANSIENT,
560
+ enforce_decoupling = enforce_decoupling,
561
+ alias = alias
562
+ )
370
563
 
371
- # Register the service with transient lifetime
372
- self.__bindings[abstract] = Binding(
373
- contract = abstract,
374
- concrete = concrete,
375
- lifetime = Lifetime.TRANSIENT,
376
- enforce_decoupling = enforce_decoupling,
377
- alias = alias
378
- )
564
+ # Register the alias for lookup
565
+ self.__aliases[alias] = self.__bindings[abstract]
379
566
 
380
- # Register the alias
381
- self.__aliases[alias] = self.__bindings[abstract]
567
+ # Return True to indicate successful registration
568
+ return True
382
569
 
383
- # Return True to indicate successful registration
384
- return True
570
+ except Exception as e:
571
+
572
+ # Raise a container exception with details if registration fails
573
+ raise OrionisContainerException(
574
+ f"Unexpected error registering {Lifetime.TRANSIENT} service: {e}"
575
+ ) from e
576
+
577
+ def instance(
578
+ self,
579
+ abstract: Callable[..., Any],
580
+ instance: Any,
581
+ *,
582
+ alias: str = None,
583
+ enforce_decoupling: bool = False
584
+ ) -> Optional[bool]:
585
+ """
586
+ Registers an instance of a class or interface in the container with singleton lifetime.
587
+
588
+ This method validates the abstract type, the instance, and the alias (if provided).
589
+ It ensures that the instance is a valid implementation of the abstract class or interface,
590
+ optionally enforces decoupling, and registers the instance in the container under both
591
+ the abstract type and the alias. The registered instance will be shared across all resolutions
592
+ of the abstract type or alias.
593
+
594
+ Parameters
595
+ ----------
596
+ abstract : Callable[..., Any]
597
+ The abstract class or interface to associate with the instance.
598
+ instance : Any
599
+ The concrete instance to register.
600
+ alias : str, optional
601
+ An optional alias to register the instance under. If not provided,
602
+ a default alias is generated from the abstract's module and class name.
603
+ enforce_decoupling : bool, optional
604
+ If True, enforces that the instance's class does NOT inherit from the abstract class.
605
+ If False, requires that the instance's class is a subclass of the abstract.
606
+
607
+ Returns
608
+ -------
609
+ bool or None
610
+ Returns True if the instance was successfully registered.
611
+ Returns None if registration fails due to an exception.
612
+
613
+ Raises
614
+ ------
615
+ OrionisContainerTypeError
616
+ If `abstract` is not an abstract class or if `alias` is not a valid string.
617
+ OrionisContainerException
618
+ If the instance is not a valid implementation, fails decoupling check,
619
+ or if registration fails for any other reason.
620
+
621
+ Notes
622
+ -----
623
+ - The instance is registered with singleton lifetime, meaning it will be shared
624
+ across all resolutions of the abstract type or alias.
625
+ - All abstract methods must be implemented by the instance.
626
+ - If a service is already registered under the same abstract or alias, it is removed
627
+ before registering the new instance.
628
+ """
629
+
630
+ try:
631
+
632
+ # Ensure that the abstract is an abstract class
633
+ ReflectionAbstract.ensureIsAbstractClass(abstract)
634
+
635
+ # Ensure that the instance is a valid instance of the abstract
636
+ ReflectionInstance.ensureIsInstance(instance)
637
+
638
+ # Enforce decoupling or subclass relationship as specified
639
+ self.__decouplingCheck(abstract, instance.__class__, enforce_decoupling)
640
+
641
+ # Ensure all abstract methods are implemented by the instance
642
+ self.__implementsAbstractMethods(
643
+ abstract=abstract,
644
+ instance=instance
645
+ )
646
+
647
+ # Validate and generate the alias key (either provided or default)
648
+ alias = self.__makeAliasKey(abstract, alias)
649
+
650
+ # Remove any existing binding for this abstract or alias
651
+ self.drop(abstract, alias)
652
+
653
+ # Register the instance with singleton lifetime
654
+ self.__bindings[abstract] = Binding(
655
+ contract = abstract,
656
+ instance = instance,
657
+ lifetime = Lifetime.SINGLETON,
658
+ enforce_decoupling = enforce_decoupling,
659
+ alias = alias
660
+ )
661
+
662
+ # Register the alias for lookup
663
+ self.__aliases[alias] = self.__bindings[abstract]
664
+
665
+ # Return True to indicate successful registration
666
+ return True
667
+
668
+ except Exception as e:
669
+
670
+ # Raise a container exception with details if registration fails
671
+ raise OrionisContainerException(
672
+ f"Unexpected error registering instance: {e}"
673
+ ) from e
385
674
 
386
675
  def singleton(
387
676
  self,
@@ -394,76 +683,91 @@ class Container(IContainer):
394
683
  """
395
684
  Registers a service with a singleton lifetime.
396
685
 
686
+ This method binds a concrete implementation to an abstract base type or interface
687
+ in the container, ensuring that only one instance of the concrete class is created
688
+ and shared throughout the application's lifetime. It validates the abstract and
689
+ concrete types, enforces decoupling rules if specified, checks that all abstract
690
+ methods are implemented, and manages service aliases.
691
+
397
692
  Parameters
398
693
  ----------
399
694
  abstract : Callable[..., Any]
400
- The abstract base type or interface to be bound.
695
+ The abstract base type or interface to be bound. Must be an abstract class or interface.
401
696
  concrete : Callable[..., Any]
402
- The concrete implementation to associate with the abstract type.
697
+ The concrete implementation to associate with the abstract type. Must be a concrete class.
403
698
  alias : str, optional
404
- An alternative name to register the service under. If not provided, the abstract's class name is used.
699
+ An alternative name to register the service under. If not provided, a default alias is generated
700
+ using the abstract's module and class name.
701
+ enforce_decoupling : bool, optional
702
+ If True, enforces that the concrete class does NOT inherit from the abstract class.
703
+ If False, requires that the concrete class is a subclass of the abstract.
405
704
 
406
705
  Returns
407
706
  -------
408
- bool
409
- True if the service was registered successfully.
707
+ bool or None
708
+ Returns True if the service was registered successfully.
709
+ Returns None if registration fails due to an exception.
410
710
 
411
711
  Raises
412
712
  ------
413
713
  OrionisContainerTypeError
414
- If the abstract or concrete class checks fail.
714
+ If the abstract or concrete class validation fails.
415
715
  OrionisContainerException
416
- If the concrete class inherits from the abstract class.
716
+ If the decoupling check fails or if an unexpected error occurs during registration.
417
717
 
418
718
  Notes
419
719
  -----
420
- Registers the given concrete implementation to the abstract type with a singleton lifetime,
421
- meaning a single instance will be created and shared. Optionally, an alias can be provided for registration.
720
+ - Registers the given concrete implementation to the abstract type with a singleton lifetime,
721
+ meaning a single instance will be created and shared for all resolutions.
722
+ - If a service is already registered under the same abstract or alias, it is removed before registering the new binding.
723
+ - All abstract methods must be implemented by the concrete class.
724
+ - Aliases are validated and managed for lookup.
422
725
  """
423
726
 
424
- # Ensure that abstract is an abstract class
425
- IsAbstractClass(abstract, Lifetime.SINGLETON)
727
+ try:
426
728
 
427
- # Ensure that concrete is a concrete class
428
- IsConcreteClass(concrete, Lifetime.SINGLETON)
729
+ # Ensure that abstract is an abstract class
730
+ ReflectionAbstract.ensureIsAbstractClass(abstract)
429
731
 
430
- # Ensure that concrete is NOT a subclass of abstract
431
- if enforce_decoupling:
432
- IsNotSubclass(abstract, concrete)
732
+ # Ensure that concrete is a concrete class
733
+ ReflectionConcrete.ensureIsConcreteClass(concrete)
433
734
 
434
- # Validate that concrete is a subclass of abstract
435
- else:
436
- IsSubclass(abstract, concrete)
735
+ # Enforce decoupling or subclass relationship as specified
736
+ self.__decouplingCheck(abstract, concrete, enforce_decoupling)
437
737
 
438
- # Ensure implementation
439
- ImplementsAbstractMethods(
440
- abstract=abstract,
441
- concrete=concrete
442
- )
738
+ # Ensure all abstract methods are implemented by the concrete class
739
+ self.__implementsAbstractMethods(
740
+ abstract=abstract,
741
+ concrete=concrete
742
+ )
443
743
 
444
- # Ensure that the alias is a valid string if provided
445
- if alias:
446
- IsValidAlias(alias)
447
- else:
448
- alias = f"{abstract.__module__}.{abstract.__name__}"
744
+ # Validate and generate the alias key (either provided or default)
745
+ alias = self.__makeAliasKey(abstract, alias)
449
746
 
450
- # If the service is already registered, drop it
451
- self.drop(abstract, alias)
747
+ # If the service is already registered, remove the existing binding
748
+ self.drop(abstract, alias)
452
749
 
453
- # Register the service with singleton lifetime
454
- self.__bindings[abstract] = Binding(
455
- contract = abstract,
456
- concrete = concrete,
457
- lifetime = Lifetime.SINGLETON,
458
- enforce_decoupling = enforce_decoupling,
459
- alias = alias
460
- )
750
+ # Register the service with singleton lifetime
751
+ self.__bindings[abstract] = Binding(
752
+ contract = abstract,
753
+ concrete = concrete,
754
+ lifetime = Lifetime.SINGLETON,
755
+ enforce_decoupling = enforce_decoupling,
756
+ alias = alias
757
+ )
461
758
 
462
- # Register the alias
463
- self.__aliases[alias] = self.__bindings[abstract]
759
+ # Register the alias for lookup
760
+ self.__aliases[alias] = self.__bindings[abstract]
464
761
 
465
- # Return True to indicate successful registration
466
- return True
762
+ # Return True to indicate successful registration
763
+ return True
764
+
765
+ except Exception as e:
766
+
767
+ # Raise a container exception with details if registration fails
768
+ raise OrionisContainerException(
769
+ f"Unexpected error registering {Lifetime.SINGLETON} service: {e}"
770
+ ) from e
467
771
 
468
772
  def scoped(
469
773
  self,
@@ -476,76 +780,101 @@ class Container(IContainer):
476
780
  """
477
781
  Registers a service with a scoped lifetime.
478
782
 
783
+ This method binds a concrete implementation to an abstract base type or interface
784
+ in the container, ensuring that a new instance of the concrete class is created
785
+ for each scope context. It validates the abstract and concrete types, enforces
786
+ decoupling rules if specified, checks that all abstract methods are implemented,
787
+ and manages service aliases.
788
+
479
789
  Parameters
480
790
  ----------
481
791
  abstract : Callable[..., Any]
482
- The abstract base type or interface to be bound.
792
+ The abstract base type or interface to be bound. Must be an abstract class or interface.
483
793
  concrete : Callable[..., Any]
484
- The concrete implementation to associate with the abstract type.
794
+ The concrete implementation to associate with the abstract type. Must be a concrete class.
485
795
  alias : str, optional
486
- An alternative name to register the service under. If not provided, the abstract's class name is used.
796
+ An alternative name to register the service under. If not provided, a default alias is generated
797
+ using the abstract's module and class name.
798
+ enforce_decoupling : bool, optional
799
+ If True, enforces that the concrete class does NOT inherit from the abstract class.
800
+ If False, requires that the concrete class is a subclass of the abstract.
487
801
 
488
802
  Returns
489
803
  -------
490
- bool
491
- True if the service was registered successfully.
804
+ bool or None
805
+ Returns True if the service was registered successfully.
806
+ Returns None if registration fails due to an exception.
492
807
 
493
808
  Raises
494
809
  ------
495
810
  OrionisContainerTypeError
496
- If the abstract or concrete class checks fail.
811
+ If the abstract or concrete class validation fails.
497
812
  OrionisContainerException
498
- If the concrete class inherits from the abstract class.
813
+ If the decoupling check fails or if an unexpected error occurs during registration.
499
814
 
500
815
  Notes
501
816
  -----
502
- Registers the given concrete implementation to the abstract type with a scoped lifetime,
503
- meaning a new instance will be created per scope. Optionally, an alias can be provided for registration.
817
+ - Registers the given concrete implementation to the abstract type with a scoped lifetime,
818
+ meaning a new instance will be created for each scope context.
819
+ - Validates the abstract and concrete types, checks decoupling rules, ensures all abstract methods
820
+ are implemented, and manages service aliases.
821
+ - If a service is already registered under the same abstract or alias, it is removed before registering
822
+ the new binding.
504
823
  """
505
824
 
506
- # Ensure that abstract is an abstract class
507
- IsAbstractClass(abstract, Lifetime.SCOPED)
825
+ try:
826
+
827
+ # Ensure that abstract is an abstract class
828
+ ReflectionAbstract.ensureIsAbstractClass(abstract)
829
+
830
+ # Ensure that concrete is a concrete class
831
+ ReflectionConcrete.ensureIsConcreteClass(concrete)
832
+
833
+ # Enforce decoupling or subclass relationship as specified
834
+ self.__decouplingCheck(abstract, concrete, enforce_decoupling)
835
+
836
+ # Ensure all abstract methods are implemented by the concrete class
837
+ self.__implementsAbstractMethods(
838
+ abstract=abstract,
839
+ concrete=concrete
840
+ )
841
+
842
+ # Validate and generate the alias key (either provided or default)
843
+ alias = self.__makeAliasKey(abstract, alias)
844
+
845
+ # If the service is already registered, remove the existing binding
846
+ self.drop(abstract, alias)
847
+
848
+ # Register the service with scoped lifetime
849
+ self.__bindings[abstract] = Binding(
850
+ contract = abstract,
851
+ concrete = concrete,
852
+ lifetime = Lifetime.SCOPED,
853
+ enforce_decoupling = enforce_decoupling,
854
+ alias = alias
855
+ )
856
+
857
+ # Register the alias for lookup
858
+ self.__aliases[alias] = self.__bindings[abstract]
859
+
860
+ # Return True to indicate successful registration
861
+ return True
862
+
863
+ except Exception as e:
864
+
865
+ # Raise a container exception with details if registration fails
866
+ raise OrionisContainerException(
867
+ f"Unexpected error registering {Lifetime.SCOPED} service: {e}"
868
+ ) from e
508
869
 
509
- # Ensure that concrete is a concrete class
510
- IsConcreteClass(concrete, Lifetime.SCOPED)
511
870
 
512
- # Ensure that concrete is NOT a subclass of abstract
513
- if enforce_decoupling:
514
- IsNotSubclass(abstract, concrete)
515
871
 
516
- # Validate that concrete is a subclass of abstract
517
- else:
518
- IsSubclass(abstract, concrete)
519
872
 
520
- # Ensure implementation
521
- ImplementsAbstractMethods(
522
- abstract=abstract,
523
- concrete=concrete
524
- )
525
873
 
526
- # Ensure that the alias is a valid string if provided
527
- if alias:
528
- IsValidAlias(alias)
529
- else:
530
- alias = f"{abstract.__module__}.{abstract.__name__}"
531
874
 
532
- # If the service is already registered, drop it
533
- self.drop(abstract, alias)
534
875
 
535
- # Register the service with scoped lifetime
536
- self.__bindings[abstract] = Binding(
537
- contract = abstract,
538
- concrete = concrete,
539
- lifetime = Lifetime.SCOPED,
540
- enforce_decoupling = enforce_decoupling,
541
- alias = alias
542
- )
543
876
 
544
- # Register the alias
545
- self.__aliases[alias] = self.__bindings[abstract]
546
877
 
547
- # Return True to indicate successful registration
548
- return True
549
878
 
550
879
  def scopedInstance(
551
880
  self,
@@ -641,96 +970,7 @@ class Container(IContainer):
641
970
  # Return True to indicate successful registration
642
971
  return True
643
972
 
644
- def instance(
645
- self,
646
- abstract: Callable[..., Any],
647
- instance: Any,
648
- *,
649
- alias: str = None,
650
- enforce_decoupling: bool = False
651
- ) -> Optional[bool]:
652
- """
653
- Registers an instance of a class or interface in the container.
654
- Parameters
655
- ----------
656
- abstract : Callable[..., Any]
657
- The abstract class or interface to associate with the instance.
658
- instance : Any
659
- The concrete instance to register.
660
- alias : str, optional
661
- An optional alias to register the instance under. If not provided,
662
- the abstract's `__name__` attribute will be used as the alias if available.
663
- Returns
664
- -------
665
- bool
666
- True if the instance was successfully registered.
667
- Raises
668
- ------
669
- TypeError
670
- If `abstract` is not an abstract class or if `alias` is not a valid string.
671
- ValueError
672
- If `instance` is not a valid instance of `abstract`.
673
- Notes
674
- -----
675
- This method ensures that the abstract is a valid abstract class, the instance
676
- is valid, and the alias (if provided) is a valid string. The instance is then
677
- registered in the container under both the abstract and the alias.
678
- """
679
-
680
- # Validate the enforce_decoupling parameter
681
- if isinstance(enforce_decoupling, bool):
682
-
683
- # Ensure that the abstract is an abstract class
684
- IsAbstractClass(abstract, f"Instance {Lifetime.SINGLETON}")
685
-
686
- # Ensure that the instance is a valid instance
687
- IsInstance(instance)
688
-
689
- # Ensure that instance is NOT a subclass of abstract
690
- if enforce_decoupling:
691
- IsNotSubclass(abstract, instance.__class__)
692
-
693
- # Validate that instance is a subclass of abstract
694
- else:
695
- IsSubclass(abstract, instance.__class__)
696
-
697
- # Ensure implementation
698
- ImplementsAbstractMethods(
699
- abstract=abstract,
700
- instance=instance
701
- )
702
-
703
- # Ensure that the alias is a valid string if provided
704
- if alias:
705
- IsValidAlias(alias)
706
- else:
707
- alias = f"{abstract.__module__}.{abstract.__name__}"
708
-
709
- # If the service is already registered, drop it
710
- self.drop(abstract, alias)
711
-
712
- else:
713
-
714
- # Drop the existing alias if it exists
715
- self.drop(alias=alias)
716
-
717
- # If enforce_decoupling is not a boolean, set it to False
718
- enforce_decoupling = False
719
-
720
- # Register the instance with the abstract type
721
- self.__bindings[abstract] = Binding(
722
- contract = abstract,
723
- instance = instance,
724
- lifetime = Lifetime.SINGLETON,
725
- enforce_decoupling = enforce_decoupling,
726
- alias = alias
727
- )
728
-
729
- # Register the alias
730
- self.__aliases[alias] = self.__bindings[abstract]
731
-
732
- # Return True to indicate successful registration
733
- return True
973
+
734
974
 
735
975
  def callable(
736
976
  self,
@@ -2198,12 +2198,7 @@ class Application(Container, IApplication):
2198
2198
  if not self.__booted:
2199
2199
 
2200
2200
  # Register the application instance in the container
2201
- self.instance(
2202
- IApplication,
2203
- self,
2204
- alias="x-orionis.foundation.contracts.application.IApplication",
2205
- enforce_decoupling=None
2206
- )
2201
+ self.instance(IApplication, self, alias="x-orionis.foundation.contracts.application.IApplication")
2207
2202
 
2208
2203
  # Load configuration if not already set
2209
2204
  self.__loadConfig()
@@ -2213,8 +2208,6 @@ class Application(Container, IApplication):
2213
2208
  self.__loadFrameworkProviders()
2214
2209
  self.__registerProviders()
2215
2210
  self.__bootProviders()
2216
-
2217
- # Mark as booted
2218
2211
  self.__booted = True
2219
2212
 
2220
2213
  # Load core framework kernels with app booted
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.651.0"
8
+ VERSION = "0.653.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orionis
3
- Version: 0.651.0
3
+ Version: 0.653.0
4
4
  Summary: Orionis Framework – Elegant, Fast, and Powerful.
5
5
  Home-page: https://github.com/orionis-framework/framework
6
6
  Author: Raul Mauricio Uñate Castro
@@ -67,7 +67,7 @@ orionis/console/stubs/listener.stub,sha256=DbX-ghx2-vb73kzQ6L20lyg5vSKn58jSLTwFu
67
67
  orionis/console/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
68
  orionis/console/tasks/schedule.py,sha256=ZeeuQ9Tbu5KNowKC5oIk7yWeJXzlDQiQ278mWEgoCXc,87989
69
69
  orionis/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- orionis/container/container.py,sha256=yfhRs85YpPvqGRfrjblkHEuZGCN0BijremI-3r1Epak,96842
70
+ orionis/container/container.py,sha256=cbwsntr_yRy-W18MipLZ8Gy2d7mCoO51_eq4z53bxVk,109309
71
71
  orionis/container/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
72
  orionis/container/context/manager.py,sha256=I08K_jKXSKmrq18Pv33qYyMKIlAovVOwIgmfiVm-J7c,2971
73
73
  orionis/container/context/scope.py,sha256=p_oCzR7dDz-5ZAd16ab4vfLc3gBf34CaN0f4iR9D0bQ,1155
@@ -104,7 +104,7 @@ orionis/failure/contracts/handler.py,sha256=AeJfkJfD3hTkOIYAtADq6GnQfq-qWgDfUc7b
104
104
  orionis/failure/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
105
  orionis/failure/entities/throwable.py,sha256=1zD-awcuAyEtlR-L7V7ZIfPSF4GpXkf-neL5sXul7dc,1240
106
106
  orionis/foundation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
- orionis/foundation/application.py,sha256=Xe8lA5dXLN9gTYtpcfK_09bu1oYOsIdzs16opErJlaE,94168
107
+ orionis/foundation/application.py,sha256=5A93XNiYJi1FEu8hIv2UStxZu--ac4KanhcWsSU25hU,94028
108
108
  orionis/foundation/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
109
  orionis/foundation/config/startup.py,sha256=btqvryeIf9lLNQ9fBff7bJMrfraEY8qrWi4y_5YAR0Q,9681
110
110
  orionis/foundation/config/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -217,7 +217,7 @@ orionis/foundation/providers/scheduler_provider.py,sha256=IrPQJwvQVLRm5Qnz0Cxon4
217
217
  orionis/foundation/providers/testing_provider.py,sha256=eI1p2lUlxl25b5Z487O4nmqLE31CTDb4c3Q21xFadkE,1615
218
218
  orionis/foundation/providers/workers_provider.py,sha256=GdHENYV_yGyqmHJHn0DCyWmWId5xWjD48e6Zq2PGCWY,1674
219
219
  orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
220
- orionis/metadata/framework.py,sha256=fp2JS6twbQLr_mroF8uKJfv9U1S8Gc67se4Z7VSVj6M,4089
220
+ orionis/metadata/framework.py,sha256=otOsCfm3C9Cc9HHO-slNc79h08eMiZuSsG_zqA0F3i4,4089
221
221
  orionis/metadata/package.py,sha256=k7Yriyp5aUcR-iR8SK2ec_lf0_Cyc-C7JczgXa-I67w,16039
222
222
  orionis/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
223
223
  orionis/services/asynchrony/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -404,8 +404,8 @@ orionis/test/validators/workers.py,sha256=rWcdRexINNEmGaO7mnc1MKUxkHKxrTsVuHgbnI
404
404
  orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
405
405
  orionis/test/view/render.py,sha256=R55ykeRs0wDKcdTf4O1YZ8GDHTFmJ0IK6VQkbJkYUvo,5571
406
406
  orionis/test/view/report.stub,sha256=QLqqCdRoENr3ECiritRB3DO_MOjRQvgBh5jxZ3Hs1r0,28189
407
- orionis-0.651.0.dist-info/licenses/LICENCE,sha256=JhC-z_9mbpUrCfPjcl3DhDA8trNDMzb57cvRSam1avc,1463
408
- orionis-0.651.0.dist-info/METADATA,sha256=TLevEpD31eIGH5VdliEHg8hsaVvugiry2GQbXn8oDNc,4772
409
- orionis-0.651.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
410
- orionis-0.651.0.dist-info/top_level.txt,sha256=lyXi6jArpqJ-0zzNqd_uwsH-z9TCEBVBL-pC3Ekv7hU,8
411
- orionis-0.651.0.dist-info/RECORD,,
407
+ orionis-0.653.0.dist-info/licenses/LICENCE,sha256=JhC-z_9mbpUrCfPjcl3DhDA8trNDMzb57cvRSam1avc,1463
408
+ orionis-0.653.0.dist-info/METADATA,sha256=QjGBZ4J_obp_gOTBFsYVPHTNKkvUTAm8yZMmLT-Tszk,4772
409
+ orionis-0.653.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
410
+ orionis-0.653.0.dist-info/top_level.txt,sha256=lyXi6jArpqJ-0zzNqd_uwsH-z9TCEBVBL-pC3Ekv7hU,8
411
+ orionis-0.653.0.dist-info/RECORD,,