orionis 0.538.0__py3-none-any.whl → 0.540.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.
Files changed (58) hide show
  1. orionis/console/base/command.py +1 -1
  2. orionis/console/base/scheduler.py +1 -1
  3. orionis/console/contracts/base_command.py +123 -0
  4. orionis/console/contracts/cli_request.py +85 -0
  5. orionis/console/contracts/command.py +90 -108
  6. orionis/console/contracts/event.py +1 -1
  7. orionis/console/contracts/reactor.py +40 -0
  8. orionis/console/core/reactor.py +325 -91
  9. orionis/console/dumper/dump.py +1 -1
  10. orionis/console/dynamic/progress_bar.py +1 -1
  11. orionis/console/{enums → entities}/command.py +3 -0
  12. orionis/console/{enums → entities}/event.py +1 -1
  13. orionis/console/fluent/command.py +216 -0
  14. orionis/console/{tasks → fluent}/event.py +1 -1
  15. orionis/console/kernel.py +28 -11
  16. orionis/console/output/console.py +1 -1
  17. orionis/console/output/executor.py +1 -1
  18. orionis/console/request/cli_request.py +113 -31
  19. orionis/console/tasks/schedule.py +4 -4
  20. orionis/container/container.py +102 -1
  21. orionis/container/contracts/container.py +46 -0
  22. orionis/failure/base/handler.py +6 -6
  23. orionis/failure/catch.py +1 -1
  24. orionis/failure/contracts/handler.py +3 -3
  25. orionis/foundation/application.py +1 -1
  26. orionis/foundation/providers/console_provider.py +1 -1
  27. orionis/foundation/providers/dumper_provider.py +1 -1
  28. orionis/foundation/providers/executor_provider.py +1 -1
  29. orionis/foundation/providers/progress_bar_provider.py +1 -1
  30. orionis/metadata/framework.py +1 -1
  31. orionis/support/facades/application.pyi +5 -0
  32. orionis/support/facades/console.pyi +4 -0
  33. orionis/support/facades/directory.py +21 -0
  34. orionis/support/facades/directory.pyi +4 -0
  35. orionis/support/facades/dumper.pyi +4 -0
  36. orionis/support/facades/executor.pyi +4 -0
  37. orionis/support/facades/inspire.pyi +4 -0
  38. orionis/support/facades/logger.pyi +4 -0
  39. orionis/support/facades/performance_counter.pyi +4 -0
  40. orionis/support/facades/progress_bar.pyi +4 -0
  41. orionis/support/facades/reactor.pyi +97 -0
  42. orionis/support/facades/testing.pyi +4 -0
  43. orionis/test/kernel.py +1 -1
  44. {orionis-0.538.0.dist-info → orionis-0.540.0.dist-info}/METADATA +1 -1
  45. {orionis-0.538.0.dist-info → orionis-0.540.0.dist-info}/RECORD +56 -43
  46. orionis/console/entities/request.py +0 -37
  47. orionis/console/output/contracts/__init__.py +0 -0
  48. /orionis/console/contracts/{scheduler.py → base_scheduler.py} +0 -0
  49. /orionis/console/{output/contracts → contracts}/console.py +0 -0
  50. /orionis/console/{dumper/contracts → contracts}/dump.py +0 -0
  51. /orionis/console/{output/contracts → contracts}/executor.py +0 -0
  52. /orionis/console/{dynamic/contracts → contracts}/progress_bar.py +0 -0
  53. /orionis/console/{dumper/contracts → fluent}/__init__.py +0 -0
  54. /orionis/console/{dynamic/contracts → request}/__init__.py +0 -0
  55. {orionis-0.538.0.dist-info → orionis-0.540.0.dist-info}/WHEEL +0 -0
  56. {orionis-0.538.0.dist-info → orionis-0.540.0.dist-info}/licenses/LICENCE +0 -0
  57. {orionis-0.538.0.dist-info → orionis-0.540.0.dist-info}/top_level.txt +0 -0
  58. {orionis-0.538.0.dist-info → orionis-0.540.0.dist-info}/zip-safe +0 -0
