orionis 0.380.0__py3-none-any.whl → 0.381.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.
@@ -8,6 +8,7 @@ from orionis.container.exceptions import OrionisContainerException
8
8
  from orionis.services.introspection.callables.reflection import ReflectionCallable
9
9
  from orionis.services.introspection.concretes.reflection import ReflectionConcrete
10
10
  from orionis.services.introspection.dependencies.entities.known_dependencies import KnownDependency
11
+ from orionis.services.introspection.dependencies.entities.method_dependencies import MethodDependency
11
12
 
12
13
  class Resolver(IResolver):
13
14
  """
@@ -65,6 +66,146 @@ class Resolver(IResolver):
65
66
  elif binding.lifetime == Lifetime.SCOPED:
66
67
  return self.__resolveScoped(binding, *args, **kwargs)
67
68
 
69
+ def resolveType(
70
+ self,
71
+ type_: Callable[..., Any],
72
+ *args,
73
+ **kwargs
74
+ ) -> Any:
75
+ """
76
+ Forces resolution of a type whether it's registered in the container or not.
77
+
78
+ Parameters
79
+ ----------
80
+ type_ : Callable[..., Any]
81
+ The type or callable to resolve.
82
+ *args : tuple
83
+ Positional arguments to pass to the constructor/callable.
84
+ **kwargs : dict
85
+ Keyword arguments to pass to the constructor/callable.
86
+
87
+ Returns
88
+ -------
89
+ Any
90
+ The resolved instance.
91
+
92
+ Raises
93
+ ------
94
+ OrionisContainerException
95
+ If the type cannot be resolved.
96
+ """
97
+
98
+ # Try to resolve the type with the provided arguments
99
+ # If the type is already bound in the container, resolve it directly
100
+ # or if args or kwargs are provided, instantiate it directly
101
+ # If the type is a concrete class, instantiate it with resolved dependencies
102
+ try:
103
+
104
+ # Check if the type is already bound in the container
105
+ if self.container.bound(type_):
106
+ binding = self.container.getBinding(type_)
107
+ return self.resolve(binding, *args, **kwargs)
108
+
109
+ # If args or kwargs are provided, use them directly
110
+ if args or kwargs:
111
+
112
+ # For classes
113
+ if isinstance(type_, type):
114
+ return type_(*args, **kwargs)
115
+
116
+ # For callables
117
+ elif callable(type_):
118
+ return type_(*args, **kwargs)
119
+
120
+ # Otherwise use reflection to resolve dependencies
121
+ # If the type is a concrete class, instantiate it with resolved dependencies
122
+ elif ReflectionConcrete.isConcreteClass(type_):
123
+ return type_(**self.__resolveDependencies(type_, is_class=True))
124
+
125
+ # Try to call directly if it's a callable
126
+ elif callable(type_) and not isinstance(type_, type):
127
+ return type_(**self.__resolveDependencies(type_, is_class=False))
128
+
129
+ # If the type is neither a concrete class nor a callable, raise an exception
130
+ raise OrionisContainerException(
131
+ f"Cannot force resolve: {getattr(type_, '__name__', str(type_))} is neither a concrete class nor a callable."
132
+ )
133
+
134
+ except Exception as e:
135
+
136
+ # Get the type name safely to avoid AttributeError
137
+ type_name = getattr(type_, '__name__', str(type_))
138
+ module_name = getattr(type_, '__module__', "unknown module")
139
+
140
+ # Provide more detailed error message
141
+ error_msg = f"Error while force-resolving '{type_name}' from '{module_name}':\n{str(e)}"
142
+
143
+ # If it's already an OrionisContainerException, just re-raise it with the context
144
+ if isinstance(e, OrionisContainerException):
145
+ raise e from None
146
+
147
+ # Raise a new OrionisContainerException with the original exception as context
148
+ else:
149
+ raise OrionisContainerException(error_msg) from e
150
+
151
+ def resolveSignature(
152
+ self,
153
+ signature: MethodDependency
154
+ ) -> dict:
155
+ """
156
+ Resolves dependencies defined in a method signature.
157
+
158
+ Parameters
159
+ ----------
160
+ signature : MethodDependency
161
+ The method dependency information to resolve.
162
+
163
+ Returns
164
+ -------
165
+ dict
166
+ A dictionary of resolved parameter values.
167
+
168
+ Raises
169
+ ------
170
+ OrionisContainerException
171
+ If any dependencies cannot be resolved.
172
+ """
173
+ # If no dependencies to resolve, return empty dict
174
+ if not signature.resolved and not signature.unresolved:
175
+ return {}
176
+
177
+ # If there are unresolved dependencies, raise an error
178
+ if signature.unresolved:
179
+ raise OrionisContainerException(
180
+ f"Cannot resolve method dependencies because the following parameters are unresolved: {', '.join(signature.unresolved)}."
181
+ )
182
+
183
+ # Create a dict of resolved dependencies
184
+ params = {}
185
+ for param_name, dep in signature.resolved.items():
186
+ if isinstance(dep, KnownDependency):
187
+ # Try to resolve the dependency using the container
188
+ if self.container.bound(dep.type):
189
+ params[param_name] = self.resolve(
190
+ self.container.getBinding(dep.type)
191
+ )
192
+ elif self.container.bound(dep.full_class_path):
193
+ params[param_name] = self.resolve(
194
+ self.container.getBinding(dep.full_class_path)
195
+ )
196
+ # Try to resolve directly if it's a concrete type
197
+ elif ReflectionConcrete.isConcreteClass(dep.type):
198
+ params[param_name] = self.resolveType(dep.type)
199
+ else:
200
+ raise OrionisContainerException(
201
+ f"Cannot resolve dependency '{param_name}' of type '{dep.type.__name__}'."
202
+ )
203
+ else:
204
+ # Use default value
205
+ params[param_name] = dep
206
+
207
+ return params
208
+
68
209
  def __resolveTransient(self, binding: Binding, *args, **kwargs) -> Any:
69
210
  """
