orionis 0.321.0__py3-none-any.whl → 0.323.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.
@@ -1,5 +1,6 @@
1
1
  import threading
2
2
  from typing import Any, Callable
3
+ from orionis.container.context.manager import ScopeManager
3
4
  from orionis.container.contracts.container import IContainer
4
5
  from orionis.container.entities.binding import Binding
5
6
  from orionis.container.enums.lifetimes import Lifetime
@@ -430,7 +431,7 @@ class Container(IContainer):
430
431
  # Return True to indicate successful registration
431
432
  return True
432
433
 
433
- def function(
434
+ def callable(
434
435
  self,
435
436
  alias: str,
436
437
  fn: Callable[..., Any],
@@ -633,4 +634,26 @@ class Container(IContainer):
633
634
  binding,
634
635
  *args,
635
636
  **kwargs
636
- )
637
+ )
638
+
639
+ def createContext(self) -> ScopeManager:
640
+ """
641
+ Creates a new context for managing scoped services.
642
+
643
+ This method returns a context manager that can be used with a 'with' statement
644
+ to control the lifecycle of scoped services.
645
+
646
+ Returns
647
+ -------
648
+ ScopeManager
649
+ A context manager for scoped services.
650
+
651
+ Usage
652
+ -------
653
+ with container.createContext():
654
+ # Scoped services created here will be disposed when exiting this block
655
+ service = container.make(IScopedService)
656
+ ...
657
+ # Scoped services are automatically disposed here
658
+ """
659
+ return ScopeManager()
@@ -0,0 +1,94 @@
1
+ from orionis.container.context.scope import ScopedContext
2
+
3
+ class ScopeManager:
4
+ """
5
+ A context manager to manage scoped lifetimes in the container.
6
+ """
7
+ def __init__(self):
8
+ """
9
+ Initialize a new ScopeManager with an empty instances dictionary.
10
+ """
11
+ self._instances = {}
12
+
13
+ def __getitem__(self, key):
14
+ """
15
+ Get an instance by key.
16
+
17
+ Parameters
18
+ ----------
19
+ key : hashable
20
+ The key of the instance to retrieve.
21
+
22
+ Returns
23
+ -------
24
+ object or None
25
+ The instance associated with the key or None if not found.
26
+ """
27
+ return self._instances.get(key)
28
+
29
+ def __setitem__(self, key, value):
30
+ """
31
+ Store an instance by key.
32
+
33
+ Parameters
34
+ ----------
35
+ key : hashable
36
+ The key to associate with the instance.
37
+ value : object
38
+ The instance to store.
39
+ """
40
+ self._instances[key] = value
41
+
42
+ def __contains__(self, key):
43
+ """
44
+ Check if a key exists in this scope.
45
+
46
+ Parameters
47
+ ----------
48
+ key : hashable
49
+ The key to check.
50
+
51
+ Returns
52
+ -------
53
+ bool
54
+ True if the key exists in the scope, False otherwise.
55
+ """
56
+ return key in self._instances
57
+
58
+ def clear(self):
59
+ """
60
+ Clear all instances from this scope.
61
+ """
62
+ self._instances.clear()
63
+
64
+ def __enter__(self):
65
+ """
66
+ Enter the scope context.
67
+
68
+ Sets this scope as the current active scope.
69
+
70
+ Returns
71
+ -------
72
+ ScopeManager
73
+ This scope manager instance.
74
+ """
75
+ ScopedContext.setCurrentScope(self)
76
+ return self
77
+
78
+ def __exit__(self, exc_type, exc_val, exc_tb):
79
+ """
80
+ Exit the scope context.
81
+
82
+ Clears this scope and the active scope reference.
83
+
84
+ Parameters
85
+ ----------
86
+ exc_type : type or None
87
+ The exception type if an exception was raised, None otherwise.
88
+ exc_val : Exception or None
89
+ The exception instance if an exception was raised, None otherwise.
90
+ exc_tb : traceback or None
91
+ The exception traceback if an exception was raised, None otherwise.
92
+ """
93
+ self.clear()
94
+ ScopedContext.clear()
@@ -0,0 +1,40 @@
1
+ import contextvars
2
+
3
+ class ScopedContext:
4
+ """
5
+ Holds scoped instances for the current context.
6
+ """
7
+ _active_scope = contextvars.ContextVar("orionis_scope", default=None)
8
+
9
+ @classmethod
10
+ def getCurrentScope(cls):
11
+ """
12
+ Get the currently active scope.
13
+
14
+ Returns
15
+ -------
16
+ object or None
17
+ The current active scope or None if no scope is active.
18
+ """
19
+ return cls._active_scope.get()
20
+
21
+ @classmethod
22
+ def setCurrentScope(cls, scope):
23
+ """
24
+ Set the current active scope.
25
+
26
+ Parameters
27
+ ----------
28
+ scope : object
29
+ The scope object to set as active.
30
+ """
31
+ cls._active_scope.set(scope)
32
+
33
+ @classmethod
34
+ def clear(cls):
35
+ """
36
+ Clear the current active scope.
37
+
38
+ Sets the active scope to None.
39
+ """
40
+ cls._active_scope.set(None)
@@ -131,7 +131,7 @@ class IContainer(ABC):
131
131
  pass