@@ -0,0 +1,216 @@
1
+ from typing import Any, Callable
2
+ from orionis.console.args.argument import CLIArgument
3
+ from orionis.console.entities.command import Command as CommandEntity
4
+ from orionis.services.introspection.concretes.reflection import ReflectionConcrete
5
+
6
+ class Command:
7
+
8
+ def __init__(
9
+ self,
10
+ signature: str,
11
+ concrete: Callable[..., Any],
12
+ method: str = 'handle'
13
+ ) -> None:
14
+ """
15
+ Initialize a new Command instance.
16
+
17
+ This constructor creates a Command object that encapsulates a command signature,
18
+ the concrete class that implements the command logic, and the method to be called
19
+ when executing the command. It validates that the provided concrete is a valid
20
+ class with the specified callable method.
21
+
22
+ Parameters
23
+ ----------
24
+ signature : str
25
+ The command signature string that defines how the command should be invoked
26
+ from the command line interface.
27
+ concrete : Callable[..., Any]
28
+ The concrete class that contains the implementation logic for the command.
29
+ Must be a valid class (not an instance) that will be instantiated when
30
+ the command is executed.
31
+ method : str, default='handle'
32
+ The name of the method within the concrete class that will be called
33
+ when executing the command. The method must exist and be callable.
34
+
35
+ Returns
36
+ -------
37
+ None
38
+ This is a constructor method and does not return a value.
39
+
40
+ Raises
41
+ ------
42
+ TypeError
43
+ If the provided concrete is not a class, or if the method parameter
44
+ is not a string value.
45
+ AttributeError
46
+ If the specified method does not exist in the concrete class or
47
+ is not callable.
48
+ """
49
+
50
+ # Validate that the concrete parameter is actually a class
51
+ if not ReflectionConcrete.isConcreteClass(concrete):
52
+ raise TypeError("The provided concrete must be a class.")
53
+
54
+ # Validate that the method parameter is a string
55
+ if not isinstance(method, str):
56
+ raise TypeError("The method name must be a string.")
57
+
58
+ # Validate that the specified method exists in the concrete class and is callable
59
+ if not hasattr(concrete, method) or not callable(getattr(concrete, method)):
60
+ raise AttributeError(f"The method '{method}' does not exist or is not callable in the provided concrete class.")
61
+
62
+ # Store the command signature for later use during command parsing
63
+ self.__signature = signature
64
+
65
+ # Store the concrete class reference for instantiation during execution
66
+ self.__concrete = concrete
67
+
68
+ # Store the method name to be called on the concrete instance
69
+ self.__method = method
70
+
71
+ # Initialize timestamp display as enabled by default
72
+ self.__timestamp = True
73
+
74
+ # Set default description for commands that don't provide one
75
+ self.__description = "No description provided."
76
+
77
+ # Initialize empty arguments list to be populated later
78
+ self.__arguments = []
79
+
80
+ def timestamp(self, enabled: bool = True) -> 'Command':
81
+ """
82
+ Configure whether timestamps should be included in command output.
83
+
84
+ This method allows enabling or disabling timestamp display for the command.
85
+ When enabled, timestamps will be shown alongside command execution results.
86
+
87
+ Parameters
88
+ ----------
89
+ enabled : bool, default=True
90
+ Flag to enable or disable timestamp display. True enables timestamps,
91
+ False disables them.
92
+
93
+ Returns
94
+ -------
95
+ Command
96
+ Returns the current Command instance to allow method chaining.
97
+
98
+ Raises
99
+ ------
100
+ TypeError
101
+ If the enabled parameter is not a boolean value.
102
+ """
103
+
104
+ # Validate that the enabled parameter is a boolean
105
+ if not isinstance(enabled, bool):
106
+ raise TypeError("The timestamp flag must be a boolean value.")
107
+
108
+ # Set the internal timestamp flag
109
+ self.__timestamp = enabled
110
+
111
+ # Return self to enable method chaining
112
+ return self
113
+
114
+ def description(self, desc: str) -> 'Command':
115
+ """
116
+ Set the description for the command.
117
+
118
+ This method allows setting a descriptive text that explains what the command
119
+ does. The description is used for help text and documentation purposes when
120
+ displaying command information to users.
121
+
122
+ Parameters
123
+ ----------
124
+ desc : str
125
+ The description text for the command. Must be a non-empty string that
126
+ describes the command's purpose and functionality.
127
+
128
+ Returns
129
+ -------
130
+ Command
131
+ Returns the current Command instance to allow method chaining.
132
+
133
+ Raises
134
+ ------
135
+ TypeError
136
+ If the desc parameter is not a string value.
137
+ """
138
+
139
+ # Validate that the description parameter is a string
140
+ if not isinstance(desc, str):
141
+ raise TypeError("The description must be a string.")
142
+
143
+ # Set the internal description attribute
144
+ self.__description = desc
145
+
146
+ # Return self to enable method chaining
147
+ return self
148
+
149
+ def arguments(self, args: list) -> 'Command':
150
+ """
151
+ Set the list of CLI arguments for the command.
152
+
153
+ This method configures the command-line arguments that the command will accept.
154
+ Each argument must be a properly configured CLIArgument instance that defines
155
+ the argument's name, type, validation rules, and other properties. The arguments
156
+ are used during command parsing to validate and process user input.
157
+
158
+ Parameters
159
+ ----------
160
+ args : list
161
+ A list of CLIArgument instances that define the command's accepted arguments.
162
+ Each element in the list must be a valid CLIArgument object with proper
163
+ configuration for argument parsing and validation.
164
+
165
+ Returns
166
+ -------
167
+ Command
168
+ Returns the current Command instance to allow method chaining and enable
169
+ fluent interface pattern for command configuration.
170
+
171
+ Raises
172
+ ------
173
+ TypeError
174
+ If the args parameter is not a list, or if any element in the list
175
+ is not an instance of CLIArgument.
176
+ """
177
+
178
+ # Validate that the arguments parameter is a list
179
+ if not isinstance(args, list):
180
+ raise TypeError("Arguments must be provided as a list.")
181
+
182
+ # Validate that each argument in the list is a CLIArgument instance
183
+ for arg in args:
184
+ if not isinstance(arg, CLIArgument):
185
+ raise TypeError("All arguments must be instances of CLIArgument.")
186
+
187
+ # Set the internal arguments list with the validated arguments
188
+ self.__arguments = args
189
+
190
+ # Return self to enable method chaining
191
+ return self
192
+
193
+ def get(self) -> tuple[str, CommandEntity]:
194
+ """
195
+ Retrieve the configured Command entity.
196
+
197
+ This method constructs and returns a Command entity object that encapsulates
198
+ all the configuration details of the command, including its signature, concrete
199
+ class, method, description, arguments, and timestamp setting. The returned
200
+ Command entity can be used for command execution and management.
201
+
202
+ Returns
203
+ -------
204
+ CommandEntity
205
+ A Command entity object containing all the command's configuration details.
206
+ """
207
+
208
+ # Create and return a Command entity with all the configured properties
209
+ return self.__signature, CommandEntity(
210
+ obj=self.__concrete,
211
+ method=self.__method,
212
+ timestamps=self.__timestamp,
213
+ signature=self.__signature,
214
+ description=self.__description,
215
+ args=self.__arguments
216
+ )
@@ -6,7 +6,7 @@ from apscheduler.triggers.date import DateTrigger
6
6
  from apscheduler.triggers.interval import IntervalTrigger
