orionis 0.407.0__py3-none-any.whl → 0.408.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 (29) hide show
  1. orionis/container/container.py +11 -9
  2. orionis/container/enums/lifetimes.py +2 -0
  3. orionis/container/validators/__init__.py +21 -0
  4. orionis/metadata/framework.py +1 -1
  5. {orionis-0.407.0.dist-info → orionis-0.408.0.dist-info}/METADATA +1 -1
  6. {orionis-0.407.0.dist-info → orionis-0.408.0.dist-info}/RECORD +29 -29
  7. tests/container/context/test_manager.py +15 -5
  8. tests/container/context/test_scope.py +12 -4
  9. tests/container/entities/test_binding.py +130 -21
  10. tests/container/enums/test_lifetimes.py +52 -18
  11. tests/container/facades/test_facade.py +29 -12
  12. tests/container/providers/test_providers.py +17 -10
  13. tests/container/resolver/test_resolver.py +14 -7
  14. tests/container/test_container.py +226 -71
  15. tests/container/test_singleton.py +43 -24
  16. tests/container/test_thread_safety.py +28 -156
  17. tests/container/validators/test_implements.py +59 -13
  18. tests/container/validators/test_is_abstract_class.py +73 -25
  19. tests/container/validators/test_is_callable.py +55 -26
  20. tests/container/validators/test_is_concrete_class.py +80 -17
  21. tests/container/validators/test_is_instance.py +67 -22
  22. tests/container/validators/test_is_not_subclass.py +28 -95
  23. tests/container/validators/test_is_subclass.py +84 -21
  24. tests/container/validators/test_is_valid_alias.py +46 -12
  25. tests/container/validators/test_lifetime.py +45 -14
  26. {orionis-0.407.0.dist-info → orionis-0.408.0.dist-info}/WHEEL +0 -0
  27. {orionis-0.407.0.dist-info → orionis-0.408.0.dist-info}/licenses/LICENCE +0 -0
  28. {orionis-0.407.0.dist-info → orionis-0.408.0.dist-info}/top_level.txt +0 -0
  29. {orionis-0.407.0.dist-info → orionis-0.408.0.dist-info}/zip-safe +0 -0
@@ -5,55 +5,64 @@ from orionis.container.container import Container
5
5
  from orionis.test.cases.asynchronous import AsyncTestCase
6
6
 
7
7
  class TestSingleton(AsyncTestCase):
8
- """Test suite for singleton pattern implementation."""
9
8
 
10
9
  async def testSingletonBasicFunctionality(self) -> None:
11
10
  """
12
- Test basic singleton functionality.
11
+ Tests the fundamental behavior of the singleton pattern for `Container` and `Orionis` classes.
13
12
 
14
- This test verifies that:
15
- 1. Multiple Container instances are the same object
16
- 2. Multiple Orionis instances are the same object
17
- 3. Container and Orionis are different singletons
13
+ This method verifies the following:
14
+ - Multiple instances of `Container` refer to the same object.
15
+ - Multiple instances of `Orionis` refer to the same object.
16
+ - The singleton instances of `Container` and `Orionis` are distinct from each other.
17
+
18
+ Returns
19
+ -------
20
+ None
21
+ This method does not return any value. Assertions are used to validate singleton behavior.
18
22
  """
19
- # Create multiple instances
23
+ # Create multiple instances of Container and Orionis
20
24
  container1 = Container()
21
25
  container2 = Container()
22
26
  orionis1 = Orionis()
23
27
  orionis2 = Orionis()
24
28
 
25
- # Test that Container instances are the same
29
+ # Assert that all Container instances are the same object
26
30
  self.assertIs(container1, container2)
27
31
  self.assertEqual(id(container1), id(container2))
28
32
 
29
- # Test that Orionis instances are the same
33
+ # Assert that all Orionis instances are the same object
30
34
  self.assertIs(orionis1, orionis2)
31
35
  self.assertEqual(id(orionis1), id(orionis2))
32
36
 
33
- # Test that Container and Orionis are different singletons
37
+ # Assert that Container and Orionis are different singleton instances
34
38
  self.assertIsNot(container1, orionis1)
35
39
 
36
40
  async def testSingletonThreadingSafety(self) -> None:
37
41
  """
38
- Test singleton in multi-threaded environment.
42
+ Validates the thread safety of the singleton pattern for `Container` and `Orionis` classes.
43
+
44
+ This method ensures that, even when multiple threads attempt to instantiate
45
+ `Container` and `Orionis` simultaneously, only one instance of each class is created.
39
46
 
40
- This test verifies that singleton pattern works correctly
41
- when instances are created from multiple threads simultaneously.
47
+ Returns
48
+ -------
49
+ None
50
+ This method does not return any value. Assertions are used to validate thread-safe singleton behavior.
42
51
  """
