tiferet 1.0.0a0__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.
@@ -0,0 +1,167 @@
1
+ # *** imports
2
+
3
+ # ** core
4
+ from typing import Any
5
+
6
+ # ** app
7
+ from ..configs import container
8
+ from ..domain import *
9
+ from ..services import container_service
10
+ from ..repos.container import ContainerRepository
11
+
12
+
13
+ # *** contexts
14
+
15
+ # ** contexts: container_context
16
+ class ContainerContext(Model):
17
+ '''
18
+ A container context is a class that is used to create a container object.
19
+ '''
20
+
21
+ # * attribute: attributes
22
+ attributes = DictType(
23
+ ModelType(ContainerAttribute),
24
+ default={},
25
+ required=True,
26
+ metadata=dict(
27
+ description='The container attributes.'
28
+ ),
29
+ )
30
+
31
+ # * attribute: constants
32
+ constants = DictType(
33
+ StringType,
34
+ default={},
35
+ metadata=dict(
36
+ description='The container constants.'
37
+ ),
38
+ )
39
+
40
+ # * attribute: feature_flag
41
+ feature_flag = StringType(
42
+ required=True,
43
+ default='core',
44
+ metadata=dict(
45
+ description='The feature flag.'
46
+ ),
47
+ )
48
+
49
+ # * attribute: data_flag
50
+ data_flag = StringType(
51
+ required=True,
52
+ metadata=dict(
53
+ description='The data flag.'
54
+ ),
55
+ )
56
+
57
+ # * method: init
58
+ def __init__(self, interface_id: str, container_repo: ContainerRepository, feature_flag: str, data_flag: str):
59
+ '''
60
+ Initialize the container context.
61
+
62
+ :param interface_id: The interface ID.
63
+ :type interface_id: str
64
+ :param container_repo: The container repository.
65
+ :type container_repo: ContainerRepository
66
+ :param interface_flag: The interface flag.
67
+ :type interface_flag: str
68
+ :param feature_flag: The feature flag.
69
+ :type feature_flag: str
70
+ :param data_flag: The data flag.
71
+ :type data_flag: str
72
+ '''
73
+
74
+ # Then set the interface id and injector flags.
75
+ self.interface_id = interface_id
76
+ self.feature_flag = feature_flag
77
+ self.data_flag = data_flag
78
+
79
+ # Add the default container attributes first if any.
80
+ self.attributes = vars(container)
81
+
82
+ # Get and set attributes and constants.
83
+ attrs, consts = container_repo.list_all()
84
+
85
+ # Add the attributes to the context.
86
+ for attr in attrs:
87
+ attribute: ContainerAttribute = self.attributes[attr.id]
88
+
89
+ # If the attribute already exists, set the dependencies.
90
+ if attr.id in self.attributes:
91
+ for dep in attr.dependencies:
92
+ attribute.set_dependency(dep)
93
+ continue
94
+
95
+ # Otherwise, add the attribute.
96
+ self.attributes[attr.id] = attr
97
+
98
+ # Add the constants to the context.
99
+ self.constants = consts
100
+
101
+ # * method: get_dependency
102
+ def get_dependency(self, attribute_id: str):
103
+ '''
104
+ Get a dependency from the container.
105
+
106
+ :param attribute_id: The attribute id of the dependency.
107
+ :type attribute_id: str
108
+ :return: The attribute value.
109
+ :rtype: Any
110
+ '''
111
+
112
+ # Create the injector.
113
+ injector = self.create_injector()
114
+
115
+ # Get attribute.
116
+ return getattr(injector, attribute_id)
117
+
118
+ # * method: create_injector
119
+ def create_injector(self, **kwargs) -> Any:
120
+ '''
121
+ Add a container to the context.
122
+
123
+ :param kwargs: Additional keyword arguments.
124
+ :type kwargs: dict
125
+ :return: The container injector object.
126
+ :rtype: Any
127
+ '''
128
+
129
+ # Import dependencies.
130
+ dependencies = {}
131
+ for attribute_id in self.attributes:
132
+ attribute = self.attributes[attribute_id]
133
+ flag_map = dict(
134
+ feature=self.feature_flag,
135
+ data=self.data_flag,
136
+ )
137
+ dependencies[attribute_id] = self.import_dependency(attribute, flag_map[attribute.type])
138
+
139
+ # Create container.
140
+ return container_service.create_injector(self,
141
+ self.interface_id,
142
+ **self.constants,
143
+ **dependencies,
144
+ **kwargs)
145
+
146
+ # * method: import_dependency
147
+ def import_dependency(self, attribute: ContainerAttribute, flag: str) -> Any:
148
+ '''
149
+ Import a container attribute dependency from its configured Python module.
150
+
151
+ :param attribute: The container attribute.
152
+ :type attribute: ContainerAttribute
153
+ :param flag: The flag for the dependency.
154
+ :type flag: str
155
+ :return: The dependency.
156
+ :rtype: Any
157
+ '''
158
+
159
+ # Get the dependency.
160
+ dependency = attribute.get_dependency(flag)
161
+
162
+ # If there is no dependency and the attribute is a feature, import the default feature.
163
+ if not dependency and attribute.type == 'feature':
164
+ dependency = attribute.get_dependency('core')
165
+
166
+ # Import the dependency.
167
+ return container_service.import_dependency(dependency.module_path, dependency.class_name)
app/contexts/env.py ADDED
@@ -0,0 +1,109 @@
1
+ # *** imports
2
+
3
+ # ** infra
4
+ from schematics import Model
5
+
6
+ # ** app
7
+ from .app import AppInterfaceContext
8
+ from ..services import container_service
9
+ from ..domain import *
10
+ from ..repos.app import AppRepository
11
+
12
+
13
+ # *** contexts
14
+
15
+ # ** context: environment_context
16
+ class EnvironmentContext(Model):
17
+ '''
18
+ An environment context is a class that is used to create and run the app interface context.
19
+ '''
20
+
21
+ # * attribute: interfaces
22
+ interfaces = DictType(
23
+ ModelType(AppInterface),
24
+ default={},
25
+ metadata=dict(
26
+ description='The app interfaces keyed by interface ID.'
27
+ ),
28
+ )
29
+
30
+ # * method: init
31
+ def __init__(self, **kwargs):
32
+ '''
33
+ Initialize the environment context.
34
+
35
+ :param kwargs: Additional keyword arguments.
36
+ :type kwargs: dict
37
+ '''
38
+
39
+ # Load the app repository.
40
+ app_repo = self.load_app_repo()
41
+
42
+ # Load the interface configuration.
43
+ self.interfaces = {interface.id: interface for interface in app_repo.list_interfaces()}
44
+
45
+ # * method: start
46
+ def start(self, interface_id: str, **kwargs):
47
+ '''
48
+ Start the environment context.
49
+
50
+ :param kwargs: Additional keyword arguments.
51
+ :type kwargs: dict
52
+ '''
53
+
54
+ # Load the app context.
55
+ app_context = self.load_app_context(interface_id)
56
+
57
+ # Run the app context.
58
+ app_context.run(
59
+ interface_id=interface_id,
60
+ **kwargs
61
+ )
62
+
63
+ # * method: load_app_repo
64
+ def load_app_repo(self) -> AppRepository:
65
+ '''
66
+ Load the app interface repository.
67
+
68
+ :return: The app repository.
69
+ :rtype: AppRepository
70
+ '''
71
+
72
+ # Load the app repository configuration.
73
+ from ..configs.app import APP_REPO
74
+
75
+ # Return the app repository.
76
+ return container_service.import_dependency(APP_REPO.module_path, APP_REPO.class_name)(**APP_REPO.params)
77
+
78
+ # * method: load_app_context
79
+ def load_app_context(self, interface_id: str) -> AppInterfaceContext:
80
+ '''
81
+ Load the app context.
82
+
83
+ :param container: The app container.
84
+ :type container: AppContainer
85
+ :return: The app context.
86
+ :rtype: AppContext
87
+ '''
88
+
89
+ # Get the app interface.
90
+ app_interface: AppInterface = self.interfaces.get(interface_id)
91
+
92
+ # Get the dependencies for the app interface.
93
+ dependencies = dict(
94
+ interface_id=app_interface.id,
95
+ feature_flag=app_interface.feature_flag,
96
+ data_flag=app_interface.data_flag,
97
+ **app_interface.constants
98
+ )
99
+ for dep in app_interface.get_dependencies():
100
+ dependencies[dep.attribute_id] = container_service.import_dependency(dep.module_path, dep.class_name)
101
+
102
+ # Create the injector from the dependencies, constants, and the app interface.
103
+ injector = container_service.create_injector(
104
+ app_interface.id
105
+ **dependencies
106
+ )
107
+
108
+ # Return the app context.
109
+ return getattr(injector, 'app_context')
app/contexts/error.py ADDED
@@ -0,0 +1,95 @@
1
+ # *** imports
2
+
3
+ # ** core
4
+ from typing import Any, Tuple
5
+
6
+ # ** app
7
+ from ..domain import *
8
+ from ..repos.error import ErrorRepository
9
+
10
+
11
+ # *** contexts
12
+
13
+ # ** context: error_context
14
+ class ErrorContext(Model):
15
+ '''
16
+ The error context object.
17
+ '''
18
+
19
+ # * attribute: errors
20
+ errors = DictType(
21
+ ModelType(Error),
22
+ required=True,
23
+ metadata=dict(
24
+ description='The errors lookup.'
25
+ )
26
+ )
27
+
28
+ # * method: init
29
+ def __init__(self, error_repo: ErrorRepository):
30
+ '''
31
+ Initialize the error context object.
32
+
33
+ :param error_repo: The error repository.
34
+ :type error_repo: ErrorRepository
35
+ '''
36
+
37
+ # Create the errors lookup from the error repository.
38
+ errors = {error.name: error for error in error_repo.list()}
39
+
40
+ # Set the errors lookup and validate.
41
+ super().__init__(dict(errors=errors))
42
+ self.validate()
43
+
44
+ # * method: handle_error
45
+ def handle_error(self, execute_feature: Any) -> Tuple[bool, Any]:
46
+ '''
47
+ Handle an error.
48
+
49
+ :param func: The execute feature function to handle.
50
+ :type func: function
51
+ :return: Whether the error was handled.
52
+ :rtype: bool
53
+ '''
54
+
55
+ # Execute the feature function and handle the errors.
56
+ try:
57
+ execute_feature()
58
+ return (False, None)
59
+ except AssertionError as e:
60
+ return (True, self.format_error_response(str(e)))
61
+
62
+ # * method: format_error_response
63
+ def format_error_response(self, error_message: str, lang: str = 'en_US', **kwargs) -> Any:
64
+ '''
65
+ Format the error response.
66
+
67
+ :param error_message: The error message.
68
+ :type error_message: str
69
+ :param kwargs: Additional keyword arguments.
70
+ :type kwargs: dict
71
+ :return: The formatted error message.
72
+ :rtype: Any
73
+ '''
74
+
75
+ # Split error message.
76
+ try:
77
+ error_name, error_data = error_message.split(': ')
78
+ except ValueError:
79
+ error_name = error_message
80
+ error_data = None
81
+
82
+ # Format error data if present.
83
+ error_data = error_data.split(', ') if error_data else None
84
+
85
+ # Get error.
86
+ error = self.errors.get(error_name)
87
+
88
+ # Set error response.
89
+ error_response = dict(
90
+ message=error.format(lang, *error_data if error_data else []),
91
+ **kwargs
92
+ )
93
+
94
+ # Return error response.
95
+ return error_response
@@ -0,0 +1,79 @@
1
+ # *** imports
2
+
3
+ # ** app
4
+ from .container import ContainerContext
5
+ from .request import RequestContext
6
+ from ..domain import *
7
+ from ..repos.feature import FeatureRepository
8
+
9
+
10
+ # *** contexts
11
+
12
+ # ** context: feature_context
13
+ class FeatureContext(Model):
14
+
15
+ # * attribute: features
16
+ features = DictType(
17
+ ModelType(Feature),
18
+ required=True,
19
+ metadata=dict(
20
+ description='The features lookup.'
21
+ )
22
+ )
23
+
24
+ # * attribute: container
25
+ container = ModelType(
26
+ ContainerContext,
27
+ required=True,
28
+ metadata=dict(
29
+ description='The container context.'
30
+ ),
31
+ )
32
+
33
+ # * method: init
34
+ def __init__(self, feature_repo: FeatureRepository, container: ContainerContext):
35
+
36
+ # Set the features.
37
+ self.features = {feature.id: feature for feature in feature_repo.list()}
38
+
39
+ # Set the container context.
40
+ self.container = container
41
+
42
+ # * method: execute
43
+ def execute(self, request: RequestContext, debug: bool = False, **kwargs):
44
+ '''
45
+ Execute the feature request.
46
+
47
+ :param request: The request context object.
48
+ :type request: r.RequestContext
49
+ :param debug: Debug flag.
50
+ :type debug: bool
51
+ :param kwargs: Additional keyword arguments.
52
+ :type kwargs: dict
53
+ '''
54
+
55
+ # Iterate over the feature commands.
56
+ for command in self.features[request.feature_id].commands:
57
+
58
+ # Get the feature command handler instance.
59
+ handler = self.container.get_dependency(command.attribute_id)
60
+
61
+ # Execute the handler function.
62
+ # Handle assertion errors if pass on error is not set.
63
+ try:
64
+ result = handler.execute(
65
+ **request.data,
66
+ **command.params,
67
+ **kwargs)
68
+ except AssertionError as e:
69
+ if not command.pass_on_error:
70
+ raise e
71
+
72
+ # Return the result to the session context if return to data is set.
73
+ if command.return_to_data:
74
+ request.data[command.data_key] = result
75
+ continue
76
+
77
+ # Set the result in the request context.
78
+ if result:
79
+ request.set_result(result)
@@ -0,0 +1,108 @@
1
+ # *** imports
2
+
3
+ # ** core
4
+ from typing import Dict
5
+
6
+ # ** app
7
+ from ..domain import *
8
+
9
+
10
+ # *** contexts
11
+
12
+ # ** context: request_context
13
+ class RequestContext(Model):
14
+ '''
15
+ The context for an application request.
16
+ '''
17
+
18
+ # * attribute: feature_id
19
+ feature_id = StringType(
20
+ required=True,
21
+ metadata=dict(
22
+ description='The feature identifier for the request.'
23
+ )
24
+ )
25
+
26
+ # * attribute: headers
27
+ headers = DictType(
28
+ StringType(),
29
+ metadata=dict(
30
+ description='The request headers.'
31
+ )
32
+ )
33
+
34
+ # * attribute: data
35
+ data = DictType(
36
+ StringType(),
37
+ metadata=dict(
38
+ description='The request data.'
39
+ )
40
+ )
41
+
42
+ # * attribute: result
43
+ result = StringType(
44
+ metadata=dict(
45
+ description='The request result.'
46
+ )
47
+ )
48
+
49
+ # * method: init
50
+ def __init__(self, feature_id: str, headers: Dict[str, str], data: Dict[str, str]):
51
+ '''
52
+ Initialize the request context object.
53
+
54
+ :param feature_id: The feature identifier.
55
+ :type feature_id: str
56
+ :param headers: The request headers.
57
+ :type headers: dict
58
+ :param data: The request data.
59
+ :type data: dict
60
+ :param kwargs: Additional keyword arguments.
61
+ :type kwargs: dict
62
+ '''
63
+
64
+ # Set the context attributes.
65
+ self.feature_id = feature_id
66
+ self.headers = headers
67
+ self.data = data
68
+
69
+ # Validate the context.
70
+ self.validate()
71
+
72
+ # * method: set_result
73
+ def set_result(self, result: Any):
74
+ '''
75
+ Set the serialized result value.
76
+
77
+ :param result: The result object.
78
+ :type result: Any
79
+ '''
80
+
81
+ # Import the json module.
82
+ import json
83
+
84
+ # Set the result as a serialized empty dictionary if it is None.
85
+ if not result:
86
+ self.result = json.dumps({})
87
+ return
88
+
89
+ # If the result is a Model, convert it to a primitive dictionary and serialize it.
90
+ if isinstance(result, Model):
91
+ self.result = json.dumps(result.to_primitive())
92
+ return
93
+
94
+ # If the result is not a list, it must be a dict, so serialize it and set it.
95
+ if type(self.result) != list:
96
+ self.result = json.dumps(result)
97
+ return
98
+
99
+ # If the result is a list, convert each item to a primitive dictionary.
100
+ result_list = []
101
+ for item in result:
102
+ if isinstance(item, Model):
103
+ result_list.append(item.to_primitive())
104
+ else:
105
+ result_list.append(item)
106
+
107
+ # Serialize the result and set it.
108
+ self.result = json.dumps(result_list)
app/data/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from typing import List, Dict
2
+
3
+ from .error import *
4
+ from .feature import *