flamapy-fw 2.0.0.dev0__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 (51) hide show
  1. flamapy-fw-2.0.0.dev0/PKG-INFO +41 -0
  2. flamapy-fw-2.0.0.dev0/README.md +23 -0
  3. flamapy-fw-2.0.0.dev0/flamapy/commands/__init__.py +88 -0
  4. flamapy-fw-2.0.0.dev0/flamapy/core/__init__.py +0 -0
  5. flamapy-fw-2.0.0.dev0/flamapy/core/config.py +3 -0
  6. flamapy-fw-2.0.0.dev0/flamapy/core/discover.py +320 -0
  7. flamapy-fw-2.0.0.dev0/flamapy/core/exceptions.py +30 -0
  8. flamapy-fw-2.0.0.dev0/flamapy/core/models/__init__.py +4 -0
  9. flamapy-fw-2.0.0.dev0/flamapy/core/models/ast.py +304 -0
  10. flamapy-fw-2.0.0.dev0/flamapy/core/models/variability_model.py +15 -0
  11. flamapy-fw-2.0.0.dev0/flamapy/core/operations/__init__.py +28 -0
  12. flamapy-fw-2.0.0.dev0/flamapy/core/operations/abstract_operation.py +15 -0
  13. flamapy-fw-2.0.0.dev0/flamapy/core/operations/atomic_sets.py +18 -0
  14. flamapy-fw-2.0.0.dev0/flamapy/core/operations/average_branching_factor.py +14 -0
  15. flamapy-fw-2.0.0.dev0/flamapy/core/operations/commonality.py +19 -0
  16. flamapy-fw-2.0.0.dev0/flamapy/core/operations/configurations.py +15 -0
  17. flamapy-fw-2.0.0.dev0/flamapy/core/operations/configurations_number.py +14 -0
  18. flamapy-fw-2.0.0.dev0/flamapy/core/operations/core_features.py +15 -0
  19. flamapy-fw-2.0.0.dev0/flamapy/core/operations/count_leafs.py +14 -0
  20. flamapy-fw-2.0.0.dev0/flamapy/core/operations/dead_features.py +16 -0
  21. flamapy-fw-2.0.0.dev0/flamapy/core/operations/error_detection.py +15 -0
  22. flamapy-fw-2.0.0.dev0/flamapy/core/operations/error_diagnosis.py +15 -0
  23. flamapy-fw-2.0.0.dev0/flamapy/core/operations/estimated_configurations_number.py +14 -0
  24. flamapy-fw-2.0.0.dev0/flamapy/core/operations/false_optional_features.py +17 -0
  25. flamapy-fw-2.0.0.dev0/flamapy/core/operations/filter.py +20 -0
  26. flamapy-fw-2.0.0.dev0/flamapy/core/operations/metrics_operation.py +105 -0
  27. flamapy-fw-2.0.0.dev0/flamapy/core/operations/sampling.py +30 -0
  28. flamapy-fw-2.0.0.dev0/flamapy/core/operations/satisfiable.py +14 -0
  29. flamapy-fw-2.0.0.dev0/flamapy/core/operations/satisfiable_configuration.py +19 -0
  30. flamapy-fw-2.0.0.dev0/flamapy/core/operations/variability.py +14 -0
  31. flamapy-fw-2.0.0.dev0/flamapy/core/plugins.py +194 -0
  32. flamapy-fw-2.0.0.dev0/flamapy/core/transformations/__init__.py +6 -0
  33. flamapy-fw-2.0.0.dev0/flamapy/core/transformations/abstract_transformation.py +10 -0
  34. flamapy-fw-2.0.0.dev0/flamapy/core/transformations/model_to_model.py +21 -0
  35. flamapy-fw-2.0.0.dev0/flamapy/core/transformations/model_to_text.py +20 -0
  36. flamapy-fw-2.0.0.dev0/flamapy/core/transformations/text_to_model.py +20 -0
  37. flamapy-fw-2.0.0.dev0/flamapy/core/utils.py +9 -0
  38. flamapy-fw-2.0.0.dev0/flamapy/endpoint/diverso_lab.py +89 -0
  39. flamapy-fw-2.0.0.dev0/flamapy/metamodels/configuration_metamodel/__init__.py +0 -0
  40. flamapy-fw-2.0.0.dev0/flamapy/metamodels/configuration_metamodel/models/__init__.py +3 -0
  41. flamapy-fw-2.0.0.dev0/flamapy/metamodels/configuration_metamodel/models/configuration.py +32 -0
  42. flamapy-fw-2.0.0.dev0/flamapy/metamodels/configuration_metamodel/transformations/__init__.py +4 -0
  43. flamapy-fw-2.0.0.dev0/flamapy/metamodels/configuration_metamodel/transformations/configuration_basic_reader.py +34 -0
  44. flamapy-fw-2.0.0.dev0/flamapy_fw.egg-info/PKG-INFO +41 -0
  45. flamapy-fw-2.0.0.dev0/flamapy_fw.egg-info/SOURCES.txt +49 -0
  46. flamapy-fw-2.0.0.dev0/flamapy_fw.egg-info/dependency_links.txt +1 -0
  47. flamapy-fw-2.0.0.dev0/flamapy_fw.egg-info/entry_points.txt +3 -0
  48. flamapy-fw-2.0.0.dev0/flamapy_fw.egg-info/requires.txt +7 -0
  49. flamapy-fw-2.0.0.dev0/flamapy_fw.egg-info/top_level.txt +1 -0
  50. flamapy-fw-2.0.0.dev0/setup.cfg +4 -0
  51. flamapy-fw-2.0.0.dev0/setup.py +38 -0