43
52
  container_instances = []
44
53
  orionis_instances = []
45
54
 
46
55
  def create_container():
47
- """Create container instance in thread."""
48
- time.sleep(0.01) # Small delay to increase chance of race condition
56
+ """Create and append a Container instance in a thread."""
57
+ time.sleep(0.01) # Increase chance of race condition
49
58
  container_instances.append(Container())
50
59
 
51
60
  def create_orionis():
52
- """Create orionis instance in thread."""
53
- time.sleep(0.01) # Small delay to increase chance of race condition
61
+ """Create and append an Orionis instance in a thread."""
62
+ time.sleep(0.01) # Increase chance of race condition
54
63
  orionis_instances.append(Orionis())
55
64
 
56
- # Create multiple threads
65
+ # Create threads for concurrent instantiation
57
66
  threads = []
58
67
  for i in range(10):
59
68
  t1 = threading.Thread(target=create_container)
@@ -68,10 +77,11 @@ class TestSingleton(AsyncTestCase):
68
77
  for t in threads:
69
78
  t.join()
70
79
 
71
- # Check that all instances are the same
80
+ # Collect instance IDs for verification
72
81
  container_ids = [id(c) for c in container_instances]
73
82
  orionis_ids = [id(o) for o in orionis_instances]
74
83
 
84
+ # Assert that only one unique instance exists for each class
75
85
  self.assertEqual(len(set(container_ids)), 1)
76
86
  self.assertEqual(len(set(orionis_ids)), 1)
77
87
  self.assertEqual(len(container_instances), 10)
@@ -79,20 +89,29 @@ class TestSingleton(AsyncTestCase):
79
89
 
80
90
  async def testInheritanceSeparation(self) -> None:
81
91
  """
82
- Test that Container and Orionis maintain separate singleton instances.
92
+ Ensures that singleton instances are maintained separately for `Container` and `Orionis` classes.
83
93
 
84
- This test verifies that different singleton classes maintain
85
- their own separate instances while both implementing singleton pattern.
94
+ This method checks that:
95
+ - Each class maintains its own singleton instance.
96
+ - Data added to one singleton does not affect the other.
97
+ - Both classes correctly implement the singleton pattern independently.
98
+
99
+ Returns
100
+ -------
101
+ None
102
+ This method does not return any value. Assertions are used to validate singleton separation.
86
103
  """
87
104
  container = Container()
88
105
  orionis = Orionis()
89
106
 
90
- # Add some data to each to verify they're separate
107
+ # Add a callable to the Container singleton
91
108
  container.callable("test_container", lambda: "container_value")
92
109
 
93
- # Check that they're different instances but both are singletons
110
+ # Verify that Container and Orionis are distinct singletons
94
111
  self.assertEqual(type(container).__name__, "Container")
95
112
  self.assertEqual(type(orionis).__name__, "Application")
96
113
  self.assertIsNot(container, orionis)
114
+
115
+ # Check that the callable is bound only to Container
97
116
  self.assertTrue(container.bound('test_container'))
98
117
  self.assertFalse(orionis.bound('test_container'))
@@ -10,16 +10,34 @@ class TestThreadSafety(AsyncTestCase):
10
10
 
11
11
  async def testStressSingleton(self) -> None:
12
12
  """
13
- Test singleton under extreme concurrent conditions.
14
-
15
- This test creates a large number of threads that simultaneously
16
- attempt to create singleton instances to verify thread-safety.
13
+ Stress test singleton behavior under extreme concurrent conditions.
14
+
15
+ This method creates a large number of threads that simultaneously attempt to
16
+ instantiate singleton objects (`Container` and `Orionis`). It verifies that
17
+ only one unique instance of each singleton exists, regardless of concurrent
18
+ access, and that the singleton instances are distinct from each other.
19
+
20
+ Parameters
21
+ ----------
22
+ None
23
+
24
+ Returns
25
+ -------
26
+ None
27
+ This method does not return any value. Assertions are used to validate
28
+ singleton behavior under stress.
29
+
30
+ Notes
31
+ -----
32
+ - Random delays are introduced to increase the likelihood of race conditions.
33
+ - ThreadPoolExecutor is used to simulate high concurrency.
34
+ - The test ensures that singleton integrity is maintained even under heavy load.
17
35
  """
18
36
  container_instances = []
19
37
  orionis_instances = []
20
38
 
21
39
  def create_container_with_delay():
