orionis 0.407.0__py3-none-any.whl → 0.409.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 (37) 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/services/log/contracts/log_service.py +56 -4
  6. orionis/services/log/handlers/filename.py +20 -12
  7. orionis/services/log/handlers/size_rotating.py +11 -3
  8. orionis/services/log/handlers/timed_rotating.py +11 -3
  9. orionis/services/log/log_service.py +122 -72
  10. orionis/services/paths/contracts/resolver.py +0 -1
  11. {orionis-0.407.0.dist-info → orionis-0.409.0.dist-info}/METADATA +1 -1
  12. {orionis-0.407.0.dist-info → orionis-0.409.0.dist-info}/RECORD +37 -35
  13. tests/container/context/test_manager.py +15 -5
  14. tests/container/context/test_scope.py +12 -4
  15. tests/container/entities/test_binding.py +130 -21
  16. tests/container/enums/test_lifetimes.py +52 -18
  17. tests/container/facades/test_facade.py +29 -12
  18. tests/container/providers/test_providers.py +17 -10
  19. tests/container/resolver/test_resolver.py +14 -7
  20. tests/container/test_container.py +226 -71
  21. tests/container/test_singleton.py +43 -24
  22. tests/container/test_thread_safety.py +28 -156
  23. tests/container/validators/test_implements.py +59 -13
  24. tests/container/validators/test_is_abstract_class.py +73 -25
  25. tests/container/validators/test_is_callable.py +55 -26
  26. tests/container/validators/test_is_concrete_class.py +80 -17
  27. tests/container/validators/test_is_instance.py +67 -22
  28. tests/container/validators/test_is_not_subclass.py +28 -95
  29. tests/container/validators/test_is_subclass.py +84 -21
  30. tests/container/validators/test_is_valid_alias.py +46 -12
  31. tests/container/validators/test_lifetime.py +45 -14
  32. tests/services/log/__init__.py +0 -0
  33. tests/services/log/test_log.py +97 -0
  34. {orionis-0.407.0.dist-info → orionis-0.409.0.dist-info}/WHEEL +0 -0
  35. {orionis-0.407.0.dist-info → orionis-0.409.0.dist-info}/licenses/LICENCE +0 -0
  36. {orionis-0.407.0.dist-info → orionis-0.409.0.dist-info}/top_level.txt +0 -0
  37. {orionis-0.407.0.dist-info → orionis-0.409.0.dist-info}/zip-safe +0 -0
@@ -7,16 +7,23 @@ class TestServiceProviderMethods(AsyncTestCase):
7
7
 
8
8
  async def testMethodsExist(self):
9
9
  """
10
- Checks that the ServiceProvider class implements the required methods and constructor.
10
+ Validates the implementation of required methods and inheritance in the ServiceProvider class.
11
11
 
12
- This test verifies the following:
13
- - The existence of the '__init__', 'register', and 'boot' methods in ServiceProvider.
14
- - That 'register' and 'boot' are asynchronous methods.
15
- - That ServiceProvider inherits from IServiceProvider.
12
+ This test performs the following checks:
13
+ - Verifies that the ServiceProvider class defines the '__init__', 'register', and 'boot' methods.
14
+ - Ensures that the 'register' and 'boot' methods are asynchronous coroutine functions.
15
+ - Confirms that ServiceProvider is a subclass of IServiceProvider.
16
16
 
17
- Returns:
18
- None. The method uses assertions to validate class structure and method types.
17
+ Parameters
18
+ ----------
19
+ None
20
+
21
+ Returns
22
+ -------
23
+ None
24
+ The method does not return any value. Assertions are used to validate class structure and method types.
19
25
  """
26
+
20
27
  # List of required methods and their associated class
