tiferet 1.0.0a19__py3-none-any.whl → 1.0.0b0__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.
- tiferet/__init__.py +3 -2
- tiferet/commands/__init__.py +6 -0
- tiferet/commands/app.py +102 -0
- tiferet/commands/core.py +124 -0
- tiferet/commands/dependencies.py +76 -0
- tiferet/commands/settings.py +101 -0
- tiferet/configs/__init__.py +3 -67
- tiferet/configs/error.py +48 -0
- tiferet/configs/settings.py +37 -0
- tiferet/contexts/__init__.py +0 -8
- tiferet/contexts/app.py +215 -182
- tiferet/contexts/cache.py +76 -0
- tiferet/contexts/container.py +92 -190
- tiferet/contexts/error.py +78 -80
- tiferet/contexts/feature.py +120 -83
- tiferet/contracts/__init__.py +4 -0
- tiferet/contracts/app.py +92 -0
- tiferet/contracts/cache.py +70 -0
- tiferet/contracts/container.py +147 -0
- tiferet/contracts/error.py +177 -0
- tiferet/contracts/feature.py +159 -0
- tiferet/contracts/settings.py +34 -0
- tiferet/data/__init__.py +3 -4
- tiferet/data/app.py +71 -208
- tiferet/data/container.py +52 -38
- tiferet/data/error.py +15 -24
- tiferet/data/feature.py +27 -8
- tiferet/{domain/core.py → data/settings.py} +36 -96
- tiferet/handlers/__init__.py +0 -0
- tiferet/handlers/container.py +116 -0
- tiferet/handlers/error.py +49 -0
- tiferet/handlers/feature.py +94 -0
- tiferet/models/__init__.py +4 -0
- tiferet/models/app.py +150 -0
- tiferet/models/container.py +135 -0
- tiferet/{domain → models}/error.py +86 -36
- tiferet/{domain → models}/feature.py +107 -47
- tiferet/models/settings.py +148 -0
- tiferet/proxies/__init__.py +0 -0
- tiferet/proxies/yaml/__init__.py +0 -0
- tiferet/{repos → proxies/yaml}/app.py +13 -41
- tiferet/{repos → proxies/yaml}/container.py +26 -56
- tiferet/{repos → proxies/yaml}/error.py +11 -70
- tiferet/proxies/yaml/feature.py +92 -0
- {tiferet-1.0.0a19.dist-info → tiferet-1.0.0b0.dist-info}/METADATA +12 -3
- tiferet-1.0.0b0.dist-info/RECORD +51 -0
- {tiferet-1.0.0a19.dist-info → tiferet-1.0.0b0.dist-info}/WHEEL +1 -1
- tiferet/commands/container.py +0 -54
- tiferet/commands/error.py +0 -21
- tiferet/commands/feature.py +0 -90
- tiferet/contexts/request.py +0 -110
- tiferet/domain/__init__.py +0 -5
- tiferet/domain/app.py +0 -131
- tiferet/domain/container.py +0 -141
- tiferet/repos/__init__.py +0 -7
- tiferet/repos/feature.py +0 -151
- tiferet-1.0.0a19.dist-info/RECORD +0 -35
- {tiferet-1.0.0a19.dist-info → tiferet-1.0.0b0.dist-info/licenses}/LICENSE +0 -0
- {tiferet-1.0.0a19.dist-info → tiferet-1.0.0b0.dist-info}/top_level.txt +0 -0
tiferet/contexts/feature.py
CHANGED
@@ -2,122 +2,159 @@
|
|
2
2
|
|
3
3
|
# ** app
|
4
4
|
from .container import ContainerContext
|
5
|
-
from .
|
6
|
-
from ..
|
7
|
-
from ..
|
5
|
+
from .cache import CacheContext
|
6
|
+
from ..handlers.feature import FeatureService
|
7
|
+
from ..models.feature import Request
|
8
|
+
from ..commands import *
|
8
9
|
|
9
10
|
|
10
11
|
# *** contexts
|
11
12
|
|
12
13
|
# ** context: feature_context
|
13
|
-
class FeatureContext(
|
14
|
-
|
15
|
-
# * attribute: features
|
16
|
-
features = DictType(
|
17
|
-
ModelType(Feature),
|
18
|
-
required=True,
|
19
|
-
metadata=dict(
|
20
|
-
description='The features lookup.'
|
21
|
-
)
|
22
|
-
)
|
14
|
+
class FeatureContext(object):
|
23
15
|
|
24
16
|
# * attribute: container
|
25
|
-
container
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
17
|
+
container: ContainerContext
|
18
|
+
|
19
|
+
# * attribute: cache
|
20
|
+
cache: CacheContext
|
21
|
+
|
22
|
+
# * attribute: feature_service
|
23
|
+
feature_service: FeatureService
|
32
24
|
|
33
25
|
# * method: init
|
34
|
-
def __init__(self,
|
26
|
+
def __init__(self,
|
27
|
+
feature_service: FeatureService,
|
28
|
+
container: ContainerContext,
|
29
|
+
cache: CacheContext = None):
|
35
30
|
'''
|
36
31
|
Initialize the feature context.
|
37
32
|
|
38
|
-
:param
|
39
|
-
:type
|
40
|
-
:param
|
41
|
-
:type
|
33
|
+
:param feature_service: The feature service to use for executing feature requests.
|
34
|
+
:type feature_service: FeatureService
|
35
|
+
:param container: The container context for dependency injection.
|
36
|
+
:type container: ContainerContext
|
37
|
+
:param cache: The cache context to use for caching feature data.
|
38
|
+
:type cache: CacheContext
|
42
39
|
'''
|
43
40
|
|
44
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
## NOTE: There is a bug in the schematics library that does not allow us to initialize
|
49
|
-
## the feature context with the container context directly.
|
50
|
-
super().__init__(dict(
|
51
|
-
features=features,
|
52
|
-
))
|
53
|
-
self.container = container_context
|
41
|
+
# Assign the attributes.
|
42
|
+
self.feature_service = feature_service
|
43
|
+
self.container = container
|
44
|
+
self.cache = cache if cache else CacheContext()
|
54
45
|
|
55
|
-
# * method:
|
56
|
-
def
|
46
|
+
# * method: load_feature_command
|
47
|
+
def load_feature_command(self, attribute_id: str) -> Command:
|
57
48
|
'''
|
58
|
-
|
49
|
+
Load a feature command by its attribute ID from the container.
|
59
50
|
|
60
|
-
:param
|
61
|
-
:type
|
62
|
-
:return: The
|
63
|
-
:rtype:
|
51
|
+
:param attribute_id: The attribute ID of the command to load.
|
52
|
+
:type attribute_id: str
|
53
|
+
:return: The command object.
|
54
|
+
:rtype: Command
|
64
55
|
'''
|
65
56
|
|
66
|
-
#
|
67
|
-
|
57
|
+
# Attempt to retrieve the command from the container.
|
58
|
+
try:
|
59
|
+
return self.container.get_dependency(attribute_id)
|
68
60
|
|
69
|
-
|
70
|
-
|
61
|
+
# If the command is not found, raise an error.
|
62
|
+
except Exception as e:
|
63
|
+
raise_error.execute(
|
64
|
+
'FEATURE_COMMAND_LOADING_FAILED',
|
65
|
+
f'Failed to load feature command attribute: {attribute_id}. Ensure the container attributes is configured with the appropriate default settings/flags.',
|
66
|
+
attribute_id,
|
67
|
+
str(e)
|
68
|
+
)
|
69
|
+
|
70
|
+
# * method: handle_command
|
71
|
+
def handle_command(self,
|
72
|
+
command: Command,
|
73
|
+
request: Request,
|
74
|
+
data_key: str = None,
|
75
|
+
pass_on_error: bool = False,
|
76
|
+
**kwargs
|
77
|
+
):
|
71
78
|
'''
|
72
|
-
|
79
|
+
Handle the execution of a command with the provided request and command-handling options.
|
73
80
|
|
81
|
+
:param command: The command to execute.
|
82
|
+
:type command: Command
|
74
83
|
:param request: The request context object.
|
75
|
-
:type request:
|
84
|
+
:type request: Request
|
76
85
|
:param debug: Debug flag.
|
77
86
|
:type debug: bool
|
87
|
+
:param data_key: Optional key to store the result in the request data.
|
88
|
+
:type data_key: str
|
89
|
+
:param pass_on_error: If True, pass on the error instead of raising it.
|
90
|
+
:type pass_on_error: bool
|
78
91
|
:param kwargs: Additional keyword arguments.
|
79
92
|
:type kwargs: dict
|
80
93
|
'''
|
81
94
|
|
82
|
-
#
|
83
|
-
|
95
|
+
# Execute the command with the request data and parameters, and optional contexts.
|
96
|
+
try:
|
97
|
+
result = command.execute(
|
98
|
+
**request.data,
|
99
|
+
**kwargs
|
100
|
+
)
|
101
|
+
except Exception as e:
|
102
|
+
if not pass_on_error:
|
103
|
+
raise e
|
104
|
+
finally:
|
105
|
+
print(f'Command {command.attribute_id} execution failed: {e}' if 'e' in locals() else '')
|
106
|
+
|
107
|
+
# If a data key is provided, store the result in the request data.
|
108
|
+
if data_key:
|
109
|
+
request.data[data_key] = result
|
110
|
+
|
111
|
+
# Otherwise, set the result.
|
112
|
+
else:
|
113
|
+
request.set_result(result)
|
84
114
|
|
85
|
-
|
86
|
-
|
115
|
+
|
116
|
+
# * method: execute_feature
|
117
|
+
def execute_feature(self, feature_id: str, request: Request, **kwargs):
|
118
|
+
'''
|
119
|
+
Execute a feature by its ID with the provided request.
|
120
|
+
|
121
|
+
:param request: The request object.
|
122
|
+
:type request: Request
|
123
|
+
:param kwargs: Additional keyword arguments.
|
124
|
+
:type kwargs: dict
|
125
|
+
'''
|
87
126
|
|
88
|
-
|
89
|
-
|
127
|
+
# Try to get the feature by its id from the cache.
|
128
|
+
# If it does not exist, retrieve it from the feature handler and cache it.
|
129
|
+
feature = self.cache.get(feature_id)
|
130
|
+
if not feature:
|
131
|
+
feature = self.feature_service.get_feature(feature_id)
|
132
|
+
self.cache.set(feature_id, feature)
|
133
|
+
|
134
|
+
# Load the command dependencies from the container for the feature.
|
135
|
+
commands = [
|
136
|
+
self.load_feature_command(cmd.attribute_id)
|
137
|
+
for cmd in feature.commands
|
138
|
+
]
|
139
|
+
|
140
|
+
# Execute the feature with the request and commands.
|
141
|
+
for index, cmd in enumerate(commands):
|
90
142
|
|
91
143
|
# Parse the command parameters
|
92
144
|
params = {
|
93
|
-
param:
|
94
|
-
|
95
|
-
command.params.get(param)
|
96
|
-
)
|
97
|
-
for param in command.params
|
145
|
+
param: self.feature_service.parse_parameter(value, request)
|
146
|
+
for param, value in feature.commands[index].parameters.items()
|
98
147
|
}
|
99
148
|
|
100
|
-
# Execute the
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
continue
|
113
|
-
|
114
|
-
# Set the result in the request context.
|
115
|
-
if result:
|
116
|
-
request.set_result(result)
|
117
|
-
|
118
|
-
# Handle assertion errors if pass on error is not set.
|
119
|
-
except AssertionError as e:
|
120
|
-
if not command.pass_on_error:
|
121
|
-
raise e
|
122
|
-
|
123
|
-
|
149
|
+
# Execute the command with the request data and parameters.
|
150
|
+
self.handle_command(
|
151
|
+
cmd,
|
152
|
+
request,
|
153
|
+
**params,
|
154
|
+
features=self.feature_service,
|
155
|
+
container=self.container,
|
156
|
+
cache=self.cache,
|
157
|
+
**kwargs
|
158
|
+
)
|
159
|
+
|
160
|
+
|
tiferet/contracts/app.py
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# *** imports
|
2
|
+
|
3
|
+
# ** core
|
4
|
+
from typing import List, Dict, Any
|
5
|
+
|
6
|
+
# ** app
|
7
|
+
from .settings import *
|
8
|
+
|
9
|
+
|
10
|
+
# *** contracts
|
11
|
+
|
12
|
+
# ** contract: app_attribute
|
13
|
+
class AppAttribute(ModelContract):
|
14
|
+
'''
|
15
|
+
An app dependency contract that defines the dependency attributes for an app interface.
|
16
|
+
'''
|
17
|
+
|
18
|
+
# * attribute: module_path
|
19
|
+
module_path: str
|
20
|
+
|
21
|
+
# * attribute: class_name
|
22
|
+
class_name: str
|
23
|
+
|
24
|
+
# * attribute: attribute_id
|
25
|
+
attribute_id: str
|
26
|
+
|
27
|
+
|
28
|
+
# ** contract: app_interface
|
29
|
+
class AppInterface(ModelContract):
|
30
|
+
'''
|
31
|
+
An app interface settings contract that defines the settings for an app interface.
|
32
|
+
'''
|
33
|
+
|
34
|
+
# * attribute: id
|
35
|
+
id: str
|
36
|
+
|
37
|
+
# * attribute: name
|
38
|
+
name: str
|
39
|
+
|
40
|
+
# * attribute: module_path
|
41
|
+
module_path: str
|
42
|
+
|
43
|
+
# * attribute: class_name
|
44
|
+
class_name: str
|
45
|
+
|
46
|
+
# * attribute: description
|
47
|
+
description: str
|
48
|
+
|
49
|
+
# * attribute: feature_flag
|
50
|
+
feature_flag: str
|
51
|
+
|
52
|
+
# * attribute: data_flag
|
53
|
+
data_flag: str
|
54
|
+
|
55
|
+
# * attribute: attributes
|
56
|
+
attributes: List[AppAttribute]
|
57
|
+
|
58
|
+
# * attribute: constants
|
59
|
+
constants: Dict[str, Any]
|
60
|
+
|
61
|
+
|
62
|
+
# ** interface: app_repository
|
63
|
+
class AppRepository(Repository):
|
64
|
+
'''
|
65
|
+
An app repository is a class that is used to get an app interface.
|
66
|
+
'''
|
67
|
+
|
68
|
+
# * method: get_interface
|
69
|
+
@abstractmethod
|
70
|
+
def get_interface(self, app_name: str) -> AppInterface:
|
71
|
+
'''
|
72
|
+
Get the app interface settings by name.
|
73
|
+
|
74
|
+
:param app_name: The name of the app.
|
75
|
+
:type app_name: str
|
76
|
+
:return: The app interface.
|
77
|
+
:rtype: AppInterface
|
78
|
+
'''
|
79
|
+
# Not implemented.
|
80
|
+
raise NotImplementedError()
|
81
|
+
|
82
|
+
# * method: list_interfaces
|
83
|
+
@abstractmethod
|
84
|
+
def list_interfaces(self) -> List[AppInterface]:
|
85
|
+
'''
|
86
|
+
List all app inferface settings.
|
87
|
+
|
88
|
+
:return: A list of app settings.
|
89
|
+
:rtype: List[AppInterface]
|
90
|
+
'''
|
91
|
+
# Not implemented.
|
92
|
+
raise NotImplementedError()
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# *** imports
|
2
|
+
|
3
|
+
# ** core
|
4
|
+
from abc import abstractmethod
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
# ** app
|
8
|
+
from .settings import Service
|
9
|
+
|
10
|
+
|
11
|
+
# ** contracts
|
12
|
+
|
13
|
+
# ** contract: cache_service
|
14
|
+
class CacheService(Service):
|
15
|
+
'''
|
16
|
+
A contract for cache services in Tiferet applications.
|
17
|
+
'''
|
18
|
+
|
19
|
+
# * method: get
|
20
|
+
@abstractmethod
|
21
|
+
def get(self, key: str) -> Any:
|
22
|
+
'''
|
23
|
+
Retrieve an item from the cache.
|
24
|
+
|
25
|
+
:param key: The key of the item to retrieve.
|
26
|
+
:type key: str
|
27
|
+
:return: The cached item or None if not found.
|
28
|
+
:rtype: Any
|
29
|
+
'''
|
30
|
+
raise NotImplementedError(
|
31
|
+
'The get method must be implemented by the cache service.'
|
32
|
+
)
|
33
|
+
|
34
|
+
# * method: set
|
35
|
+
@abstractmethod
|
36
|
+
def set(self, key: str, value: Any):
|
37
|
+
'''
|
38
|
+
Store an item in the cache.
|
39
|
+
|
40
|
+
:param key: The key to store the value under.
|
41
|
+
:type key: str
|
42
|
+
:param value: The value to store.
|
43
|
+
:type value: Any
|
44
|
+
'''
|
45
|
+
raise NotImplementedError(
|
46
|
+
'The set method must be implemented by the cache service.'
|
47
|
+
)
|
48
|
+
|
49
|
+
# * method: delete
|
50
|
+
@abstractmethod
|
51
|
+
def delete(self, key: str):
|
52
|
+
'''
|
53
|
+
Delete an item from the cache.
|
54
|
+
|
55
|
+
:param key: The key of the item to delete.
|
56
|
+
:type key: str
|
57
|
+
'''
|
58
|
+
raise NotImplementedError(
|
59
|
+
'The delete method must be implemented by the cache service.'
|
60
|
+
)
|
61
|
+
|
62
|
+
# * method: clear
|
63
|
+
@abstractmethod
|
64
|
+
def clear(self):
|
65
|
+
'''
|
66
|
+
Clear the entire cache.
|
67
|
+
'''
|
68
|
+
raise NotImplementedError(
|
69
|
+
'The clear method must be implemented by the cache service.'
|
70
|
+
)
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# *** imports
|
2
|
+
|
3
|
+
# ** core
|
4
|
+
from typing import List, Dict, Tuple, Any
|
5
|
+
from abc import abstractmethod
|
6
|
+
|
7
|
+
# ** app
|
8
|
+
from .settings import *
|
9
|
+
|
10
|
+
|
11
|
+
# *** contracts
|
12
|
+
|
13
|
+
# ** contract: flagged_dependency
|
14
|
+
class FlaggedDependency(ModelContract):
|
15
|
+
'''
|
16
|
+
A contract for flagged dependencies.
|
17
|
+
'''
|
18
|
+
|
19
|
+
# * attribute: flag
|
20
|
+
flag: str
|
21
|
+
|
22
|
+
# * attribute: parameters
|
23
|
+
parameters: Dict[str, str]
|
24
|
+
|
25
|
+
# * attribute: module_path
|
26
|
+
module_path: str
|
27
|
+
|
28
|
+
# * attribute: class_name
|
29
|
+
class_name: str
|
30
|
+
|
31
|
+
|
32
|
+
# ** contract: container_attribute
|
33
|
+
class ContainerAttribute(ModelContract):
|
34
|
+
'''
|
35
|
+
A contract defining container injector attributes.
|
36
|
+
'''
|
37
|
+
|
38
|
+
# * attribute: id
|
39
|
+
id: str
|
40
|
+
|
41
|
+
# * attribute: module_path
|
42
|
+
module_path: str
|
43
|
+
|
44
|
+
# * attribute: class_name
|
45
|
+
class_name: str
|
46
|
+
|
47
|
+
# * attribute: parameters
|
48
|
+
parameters: Dict[str, Any]
|
49
|
+
|
50
|
+
# * attribute: dependencies
|
51
|
+
dependencies: List[FlaggedDependency]
|
52
|
+
|
53
|
+
# * method: get_dependency
|
54
|
+
@abstractmethod
|
55
|
+
def get_dependency(self, *flags) -> FlaggedDependency:
|
56
|
+
'''
|
57
|
+
Gets a container dependency by flag.
|
58
|
+
|
59
|
+
:param flags: The flags for the flagged container dependency.
|
60
|
+
:type flags: Tuple[str, ...]
|
61
|
+
:return: The container dependency.
|
62
|
+
:rtype: FlaggedDependency
|
63
|
+
'''
|
64
|
+
raise NotImplementedError('get_dependency method must be implemented in the ContainerAttribute class.')
|
65
|
+
|
66
|
+
|
67
|
+
# ** contract: container_repository
|
68
|
+
class ContainerRepository(Repository):
|
69
|
+
'''
|
70
|
+
An interface for accessing container attributes.
|
71
|
+
'''
|
72
|
+
|
73
|
+
# * method: get_attribute
|
74
|
+
@abstractmethod
|
75
|
+
def get_attribute(self, attribute_id: str, flag: str = None) -> ContainerAttribute:
|
76
|
+
'''
|
77
|
+
Get the attribute from the container repository.
|
78
|
+
|
79
|
+
:param attribute_id: The attribute id.
|
80
|
+
:type attribute_id: str
|
81
|
+
:param flag: An optional flag to filter the attribute.
|
82
|
+
:type flag: str
|
83
|
+
:return: The container attribute.
|
84
|
+
:rtype: ContainerAttribute
|
85
|
+
'''
|
86
|
+
raise NotImplementedError('get_attribute method must be implemented in the ContainerRepository class.')
|
87
|
+
|
88
|
+
# * method: list_all
|
89
|
+
@abstractmethod
|
90
|
+
def list_all(self) -> Tuple[List[ContainerAttribute], Dict[str, str]]:
|
91
|
+
'''
|
92
|
+
List all the container attributes and constants.
|
93
|
+
|
94
|
+
:return: The list of container attributes and constants.
|
95
|
+
:rtype: Tuple[List[ContainerAttribute], Dict[str, str]]
|
96
|
+
'''
|
97
|
+
raise NotImplementedError('list_all method must be implemented in the ContainerRepository class.')
|
98
|
+
|
99
|
+
|
100
|
+
# ** contract: container_service
|
101
|
+
class ContainerService(Service):
|
102
|
+
'''
|
103
|
+
An interface for accessing container dependencies.
|
104
|
+
'''
|
105
|
+
|
106
|
+
# * method: list_all
|
107
|
+
@abstractmethod
|
108
|
+
def list_all(self) -> Tuple[List[ContainerAttribute], Dict[str, str]]:
|
109
|
+
'''
|
110
|
+
List all container attributes and constants.
|
111
|
+
|
112
|
+
:return: A tuple containing a list of container attributes and a dictionary of constants.
|
113
|
+
:rtype: Tuple[List[ContainerAttribute], Dict[str, str]]
|
114
|
+
'''
|
115
|
+
raise NotImplementedError('list_all method must be implemented in the ContainerService class.')
|
116
|
+
|
117
|
+
# * method: load_constants
|
118
|
+
@abstractmethod
|
119
|
+
def load_constants(self, attributes: List[ContainerAttribute], constants: Dict[str, str] = {}, flags: List[str] = []) -> Dict[str, str]:
|
120
|
+
'''
|
121
|
+
Load constants from the container attributes.
|
122
|
+
|
123
|
+
:param attributes: The list of container attributes.
|
124
|
+
:type attributes: List[ContainerAttribute]
|
125
|
+
:param constants: The dictionary of constants.
|
126
|
+
:type constants: Dict[str, str]
|
127
|
+
:param flags: Optional list of flags to filter the constants.
|
128
|
+
:type flags: List[str]
|
129
|
+
:return: A dictionary of constants.
|
130
|
+
:rtype: Dict[str, str]
|
131
|
+
'''
|
132
|
+
raise NotImplementedError('load_constants method must be implemented in the ContainerService class.')
|
133
|
+
|
134
|
+
# * method: get_dependency_type
|
135
|
+
@abstractmethod
|
136
|
+
def get_dependency_type(self, attribute: ContainerAttribute, flags: List[str] = []) -> type:
|
137
|
+
'''
|
138
|
+
Get the type of a container attribute.
|
139
|
+
|
140
|
+
:param attribute: The container attribute.
|
141
|
+
:type attribute: ContainerAttribute
|
142
|
+
:param flags: Optional list of flags to filter the dependency type.
|
143
|
+
:type flags: List[str]
|
144
|
+
:return: The type of the container attribute.
|
145
|
+
:rtype: type
|
146
|
+
'''
|
147
|
+
raise NotImplementedError('get_dependency_type method must be implemented in the ContainerService class.')
|