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.
Files changed (59) hide show
  1. tiferet/__init__.py +3 -2
  2. tiferet/commands/__init__.py +6 -0
  3. tiferet/commands/app.py +102 -0
  4. tiferet/commands/core.py +124 -0
  5. tiferet/commands/dependencies.py +76 -0
  6. tiferet/commands/settings.py +101 -0
  7. tiferet/configs/__init__.py +3 -67
  8. tiferet/configs/error.py +48 -0
  9. tiferet/configs/settings.py +37 -0
  10. tiferet/contexts/__init__.py +0 -8
  11. tiferet/contexts/app.py +215 -182
  12. tiferet/contexts/cache.py +76 -0
  13. tiferet/contexts/container.py +92 -190
  14. tiferet/contexts/error.py +78 -80
  15. tiferet/contexts/feature.py +120 -83
  16. tiferet/contracts/__init__.py +4 -0
  17. tiferet/contracts/app.py +92 -0
  18. tiferet/contracts/cache.py +70 -0
  19. tiferet/contracts/container.py +147 -0
  20. tiferet/contracts/error.py +177 -0
  21. tiferet/contracts/feature.py +159 -0
  22. tiferet/contracts/settings.py +34 -0
  23. tiferet/data/__init__.py +3 -4
  24. tiferet/data/app.py +71 -208
  25. tiferet/data/container.py +52 -38
  26. tiferet/data/error.py +15 -24
  27. tiferet/data/feature.py +27 -8
  28. tiferet/{domain/core.py → data/settings.py} +36 -96
  29. tiferet/handlers/__init__.py +0 -0
  30. tiferet/handlers/container.py +116 -0
  31. tiferet/handlers/error.py +49 -0
  32. tiferet/handlers/feature.py +94 -0
  33. tiferet/models/__init__.py +4 -0
  34. tiferet/models/app.py +150 -0
  35. tiferet/models/container.py +135 -0
  36. tiferet/{domain → models}/error.py +86 -36
  37. tiferet/{domain → models}/feature.py +107 -47
  38. tiferet/models/settings.py +148 -0
  39. tiferet/proxies/__init__.py +0 -0
  40. tiferet/proxies/yaml/__init__.py +0 -0
  41. tiferet/{repos → proxies/yaml}/app.py +13 -41
  42. tiferet/{repos → proxies/yaml}/container.py +26 -56
  43. tiferet/{repos → proxies/yaml}/error.py +11 -70
  44. tiferet/proxies/yaml/feature.py +92 -0
  45. {tiferet-1.0.0a19.dist-info → tiferet-1.0.0b0.dist-info}/METADATA +12 -3
  46. tiferet-1.0.0b0.dist-info/RECORD +51 -0
  47. {tiferet-1.0.0a19.dist-info → tiferet-1.0.0b0.dist-info}/WHEEL +1 -1
  48. tiferet/commands/container.py +0 -54
  49. tiferet/commands/error.py +0 -21
  50. tiferet/commands/feature.py +0 -90
  51. tiferet/contexts/request.py +0 -110
  52. tiferet/domain/__init__.py +0 -5
  53. tiferet/domain/app.py +0 -131
  54. tiferet/domain/container.py +0 -141
  55. tiferet/repos/__init__.py +0 -7
  56. tiferet/repos/feature.py +0 -151
  57. tiferet-1.0.0a19.dist-info/RECORD +0 -35
  58. {tiferet-1.0.0a19.dist-info → tiferet-1.0.0b0.dist-info/licenses}/LICENSE +0 -0
  59. {tiferet-1.0.0a19.dist-info → tiferet-1.0.0b0.dist-info}/top_level.txt +0 -0
@@ -2,122 +2,159 @@
2
2
 
3
3
  # ** app
4
4
  from .container import ContainerContext
5
- from .request import RequestContext
6
- from ..domain import *
7
- from ..repos.feature import FeatureRepository
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(Model):
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 = ModelType(
26
- ContainerContext,
27
- required=True,
28
- metadata=dict(
29
- description='The container context.'
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, feature_repo: FeatureRepository, container_context: ContainerContext):
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 feature_repo: The feature repository.
39
- :type feature_repo: FeatureRepository
40
- :param container_context: The container context.
41
- :type container_context: ContainerContext
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
- # Create the features.
45
- features = {feature.id: feature for feature in feature_repo.list()}
46
-
47
- # Set the features and container.
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: parse_parameter
56
- def parse_parameter(self, parameter: str) -> str:
46
+ # * method: load_feature_command
47
+ def load_feature_command(self, attribute_id: str) -> Command:
57
48
  '''
58
- Parse a parameter.
49
+ Load a feature command by its attribute ID from the container.
59
50
 
60
- :param parameter: The parameter to parse.
61
- :type parameter: str
62
- :return: The parsed parameter.
63
- :rtype: str
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
- # Parse the parameter.
67
- return self.container.parse_parameter(parameter)
57
+ # Attempt to retrieve the command from the container.
58
+ try:
59
+ return self.container.get_dependency(attribute_id)
68
60
 
69
- # * method: execute
70
- def execute(self, request: RequestContext, debug: bool = False, **kwargs):
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
- Execute the feature request.
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: r.RequestContext
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
- # Assert the feature exists.
83
- assert request.feature_id in self.features, 'FEATURE_NOT_FOUND, {}'.format(request.feature_id)
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
- # Iterate over the feature commands.
86
- for command in self.features[request.feature_id].commands:
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
- # Get the feature command handler instance.
89
- handler = self.container.get_dependency(command.attribute_id)
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
- self.parse_parameter(
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 handler function.
101
- # Handle assertion errors if pass on error is not set.
102
- try:
103
- result = handler.execute(
104
- **request.data,
105
- **params,
106
- debug=debug,
107
- **kwargs)
108
-
109
- # Return the result to the session context if return to data is set.
110
- if command.return_to_data:
111
- request.data[command.data_key] = result
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
+
@@ -0,0 +1,4 @@
1
+ # *** imports
2
+
3
+ # ** app
4
+ from .settings import *
@@ -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.')