7
7
  from orionis.console.contracts.event import IEvent
8
8
  from orionis.console.contracts.schedule_event_listener import IScheduleEventListener
9
- from orionis.console.enums.event import Event as EventEntity
9
+ from orionis.console.entities.event import Event as EventEntity
10
10
  from orionis.console.exceptions import CLIOrionisValueError
11
11
 
12
12
  class Event(IEvent):
orionis/console/kernel.py CHANGED
@@ -1,8 +1,7 @@
1
1
  from typing import List
2
2
  from orionis.console.contracts.kernel import IKernelCLI
3
3
  from orionis.console.contracts.reactor import IReactor
4
- from orionis.console.entities.request import CLIRequest
5
- from orionis.console.request.cli_request import Request
4
+ from orionis.console.request.cli_request import CLIRequest
6
5
  from orionis.failure.contracts.catch import ICatch
7
6
  from orionis.foundation.contracts.application import IApplication
8
7
  from orionis.console.exceptions import CLIOrionisValueError
@@ -38,6 +37,9 @@ class KernelCLI(IKernelCLI):
38
37
  f"Failed to initialize TestKernel: expected IApplication, got {type(app).__module__}.{type(app).__name__}."
39
38
  )
40
39
 
40
+ # Store the application container as a private attribute for internal use
41
+ self.__app: IApplication = app
42
+
41
43
  # Retrieve and initialize the reactor instance from the application container.