22
- """Create container with random delay to simulate real conditions."""
40
+ """Create a Container instance after a random delay to simulate real-world concurrency."""
23
41
  # Random delay to increase chance of race conditions
24
42
  time.sleep(random.uniform(0.001, 0.01))
25
43
  container = Container()
@@ -27,16 +45,17 @@ class TestThreadSafety(AsyncTestCase):
27
45
  return id(container)
28
46
 
29
47
  def create_orionis_with_delay():
30
- """Create orionis with random delay to simulate real conditions."""
48
+ """Create an Orionis instance after a random delay to simulate real-world concurrency."""
31
49
  # Random delay to increase chance of race conditions
32
50
  time.sleep(random.uniform(0.001, 0.01))
33
51
  orionis = Orionis()
34
52
  orionis_instances.append(orionis)
35
53
  return id(orionis)
36
54
 
37
- # Create a large number of threads
55
+ # Number of concurrent threads to simulate
38
56
  num_threads = 100
39
57
 
58
+ # Use ThreadPoolExecutor to run tasks concurrently
40
59
  with ThreadPoolExecutor(max_workers=50) as executor:
41
60
  # Submit container creation tasks
42
61
  container_futures = [
@@ -50,11 +69,11 @@ class TestThreadSafety(AsyncTestCase):
50
69
  for _ in range(num_threads)
51
70
  ]
52
71
 
53
- # Wait for all tasks to complete
72
+ # Wait for all tasks to complete and collect instance IDs
54
73
  container_ids = [future.result() for future in as_completed(container_futures)]
55
74
  orionis_ids = [future.result() for future in as_completed(orionis_futures)]
56
75
 
57
- # Verify all instances are the same
76
+ # Verify all instances are the same (singleton property)
58
77
  unique_container_ids = set(container_ids)
59
78
  unique_orionis_ids = set(orionis_ids)
60
79
 
@@ -68,150 +87,3 @@ class TestThreadSafety(AsyncTestCase):
68
87
  orionis_id = list(unique_orionis_ids)[0] if unique_orionis_ids else None
69
88
 
70
89
  self.assertNotEqual(container_id, orionis_id)
71
-
72
- async def testRapidAccess(self) -> None:
73
- """
74
- Test rapid concurrent access to existing singleton instances.
75
-
76
- This test verifies that rapid concurrent access to already
77
- created singleton instances maintains consistency.
78
- """
79
- # Create initial instances
80
- initial_container = Container()
81
- initial_orionis = Orionis()
82
-
83
- container_ids = []
84
- orionis_ids = []
85
-
86
- def rapid_container_access():
87
- """Rapidly access container singleton."""
88
- for _ in range(100):
89
- container = Container()
90
- container_ids.append(id(container))
91
-
92
- def rapid_orionis_access():
93
- """Rapidly access orionis singleton."""
94
- for _ in range(100):
95
- orionis = Orionis()
96
- orionis_ids.append(id(orionis))
97
-
98
- # Create threads for rapid access
99
- threads = []
100
- for _ in range(20):
101
- t1 = threading.Thread(target=rapid_container_access)
102
- t2 = threading.Thread(target=rapid_orionis_access)
103
- threads.extend([t1, t2])
104
-
105
- # Start all threads simultaneously
106
- start_time = time.time()
107
- for t in threads:
108
- t.start()
109
-
110
- # Wait for all threads to complete
111
- for t in threads:
112
- t.join()
113
-
114
- end_time = time.time()
115
-
116
- # Verify consistency
117
- unique_container_ids = set(container_ids)
118
- unique_orionis_ids = set(orionis_ids)
119
-
120
- self.assertEqual(len(container_ids), 20 * 100) # 20 threads * 100 accesses each
121
- self.assertEqual(len(unique_container_ids), 1)
122
- self.assertEqual(len(orionis_ids), 20 * 100)
123
- self.assertEqual(len(unique_orionis_ids), 1)
124
-
125
- # Verify performance is reasonable (should complete quickly)
126
- self.assertLess(end_time - start_time, 10.0) # Should complete in less than 10 seconds
127
-
128
- async def testMixedOperations(self) -> None:
129
- """
130
- Test mixed read/write operations on singletons.
131
-
132
- This test verifies that concurrent read/write operations
133
- maintain singleton consistency and data integrity.
134
- """
135
- errors = []
136
-
137
- def mixed_operations():
138
- """Perform mixed operations on containers."""
139
- try:
140
- # Get instances
141
- container = Container()
142
- orionis = Orionis()
143
-
144
- # Perform some operations
145
- test_key = f"test_func_{threading.current_thread().ident}"
146
- container.callable(test_key, lambda: "test_value")
147
-
148
- # Verify the same instance
149
- container2 = Container()
150
- orionis2 = Orionis()
151
-
152
- if container is not container2:
153
- errors.append("Container singleton violated")
154
-
155
- if orionis is not orionis2:
156
- errors.append("Orionis singleton violated")
157
-
158
- # Check bindings consistency
159
- if not container2.bound(test_key):
160
- errors.append("Binding not consistent across instances")
161
-
162
- except Exception as e:
163
- errors.append(f"Exception in mixed operations: {e}")
164
-
165
- # Run mixed operations in multiple threads
166
- threads = []
167
- for _ in range(50):
168
- t = threading.Thread(target=mixed_operations)
169
- threads.append(t)
170
-
171
- for t in threads:
172
- t.start()
173
-
174
- for t in threads:
175
- t.join()
176
-
177
- # Assert no errors occurred
178
- self.assertEqual(len(errors), 0, f"Errors found: {errors[:5]}")
179
-
180
- async def testMemoryConsistency(self) -> None:
181
- """
182
- Test memory consistency across threads.
183
-
184
- This test verifies that changes made in one thread
185
- are visible in other threads due to singleton behavior.
186
- """
187
- results = []
188
-
189
- def thread_a():
190
- """Thread A - modifies the container."""
191
- container = Container()
192
- container.callable("thread_a_marker", lambda: "from_thread_a")
193
- results.append("A_completed")
194
-
195
- def thread_b():
196
- """Thread B - reads from the container."""
197
- # Small delay to ensure thread A runs first
198
- time.sleep(0.01)
199
- container = Container()
200
- has_marker = container.bound("thread_a_marker")
201
- results.append(f"B_sees_marker: {has_marker}")
202
-
203
- t1 = threading.Thread(target=thread_a)
204
- t2 = threading.Thread(target=thread_b)
205
-
206
- t1.start()
207
- t2.start()
208
-
209
- t1.join()
210
- t2.join()
211
-
212
- # Verify that thread B saw the changes made by thread A
213
- a_completed = "A_completed" in results
214
- b_saw_marker = any("B_sees_marker: True" in r for r in results)
215
-
216
- self.assertTrue(a_completed, "Thread A should have completed")
217
- self.assertTrue(b_saw_marker, "Thread B should see Thread A's changes")
@@ -4,20 +4,25 @@ from orionis.container.validators.implements import ImplementsAbstractMethods
4
4
  from orionis.container.exceptions.exception import OrionisContainerException