132
132
 
133
133
  @abstractmethod
134
- def function(
134
+ def callable(
135
135
  self,
136
136
  alias: str,
137
137
  fn: Callable[..., Any],
File without changes
@@ -0,0 +1,60 @@
1
+ from typing import Any
2
+ from orionis.container.container import Container
3
+
4
+ class FacadeMeta(type):
5
+
6
+ def __getattr__(cls, name: str) -> Any:
7
+ """
8
+ When an undefined attribute is accessed, this method resolves the service and delegates the call.
9
+ It's like having a genie in a bottle, but for services.
10
+
11
+ Args:
12
+ name: The name of the attribute to access
13
+
14
+ Returns:
15
+ The requested attribute from the underlying service
16
+ """
17
+ service = cls.resolve()
18
+ if not hasattr(service, name):
19
+ raise AttributeError(f"'{cls.__name__}' facade's service has no attribute '{name}'")
20
+ return getattr(service, name)
21
+
22
+
23
+ class Facade(metaclass=FacadeMeta):
24
+
25
+ _container = Container()
26
+
27
+ @classmethod
28
+ def getFacadeAccessor(cls) -> str:
29
+ """
30
+ This method must be overridden by subclasses to return the name of the service to be resolved.
31
+ If not, it throws a tantrum (NotImplementedError).
32
+
33
+ Returns:
34
+ The service name to be resolved from the container
35
+ """
36
+ raise NotImplementedError(f"Class {cls.__name__} must define the getFacadeAccessor method")
37
+
38
+ @classmethod
39
+ def resolve(cls) -> Any:
40
+ """
41
+ Resolves the service from the Container with caching for improved performance.
42
+ It's like calling the butler to fetch something from the pantry.
43
+
44
+ Returns:
45
+ The resolved service instance
46
+ """
47
+
48
+ # Get the service name from the facade accessor
49
+ service_name = cls.getFacadeAccessor()
50
+
51
+ # Check if the service is bound in the container
52
+ if not cls._container.bound(service_name):
53
+ raise RuntimeError(
54
+ f"The service '{service_name}' is not bound in the container. "
55
+ "Did you forget to register it?"
56
+ )
57
+
58
+ # Resolve the service instance from the container
59
+ service_instance = cls._container.make(service_name)
60
+ return service_instance
@@ -1,4 +1,5 @@
1
1
  from typing import Any, Callable
2
+ from orionis.container.context.scope import ScopedContext
2
3
  from orionis.container.contracts.container import IContainer
3
4
  from orionis.container.entities.binding import Binding
4
5
  from orionis.container.enums.lifetimes import Lifetime
@@ -61,10 +62,7 @@ class Resolver:
61
62
  elif binding.lifetime == Lifetime.SINGLETON:
62
63
  return self.__resolveSingleton(binding, *args, **kwargs)
63
64
  elif binding.lifetime == Lifetime.SCOPED:
64
- # TODO: Implement scoped lifetime resolution
65
- raise OrionisContainerException(
66
- "Scoped lifetime resolution is not yet implemented."
67
- )
65
+ return self.__resolveScoped(binding, *args, **kwargs)
68
66
 
69
67
  def __resolveTransient(self, binding: Binding, *args, **kwargs) -> Any:
70
68
  """
@@ -153,6 +151,58 @@ class Resolver:
153
151
  "Cannot resolve singleton binding: neither a concrete class, instance, nor function is defined."
154
152
  )
155
153
 
154
+ def __resolveScoped(self, binding: Binding, *args, **kwargs) -> Any:
155
+ """
156
+ Resolves a service with scoped lifetime.
157
+
158
+ Parameters
159
+ ----------
160
+ binding : Binding
161
+ The binding to resolve.
162
+ *args : tuple
163
+ Positional arguments to pass to the constructor.
164
+ **kwargs : dict
165
+ Keyword arguments to pass to the constructor.
166
+
167
+ Returns
168
+ -------
169
+ Any
170
+ The scoped instance of the requested service.
171
+
172
+ Raises
173
+ ------
174
+ OrionisContainerException
175
+ If no scope is active or service can't be resolved.
176
+ """
177
+ scope = ScopedContext.getCurrentScope()
178
+ if scope is None:
179
+ raise OrionisContainerException(
180
+ f"No active scope found while resolving scoped service '{binding.alias}'. "
181
+ f"Use 'with container.createContext():' to create a scope context."
182
+ )
183
+
184
+ if binding.alias in scope:
185
+ return scope[binding.alias]
186
+
187
+ # Create a new instance
188
+ if binding.concrete:
189
+ if args or kwargs:
190
+ instance = self.__instantiateConcreteWithArgs(binding.concrete, *args, **kwargs)
191
+ else:
192
+ instance = self.__instantiateConcreteReflective(binding.concrete)
193
+ elif binding.function:
194
+ if args or kwargs:
195
+ instance = self.__instantiateCallableWithArgs(binding.function, *args, **kwargs)
196
+ else:
197
+ instance = self.__instantiateCallableReflective(binding.function)
198
+ else:
199
+ raise OrionisContainerException(
200
+ "Cannot resolve scoped binding: neither a concrete class nor a function is defined."
201
+ )
202
+
203
+ scope[binding.alias] = instance
204
+ return instance
205
+
156
206
  def __instantiateConcreteWithArgs(self, concrete: Callable[..., Any], *args, **kwargs) -> Any:
157
207
  """
158
208
  Instantiates a concrete class with the provided arguments.
@@ -401,4 +451,3 @@ class Resolver:
401
451
 
402
452
  # Raise a more informative exception
403
453
  raise OrionisContainerException(error_msg) from e
404
-
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.321.0"
8
+ VERSION = "0.323.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.321.0
3
+ Version: 0.323.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
@@ -2,12 +2,6 @@ orionis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  orionis/_application.py,sha256=dMjJ0nFcIIOBGb5zr-tHNzcgTOZ1vJ7iMdFAlqSQph0,9405
3
3
  orionis/application.py,sha256=Off5uOUj-IYvvR8DcqLUoBW_98opWa7MQrtqTr0SZGc,292
4
4
  orionis/unittesting.py,sha256=_NU3_sm3R6bUUH_Y-KSPgNVBajUGCtKo_CGgjB1YD5k,2094
5
- orionis/_container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- orionis/_container/container.py,sha256=0AOqTNwpN_OtWbq9mBI99qfJ7LMkN71y0lP0JWKzut0,18289
7
- orionis/_container/container_integrity.py,sha256=vrqZrkJaP6ghbiAzr-nEul9f_lEWVa2nMUSugQXDfWk,10095
8
- orionis/_container/exception.py,sha256=ap1SqYEjQEEHXJJTNmL7V1jrmRjgT5_7geZ95MYkhMA,1691
9
- orionis/_container/lifetimes.py,sha256=2lbdiV7R2WlJf1cLD6eBxLnJud_lZvX1IhQH2Djy3Ww,375
10
- orionis/_container/resolve.py,sha256=5qVE--fBpbVFiTYM_jXKbjHNssm28aM4cwd49AhiLkY,2231
11
5
  orionis/_contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
6
  orionis/_contracts/application.py,sha256=ltuDA1mN5P73l89jJto_A96ePJWE02OZ_B2NOPpfeWs,1061
13
7
  orionis/_contracts/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -135,14 +129,19 @@ orionis/console/output/console.py,sha256=TE_Hl720ADd82dbERFSWhkoQRukDQZmETSw4nkw
135
129
  orionis/console/output/executor.py,sha256=bdvkzW2-buy0BPpy2r5qUGrRFW2Ay6k-5rSeHb0gQ3o,3352
136
130
  orionis/console/output/progress_bar.py,sha256=vFy582z6VJS46LV6tuyrmr9qvdVeTEtw3hyNcEHezeg,3088
137
131
  orionis/console/output/styles.py,sha256=6a4oQCOBOKMh2ARdeq5GlIskJ3wjiylYmh66tUKKmpQ,4053
138
- orionis/container/container.py,sha256=eBVQMzfSvXcxrdm-0hq0gmXZzxf3uabIcxbGVvGZmIo,22342
139
- orionis/container/resolver.py,sha256=iuyTWCnCqWXQCtWvFXPdS9cR_g3Q58_wjHVn0Yg0YNM,16209
140
- orionis/container/contracts/container.py,sha256=ksMn6ZLnFkIxd9_hbUGz3OpHzaYTA5izcDtH959kOow,7603
132
+ orionis/container/container.py,sha256=dIfHPCC28_Eho7kgp0AUFS1S5iaCD4ws7udCy_jQ6sE,23112
133
+ orionis/container/resolver.py,sha256=bR26v1bAtg9FgDtfs6zP30-g75aWrSO43UJPweJ7jfo,18014
134
+ orionis/container/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
+ orionis/container/context/manager.py,sha256=9yODWkHBoJ2kgJZ5ONLqcEcex50vaWuMcxsvmDgnQo4,2437
136
+ orionis/container/context/scope.py,sha256=CWFiLLTAC_IdmeFKWX-jrphdxB0_TMEVBlz6lQVMPC8,937
137
+ orionis/container/contracts/container.py,sha256=hOO3w2yqVhp2nPTeS1uJEYgXSTbM3xwezDCOSNMC_a0,7603
141
138
  orionis/container/entities/binding.py,sha256=Qp6Lf4XUDp2NjqXDAC2lzvhOFQWiBDKiGFcKfwb4axw,4342
142
139
  orionis/container/enums/lifetimes.py,sha256=RqQmugMIB1Ev_j_vFLcWorndm-to7xg4stQ7yKFDdDw,190
143
140
  orionis/container/exceptions/container_exception.py,sha256=goTDEwC70xTMD2qppN8KV-xyR0Nps218OD4D1LZ2-3s,470
144
141
  orionis/container/exceptions/type_error_exception.py,sha256=cYuvoXVOgRYj3tZPfK341aUERkf33-buOiI2eXxcrAw,470
145
142
  orionis/container/exceptions/value_exception.py,sha256=hjY0YEusoL3DurME1ornxvIv1wyGaf6tBggLFlGHblo,472
143
+ orionis/container/facades/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
+ orionis/container/facades/facade.py,sha256=8YMuZp9Mc--OfGzcyDbSke8Xi5V1kpRglHvMWftr1DQ,2075
146
145
  orionis/container/validators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
146
  orionis/container/validators/implements.py,sha256=iSoDxxTalQKhyKjvsojFlkROhBFvAjvJxRvPJlmGrSg,2843
148
147
  orionis/container/validators/is_abstract_class.py,sha256=Q-Lqyrrps6oj2XWI0KFRp-hDZf4_sgbZlEbfBXj5XT4,1169
@@ -244,7 +243,7 @@ orionis/foundation/contracts/config.py,sha256=Rpz6U6t8OXHO9JJKSTnCimytXE-tfCB-1i
244
243
  orionis/foundation/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
245
244
  orionis/foundation/exceptions/integrity.py,sha256=mc4pL1UMoYRHEmphnpW2oGk5URhu7DJRREyzHaV-cs8,472
246
245
  orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
247
- orionis/metadata/framework.py,sha256=gv-aVbvyQwRvOTPPCJTIwJffZOEd9Ak6L9jdwT46oOE,4960
246
+ orionis/metadata/framework.py,sha256=Oj4bwfkR8QW41Dfg0E01NAG-hIkgTRZiDT9rY93ZFqM,4960
248
247
  orionis/metadata/package.py,sha256=tqLfBRo-w1j_GN4xvzUNFyweWYFS-qhSgAEc-AmCH1M,5452
249
248
  orionis/patterns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
250
249
  orionis/patterns/singleton/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -356,7 +355,7 @@ orionis/test/suite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
356
355
  orionis/test/suite/test_unit.py,sha256=MWgW8dRCRyT1XZ5LsbXQ7-KVPReasoXwzEEL1EWWfE4,52190
357
356
  orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
358
357
  orionis/test/view/render.py,sha256=jXZkbITBknbUwm_mD8bcTiwLDvsFkrO9qrf0ZgPwqxc,4903
359
- orionis-0.321.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
358
+ orionis-0.323.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
360
359
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
361
360
  tests/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
362
361
  tests/example/test_example.py,sha256=kvWgiW3ADEZf718dGsMPtDh_rmOSx1ypEInKm7_6ZPQ,601
@@ -457,8 +456,8 @@ tests/support/wrapper/test_services_wrapper_docdict.py,sha256=yeVwl-VcwkWSQYyxZu
457
456
  tests/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
458
457
  tests/testing/test_testing_result.py,sha256=MrGK3ZimedL0b5Ydu69Dg8Iul017AzLTm7VPxpXlpfU,4315
459
458
  tests/testing/test_testing_unit.py,sha256=DjLBtvVn8B1KlVJNNkstBT8_csA1yeaMqnGrbanN_J4,7438
460
- orionis-0.321.0.dist-info/METADATA,sha256=kQ_DamwqrnAqkqPJQZd6mR4u-U_ERbRLwEw8FqTgTwE,4772
461
- orionis-0.321.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
462
- orionis-0.321.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
463
- orionis-0.321.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
464
- orionis-0.321.0.dist-info/RECORD,,
459
+ orionis-0.323.0.dist-info/METADATA,sha256=9ItAv8CGa3j5dBYJeOZyBivus_9cy2pl5mjtNvMCIwY,4772
460
+ orionis-0.323.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
461
+ orionis-0.323.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
462
+ orionis-0.323.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
463
+ orionis-0.323.0.dist-info/RECORD,,