asteex 0.0.1__tar.gz

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. asteex-0.0.1/LICENSE +21 -0
  2. asteex-0.0.1/PKG-INFO +20 -0
  3. asteex-0.0.1/README_PYPI.rst +11 -0
  4. asteex-0.0.1/VERSION +1 -0
  5. asteex-0.0.1/pyproject.toml +19 -0
  6. asteex-0.0.1/setup.cfg +4 -0
  7. asteex-0.0.1/src/asteex/__init__.py +6 -0
  8. asteex-0.0.1/src/asteex/abstract/__init__.py +1 -0
  9. asteex-0.0.1/src/asteex/abstract/case.py +27 -0
  10. asteex-0.0.1/src/asteex/abstract/context.py +21 -0
  11. asteex-0.0.1/src/asteex/abstract/executor.py +26 -0
  12. asteex-0.0.1/src/asteex/abstract/generator.py +24 -0
  13. asteex-0.0.1/src/asteex/abstract/instance.py +39 -0
  14. asteex-0.0.1/src/asteex/abstract/reporter.py +24 -0
  15. asteex-0.0.1/src/asteex/abstract/result.py +58 -0
  16. asteex-0.0.1/src/asteex/core/__init__.py +1 -0
  17. asteex-0.0.1/src/asteex/core/case/__init__.py +1 -0
  18. asteex-0.0.1/src/asteex/core/case/executable/__init__.py +1 -0
  19. asteex-0.0.1/src/asteex/core/case/executable/abstract.py +21 -0
  20. asteex-0.0.1/src/asteex/core/case/executable/delegate.py +306 -0
  21. asteex-0.0.1/src/asteex/core/context/__init__.py +1 -0
  22. asteex-0.0.1/src/asteex/core/context/source/__init__.py +1 -0
  23. asteex-0.0.1/src/asteex/core/context/source/fixture.py +19 -0
  24. asteex-0.0.1/src/asteex/core/context/source/static.py +19 -0
  25. asteex-0.0.1/src/asteex/core/context/source/strategy.py +9 -0
  26. asteex-0.0.1/src/asteex/core/decorator/__init__.py +1 -0
  27. asteex-0.0.1/src/asteex/core/decorator/argument.py +311 -0
  28. asteex-0.0.1/src/asteex/core/executor/__init__.py +1 -0
  29. asteex-0.0.1/src/asteex/core/executor/executable.py +493 -0
  30. asteex-0.0.1/src/asteex/core/fixture/__init__.py +1 -0
  31. asteex-0.0.1/src/asteex/core/fixture/abstract.py +42 -0
  32. asteex-0.0.1/src/asteex/core/fixture/producer.py +404 -0
  33. asteex-0.0.1/src/asteex/core/generator/__init__.py +1 -0
  34. asteex-0.0.1/src/asteex/core/generator/group/__init__.py +1 -0
  35. asteex-0.0.1/src/asteex/core/generator/group/abstract.py +100 -0
  36. asteex-0.0.1/src/asteex/core/generator/group/module.py +45 -0
  37. asteex-0.0.1/src/asteex/core/instance/__init__.py +1 -0
  38. asteex-0.0.1/src/asteex/core/instance/static.py +55 -0
  39. asteex-0.0.1/src/asteex/core/reporter/__init__.py +1 -0
  40. asteex-0.0.1/src/asteex/core/reporter/composite.py +84 -0
  41. asteex-0.0.1/src/asteex/core/reporter/log.py +61 -0
  42. asteex-0.0.1/src/asteex/core/result/__init__.py +1 -0
  43. asteex-0.0.1/src/asteex/core/result/static.py +126 -0
  44. asteex-0.0.1/src/asteex/core/result/status.py +11 -0
  45. asteex-0.0.1/src/asteex/core/runner.py +55 -0
  46. asteex-0.0.1/src/asteex/runpackage/__init__.py +1 -0
  47. asteex-0.0.1/src/asteex/runpackage/__main__.py +40 -0
  48. asteex-0.0.1/src/asteex/runpackage/case.py +147 -0
  49. asteex-0.0.1/src/asteex/runpackage/cli.py +323 -0
  50. asteex-0.0.1/src/asteex/runpackage/default.py +54 -0
  51. asteex-0.0.1/src/asteex/runpackage/fixture.py +224 -0
  52. asteex-0.0.1/src/asteex/runpackage/generator.py +319 -0
  53. asteex-0.0.1/src/asteex/runpackage/reporter.py +230 -0
  54. asteex-0.0.1/src/asteex/runpackage/value.py +70 -0
  55. asteex-0.0.1/src/asteex.egg-info/PKG-INFO +20 -0
  56. asteex-0.0.1/src/asteex.egg-info/SOURCES.txt +57 -0
  57. asteex-0.0.1/src/asteex.egg-info/dependency_links.txt +1 -0
  58. asteex-0.0.1/src/asteex.egg-info/entry_points.txt +2 -0
  59. asteex-0.0.1/src/asteex.egg-info/top_level.txt +1 -0