5
5
 
6
6
  class TestImplementsAbstractMethods(AsyncTestCase):
7
- """
8
- Test cases for the ImplementsAbstractMethods validator in orionis.container.validators.implements.
9
-
10
- Notes
11
- -----
12
- This test suite validates the functionality of the ImplementsAbstractMethods validator
13
- which ensures that concrete classes correctly implement abstract methods.
14
- """
15
7
 
16
8
  async def asyncSetUp(self) -> None:
17
9
  """
18
- Set up test fixtures.
10
+ Set up test fixtures for ImplementsAbstractMethods validator tests.
11
+
12
+ This method defines several abstract and concrete classes to be used in the test cases:
13
+ - An abstract base class with two abstract methods.
14
+ - A concrete class that correctly implements all abstract methods.
15
+ - A concrete class that does not implement all abstract methods.
16
+ - A non-abstract base class for negative test cases.
17
+
18
+ The defined classes are assigned to instance attributes for use in subsequent tests.
19
+
20
+ Returns
21
+ -------
22
+ None
23
+ This method does not return any value.
19
24
  """
20
- # Define abstract classes for testing
25
+ # Define an abstract base class with two abstract methods
21
26
  class AbstractBase(ABC):
22
27
  @abstractmethod
23
28
  def abstract_method(self) -> None:
@@ -27,6 +32,7 @@ class TestImplementsAbstractMethods(AsyncTestCase):
27
32
  def another_abstract_method(self) -> str:
28
33
  pass
29
34
 
35
+ # Concrete class that implements all abstract methods
30
36
  class ConcreteCorrect(AbstractBase):
31
37
  def abstract_method(self) -> None:
32
38
  pass
@@ -34,14 +40,17 @@ class TestImplementsAbstractMethods(AsyncTestCase):
34
40
  def another_abstract_method(self) -> str:
35
41
  return "implemented"
36
42
 
43
+ # Concrete class that does not implement all abstract methods
37
44
  class ConcreteIncomplete(AbstractBase):
38
45
  def abstract_method(self) -> None:
39
46
  pass
40
47
 
48
+ # Non-abstract base class for negative test cases
41
49
  class NonAbstractBase:
42
50
  def regular_method(self) -> None:
43
51
  pass
44
52
 
53
+ # Assign classes to instance attributes for use in tests
45
54
  self.AbstractBase = AbstractBase
46
55
  self.ConcreteCorrect = ConcreteCorrect
47
56
  self.ConcreteIncomplete = ConcreteIncomplete
@@ -50,6 +59,11 @@ class TestImplementsAbstractMethods(AsyncTestCase):
50
59
  async def testValidImplementation(self) -> None:
51
60
  """
52
61
  Test that validation passes when all abstract methods are implemented.
62
+
63
+ Returns
64
+ -------
65
+ None
66
+ This method does not return any value.
53
67
  """
54
68
  # Test with class
55
69
  ImplementsAbstractMethods(
@@ -67,8 +81,13 @@ class TestImplementsAbstractMethods(AsyncTestCase):
67
81
  async def testIncompleteImplementation(self) -> None:
68
82
  """
69
83
  Test that validation fails when not all abstract methods are implemented.
84
+
85
+ Returns
86
+ -------
87
+ None
88
+ This method does not return any value.
70
89
  """
71
- # Test with class
90
+ # Test with class missing an abstract method
72
91
  with self.assertRaises(OrionisContainerException) as context:
73
92
  ImplementsAbstractMethods(
74
93
  abstract=self.AbstractBase,
@@ -78,7 +97,7 @@ class TestImplementsAbstractMethods(AsyncTestCase):
78
97
  self.assertIn("does not implement the following abstract methods", str(context.exception))
79
98
  self.assertIn("another_abstract_method", str(context.exception))
80
99
 
81
- # Test with instance
100
+ # Test with instance missing an abstract method
82
101
  with self.assertRaises(TypeError):
83
102
  ImplementsAbstractMethods(
84
103
  abstract=self.AbstractBase,
@@ -88,7 +107,13 @@ class TestImplementsAbstractMethods(AsyncTestCase):
88
107
  async def testMissingAbstractClass(self) -> None:
89
108
  """
90
109
  Test that validation fails when no abstract class is provided.
110
+
111
+ Returns
112
+ -------
113
+ None
114
+ This method does not return any value.
91
115
  """
116
+ # Should raise exception if abstract class is not provided
92
117
  with self.assertRaises(OrionisContainerException) as context:
93
118
  ImplementsAbstractMethods(
94
119
  concrete=self.ConcreteCorrect
@@ -99,7 +124,13 @@ class TestImplementsAbstractMethods(AsyncTestCase):
99
124
  async def testMissingConcreteImplementation(self) -> None:
100
125
  """
101
126
  Test that validation fails when neither concrete class nor instance is provided.
127
+
128
+ Returns
129
+ -------
130
+ None
131
+ This method does not return any value.
102
132
  """
133
+ # Should raise exception if neither concrete nor instance is provided
103
134
  with self.assertRaises(OrionisContainerException) as context:
104
135
  ImplementsAbstractMethods(
105
136
  abstract=self.AbstractBase
@@ -110,7 +141,13 @@ class TestImplementsAbstractMethods(AsyncTestCase):
110
141
  async def testNonAbstractClass(self) -> None:
111
142
  """
112
143
  Test that validation fails when the provided abstract class has no abstract methods.
144
+
145
+ Returns
146
+ -------
147
+ None
148
+ This method does not return any value.
113
149
  """
150
+ # Should raise exception if abstract class does not define any abstract methods
114
151
  with self.assertRaises(OrionisContainerException) as context:
115
152
  ImplementsAbstractMethods(
116
153
  abstract=self.NonAbstractBase,
@@ -122,13 +159,22 @@ class TestImplementsAbstractMethods(AsyncTestCase):
122
159
  async def testRenamedAbstractMethods(self) -> None:
123
160
  """
124
161
  Test handling of renamed abstract methods with class name prefixes.
162
+
163
+ This test verifies that the validator correctly handles cases where abstract methods
164
+ are renamed with class name prefixes in the concrete implementation.
165
+
166
+ Returns
167
+ -------
168
+ None
169
+ This method does not return any value.
125
170
  """
126
- # Define classes with renamed methods
171
+ # Define an abstract class with a prefixed abstract method
127
172
  class AbstractWithPrefix(ABC):
128
173
  @abstractmethod
129
174
  def _AbstractWithPrefix_method(self) -> None:
130
175
  pass
131
176
 
177
+ # Concrete class with a similarly prefixed method
132
178
  class ConcreteWithPrefix:
133
179
  def _ConcreteWithPrefix_method(self) -> None:
134
180
  pass