70
211
  Resolves a service with transient lifetime.
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.380.0"
8
+ VERSION = "0.381.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -667,26 +667,35 @@ class UnitTest(IUnitTest):
667
667
  # Return the result along with captured output and error streams
668
668
  return result, output_buffer, error_buffer
669
669
 
670
- def __runTestsSequentially(
671
- self,
672
- output_buffer: io.StringIO,
673
- error_buffer: io.StringIO
674
- ) -> unittest.TestResult:
670
+ def __resolveFlattenedTestSuite(
671
+ self
672
+ ) -> unittest.TestSuite:
675
673
  """
676
- Executes the test suite sequentially, capturing the output and error streams.
674
+ Resolves dependencies for all test cases in the suite and creates a flattened test suite with injected dependencies.
675
+
676
+ Processes each test case, identifies dependencies in test method signatures, and resolves
677
+ them using the application's dependency resolver. Creates wrapper methods that automatically
678
+ inject the resolved dependencies when tests are executed.
677
679
 
678
680
  Parameters
679
681
  ----------
680
- output_buffer : io.StringIO
681
- A buffer to capture the standard output during test execution.
682
- error_buffer : io.StringIO
683
- A buffer to capture the standard error during test execution.
682
+ None
684
683
 
685
684
  Returns
686
685
  -------
687
- unittest.TestResult
688
- The result of the test suite execution, containing information about
689
- passed, failed, and skipped tests.
686
+ unittest.TestSuite
687
+ A new test suite with all tests having their dependencies resolved and injected.
688
+
689
+ Raises
690
+ ------
691
+ OrionisTestValueError
692
+ If any test method has dependencies that cannot be resolved.
693
+
694
+ Notes
695
+ -----
696
+ - Test methods with decorators are left as-is
697
+ - Test methods without dependencies are added directly
698
+ - Test methods with unresolved dependencies trigger an error
690
699
  """
691
700
 
692
701
  # Create a new test suite with tests that have their dependencies resolved
@@ -746,49 +755,7 @@ class UnitTest(IUnitTest):
746
755
  original_method = getattr(test_class, method_name)
747
756
 
748
757
  # Create a dict of resolved dependencies