21
28
  expected_methods = [
22
29
  ("__init__", ServiceProvider),
@@ -34,15 +41,15 @@ class TestServiceProviderMethods(AsyncTestCase):
34
41
  # Ensure 'register' and 'boot' are asynchronous methods
35
42
  self.assertTrue(
36
43
  inspect.iscoroutinefunction(ServiceProvider.register),
37
- "register must be async"
44
+ "'register' must be an async coroutine function."
38
45
  )
39
46
  self.assertTrue(
40
47
  inspect.iscoroutinefunction(ServiceProvider.boot),
41
- "boot must be async"
48
+ "'boot' must be an async coroutine function."
42
49
  )
43
50
 
44
51
  # Ensure ServiceProvider inherits from IServiceProvider
45
52
  self.assertTrue(
46
53
  issubclass(ServiceProvider, IServiceProvider),
47
- "ServiceProvider must inherit from IServiceProvider"
54
+ "ServiceProvider must inherit from IServiceProvider."
48
55
  )
@@ -7,15 +7,22 @@ class TestResolverMethods(AsyncTestCase):
7
7
 
8
8
  async def testMethodsExist(self):
9
9
  """
10
- Checks that the `Resolver` class implements all required methods and inherits from `IResolver`.
10
+ Validates the implementation and structure of the `Resolver` class.
11
11
 
12
- This test verifies the presence of specific methods in the `Resolver` class, ensures that
13
- `Resolver` is a subclass of `IResolver`, and confirms that the main public methods are not asynchronous.
12
+ This test checks that the `Resolver` class:
13
+ - Implements all required methods.
14
+ - Inherits from the `IResolver` interface.
15
+ - Has main public methods (`resolve`, `resolveType`, `resolveSignature`) that are synchronous.
16
+
17
+ Parameters
18
+ ----------
19
+ self : TestResolverMethods
20
+ The test case instance.
14
21
 
15
22
  Returns
16
23
  -------
17
24
  None
18
- This method does not return a value. Assertions are used to validate class structure.
25
+ The method does not return any value. Assertions are used to validate the class structure.
19
26
  """
20
27
 
21
28
  # List of required method names that must be implemented by Resolver
@@ -34,20 +41,20 @@ class TestResolverMethods(AsyncTestCase):
34
41
  "_Resolver__resolveDependencies",
35
42
  ]
36
43
 
37
- # Assert that each required method exists in Resolver
44
+ # Check that each required method exists in Resolver
38
45
  for method in required_methods:
39
46
  self.assertTrue(
40
47
  hasattr(Resolver, method),
41
48
  f"Resolver must implement the method '{method}'"
42
49
  )
43
50
 
44
- # Assert that Resolver inherits from IResolver
51
+ # Ensure Resolver inherits from IResolver
45
52
  self.assertTrue(
46
53
  issubclass(Resolver, IResolver),
47
54
  "Resolver must inherit from IResolver"
48
55
  )
49
56
 
50
- # Assert that main public methods are not asynchronous
57
+ # Verify that main public methods are not asynchronous
51
58
  for method in ["resolve", "resolveType", "resolveSignature"]:
52
59
  self.assertFalse(
53
60
  inspect.iscoroutinefunction(getattr(Resolver, method)),
@@ -5,63 +5,109 @@ from orionis.test.cases.asynchronous import AsyncTestCase
5
5
  from tests.container.mocks.mock_simple_classes import Car, ICar
6
6
 
7
7
  class TestContainer(AsyncTestCase):
8
- """Test suite for the Container class functionality."""
9
8
 
10
9
  async def testTransientRegistration(self) -> None:
11
10
  """
12
11
  Tests the transient registration of a service in the container.
13
- It verifies that:
14
- 1. The container.transient() method correctly registers a type (ICar) to be resolved as another type (Car)
15
- 2. The resolved instances are of the correct type (Car)
16
- 3. Each resolution returns a new instance (instance1 is not the same object as instance2)
12
+
13
+ This method verifies the following behaviors:
14
+ - The `container.transient()` method correctly registers a transient binding from an abstract type (`ICar`) to a concrete implementation (`Car`).
15
+ - Each call to `container.make(ICar)` returns a new instance of `Car`, confirming transient behavior.
16
+ - The resolved instances are of the correct type (`Car`), and each instance is a distinct object.
17
+
18
+ Parameters
19
+ ----------
20
+ self : TestContainer
21
+ The test case instance.
22
+
23
+ Returns
24
+ -------
25
+ None
26
+ This method does not return any value. Assertions are used to validate expected behavior.
27
+
28
+ Notes
29
+ -----
30
+ After the test, the registration for `ICar` is dropped from the container to clean up.
17
31
  """
18
32
  container = Container()
33
+ # Register ICar as a transient binding to Car
19
34
  container.transient(ICar, Car)
35
+ # Resolve two instances of ICar (should be different objects)
20
36
  instance1 = container.make(ICar)
21
37
  instance2 = container.make(ICar)
22
38
 
39
+ # Assert both instances are of type Car
23
40
  self.assertIsInstance(instance1, Car)
24
41
  self.assertIsInstance(instance2, Car)
42
+ # Assert that the instances are not the same object (transient behavior)
25
43
  self.assertIsNot(instance1, instance2)
26
44
 
45
+ # Clean up registration
27
46
  container.drop(abstract=ICar)
28
47
 
29
48
  async def testSingletonRegistration(self) -> None:
30
49
  """
31
- Test that verifies singleton registration and resolution from the container.
32
- This test ensures that:
33
- 1. A class can be registered as a singleton implementation of an interface
34
- 2. The container correctly returns an instance of the registered implementation
35
- 3. Multiple requests for the same interface return the same instance
36
- The test binds the ICar interface to the Car implementation as a singleton,
37
- then verifies that two requests for ICar:
38
- - Both return instances of Car
39
- - Return the exact same instance (object identity)
50
+ Tests singleton registration and resolution from the container.
51
+
52
+ This method ensures:
53
+ - A class (`Car`) can be registered as a singleton implementation of an interface (`ICar`).
54
+ - The container returns an instance of the registered implementation.
55
+ - Multiple requests for the same interface return the same instance, confirming singleton behavior.
56
+
57
+ Parameters
58
+ ----------
59
+ self : TestContainer
60
+ The test case instance.
61
+
62
+ Returns
63
+ -------
64
+ None
65
+ This method does not return any value. Assertions are used to validate expected behavior.
66
+
67
+ Notes
68
+ -----
69
+ The registration for `ICar` is dropped after the test.
40
70
  """
41
71
  container = Container()
72
+ # Register ICar as a singleton binding to Car
42
73
  container.singleton(ICar, Car)
74
+ # Resolve two instances of ICar (should be the same object)
43
75
  instance1 = container.make(ICar)
44
76
  instance2 = container.make(ICar)
45
77
 
78
+ # Assert both instances are of type Car
46
79
  self.assertIsInstance(instance1, Car)
47
80
  self.assertIsInstance(instance2, Car)
81
+ # Assert that both instances are the same object (singleton behavior)
48
82
  self.assertIs(instance1, instance2)
49
83
 
84
+ # Clean up registration
50
85
  container.drop(abstract=ICar)
51
86
 
52
87
  async def testScopedRegistration(self) -> None:
53
88
  """
54
89
  Tests the scoped registration functionality of the container.
55
- This test verifies that:
56
- 1. Within a single context, scoped registrations return the same instance
57
- when the same interface is requested multiple times
58
- 2. Different contexts produce different instances of the same registration
59
- The test creates two separate contexts and confirms that instances obtained
60
- within a single context are identical, but instances from different contexts
61
- are distinct.
62
- """
63
90
 
91
+ This method verifies:
92
+ - Within a single context, scoped registrations return the same instance when the same interface is requested multiple times.
93
+ - Different contexts produce different instances of the same registration.
94
+
95
+ Parameters
96
+ ----------
97
+ self : TestContainer
98
+ The test case instance.
99
+
100
+ Returns
101
+ -------
102
+ None
103
+ This method does not return any value. Assertions are used to validate expected behavior.
104
+
105
+ Notes
106
+ -----
107
+ The registration for `ICar` is dropped after the test.
108
+ """
64
109
  container = Container()
110
+ # First context: instances should be the same
65
111
  with container.createContext():
66
112
  container.scoped(ICar, Car)
67
113
  instance1 = container.make(ICar)
@@ -71,43 +117,70 @@ class TestContainer(AsyncTestCase):
71
117
  self.assertIsInstance(instance2, Car)
72
118
  self.assertIs(instance1, instance2)
73
119
 
120
+ # Second context: instance should be different from previous context
74
121
  with container.createContext():
75
122
  container.scoped(ICar, Car)
76
123
  instance3 = container.make(ICar)
77
124
  self.assertIsNot(instance1, instance3)
78
125
 
126
+ # Clean up registration
79
127
  container.drop(abstract=ICar)
80
128
 
81
129
  async def testInstanceRegistration(self) -> None:
82
130
  """
83
- Test case for instance registration in the container.
84
- This test ensures that when an instance is registered to a service in the container,
85
- the container returns exactly the same instance when resolving that service.
86
- The test:
87
- 1. Creates an instance of Car
88
- 2. Registers this instance to the ICar interface in the container
89
- 3. Resolves the ICar interface from the container
90
- 4. Asserts that the resolved instance is exactly the same object as the one registered
131
+ Tests instance registration in the container.
132
+
133
+ This method ensures:
134
+ - When an instance is registered to a service in the container, the container returns exactly the same instance when resolving that service.
135
+
136
+ Parameters
137
+ ----------
138
+ self : TestContainer
139
+ The test case instance.
140
+
141
+ Returns
142
+ -------
143
+ None
144
+ This method does not return any value. Assertions are used to validate expected behavior.
145
+
146
+ Notes
147
+ -----
148
+ The registration for `ICar` is dropped after the test.
91
149
  """
92
150
  car_instance = Car()
93
151
  container = Container()
152
+ # Register a specific instance of Car to ICar
94
153
  container.instance(ICar, car_instance)
154
+ # Resolve ICar and check that it returns the same instance
95
155
  resolved = container.make(ICar)
96
156
 
97
157
  self.assertIs(resolved, car_instance)
98
158
 
159
+ # Clean up registration
99
160
  container.drop(abstract=ICar)
100
161
 
101
162
  async def testCallableRegistration(self) -> None:
102
163
  """
103
- Test that callables can be registered and resolved from the container.
104
- This test verifies that:
105
- 1. Functions can be registered in the container using the callable() method
106
- 2. Registered functions can be resolved and executed using the make() method
107
- 3. Arguments can be passed to the resolved functions as positional arguments
108
- 4. Arguments can be passed to the resolved functions as keyword arguments
109
- The test registers two simple functions (add and multiply) and verifies
110
- they can be correctly resolved and executed with the expected results.
164
+ Tests that callables can be registered and resolved from the container.
165
+
166
+ This method verifies:
167
+ - Functions can be registered in the container using the `callable()` method.
168
+ - Registered functions can be resolved and executed using the `make()` method.
169
+ - Arguments can be passed to the resolved functions as positional and keyword arguments.
170
+
171
+ Parameters
172
+ ----------
173
+ self : TestContainer
174
+ The test case instance.
175
+
176
+ Returns
177
+ -------
178
+ None
179
+ This method does not return any value. Assertions are used to validate expected behavior.
180
+
181
+ Notes
182
+ -----
183
+ The registrations for 'add' and 'multiply' are dropped after the test.
111
184
  """
112
185
  def add(a: int, b: int) -> int:
113
186
  return a + b
@@ -116,28 +189,44 @@ class TestContainer(AsyncTestCase):
116
189
  return a * b
117
190
 
118
191
  container = Container()
192
+ # Register callables
119
193
  container.callable('add', add)
120
194
  container.callable('multiply', multiply)
121
195
 
196
+ # Test resolution and execution with positional and keyword arguments
122
197
  self.assertEqual(container.make('add', 1, 2), 3)
123
198
  self.assertEqual(container.make('multiply', 3, 4), 12)
124
199
  self.assertEqual(container.make('add', a=5, b=7), 12)
125
200
 
201
+ # Clean up registrations
126
202
  container.drop(alias='add')
127
203
  container.drop(alias='multiply')
128
204
 
129
205
  async def testTransientFacade(self) -> None:
130
206
  """
131
- Test case for transient instance resolution using a Facade pattern.
132
- This test validates that:
133
- 1. The container can register a transient binding between an interface and a class
134
- 2. The Facade pattern correctly resolves instances of the registered interface
135
- 3. Multiple calls to the Facade's resolve() method return different instances
136
- when the binding is transient
137
- The test creates a CarFacade that accesses ICar interface which is bound to the Car
138
- implementation in a transient manner, ensuring each resolution yields a new instance.
207
+ Tests transient instance resolution using the Facade pattern.
208
+
209
+ This method validates:
210
+ - The container can register a transient binding between an interface and a class.
211
+ - The Facade pattern correctly resolves instances of the registered interface.
212
+ - Multiple calls to the Facade's `resolve()` method return different instances, confirming transient behavior.
213
+
214
+ Parameters
215
+ ----------
216
+ self : TestContainer
217
+ The test case instance.
218
+
219
+ Returns
220
+ -------
221
+ None
222
+ This method does not return any value. Assertions are used to validate expected behavior.
223
+
224
+ Notes
225
+ -----
226
+ The registration for `ICar` is dropped after the test.
139
227
  """
140
228
  container = Application()
229
+ # Register ICar as a transient binding to Car
141
230
  container.transient(ICar, Car)
142
231
 
143
232
  class CarFacade(Facade):
@@ -145,6 +234,7 @@ class TestContainer(AsyncTestCase):
145
234
  def getFacadeAccessor(cls):
146
235
  return ICar
147
236
 
237
+ # Resolve two instances via Facade (should be different objects)
148
238
  instance1 = CarFacade.resolve()
149
239
  instance2 = CarFacade.resolve()
150
240
 
@@ -152,20 +242,35 @@ class TestContainer(AsyncTestCase):
152
242
  self.assertIsInstance(instance2, Car)
153
243
  self.assertIsNot(instance1, instance2)
154
244
 
245
+ # Clean up registration
155
246
  container.drop(abstract=ICar)
156
247
 
157
248
  async def testSingletonFacade(self) -> None:
158
249
  """
159
- Tests if the Facade pattern correctly resolves singleton instances.
160
- This test verifies that:
161
- 1. A singleton binding can be registered in the container
162
- 2. A Facade class can be created to access this binding
163
- 3. Multiple resolutions through the Facade return the same instance
164
- 4. The resolved instances are of the correct type (Car)
165
- The test demonstrates how Facades act as static proxies to container bindings,
166
- maintaining the singleton behavior defined in the container.
250
+ Tests singleton instance resolution using the Facade pattern.
251
+
252
+ This method verifies:
253
+ - A singleton binding can be registered in the container.
254
+ - A Facade class can be created to access this binding.
255
+ - Multiple resolutions through the Facade return the same instance, confirming singleton behavior.
256
+ - The resolved instances are of the correct type (`Car`).
257
+
258
+ Parameters
259
+ ----------
260
+ self : TestContainer
261
+ The test case instance.
262
+
263
+ Returns
264
+ -------
265
+ None
266
+ This method does not return any value. Assertions are used to validate expected behavior.
267
+
268
+ Notes
269
+ -----
270
+ The registration for `ICar` is dropped after the test.
167
271
  """
168
272
  container = Application()
273
+ # Register ICar as a singleton binding to Car
169
274
  container.singleton(ICar, Car)
170
275
 
171
276
  class CarFacade(Facade):
@@ -173,6 +278,7 @@ class TestContainer(AsyncTestCase):
173
278
  def getFacadeAccessor(cls):
174
279
  return ICar
175
280
 
281
+ # Resolve two instances via Facade (should be the same object)
176
282
  instance1 = CarFacade.resolve()
177
283
  instance2 = CarFacade.resolve()
178
284
 
@@ -180,22 +286,36 @@ class TestContainer(AsyncTestCase):
180
286
  self.assertIsInstance(instance2, Car)
181
287
  self.assertIs(instance1, instance2)
182
288
 
289
+ # Clean up registration
183
290
  container.drop(abstract=ICar)
184
291
 
185
292
  async def testScopedFacade(self) -> None:
186
293
  """
187
294
  Tests the functionality of a Facade accessing a scoped service within a container context.
188
- This test verifies that:
189
- 1. The Facade can properly resolve a scoped service
190
- 2. Multiple resolves within the same scope return the same instance
191
- 3. The resolved instance is of the correct type
192
- The test creates a scoped registration for ICar interface to Car implementation,
193
- defines a CarFacade class that accesses ICar, and then confirms that:
194
- - Resolved instances are of Car type
195
- - Multiple resolutions return the same instance (scoped behavior)
295
+
296
+ This method verifies:
297
+ - The Facade can properly resolve a scoped service.
298
+ - Multiple resolves within the same scope return the same instance, confirming scoped behavior.
299
+ - The resolved instance is of the correct type (`Car`).
300
+
301
+ Parameters
302
+ ----------
303
+ self : TestContainer
304
+ The test case instance.
305
+
306
+ Returns
307
+ -------
308
+ None
309
+ This method does not return any value. Assertions are used to validate expected behavior.
310
+
311
+ Notes
312
+ -----
313
+ The registration for `ICar` is dropped after the test.
196
314
  """
197
315
  container = Application()
316
+ # Create a new scope/context
198
317
  with container.createContext():
318
+ # Register ICar as a scoped binding to Car
199
319
  container.scoped(ICar, Car)
200
320
 
201
321
  class CarFacade(Facade):
@@ -203,6 +323,7 @@ class TestContainer(AsyncTestCase):
203
323
  def getFacadeAccessor(cls):
204
324
  return ICar
205
325
 
326
+ # Resolve two instances via Facade (should be the same object within the scope)
206
327
  instance1 = CarFacade.resolve()
207
328
  instance2 = CarFacade.resolve()
208
329
 
@@ -210,29 +331,60 @@ class TestContainer(AsyncTestCase):
210
331
  self.assertIsInstance(instance2, Car)
211
332
  self.assertIs(instance1, instance2)
212
333
 
334
+ # Clean up registration
213
335
  container.drop(abstract=ICar)
214
336
 
215
337
  async def testResolvingUnregisteredType(self) -> None:
216
338
  """
217
339
  Tests that attempting to resolve an unregistered type from the container raises an exception.
218
- This test ensures that the container correctly validates that a type is registered before attempting to resolve it.
219
340
 
220
- Raises:
221
- Exception: Expected to be raised when attempting to resolve an unregistered type.
341
+ This method ensures:
342
+ - The container correctly validates that a type is registered before attempting to resolve it.
343
+ - An exception is raised when attempting to resolve an unregistered type.
344
+
345
+ Parameters
346
+ ----------
347
+ self : TestContainer
348
+ The test case instance.
349
+
350
+ Returns
351
+ -------
352
+ None
353
+ This method does not return any value. Assertions are used to validate expected behavior.
354
+
355
+ Raises
356
+ ------
357
+ Exception
358
+ Raised when attempting to resolve an unregistered type.
222
359
  """
223
360
  container = Container()
361
+ # Attempt to resolve an unregistered type; should raise Exception
224
362
  with self.assertRaises(Exception):
225
363
  container.make('ICar')
226
364
 
227
365
  async def testOverridingRegistration(self) -> None:
228
366
  """
229
367
  Tests the ability of the container to override existing registrations.
230
- This test verifies that:
231
- 1. When a class is registered as a singleton for an interface
232
- 2. And later a different class is registered for the same interface
233
- 3. The container returns the new class when resolving the interface
234
- 4. The new instance is different from the previous instance
235
- This demonstrates the container's support for overriding service implementations.
368
+
369
+ This method verifies:
370
+ - When a class is registered as a singleton for an interface.
371
+ - A different class can later be registered for the same interface.
372
+ - The container returns the new class when resolving the interface.
373
+ - The new instance is different from the previous instance, confirming the override.
374
+
375
+ Parameters
376
+ ----------
377
+ self : TestContainer
378
+ The test case instance.
379
+
380
+ Returns
381
+ -------
382
+ None
383
+ This method does not return any value. Assertions are used to validate expected behavior.
384
+
385
+ Notes
386
+ -----
387
+ The registration for `ICar` is dropped after the test.
236
388
  """
237
389
  class SportsCar(Car):
238
390
  def start(self):
@@ -241,14 +393,17 @@ class TestContainer(AsyncTestCase):
241
393
  return f"{self.brand} {self.model} is stopping."
242
394
 
243
395
  container = Container()
396
+ # Register ICar as a singleton binding to Car
244
397
  container.singleton(ICar, Car)
245
398
  first = container.make(ICar)
246
399
  self.assertIsInstance(first, Car)
247
400
  self.assertNotIsInstance(first, SportsCar)
248
401
 
402
+ # Override registration: register ICar as a singleton binding to SportsCar
249
403
  container.singleton(ICar, SportsCar)
250
404
  second = container.make(ICar)
251
405
  self.assertIsInstance(second, SportsCar)
252
406
  self.assertIsNot(first, second)
253
407
 
408
+ # Clean up registration
254
409
  container.drop(abstract=ICar)