asteex-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 KillAChicken
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
asteex-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: asteex
3
+ Version: 0.0.1
4
+ Summary: Test framework for asynchronous test execution
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/x-rst
7
+ License-File: LICENSE
8
+ Dynamic: license-file
9
+
10
+ asteex
11
+ ======
12
+
13
+ asteex is a package for asyncronous test execution.
14
+
15
+ Installation
16
+ ============
17
+
18
+ .. code-block:: text
19
+
20
+ $ python -m pip install asteex
@@ -0,0 +1,11 @@
1
+ asteex
2
+ ======
3
+
4
+ asteex is a package for asyncronous test execution.
5
+
6
+ Installation
7
+ ============
8
+
9
+ .. code-block:: text
10
+
11
+ $ python -m pip install asteex
asteex-0.0.1/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 82.0.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ dynamic = ["version"]
7
+ name = "asteex"
8
+ requires-python = ">=3.10"
9
+ description = "Test framework for asynchronous test execution"
10
+ readme = {file = "README_PYPI.rst", content-type = "text/x-rst"}
11
+
12
+ [project.entry-points."asteex.reporter_factory"]
13
+ "asteex.log" = "asteex.core.reporter.log:LogReporter"
14
+
15
+ [tool.setuptools.packages.find]
16
+ where = ["src"]
17
+
18
+ [tool.setuptools.dynamic]
19
+ version = {file = "VERSION"}
asteex-0.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """Package to execute test automation."""
2
+
3
+ import logging
4
+
5
+
6
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
@@ -0,0 +1 @@
1
+ """Package with base and abstract classes of the asteex package."""
@@ -0,0 +1,27 @@
1
+ """Module for a base asteex test case."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class Case(ABC):
7
+ """Class for a asteex test case."""
8
+
9
+ @abstractmethod
10
+ def get_id(self):
11
+ """Get the unique identifier of the test case.
12
+
13
+ :returns: string id.
14
+ """
15
+
16
+ @abstractmethod
17
+ def get_dependencies(self):
18
+ """Get names of the variables that must be present in the execution context.
19
+
20
+ :returns: iterable with string names.
21
+ """
22
+
23
+ def __repr__(self):
24
+ class_name = self.__class__.__name__
25
+ case_id = self.get_id()
26
+
27
+ return f"{class_name}(case_id='{case_id}')"
@@ -0,0 +1,21 @@
1
+ """Module for a base entities of a asteex context."""
2
+
3
+
4
+ class Source:
5
+ """Class for a base source to provide a context value."""
6
+
7
+ def __init__(self, strategy):
8
+ self.__strategy = str(strategy)
9
+
10
+ def get_strategy(self):
11
+ """Get the strategy with which the value should be requested from the source.
12
+
13
+ :returns: string strategy.
14
+ """
15
+ return self.__strategy
16
+
17
+ def __repr__(self):
18
+ class_name = self.__class__.__name__
19
+ strategy = self.__strategy
20
+
21
+ return f"{class_name}(strategy='{strategy}')"
@@ -0,0 +1,26 @@
1
+ """Module for a base asteex executor."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class ExecutionError(Exception):
7
+ """Exception class for errors raised during the execution."""
8
+
9
+
10
+ class Executor(ABC):
11
+ """Class for a base executor."""
12
+
13
+ @abstractmethod
14
+ def execute(self, instances):
15
+ """Execute test instances.
16
+
17
+ :param instances:
18
+ async iterable with :class:`Instances <asteex.abstract.instance.Instance>`.
19
+ :returns: async iterable with :class:`Results <asteex.abstract.result.Result>`.
20
+ :raises: :class:`ExecutionError`.
21
+ """
22
+
23
+ def __repr__(self):
24
+ class_name = self.__class__.__name__
25
+
26
+ return f"{class_name}()"
@@ -0,0 +1,24 @@
1
+ """Module for a base asteex generator."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class GenerationError(Exception):
7
+ """Exception class for errors raised during the generation."""
8
+
9
+
10
+ class Generator(ABC):
11
+ """Class for a base generator."""
12
+
13
+ @abstractmethod
14
+ def generate(self):
15
+ """Generate test instances.
16
+
17
+ :returns: async iterable with :class:`Instances <asteex.abstract.instance.Instance>`.
18
+ :raises: :class:`GenerationError`.
19
+ """
20
+
21
+ def __repr__(self):
22
+ class_name = self.__class__.__name__
23
+
24
+ return f"{class_name}()"
@@ -0,0 +1,39 @@
1
+ """Module for a base asteex test instance."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class InstanceError(Exception):
7
+ """Exception class for errors raised by instances."""
8
+
9
+
10
+ class Instance(ABC):
11
+ """Class for a asteex test instance."""
12
+
13
+ @abstractmethod
14
+ def get_id(self):
15
+ """Get the unique identifier of the instance.
16
+
17
+ :returns: string id.
18
+ """
19
+
20
+ @abstractmethod
21
+ def get_case(self):
22
+ """Get test case.
23
+
24
+ :returns: :class:`Case <asteex.abstract.case.Case>`.
25
+ """
26
+
27
+ @abstractmethod
28
+ def get_context_sources(self):
29
+ """Get information about origins of the context values.
30
+
31
+ :returns: dictionary with string names as keys and
32
+ :class:`Source <asteex.abstract.context.Source>` as values.
33
+ """
34
+
35
+ def __repr__(self):
36
+ class_name = self.__class__.__name__
37
+ instance_id = self.get_id()
38
+
39
+ return f"{class_name}(instance_id='{instance_id}')"
@@ -0,0 +1,24 @@
1
+ """Module for a base asteex reporter."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class ReportError(Exception):
7
+ """Exception class for errors raised during the reporting."""
8
+
9
+
10
+ class Reporter(ABC):
11
+ """Class for a base reporter."""
12
+
13
+ @abstractmethod
14
+ async def report(self, results):
15
+ """Report test results.
16
+
17
+ :param results: async iterable with :class:`Results <asteex.abstract.result.Result>`.
18
+ :raises: :class:`ReportError`.
19
+ """
20
+
21
+ def __repr__(self):
22
+ class_name = self.__class__.__name__
23
+
24
+ return f"{class_name}()"
@@ -0,0 +1,58 @@
1
+ """Module for a base asteex test result."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class Result(ABC):
7
+ """Class for a base test result."""
8
+
9
+ @abstractmethod
10
+ def get_instance(self):
11
+ """Get the executed test instance.
12
+
13
+ :returns: executed :class:`Instance <asteex.abstract.instance.Instance>`.
14
+ """
15
+
16
+ @abstractmethod
17
+ def get_status(self):
18
+ """Get the status of the test execution.
19
+
20
+ :returns: string status.
21
+ """
22
+
23
+ @abstractmethod
24
+ def get_reason(self):
25
+ """Get the reason for the status.
26
+
27
+ :returns: string reason.
28
+ """
29
+
30
+ @abstractmethod
31
+ def get_case(self):
32
+ """Get the executed case.
33
+
34
+ :returns: executed :class:`Case <asteex.abstract.case.Case>` or None,
35
+ if no case was retrieved.
36
+ """
37
+
38
+ @abstractmethod
39
+ def get_context(self):
40
+ """Get the execution context.
41
+
42
+ :returns: dictionary with string names and arbitrary values, or None, if no context
43
+ was created.
44
+ """
45
+
46
+ @abstractmethod
47
+ def get_exception_information(self):
48
+ """Get information about the exception raised during the execution.
49
+
50
+ :returns: a tuple (type, value, traceback) similar to one returned by :meth:`sys.exc_info`.
51
+ """
52
+
53
+ def __repr__(self):
54
+ class_name = self.__class__.__name__
55
+ instance = self.get_instance()
56
+ status = self.get_status()
57
+
58
+ return f"{class_name}(instance={instance}, status='{status}')"
@@ -0,0 +1 @@
1
+ """Package with core implementation of the asteex entities."""
@@ -0,0 +1 @@
1
+ """Package for asteex test cases."""
@@ -0,0 +1 @@
1
+ """Package for asteex executable test cases."""
@@ -0,0 +1,21 @@
1
+ """Module for the abstract executable asteex test case."""
2
+
3
+ from abc import abstractmethod
4
+
5
+ from asteex.abstract.case import Case
6
+
7
+
8
+ class ExecutionError(Exception):
9
+ """Class for errors raised during the execution."""
10
+
11
+
12
+ class ExecutableCase(Case):
13
+ """Class for a asteex test case that can be executed."""
14
+
15
+ @abstractmethod
16
+ async def execute(self, context):
17
+ """Execute the test case.
18
+
19
+ :param context: dictionary with string keys.
20
+ :raises: :class:`ExecutionError`.
21
+ """
@@ -0,0 +1,306 @@
1
+ """Module for a test cases that delegates execution to a callable object."""
2
+
3
+ import inspect
4
+ import logging
5
+
6
+ from asteex.core.case.executable.abstract import ExecutableCase, ExecutionError
7
+ from asteex.core.decorator.argument import ContextMapper, CopyError
8
+
9
+
10
+ class BuildError(Exception):
11
+ """Exception for errors raised on attempt to build a test case."""
12
+
13
+
14
+ class DelegateCase(ExecutableCase):
15
+ """Class for a asteex test case based on a callable."""
16
+
17
+ def __init__(self, delegate, case_id, dependencies=()):
18
+ super().__init__()
19
+
20
+ if not callable(delegate):
21
+ raise ValueError("Can't create a delegate case from a non-callable object")
22
+ self.__delegate = delegate
23
+
24
+ self.__id = str(case_id)
25
+
26
+ logger = logging.getLogger(__name__)
27
+ logger.debug("Try to refine dependencies for a new delegate case.")
28
+ try:
29
+ dependencies = tuple(str(dependency) for dependency in dependencies)
30
+ except TypeError as error:
31
+ logger.exception("Can't iterate over dependencies of a new delegate case.")
32
+ raise ValueError("Can't iterate over dependencies of a new delegate case") from error
33
+ self.__dependencies = dependencies
34
+ logger.debug("Refined dependencies for a new delegate case.")
35
+
36
+ def get_id(self):
37
+ """Get the unique identifier of the test case.
38
+
39
+ :returns: string id.
40
+ """
41
+ return self.__id
42
+
43
+ def get_dependencies(self):
44
+ """Get names of the required variables.
45
+
46
+ :returns: tuple with string names of the required variables.
47
+ """
48
+ return self.__dependencies
49
+
50
+ async def execute(self, context):
51
+ """Execute the test case.
52
+
53
+ :param context: dictionary with string keys.
54
+ :raises: :class:`ExecutionError <asteex.core.case.execution.abstract.ExecutionError>`.
55
+ """
56
+ logger = logging.getLogger(__name__)
57
+
58
+ logger.debug("Try to execute the delegate of the case %s.", self)
59
+ try:
60
+ execution = self.__delegate(context=context)
61
+
62
+ if inspect.isawaitable(execution):
63
+ await execution
64
+ except Exception as error:
65
+ logger.exception("An error is raised during the execution.")
66
+ raise ExecutionError("An error is raised during the execution") from error
67
+
68
+ logger.info("Executed the delegate of the case %s.", self)
69
+
70
+
71
+ class DelegateCaseBuilder(ContextMapper):
72
+ """Class to create delegate cases from callables."""
73
+
74
+ def __init__(self):
75
+ super().__init__()
76
+ self.__case_id = None
77
+ self.__additional_dependencies = frozenset()
78
+ self.__enhancers = ()
79
+
80
+ def set_case_id(self, case_id):
81
+ """Set identifier for new test cases.
82
+
83
+ :param case_id: string identifier or None.
84
+ None value implies that the builder has to deduce case identifiers from callables.
85
+ :returns: self.
86
+ """
87
+ if case_id is not None:
88
+ case_id = str(case_id)
89
+ self.__case_id = case_id
90
+
91
+ logger = logging.getLogger(__name__)
92
+ logger.info("Set case identifier in %s.", self)
93
+
94
+ return self
95
+
96
+ def get_case_id(self):
97
+ """Get identifier for new test cases.
98
+
99
+ :returns: string identifier or None.
100
+ """
101
+ return self.__case_id
102
+
103
+ def add_dependencies(self, dependencies, overwrite=False):
104
+ """Add dependencies.
105
+
106
+ :param dependencies: iterable with string names of the dependencies.
107
+ :param overwrite: boolean whether the new dependencies should overwrite the existing ones.
108
+ :returns: self.
109
+ :raises: :class:`ValueError` if dependencies is not a valid iterable.
110
+ """
111
+ logger = logging.getLogger(__name__)
112
+ logger.debug("Try to set additional case dependencies in %s.", self)
113
+
114
+ try:
115
+ refined_case_dependencies = frozenset(str(dependency) for dependency in dependencies)
116
+ except TypeError as error:
117
+ logger.exception("Can't iterate over case dependencies.")
118
+ raise ValueError("Can't iterate over case dependencies") from error
119
+
120
+ if overwrite:
121
+ self.__additional_dependencies = refined_case_dependencies
122
+ else:
123
+ self.__additional_dependencies = self.__additional_dependencies.union(
124
+ refined_case_dependencies,
125
+ )
126
+
127
+ logger.info("Set additional case dependencies in %s.", self)
128
+
129
+ return self
130
+
131
+ def get_additional_dependencies(self):
132
+ """Get additional dependencies.
133
+
134
+ :returns: set with string names of the dependencies.
135
+ """
136
+ return self.__additional_dependencies
137
+
138
+ def add_enhancers(self, enhancers, overwrite=False):
139
+ """Add enhancers for new test cases.
140
+
141
+ :param enhancers: iterable with callables that accepts two parameters. Each callable will
142
+ receive a :class:`DelegateCase` for the first parameter and a callable for
143
+ the second parameter. The function must return :class:`DelegateCase`.
144
+ :param overwrite: boolean whether the new enhancers should overwrite the existing ones.
145
+ :returns: self.
146
+ :raises: :class:`ValueError` if enhancers is not a valid iterable.
147
+ """
148
+ logger = logging.getLogger(__name__)
149
+ logger.debug("Try to set case enhancers in %s.", self)
150
+
151
+ try:
152
+ refined_case_enhancers = tuple(enhancers)
153
+ except TypeError as error:
154
+ logger.exception("Can't iterate over case enhancers.")
155
+ raise ValueError("Can't iterate over case enhancers") from error
156
+
157
+ if overwrite:
158
+ self.__enhancers = refined_case_enhancers
159
+ else:
160
+ self.__enhancers = self.__enhancers + refined_case_enhancers
161
+
162
+ logger.info("Set case enhancers in %s.", self)
163
+
164
+ return self
165
+
166
+ def get_enhancers(self):
167
+ """Get enhancers for new test cases.
168
+
169
+ :returns: tuple with callables that accepts two parameters. Each callable accepts
170
+ a :class:`DelegateCase` for the first parameter and a callable for the second parameter.
171
+ Each callable returns :class:`DelegateCase`.
172
+ """
173
+ return self.__enhancers
174
+
175
+ def build_from_callable(self, callable_):
176
+ """Create a delegate case from a callable.
177
+
178
+ :param callable_: a callable to use as a base for the delegate case.
179
+ :returns: a new :class`DelegateCase`.
180
+ :raises: :class:`BuildError` if an error occurs on attempt to create or enhance a case.
181
+ """
182
+ logger = logging.getLogger(__name__)
183
+ logger.debug("Try to create a delegate case from '%s'.", callable_)
184
+
185
+ try:
186
+ case = self._create_case(callable_)
187
+ except Exception as error:
188
+ logger.exception("Can't create a delegate case.")
189
+ raise BuildError("Can't create a delegate case") from error
190
+
191
+ for enhancer in self.get_enhancers():
192
+ try:
193
+ case = enhancer(case, callable_)
194
+ except Exception as error:
195
+ logger.exception("Can't enhance a delegate case.")
196
+ raise BuildError("Can't enhance a delegate case") from error
197
+
198
+ logger.info("Created a delegate case from '%s'.", callable_)
199
+ return case
200
+
201
+ def _copy_data(self, other):
202
+ """Copy data from this instance to another.
203
+
204
+ :param other: instance of the same type.
205
+ :raises: :class:`CopyError <asteex.core.decorator.argument.CopyError>`
206
+ if data can't be copied.
207
+ """
208
+ super()._copy_data(other)
209
+
210
+ logger = logging.getLogger(__name__)
211
+
212
+ try:
213
+ other.set_case_id(self.get_case_id())
214
+ except Exception as error:
215
+ logger.exception("Can't copy case identifier to another instance.")
216
+ raise CopyError("Can't copy case identifier to another instance") from error
217
+
218
+ try:
219
+ other.add_dependencies(self.get_additional_dependencies(), overwrite=True)
220
+ except Exception as error:
221
+ logger.exception("Can't copy case dependencies to another instance.")
222
+ raise CopyError("Can't copy case dependencies to another instance") from error
223
+
224
+ try:
225
+ other.add_enhancers(self.get_enhancers(), overwrite=True)
226
+ except Exception as error:
227
+ logger.exception("Can't copy case enhancers to another instance.")
228
+ raise CopyError("Can't copy case enhancers to another instance") from error
229
+
230
+ def _create_case_id(self, callable_):
231
+ """Create an identifier for a delegate case from a callable.
232
+
233
+ :param callable_: callable for a delegate for the case.
234
+ :returns: string id.
235
+ """
236
+ case_id = self.get_case_id()
237
+ if case_id is None:
238
+ refined_case_id = str(callable_)
239
+ else:
240
+ refined_case_id = case_id
241
+
242
+ return refined_case_id
243
+
244
+ def _create_case_dependencies(self, callable_):
245
+ """Create dependencies for a delegate case from a callable.
246
+
247
+ :param callable_: callable for a delegate for the case.
248
+ :returns: set with string names.
249
+ """
250
+ case_dependencies = set(self.get_additional_dependencies())
251
+
252
+ positional_arguments, keyword_arguments = self.build_call_arguments(callable_)
253
+
254
+ arguments_with_dependencies = set().union(positional_arguments).union(keyword_arguments)
255
+ arguments_with_dependencies.difference_update(self.get_arguments_mapped_to_context())
256
+
257
+ dependency_map = self.get_arguments_mapped_to_context_names()
258
+
259
+ case_dependencies.update(
260
+ dependency_map.get(argument, argument) for argument in arguments_with_dependencies
261
+ )
262
+
263
+ return case_dependencies
264
+
265
+ def _create_case_delegate(self, callable_):
266
+ """Create a delegate for a delegate case from a callable.
267
+
268
+ :param callable_: callable for a delegate for the case.
269
+ :returns: callable, suitable for :class:`DelegateCase`.
270
+ """
271
+ arguments_mapped_to_context_names = self.get_arguments_mapped_to_context_names().copy()
272
+ arguments_mapped_to_context = set(self.get_arguments_mapped_to_context())
273
+
274
+ positional_arguments, keyword_arguments = self.build_call_arguments(callable_)
275
+
276
+ def _delegate(context):
277
+ def _get_delegate_value(argument):
278
+ if argument in arguments_mapped_to_context:
279
+ value = context
280
+ else:
281
+ mapped_argument = arguments_mapped_to_context_names.get(argument, argument)
282
+ value = context[mapped_argument]
283
+
284
+ return value
285
+
286
+ arg_values = [_get_delegate_value(argument) for argument in positional_arguments]
287
+ kwarg_values = {
288
+ argument: _get_delegate_value(argument)
289
+ for argument in keyword_arguments
290
+ }
291
+
292
+ return callable_(*arg_values, **kwarg_values)
293
+
294
+ return _delegate
295
+
296
+ def _create_case(self, callable_):
297
+ """Create a delegate case from a callable.
298
+
299
+ :param callable_: callable for a delegate for the case.
300
+ :returns: :class:`DelegateCase`.
301
+ """
302
+ return DelegateCase(
303
+ delegate=self._create_case_delegate(callable_),
304
+ case_id=self._create_case_id(callable_),
305
+ dependencies=self._create_case_dependencies(callable_),
306
+ )
@@ -0,0 +1 @@
1
+ """Package for asteex context entities."""
@@ -0,0 +1 @@
1
+ """Package for sources of context values."""