749
- params = {}
750
- resolve = Resolver(self.app)
751
- for key, value in signature.resolved.items():
752
-
753
- # If the dependency is a KnownDependency, resolve it
754
- if isinstance(value, KnownDependency):
755
-
756
- # If the dependency is a built-in type, raise an exception
757
- if value.module_name == 'builtins':
758
- raise OrionisTestValueError(
759
- f"Cannot resolve built-in type '{value.type.__name__}' for dependency '{key}' in test method '{method_name}'. "
760
- "Built-in types cannot be resolved by the container."
761
- )
762
-
763
- # Try to resolve from container using type (Abstract or Interface)
764
- if self.app.bound(value.type):
765
- params[key] = resolve.resolve(
766
- self.app.getBinding(value.type)
767
- )
768
-
769
- # Try to resolve from container using full class path
770
- elif self.app.bound(value.full_class_path):
771
- params[key] = resolve.resolve(
772
- self.app.getBinding(value.full_class_path)
773
- )
774
-
775
- # Try to instantiate directly if it's a concrete class
776
- elif ReflectionConcrete.isConcreteClass(value.type):
777
- params[key] = value.type(**resolve._Resolver__resolveDependencies(value.type, is_class=True))
778
-
779
- # Try to call directly if it's a callable
780
- elif callable(value.type) and not isinstance(value.type, type):
781
- params[key] = value.type(**self._Resolver__resolveDependencies(value.type, is_class=False))
782
-
783
- # If the dependency cannot be resolved, raise an exception
784
- else:
785
- raise OrionisTestValueError(
786
- f"Cannot resolve dependency '{key}' of type '{value.type.__name__}' in test method '{method_name}'. "
787
- "Ensure that the dependency is bound in the container or is a concrete class."
788
- )
789
- else:
790
- # Use default value
791
- params[key] = value
758
+ params = Resolver(self.app).resolveSignature(signature)
792
759
 
793
760
  # Create a wrapper method that injects dependencies
794
761
  def create_test_wrapper(original_test, resolved_args:dict):
@@ -803,6 +770,31 @@ class UnitTest(IUnitTest):
803
770
  # Add the modified test case to the suite
804
771
  flattened_suite.addTest(test_case)
805
772
 
773
+ # Return the flattened suite with resolved dependencies
774
+ return flattened_suite
775
+
776
+ def __runTestsSequentially(
777
+ self,
778
+ output_buffer: io.StringIO,
779
+ error_buffer: io.StringIO
780
+ ) -> unittest.TestResult:
781
+ """
782
+ Executes the test suite sequentially, capturing the output and error streams.
783
+
784
+ Parameters
785
+ ----------
786
+ output_buffer : io.StringIO
787
+ A buffer to capture the standard output during test execution.
788
+ error_buffer : io.StringIO
789
+ A buffer to capture the standard error during test execution.
790
+
791
+ Returns
792
+ -------
793
+ unittest.TestResult
794
+ The result of the test suite execution, containing information about
795
+ passed, failed, and skipped tests.
796
+ """
797
+
806
798
  # Create a custom result class to capture detailed test results
807
799
  with redirect_stdout(output_buffer), redirect_stderr(error_buffer):
808
800
  runner = unittest.TextTestRunner(
@@ -811,7 +803,7 @@ class UnitTest(IUnitTest):
811
803
  failfast=self.fail_fast,
812
804
  resultclass=self.__customResultClass()
813
805
  )
814
- result = runner.run(flattened_suite)
806
+ result = runner.run(self.__resolveFlattenedTestSuite())
815
807
 
816
808
  # Return the result object containing test outcomes
817
809
  return result