42
44
  # The reactor is responsible for dispatching CLI commands.
43
45
  self.__reactor: IReactor = app.make('x-orionis.console.core.reactor')
@@ -64,21 +66,36 @@ class KernelCLI(IKernelCLI):
64
66
  SystemExit
65
67
  If invalid arguments are provided or no command is specified, this method may terminate the process with an error message.
66
68
  """
69
+ try:
67
70
 
68
- request: CLIRequest = CLIRequest()
71
+ # Ensure the arguments are provided as a list
72
+ if not isinstance(args, list):
73
+ raise CLIOrionisValueError(
74
+ f"Failed to handle command line arguments: expected list, got {type(args).__module__}.{type(args).__name__}."
75
+ )
69
76
 
70
- try:
77
+ # If no arguments or only the script name is provided, show the default help command
78
+ if not args or len(args) <= 1:
79
+ return self.__reactor.call('help')
80
+
81
+ # Remove the first argument (script name) to process only the command and its parameters
82
+ args = args[1:]
71
83
 
72
- # create a CLIRequest instance by processing the provided arguments
73
- request = Request(args)
84
+ # If no command is provided after removing the script name, exit with an error
85
+ if len(args) == 0:
86
+ raise CLIOrionisValueError("No command provided to execute.")
87
+
88
+ # If only the command is provided, call it without additional arguments
89
+ if len(args) == 1:
90
+ return self.__reactor.call(args[0])
74
91
 
75
92
  # If command and arguments are provided, call the command with its arguments
76
- return self.__reactor.call(
77
- request.command,
78
- request.args
79
- )
93
+ return self.__reactor.call(args[0], args[1:])
80
94
 
81
95
  except BaseException as e:
82
96
 
97
+ # If an exception occurs, prepare a CLIRequest with the command (if available)
98
+ command = args[0] if args else "__unknown__"
99
+
83
100
  # Catch any exceptions that occur during command handling
84
- self.__catch.exception(self, request, e)
101
+ self.__catch.exception(self, CLIRequest(command, {}), e)
@@ -3,7 +3,7 @@ import getpass
3
3
  import os
4
4
  import sys
5
5
  from orionis.console.output.enums import ANSIColors
6
- from orionis.console.output.contracts.console import IConsole
6
+ from orionis.console.contracts.console import IConsole
7
7
 
8
8
  class Console(IConsole):
9
9
  """
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime
2
- from orionis.console.output.contracts.executor import IExecutor
2
+ from orionis.console.contracts.executor import IExecutor
3
3
  from orionis.console.output.enums.styles import ANSIColors
4
4
 
5
5
  class Executor(IExecutor):
@@ -1,45 +1,127 @@
1
- from typing import List
2
- from orionis.console.entities.request import CLIRequest as CLIRequestEntity
1
+ from typing import Any
2
+ from orionis.console.contracts.cli_request import ICLIRequest
3
3
  from orionis.console.exceptions.cli_orionis_value_error import CLIOrionisValueError
4
4
 