@@ -0,0 +1,41 @@
1
+ Metadata-Version: 2.1
2
+ Name: flamapy-fw
3
+ Version: 2.0.0.dev0
4
+ Summary: Flamapy is a Python-based AAFM framework that takes into consideration previous AAFM tool designs and enables multi-solver and multi-metamodel support for the integration of AAFM tooling on the Python ecosystem.
5
+ Home-page: https://github.com/flamapy/core
6
+ Author: Flamapy
7
+ Author-email: flamapy@us.es
8
+ License: UNKNOWN
9
+ Platform: UNKNOWN
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ Provides-Extra: dev
16
+
17
+ # Flamapy
18
+ Flamapy is a Python-based AAFM framework that takes into consideration previous AAFM tool designs and enables multi-solver and multi-metamodel support for the integration of AAFM tooling on the Python ecosystem.
19
+
20
+ The main features of the framework are:
21
+ * Easy to extend by enabling the creation of new plugins following a semi-automatic generator approach.
22
+ * Support multiple variability models. Currently, it provides support for cardinality-based feature models. However, it is easy to integrate others such as attributed feature models
23
+ * Support multiple solvers. Currently, it provides support for the PySAT metasolver, which enables more than ten different solvers.
24
+ * Support multiple operations. It is developed, having in mind multi-model operations such as those depicted by Familiar and single-model operations.
25
+
26
+ ## Available plugins
27
+ [flamapy-fm](https://github.com/flamapy/fm_metamodel)
28
+ [flamapy-sat](https://github.com/flamapy/pysat_metamodel)
29
+
30
+ ## Documentation
31
+
32
+ All the proyect related documentation can be found in wiki format at [the tool website](https://flamapy.github.io/)
33
+
34
+ ## Changelog
35
+ Detailed changes for each release are documented in the [release notes](https://github.com/diverso-lab/core/releases)
36
+
37
+ ## Contributing
38
+
39
+ See [CONTRIBUTING.md](https://github.com/diverso-lab/core/blob/master/CONTRIBUTING.md)
40
+
41
+
@@ -0,0 +1,23 @@
1
+ # Flamapy
2
+ Flamapy is a Python-based AAFM framework that takes into consideration previous AAFM tool designs and enables multi-solver and multi-metamodel support for the integration of AAFM tooling on the Python ecosystem.
3
+
4
+ The main features of the framework are:
5
+ * Easy to extend by enabling the creation of new plugins following a semi-automatic generator approach.
6
+ * Support multiple variability models. Currently, it provides support for cardinality-based feature models. However, it is easy to integrate others such as attributed feature models
7
+ * Support multiple solvers. Currently, it provides support for the PySAT metasolver, which enables more than ten different solvers.
8
+ * Support multiple operations. It is developed, having in mind multi-model operations such as those depicted by Familiar and single-model operations.
9
+
10
+ ## Available plugins
11
+ [flamapy-fm](https://github.com/flamapy/fm_metamodel)
12
+ [flamapy-sat](https://github.com/flamapy/pysat_metamodel)
13
+
14
+ ## Documentation
15
+
16
+ All the proyect related documentation can be found in wiki format at [the tool website](https://flamapy.github.io/)
17
+
18
+ ## Changelog
19
+ Detailed changes for each release are documented in the [release notes](https://github.com/diverso-lab/core/releases)
20
+
21
+ ## Contributing
22
+
23
+ See [CONTRIBUTING.md](https://github.com/diverso-lab/core/blob/master/CONTRIBUTING.md)
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/python
2
+
3
+ import argparse
4
+ import os
5
+ import sys
6
+ from pathlib import Path
7
+ from shutil import copytree
8
+
9
+ from hug import development_runner
10
+
11
+
12
+ def parser() -> argparse.ArgumentParser:
13
+ main_parser = argparse.ArgumentParser()
14
+ subparser = main_parser.add_subparsers(title="commands", dest="command")
15
+
16
+ new_plugin = subparser.add_parser("new_plugin", help="new_plugin")
17
+ new_plugin.add_argument('name', type=str, help='A name for your plugin. Ex: flama')
18
+ new_plugin.add_argument('extension', type=str, help='A extension for your plugin. Ex: fm')
19
+ new_plugin.add_argument('--path', default='.', type=str, help='Plugin project path.')
20
+
21
+ subparser.add_parser("cli", help="cli", description='flamapy_admin.py api: command line api')
22
+ return main_parser
23
+
24
+
25
+ def cmd_new_plugin(options: argparse.Namespace) -> None:
26
+ name = options.name
27
+ ext = options.extension
28
+ dst = options.path
29
+ src = 'skel_metamodel/'
30
+
31
+ # Check DST exist
32
+ if not os.path.isdir(dst):
33
+ print(f"Folder {dst} not exist")
34
+ sys.exit()
35
+
36
+ # Check DST is empty
37
+ if len(os.listdir(dst)) != 0:
38
+ print(f"Folder {dst} is not empty")
39
+ sys.exit()
40
+
41
+ # Check DST has permissions to WRITE
42
+ if not os.access(dst, os.W_OK):
43
+ print(f"Folder {dst} has not write permissions")
44
+ sys.exit()
45
+
46
+ # Generating structure
47
+ print("Generating structure ...")
48
+
49
+ copy_files = copytree(src, dst, dirs_exist_ok=True)
50
+
51
+ for copy_file in Path(copy_files).glob('**/*'):
52
+ if copy_file.is_dir():
53
+ continue
54
+ with open(copy_file, "r", encoding="utf-8") as file:
55
+ lines = file.readlines()
56
+ with open(copy_file, "w", encoding="utf-8") as filewrite:
57
+ for line in lines:
58
+ out_line = line.replace('__NAME__', name.capitalize()).replace('__EXT__', ext)
59
+ filewrite.write(out_line)
60
+
61
+ os.rename(os.path.join(dst, 'flamapy/metamodels/__NAME__'),
62
+ os.path.join(dst, f'flamapy/metamodels/{name}'))
63
+ print("Done!")
64
+
65
+
66
+ def cmd_cli() -> None:
67
+ subcommand = sys.argv[2:]
68
+ if not subcommand:
69
+ subcommand = ["help"]
70
+ sys.argv = [".", "-m", "flamapy.endpoint.diverso_lab", "cli", "-c"] + subcommand
71
+ development_runner.hug.interface.cli()
72
+
73
+
74
+ def flamapy_admin() -> None:
75
+ main_parser = parser()
76
+
77
+ options, _ = main_parser.parse_known_args()
78
+ command = options.command
79
+
80
+ if command is None:
81
+ main_parser.print_help()
82
+ sys.exit()
83
+
84
+ if command == "new_plugin":
85
+ cmd_new_plugin(options)
86
+
87
+ if command == "cli":
88
+ cmd_cli()
File without changes
@@ -0,0 +1,3 @@
1
+ PLUGIN_PATHS = [
2
+ 'flamapy.metamodels',
3
+ ]
@@ -0,0 +1,320 @@
1
+ import inspect
2
+ import logging
3
+ from importlib import import_module
4
+ from pkgutil import iter_modules
5
+ from types import ModuleType
6
+ from typing import Any, Optional, Protocol, Type, runtime_checkable, cast
7
+
8
+ from flamapy.core.config import PLUGIN_PATHS
9
+ from flamapy.core.exceptions import OperationNotFound
10
+ from flamapy.core.exceptions import TransformationNotFound
11
+ from flamapy.core.exceptions import ConfigurationNotFound
12
+ from flamapy.core.models import VariabilityModel
13
+ from flamapy.core.operations import Operation
14
+ from flamapy.core.plugins import (
15
+ Operations,
16
+ Plugin,
17
+ Plugins
18
+ )
19
+ from flamapy.core.transformations import Transformation
20
+ from flamapy.core.transformations.text_to_model import TextToModel
21
+ from flamapy.core.transformations.model_to_model import ModelToModel
22
+ from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration
23
+
24
+
25
+ LOGGER = logging.getLogger('discover')
26
+
27
+
28
+ @runtime_checkable
29
+ class OperationWithConfiguration(Protocol):
30
+ def set_configuration(self, configuration: Configuration) -> None:
31
+ pass
32
+
33
+
34
+ def filter_modules_from_plugin_paths() -> list[ModuleType]:
35
+ results: list[ModuleType] = []
36
+ for path in PLUGIN_PATHS:
37
+ try:
38
+ module: ModuleType = import_module(path)
39
+ results.append(module)
40
+ except ModuleNotFoundError:
41
+ LOGGER.exception('ModuleNotFoundError %s', path)
42
+ return results
43
+
44
+
45
+ class DiscoverMetamodels:
46
+ def __init__(self) -> None:
47
+ self.module_paths = filter_modules_from_plugin_paths()
48
+ self.plugins: Plugins = self.discover()
49
+
50
+ def search_classes(self, module: ModuleType) -> list[Any]:
51
+ classes = []
52
+ for _, file_name, ispkg in iter_modules(
53
+ module.__path__, module.__name__ + '.'
54
+ ):
55
+ if ispkg:
56
+ classes += self.search_classes(import_module(file_name))
57
+ else:
58
+ _file = import_module(file_name)
59
+ classes += inspect.getmembers(_file, inspect.isclass)
60
+ return classes
61
+
62
+ def discover(self) -> Plugins:
63
+ plugins = Plugins()
64
+ for pkg in self.module_paths:
65
+ for _, plugin_name, ispkg in iter_modules(
66
+ pkg.__path__, pkg.__name__ + '.'
67
+ ):
68
+ if not ispkg:
69
+ continue
70
+ module = import_module(plugin_name)
71
+ plugin = Plugin(module=module)
72
+
73
+ classes = self.search_classes(module)
74
+
75
+ for _, _class in classes:
76
+ if not _class.__module__.startswith(module.__package__):
77
+ continue # Exclude modules not in current package
78
+ inherit = _class.mro()
79
+
80
+ if Operation in inherit:
81
+ plugin.append_operation(_class)
82
+ elif Transformation in inherit:
83
+ plugin.append_transformations(_class)
84
+ elif VariabilityModel in inherit:
85
+ plugin.variability_model = _class
86
+ plugins.append(plugin)
87
+ return plugins
88
+
89
+ def reload(self) -> None:
90
+ self.plugins = self.discover()
91
+
92
+ def get_operations(self) -> list[Type[Operation]]:
93
+ """ Get the operations for all modules """
94
+ operations: list[Type[Operation]] = []
95
+ for plugin in self.plugins:
96
+ operations += plugin.operations
97
+ return operations
98
+
99
+ def get_name_operations(self) -> list[str]:
100
+ operations = []
101
+ for operation in self.get_operations():
102
+ operations.append(operation.__name__)
103
+ base = operation.__base__.__name__
104
+ if base != 'ABC':
105
+ operations.append(base)
106
+
107
+ return operations
108
+
109
+ def get_transformations(self) -> list[Type[Transformation]]:
110
+ """ Get transformations for all modules """
111
+ transformations: list[Type[Transformation]] = []
112
+ for plugin in self.plugins:
113
+ transformations += plugin.transformations
114
+ return transformations
115
+
116
+ def get_transformations_t2m(self) -> list[Type[TextToModel]]:
117
+ """ Get t2m transformations for all modules """
118
+
119
+ transformations: list[Type[TextToModel]] = []
120
+ for plugin in self.plugins:
121
+ transformations += [
122
+ t for t in plugin.transformations if issubclass(t, TextToModel)
123
+ ]
124
+ return transformations
125
+
126
+ def get_transformations_m2m(self) -> list[Type[ModelToModel]]:
127
+ """ Get m2m transformations for all modules """
128
+
129
+ transformations: list[Type[ModelToModel]] = []
130
+ for plugin in self.plugins:
131
+ transformations += [
132
+ t for t in plugin.transformations if issubclass(t, ModelToModel)
133
+ ]
134
+ return transformations
135
+
136
+ def get_operations_by_plugin(self, plugin_name: str) -> Operations:
137
+ return self.plugins.get_operations_by_plugin_name(plugin_name)
138
+
139
+ def get_plugins_with_operation(self, operation_name: str) -> list[Plugin]:
140
+ return [
141
+ plugin for plugin in self.plugins
142
+ if operation_name in self.get_name_operations_by_plugin(plugin.name)
143
+ ]
144
+
145
+ def get_name_operations_by_plugin(self, plugin_name: str) -> list[str]:
146
+ operations = []
147
+ for operation in self.get_operations_by_plugin(plugin_name):
148
+ operations.append(operation.__name__)
149
+ base = operation.__base__.__name__
150
+ if base != 'ABC':
151
+ operations.append(base)
152
+
153
+ return operations
154
+
155
+ def get_variability_models(self) -> list[VariabilityModel]:
156
+ return self.plugins.get_variability_models()
157
+
158
+ def get_plugins(self) -> list[str]:
159
+ return self.plugins.get_plugin_names()
160
+
161
+ def use_transformation_m2t(self, src: VariabilityModel, dst: str) -> str:
162
+ plugin = self.plugins.get_plugin_by_variability_model(src)
163
+ return plugin.use_transformation_m2t(src, dst)
164
+
165
+ def use_transformation_t2m(self, src: str, dst: str) -> VariabilityModel:
166
+ plugin = self.plugins.get_plugin_by_extension(dst)
167
+ return plugin.use_transformation_t2m(src)
168
+
169
+ def use_transformation_m2m(self, src: VariabilityModel, dst: str) -> VariabilityModel:
170
+ plugin = self.plugins.get_plugin_by_extension(dst)
171
+ return plugin.use_transformation_m2m(src, dst)
172
+
173
+ def get_operation(self, src: VariabilityModel, operation_name: str) -> Operation:
174
+ plugin = self.plugins.get_plugin_by_variability_model(src)
175
+ return plugin.get_operation(operation_name)
176
+
177
+ def use_operation(self, src: VariabilityModel, operation_name: str) -> Operation:
178
+ plugin = self.plugins.get_plugin_by_variability_model(src)
179
+ operation = plugin.get_operation(operation_name)
180
+ return plugin.use_operation(operation, src)
181
+
182
+ def use_operation_from_vm(
183
+ self,
184
+ operation_name: str,
185
+ vm_orig: VariabilityModel,
186
+ plugin_name: Optional[str] = None,
187
+ configuration_file: Optional[str] = None
188
+ ) -> Any:
189
+
190
+ if operation_name not in self.get_name_operations():
191
+ raise OperationNotFound()
192
+
193
+ if plugin_name is not None:
194
+ plugin = self.plugins.get_plugin_by_name(plugin_name)
195
+ #vm_temp = plugin.use_transformation_t2m(file)
196
+ else:
197
+ #vm_temp = self.__transform_to_model_from_file(file)
198
+ plugin = self.plugins.get_plugin_by_extension(
199
+ vm_orig.get_extension())
200
+
201
+ if operation_name not in self.get_name_operations_by_plugin(plugin.name):
202
+ transformation_way = self.__search_transformation_way(
203
+ plugin, operation_name)
204
+
205
+ for (_, dst) in transformation_way:
206
+ _plugin = self.plugins.get_plugin_by_extension(dst)
207
+ vm_temp = _plugin.use_transformation_m2m(vm_temp, dst)
208
+ plugin = _plugin
209
+
210
+ operation = plugin.get_operation(operation_name)
211
+ if isinstance(operation, OperationWithConfiguration):
212
+ if configuration_file is None:
213
+ raise ConfigurationNotFound()
214
+ configuration = self.__transform_to_model_from_file(configuration_file)
215
+ operation.set_configuration(cast(Configuration, configuration))
216
+
217
+ operation = plugin.use_operation(operation, vm_temp)
218
+
219
+ return operation.get_result()
220
+
221
+ def use_operation_from_file(
222
+ self,
223
+ operation_name: str,
224
+ file: str,
225
+ plugin_name: Optional[str] = None,
226
+ configuration_file: Optional[str] = None
227
+ ) -> Any:
228
+
229
+ if operation_name not in self.get_name_operations():
230
+ raise OperationNotFound()
231
+
232
+ if plugin_name is not None:
233
+ plugin = self.plugins.get_plugin_by_name(plugin_name)
234
+ vm_temp = plugin.use_transformation_t2m(file)
235
+ else:
236
+ vm_temp = self.__transform_to_model_from_file(file)
237
+ plugin = self.plugins.get_plugin_by_extension(
238
+ vm_temp.get_extension())
239
+
240
+ if operation_name not in self.get_name_operations_by_plugin(plugin.name):
241
+ transformation_way = self.__search_transformation_way(
242
+ plugin, operation_name)
243
+
244
+ for (_, dst) in transformation_way:
245
+ _plugin = self.plugins.get_plugin_by_extension(dst)
246
+ vm_temp = _plugin.use_transformation_m2m(vm_temp, dst)
247
+ plugin = _plugin
248
+
249
+ operation = plugin.get_operation(operation_name)
250
+ if isinstance(operation, OperationWithConfiguration):
251
+ if configuration_file is None:
252
+ raise ConfigurationNotFound()
253
+ configuration = self.__transform_to_model_from_file(configuration_file)
254
+ operation.set_configuration(cast(Configuration, configuration))
255
+
256
+ operation = plugin.use_operation(operation, vm_temp)
257
+ return operation.get_result()
258
+
259
+ def __transform_to_model_from_file(self, file: str) -> VariabilityModel:
260
+ t2m_transformations = self.get_transformations_t2m()
261
+ extension = file.split('.')[-1]
262
+ t2m_filters = filter(
263
+ lambda t2m: t2m.get_source_extension() == extension,
264
+ t2m_transformations
265
+ )
266
+
267
+ t2m = next(t2m_filters, None)
268
+ if t2m is None:
269
+ raise TransformationNotFound()
270
+
271
+ return t2m(file).transform()
272
+
273
+ def __search_transformation_way(
274
+ self,
275
+ plugin: Plugin,
276
+ operation_name: str
277
+ ) -> list[tuple[str, str]]:
278
+ '''
279
+ Search way to reach plugin with operation_name using m2m transformations
280
+ '''
281
+ way: list[tuple[str, str]] = []
282
+
283
+ plugins_with_operation = self.get_plugins_with_operation(
284
+ operation_name)
285
+ m2m_transformations = self.get_transformations_m2m()
286
+
287
+ input_extension = plugin.get_extension()
288
+
289
+ def __search_recursive_way(
290
+ input_extension: str,
291
+ output_extension: str,
292
+ tmp_way: list[tuple[str, str]]
293
+ ) -> list[tuple[str, str]]:
294
+
295
+ for m2m in m2m_transformations:
296
+ in_m2m = m2m.get_source_extension()
297
+ out_m2m = m2m.get_destination_extension()
298
+
299
+ if out_m2m == output_extension:
300
+ _next = (in_m2m, out_m2m)
301
+
302
+ if _next in tmp_way:
303
+ continue
304
+
305
+ tmp_way.insert(0, _next)
306
+
307
+ if input_extension == in_m2m:
308
+ return tmp_way
309
+
310
+ return __search_recursive_way(input_extension, in_m2m, tmp_way)
311
+
312
+ return tmp_way
313
+
314
+ for _plugin in plugins_with_operation:
315
+ output_extension = _plugin.get_extension()
316
+ way = __search_recursive_way(input_extension, output_extension, [])
317
+ if way and output_extension == way[-1][1]:
318
+ return way
319
+
320
+ raise NotImplementedError('Way to execute operation not found')
@@ -0,0 +1,30 @@
1
+ class FlamaException(Exception):
2
+ pass
3
+
4
+
5
+ class ParsingException(Exception):
6
+ pass
7
+
8
+
9
+ class PluginNotFound(FlamaException):
10
+ pass
11
+
12
+
13
+ class OperationNotFound(FlamaException):
14
+ pass
15
+
16
+
17
+ class TransformationNotFound(FlamaException):
18
+ pass
19
+
20
+
21
+ class ElementNotFound(FlamaException):
22
+ pass
23
+
24
+
25
+ class DuplicatedFeature(FlamaException):
26
+ pass
27
+
28
+
29
+ class ConfigurationNotFound(FlamaException):
30
+ pass
@@ -0,0 +1,4 @@
1
+ from .variability_model import VariabilityModel, VariabilityElement # pylint: disable=cyclic-import
2
+ from .ast import AST, ASTOperation # pylint: disable=cyclic-import
3
+
4
+ __all__ = ["VariabilityModel", "AST", "VariabilityElement", "ASTOperation"]