@@ -845,7 +837,7 @@ class UnitTest(IUnitTest):
845
837
  """
846
838
 
847
839
  # Flatten the test suite to get individual test cases
848
- test_cases = list(self.__flattenTestSuite(self.suite))
840
+ test_cases = list(self.__resolveFlattenedTestSuite())
849
841
 
850
842
  # Create a custom result instance to collect all results
851
843
  result_class = self.__customResultClass()
orionis/test/kernel.py CHANGED
@@ -277,10 +277,7 @@ class TestKernel(ITestKernel):
277
277
  throw_exception = bool(args.throw_exception),
278
278
  persistent = bool(args.persistent),
279
279
  persistent_driver = str(args.persistent_driver) if args.persistent_driver else None,
280
- web_report = bool(args.web_report),
281
- folder_path=[
282
- 'example'
283
- ]
280
+ web_report = bool(args.web_report)
284
281
  )
285
282
 
286
283
  # If requested, print the output buffer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orionis
3
- Version: 0.380.0
3
+ Version: 0.381.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
@@ -134,7 +134,7 @@ orionis/container/facades/facade.py,sha256=wIjcQKxQa0xlTGGd6UIakenHMv0mM1BVPI7kt
134
134
  orionis/container/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
135
  orionis/container/providers/service_provider.py,sha256=9SHDzeuJbktRhrLq0zo2fZhRR4xOaYGOb2wKl7iBR4k,1923
136
136
  orionis/container/resolver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
- orionis/container/resolver/resolver.py,sha256=lAlnNlQvkUBK9-8w0MO42E8x9UwT2UHKaaa05M9g2Q8,18035
137
+ orionis/container/resolver/resolver.py,sha256=8mTouPo9hhMwz1CxZcqmzZlg3vHoLCs5HEWtreZ-KWk,23523
138
138
  orionis/container/validators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
139
  orionis/container/validators/implements.py,sha256=xDXK7yhG5GGGRT9Mj1UbbVrcVTgifmjFZdaAJG4Ke8o,3110
140
140
  orionis/container/validators/is_abstract_class.py,sha256=vJqUPn610YZS0sEkV8c_gPZskIgWmFHjg3D3MF2OTs8,1141
@@ -247,7 +247,7 @@ orionis/foundation/providers/path_resolver_provider.py,sha256=rXvaVc5sSqmDgRzWJo
247
247
  orionis/foundation/providers/progress_bar_provider.py,sha256=75Jr4iEgUOUGl8Di1DioeP5_HRQlR-1lVzPmS96sWjA,737
248
248
  orionis/foundation/providers/workers_provider.py,sha256=WWlji3C69_-Y0c42aZDbR_bmcE_qZEh2SaA_cNkCivI,702
249
249
  orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
250
- orionis/metadata/framework.py,sha256=N6gwACmEpQ02LMBnzQ75QQVdUNjZoEI8mWGUqgy94q4,4960
250
+ orionis/metadata/framework.py,sha256=cGgByXu_dMZG3EWH8_xu-G5JsAAp7LgfUolLoHX4hDk,4960
251
251
  orionis/metadata/package.py,sha256=tqLfBRo-w1j_GN4xvzUNFyweWYFS-qhSgAEc-AmCH1M,5452
252
252
  orionis/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
253
253
  orionis/services/asynchrony/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -349,7 +349,7 @@ orionis/support/standard/exceptions/value.py,sha256=7xry_TZz3k-BLAZTR_uDiQ0RfXOk
349
349
  orionis/support/wrapper/__init__.py,sha256=jGoWoIGYuRYqMYQKlrX7Dpcbg-AGkHoB_aM2xhu73yc,62
350
350
  orionis/support/wrapper/dot_dict.py,sha256=VdAUH-DO6y86pl_9N6v-vU9mdLraWh5HjVzI5iC1dMs,5295
351
351
  orionis/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
352
- orionis/test/kernel.py,sha256=O_gNZwGkKJnaRZdVAd1z22MI-n-Q6LyWtQPujpyXNvw,13407
352
+ orionis/test/kernel.py,sha256=_dzms3-5IuDNIE_JjaexwaoZhkHNvU-aAUEwOVtW8sc,13337
353
353
  orionis/test/arguments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
354
354
  orionis/test/arguments/parser.py,sha256=Oz09NCbQ9JrJgviiPJ92WbRa54rXWCsr6RDzPoh05vE,6019
355
355
  orionis/test/cases/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -364,7 +364,7 @@ orionis/test/contracts/printer.py,sha256=FcTii6uglVIfvsgbmG0lj3hv1RGmDWuDo0eo4Z8
364
364
  orionis/test/contracts/render.py,sha256=0o6NjrKx2ewBf1JvAf8BaAtJQPb64yq1FJCeKb3iXys,967
365
365
  orionis/test/contracts/unit_test.py,sha256=pEceAVTJGb1ohEiiMD8X3YKT6xUsRECpB0ZO9K-RUWU,8383
366
366
  orionis/test/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
367
- orionis/test/core/unit_test.py,sha256=LA46wkU2dogXDVEX2xydrhUTC7vuFoVLSdemPVgLAHk,59117
367
+ orionis/test/core/unit_test.py,sha256=FsV1x1ooyaDFhLGglEwVsV-TzQDe9DBsIP5CKe3kNLw,58030
368
368
  orionis/test/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
369
369
  orionis/test/entities/arguments.py,sha256=V7HT-yaXzQR5m9E8AcJST2vQ4DGd7E0ks1swkRN_v6E,1394
370
370
  orionis/test/entities/result.py,sha256=8HRLg32bRLZX8NWgsGimJbSG8aX6n0VpbUEG57ClTGo,3023
@@ -385,10 +385,10 @@ orionis/test/records/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
385
385
  orionis/test/records/logs.py,sha256=EOQcloMVdhlNl2lU9igQz8H4b-OtKtiwh2pgr_QZWOI,13186
386
386
  orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
387
387
  orionis/test/view/render.py,sha256=zd7xDvVfmQ2HxZamDTzL2-z2PpyL99EaolbbM7wTah4,5014
388
- orionis-0.380.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
388
+ orionis-0.381.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
389
389
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
390
390
  tests/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
391
- tests/example/test_example.py,sha256=hmfAaYU2lPsjO9teUiXcdmeRlBrF33rhaVbiYD3VPs0,944
391
+ tests/example/test_example.py,sha256=jK_UvFhKmidN-8DcpGyCuyilFU0vHeKGuu2PJzNgWSM,700
392
392
  tests/foundation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
393
393
  tests/foundation/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
394
394
  tests/foundation/config/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -486,8 +486,8 @@ tests/support/wrapper/test_services_wrapper_docdict.py,sha256=nTNrvJkMSPx_aopEQ9
486
486
  tests/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
487
487
  tests/testing/test_testing_result.py,sha256=fnH7hjumNSErAFGITJgq2LHxSzvPF2tdtmHL9kyAv-Y,4409
488
488
  tests/testing/test_testing_unit.py,sha256=d3CRGo6608fMzYcZKIKapjx_af2aigqWiKSiuK9euIY,7600
489
- orionis-0.380.0.dist-info/METADATA,sha256=MzNtBUHGw4xdvmc1qdxZxLAbY8P6dxMjm5wltWUVRDI,4772
490
- orionis-0.380.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
491
- orionis-0.380.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
492
- orionis-0.380.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
493
- orionis-0.380.0.dist-info/RECORD,,
489
+ orionis-0.381.0.dist-info/METADATA,sha256=piUUrybU1alt7P3usyIxPG-wCVBi3XZiiKqInmwzdDQ,4772
490
+ orionis-0.381.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
491
+ orionis-0.381.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
492
+ orionis-0.381.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
493
+ orionis-0.381.0.dist-info/RECORD,,
@@ -1,5 +1,3 @@
1
- from unittest.mock import patch
2
- from orionis.console.output.console import Console
3
1
  from orionis.console.output.contracts.console import IConsole
4
2
  from orionis.test.cases.synchronous import SyncTestCase
5
3
 
@@ -8,9 +6,7 @@ class TestExample(SyncTestCase):
8
6
  Unit tests for basic example functionality.
9
7
  """
10
8
 
11
- # @patch('orionis.console.output.contracts.console.IConsole', autospec=True)
12
- # @patch('orionis.console.output.contracts.console.IConsole', autospec=True)
13
- def testUnitExample(self, cos:Console) -> None:
9
+ def testUnitExample(self, console:IConsole) -> None:
14
10
  """
15
11
  Test that basic equality assertions work as expected.
16
12