5
- class CLIRequest:
6
- def __call__(self, args: List[str]) -> "CLIRequestEntity":
5
+ class CLIRequest(ICLIRequest):
6
+
7
+ def __init__(
8
+ self,
9
+ command: str,
10
+ args: dict
11
+ ):
7
12
  """
8
- Processes command-line arguments and returns a CLIRequest instance.
13
+ Initialize a CLI request object with command line arguments.
9
14
 
10
- Parameters
11
- ----------
12
- args : List[str]
13
- The list of command-line arguments. The first argument is expected to be the script name.
15
+ Args:
16
+ args (dict, optional): Dictionary containing command line arguments and their values.
17
+ Defaults to an empty dictionary.
18
+
19
+ Raises:
20
+ CLIOrionisValueError: If the provided args parameter is not a dictionary.
21
+
22
+ Note:
23
+ The args dictionary is stored privately and used to manage CLI request parameters
24
+ throughout the lifecycle of the CLI request object.
25
+ """
26
+
27
+ # Validate that args is a dictionary
28
+ if not isinstance(args, dict):
29
+ raise CLIOrionisValueError("Args must be a dictionary")
30
+
31
+ # Validate that command is a string
32
+ if not isinstance(command, str):
33
+ raise CLIOrionisValueError("Command must be a string")
34
+
35
+ # Store the args dictionary as a private attribute
36
+ self.__command = command
37
+ self.__args = args if args is not None else {}
38
+
39
+ def command(self) -> str:
40
+ """
41
+ Retrieve the command name associated with this CLI request.
42
+
43
+ This method provides access to the command string that was specified during
44
+ the initialization of the CLIRequest object. The command represents the
45
+ primary action or operation that should be executed based on the CLI input.
46
+
47
+ Returns
48
+ -------
49
+ str
50
+ The command name stored as a string. This is the exact command value
51
+ that was passed to the constructor during object initialization.
52
+
53
+ Notes
54
+ -----
55
+ The returned command string is immutable and represents the core action
56
+ identifier for this CLI request. This value is essential for determining
57
+ which operation should be performed by the CLI handler.
58
+ """
59
+
60
+ # Return the command name stored in the private attribute
61
+ # This provides access to the command specified during initialization
62
+ return self.__command
63
+
64
+ def all(self) -> dict:
65
+ """
66
+ Retrieve all command line arguments as a complete dictionary.
67
+
68
+ This method provides access to the entire collection of command line arguments
69
+ that were passed during the initialization of the CLIRequest object. It returns
70
+ a reference to the internal arguments dictionary, allowing for comprehensive
71
+ access to all parsed CLI parameters.
14
72
 
15
73
  Returns
16
74
  -------
17
- CLIRequestEntity
18
- An instance representing the parsed command and its arguments.
75
+ dict
76
+ A dictionary containing all the parsed command line arguments as key-value
77
+ pairs, where keys are argument names (str) and values are the corresponding
78
+ argument values of any type. If no arguments were provided during
79
+ initialization, returns an empty dictionary.
19
80
 
20
- Raises
21
- ------
22
- CLIOrionisValueError
23
- If the provided arguments are not a list or if no command is provided.
81
+ Notes
82
+ -----
83
+ This method returns a reference to the internal arguments dictionary rather
84
+ than a copy. Modifications to the returned dictionary will affect the
85
+ internal state of the CLIRequest object.
24
86
  """
25
- if not isinstance(args, list):
26
- raise CLIOrionisValueError(
27
- f"Failed to handle command line arguments: expected list, got {type(args).__module__}.{type(args).__name__}."
28
- )
29
87
 
30
- if len(args) <= 1:
31
- raise CLIOrionisValueError("No command provided to execute.")
88
+ # Return the complete arguments dictionary containing all CLI parameters
89
+ return self.__args
90
+
91
+ def argument(self, name: str, default: Any = None):
92
+ """
93
+ Retrieve the value of a specific command line argument by its name.
32
94
 
33
- # Remove the script name
34
- args = args[1:]
95
+ This method provides access to individual command line arguments that were
96
+ passed during initialization. It safely retrieves argument values without
97
+ raising exceptions if the argument doesn't exist.
35
98
 
36
- if not args or not args[0]:
37
- raise CLIOrionisValueError("No command provided to execute.")
99
+ Parameters
100
+ ----------
101
+ name : str
102
+ The name of the command line argument to retrieve. This should match
103
+ the key used when the argument was originally parsed and stored.
104
+ default : Any, optional
105
+ The default value to return if the specified argument name does not
106
+ exist in the arguments dictionary. Defaults to None.
107
+
108
+ Returns
109
+ -------
110
+ Any or None
111
+ The value associated with the specified argument name if it exists
112
+ in the arguments dictionary. Returns None if the argument name is
113
+ not found or was not provided during CLI execution.
114
+
115
+ Notes
116
+ -----
117
+ This method uses the dictionary's get() method to safely access values,
118
+ ensuring that missing arguments return None rather than raising a KeyError.
119
+ """
38
120
 
39
- return CLIRequestEntity(
40
- command=args[0],
41
- args=args[1:]
42
- )
121
+ # Safely retrieve the argument value using dict.get() to avoid KeyError
122
+ # Returns None if the argument name doesn't exist in the dictionary
123
+ if name not in self.__args or self.__args[name] is None:
124
+ return default
43
125
 
44
- # Export the CLIRequest Singleton
45
- Request = CLIRequest()
126
+ # Return the value associated with the specified argument name
127
+ return self.__args.get(name, default)
@@ -25,16 +25,16 @@ from orionis.console.contracts.reactor import IReactor
25
25
  from orionis.console.contracts.schedule import ISchedule
26
26
  from orionis.console.contracts.schedule_event_listener import IScheduleEventListener
27
27
  from orionis.console.entities.event_job import EventJob
28
- from orionis.console.entities.request import CLIRequest
29
28
  from orionis.console.entities.scheduler_error import SchedulerError
30
29
  from orionis.console.entities.scheduler_paused import SchedulerPaused
31
30
  from orionis.console.entities.scheduler_resumed import SchedulerResumed
32
31
  from orionis.console.entities.scheduler_shutdown import SchedulerShutdown
33
32
  from orionis.console.entities.scheduler_started import SchedulerStarted
34
- from orionis.console.enums.event import Event as EventEntity
33
+ from orionis.console.entities.event import Event as EventEntity
35
34
  from orionis.console.enums.listener import ListeningEvent
36
35
  from orionis.console.exceptions import CLIOrionisRuntimeError
37
36
  from orionis.console.exceptions.cli_orionis_value_error import CLIOrionisValueError
37
+ from orionis.console.request.cli_request import CLIRequest
38
38
  from orionis.failure.contracts.catch import ICatch
39
39
  from orionis.foundation.contracts.application import IApplication
40
40
  from orionis.services.log.contracts.log_service import ILogger
@@ -282,7 +282,7 @@ class Schedule(ISchedule):
282
282
  raise CLIOrionisValueError(f"The command '{signature}' is not available or does not exist.")
283
283
 
284
284
  # Import Event here to avoid circular dependency issues
285
- from orionis.console.tasks.event import Event
285
+ from orionis.console.fluent.event import Event
286
286
 
287
287
  # Store the command and its arguments for scheduling
288
288
  self.__events[signature] = Event(
@@ -981,7 +981,7 @@ class Schedule(ISchedule):
981
981
 
982
982
  # Delegate exception handling to the application's error catching mechanism
983
983
  # This ensures consistent error handling across the entire application
984
- self.__catch.exception(self, CLIRequest(command="schedule:work", args=[]), exception)
984
+ self.__catch.exception(self, CLIRequest(command="schedule:work", args={}), exception)
985
985
 
986
986
  def setListener(
987
987
  self,
@@ -18,7 +18,6 @@ from orionis.services.introspection.concretes.reflection import ReflectionConcre
18
18
  from orionis.services.introspection.dependencies.entities.argument import Argument
19
19
  from orionis.services.introspection.dependencies.entities.resolve_argument import ResolveArguments
20
20
  from orionis.services.introspection.dependencies.reflection import ReflectDependencies
21
- from orionis.services.introspection.instances.reflection import ReflectionInstance
22
21
 
23
22
  class Container(IContainer):
24
23
 
@@ -541,6 +540,101 @@ class Container(IContainer):
541
540
  # Return True to indicate successful registration
542
541
  return True
543
542
 
543
+ def scopedInstance(
544
+ self,
545
+ abstract: Callable[..., Any],
546
+ instance: Any,
547
+ *,
548
+ alias: str = None,
549
+ enforce_decoupling: bool = False
550
+ ) -> Optional[bool]:
551
+ """
552
+ Registers an instance of a class or interface in the container with scoped lifetime.
553
+
554
+ Parameters
555
+ ----------
556
+ abstract : Callable[..., Any]
557
+ The abstract class or interface to associate with the instance.
558
+ instance : Any
559
+ The concrete instance to register.
560
+ alias : str, optional
561
+ An optional alias to register the instance under. If not provided,
562
+ the abstract's `__name__` attribute will be used as the alias if available.
563
+ enforce_decoupling : bool, optional
564
+ Whether to enforce decoupling between abstract and concrete types.
565
+
566
+ Returns
567
+ -------
568
+ bool
569
+ True if the instance was successfully registered.
570
+
571
+ Raises
572
+ ------
573
+ TypeError
574
+ If `abstract` is not an abstract class or if `alias` is not a valid string.
575
+ ValueError
576
+ If `instance` is not a valid instance of `abstract`.
577
+ OrionisContainerException
578
+ If no active scope is found.
579
+
580
+ Notes
581
+ -----
582
+ This method registers the instance with scoped lifetime, meaning it will be
583
+ available only within the current active scope. If no scope is active,
584
+ an exception will be raised.
585
+ """
586
+
587
+ # Ensure that the abstract is an abstract class
588
+ IsAbstractClass(abstract, f"Instance {Lifetime.SCOPED}")
589
+
590
+ # Ensure that the instance is a valid instance
591
+ IsInstance(instance)
592
+
593
+ # Ensure that instance is NOT a subclass of abstract
594
+ if enforce_decoupling:
595
+ IsNotSubclass(abstract, instance.__class__)
596
+ else:
597
+ # Validate that instance is a subclass of abstract
598
+ IsSubclass(abstract, instance.__class__)
599
+
600
+ # Ensure implementation
601
+ ImplementsAbstractMethods(
602
+ abstract=abstract,
603
+ instance=instance
604
+ )
605
+
606
+ # Ensure that the alias is a valid string if provided
607
+ if alias:
608
+ IsValidAlias(alias)
609
+ else:
610
+ rf_asbtract = ReflectionAbstract(abstract)
611
+ alias = rf_asbtract.getModuleWithClassName()
612
+
613
+ # If the service is already registered in container bindings, drop it
614
+ self.drop(abstract, alias)
615
+
616
+ # Register the binding in the container for future scope resolutions
617
+ self.__bindings[abstract] = Binding(
618
+ contract=abstract,
619
+ instance=instance,
620
+ lifetime=Lifetime.SCOPED,
621
+ enforce_decoupling=enforce_decoupling,
622
+ alias=alias
623
+ )
624
+
625
+ # Register the alias
626
+ self.__aliases[alias] = self.__bindings[abstract]
627
+
628
+ # Store the instance directly in the current scope
629
+ scope = ScopedContext.getCurrentScope()
630
+ if scope:
631
+ scope[abstract] = instance
632
+ if alias != abstract:
633
+ scope[alias] = instance
634
+
635
+ # Return True to indicate successful registration
636
+ return True
637
+
544
638
  def instance(
545
639
  self,
546
640
  abstract: Callable[..., Any],
@@ -1760,6 +1854,11 @@ class Container(IContainer):
1760
1854
  If the dependency cannot be resolved through any available method.
1761
1855
  """
1762
1856
 
1857
+ # Check if the dependency is already in the current scope (for SCOPED lifetime)
1858
+ scoped = ScopedContext.getCurrentScope()
1859
+ if scoped and (dependency.type in scoped or dependency.full_class_path in scoped):
1860
+ return scoped[dependency.type] if dependency.type in scoped else scoped[dependency.full_class_path]
1861
+
1763
1862
  # If the dependency has a default value, use it
1764
1863
  if dependency.default is not None:
1765
1864
  return dependency.default
@@ -1775,10 +1874,12 @@ class Container(IContainer):
1775
1874
  )
1776
1875
 
1777
1876
  # Try to resolve from container using type (Abstract or Interface)
1877
+ # This will automatically handle SCOPED lifetime through resolve() -> __resolveScoped()
1778
1878
  if self.bound(dependency.type):
1779
1879
  return self.resolve(self.getBinding(dependency.type))
1780
1880
 
1781
1881
  # Try to resolve from container using full class path
1882
+ # This will also handle SCOPED lifetime appropriately
1782
1883
  if self.bound(dependency.full_class_path):
1783
1884
  return self.resolve(self.getBinding(dependency.full_class_path))
1784
1885