flamapy 2.6.0.dev0__tar.gz → 2.6.0.dev2__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 (21) hide show
  1. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/PKG-INFO +7 -7
  2. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/flamapy/commands/__init__.py +2 -2
  3. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/flamapy/interfaces/python/flamapy_feature_model.py +102 -72
  4. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/flamapy.egg-info/PKG-INFO +7 -7
  5. flamapy-2.6.0.dev2/flamapy.egg-info/requires.txt +12 -0
  6. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/pyproject.toml +8 -8
  7. flamapy-2.6.0.dev0/flamapy.egg-info/requires.txt +0 -12
  8. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/LICENSE +0 -0
  9. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/README.md +0 -0
  10. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/flamapy/interfaces/python/__init__.py +0 -0
  11. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/flamapy.egg-info/SOURCES.txt +0 -0
  12. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/flamapy.egg-info/dependency_links.txt +0 -0
  13. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/flamapy.egg-info/entry_points.txt +0 -0
  14. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/flamapy.egg-info/top_level.txt +0 -0
  15. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/setup.cfg +0 -0
  16. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/setup.py +0 -0
  17. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/tests/test_basic_units.py +0 -0
  18. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/tests/test_cli_basic_units.py +0 -0
  19. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/tests/test_documentation_examples.py +0 -0
  20. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/tests/test_fama_suite_bdd.py +0 -0
  21. {flamapy-2.6.0.dev0 → flamapy-2.6.0.dev2}/tests/test_fama_suite_sat.py +0 -0
@@ -1,18 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flamapy
3
- Version: 2.6.0.dev0
3
+ Version: 2.6.0.dev2
4
4
  Summary: Flamapy feature model is a distribution of the flama framework containing all plugins required to analyze feature models. It also offers a richer API and a complete command line interface and documentation.
5
5
  Author-email: Flamapy <flamapy@us.es>
6
6
  License-Expression: GPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/flamapy/flamapy-feature-model
8
- Requires-Python: >=3.9
8
+ Requires-Python: >=3.11
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: flamapy-fw~=2.6.0.dev0
12
- Requires-Dist: flamapy-fm~=2.6.0.dev0
13
- Requires-Dist: flamapy-sat~=2.6.0.dev0
14
- Requires-Dist: flamapy-bdd~=2.6.0.dev0
15
- Requires-Dist: flamapy-z3~=2.6.0.dev0
11
+ Requires-Dist: flamapy-fw~=2.6.0.dev2
12
+ Requires-Dist: flamapy-fm~=2.6.0.dev2
13
+ Requires-Dist: flamapy-sat~=2.6.0.dev2
14
+ Requires-Dist: flamapy-bdd~=2.6.0.dev2
15
+ Requires-Dist: flamapy-z3~=2.6.0.dev2
16
16
  Provides-Extra: dev
17
17
  Requires-Dist: pytest; extra == "dev"
18
18
  Requires-Dist: pytest-mock; extra == "dev"
@@ -5,13 +5,13 @@ from functools import wraps
5
5
  import inspect
6
6
  from pathlib import Path
7
7
  from shutil import copytree
8
- from typing import List, Tuple, Optional
8
+ from typing import Any, List, Tuple, Optional
9
9
  from types import FunctionType
10
10
 
11
11
  from flamapy.interfaces.python.flamapy_feature_model import FLAMAFeatureModel
12
12
 
13
13
  # List to store registered commands and their arguments
14
- MANUAL_COMMANDS = []
14
+ MANUAL_COMMANDS: List[Tuple[str, str, FunctionType, Tuple[Any, ...]]] = []
15
15
 
16
16
 
17
17
  def command(name, description, *args): # type: ignore
@@ -1,6 +1,8 @@
1
1
  import logging
2
- from typing import Any, Dict, Generator, List, Optional, Tuple, Union
2
+ from enum import Enum
3
+ from typing import Any, Dict, List, Optional, Tuple, Union
3
4
  from flamapy.core.discover import DiscoverMetamodels
5
+ from flamapy.core.models import VariabilityModel
4
6
  from flamapy.metamodels.fm_metamodel.models import FeatureModel
5
7
  from flamapy.core.exceptions import FlamaException
6
8
  from flamapy.metamodels.configuration_metamodel.models import Configuration
@@ -8,11 +10,31 @@ from flamapy.metamodels.configuration_metamodel.models import Configuration
8
10
  logger = logging.getLogger(__name__)
9
11
 
10
12
 
13
+ class Backend(str, Enum):
14
+ """Analysis backend (plugin) used to run a facade operation."""
15
+
16
+ SAT = "sat"
17
+ BDD = "bdd"
18
+ Z3 = "z3"
19
+
20
+
11
21
  class FLAMAFeatureModel:
12
- def __init__(self, model_path: str):
22
+ # Backend -> (lazy transformation method, attribute caching the model)
23
+ _BACKENDS = {
24
+ Backend.SAT: ("_transform_to_sat", "sat_model"),
25
+ Backend.BDD: ("_transform_to_bdd", "bdd_model"),
26
+ Backend.Z3: ("_transform_to_z3", "z3_model"),
27
+ }
28
+
29
+ def __init__(self, model_path: str, backend: Optional[str] = None):
13
30
  """
14
31
  This is the path in the filesystem where the model is located.
15
- Any model in UVL, FaMaXML or FeatureIDE format are accepted
32
+ Any model in UVL, FaMaXML or FeatureIDE format are accepted.
33
+
34
+ ``backend`` optionally sets a workspace-wide default backend
35
+ ("sat", "bdd" or "z3") for every operation that supports more than
36
+ one. When left as ``None`` each operation keeps its own historical
37
+ default, so existing code behaves exactly as before.
16
38
  """
17
39
  self.model_path = model_path
18
40
  self.discover_metamodel = DiscoverMetamodels()
@@ -21,10 +43,35 @@ class FLAMAFeatureModel:
21
43
  self.bdd_model = None
22
44
  self.z3_model = None
23
45
  self.diagnosis_model = None
46
+ self.backend = Backend(backend) if backend is not None else None
24
47
 
25
48
  def _read(self, model_path: str) -> FeatureModel:
26
49
  return self.discover_metamodel.use_transformation_t2m(model_path, "fm")
27
50
 
51
+ def _select_backend(
52
+ self,
53
+ backend: Optional[str],
54
+ default: Backend,
55
+ ) -> VariabilityModel:
56
+ """Resolve the backend to use, lazily transform to it and return its model.
57
+
58
+ Resolution order: explicit per-call ``backend`` > workspace-wide
59
+ ``self.backend`` > the operation's historical ``default``. All
60
+ backends (sat, bdd, z3) are accepted; if the chosen one does not
61
+ implement the requested operation a FlamaException is raised when it
62
+ runs.
63
+ """
64
+ try:
65
+ chosen = Backend(backend) if backend is not None else (self.backend or default)
66
+ except ValueError:
67
+ raise FlamaException(
68
+ f"Unknown backend '{backend}'. "
69
+ f"Choose from {[b.value for b in Backend]}."
70
+ )
71
+ transform, attr = self._BACKENDS[chosen]
72
+ getattr(self, transform)()
73
+ return getattr(self, attr)
74
+
28
75
  def _transform_to_sat(self) -> None:
29
76
  if self.sat_model is None:
30
77
  self.sat_model = self.discover_metamodel.use_transformation_m2m(self.fm_model, "pysat")
@@ -198,55 +245,49 @@ class FLAMAFeatureModel:
198
245
  return None
199
246
 
200
247
  # The methods above rely on sat to be executed.
201
- def core_features(self) -> Union[None, List[str]]:
248
+ def core_features(self, backend: Optional[str] = None) -> Union[None, List[str]]:
202
249
  """
203
250
  These are the features that are present in all products of a product line.
204
251
  In a feature model, they are the features that are mandatory and not optional.
205
252
  Core features define the commonality among all products in a product line.
206
- This call requires sat to be called, however, there is an implementation within
207
- flamapy that does not requires sat. please use the framework in case of needing it.
253
+
254
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
208
255
  """
209
256
  try:
210
- self._transform_to_sat()
211
- features = self.discover_metamodel.use_operation(
212
- self.sat_model, "PySATCoreFeatures"
213
- ).get_result()
214
- return features
257
+ model = self._select_backend(backend, Backend.SAT)
258
+ return self.discover_metamodel.use_operation(model, "CoreFeatures").get_result()
215
259
  except FlamaException as exception:
216
260
  logger.error("Error: %s", exception)
217
261
  return None
218
262
 
219
- def dead_features(self) -> Union[None, List[str]]:
263
+ def dead_features(self, backend: Optional[str] = None) -> Union[None, List[str]]:
220
264
  """
221
265
  These are features that, due to the constraints and dependencies in the
222
266
  feature model, cannot be included in any valid product. Dead features are usually
223
267
  a sign of an error in the feature model.
268
+
269
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
224
270
  """
225
271
  try:
226
- self._transform_to_sat()
227
- features = self.discover_metamodel.use_operation(
228
- self.sat_model, "PySATDeadFeatures"
229
- ).get_result()
230
- return features
272
+ model = self._select_backend(backend, Backend.SAT)
273
+ return self.discover_metamodel.use_operation(model, "DeadFeatures").get_result()
231
274
  except FlamaException as exception:
232
275
  logger.error("Error: %s", exception)
233
276
  return None
234
277
 
235
- def false_optional_features(self) -> Union[None, List[str]]:
278
+ def false_optional_features(self, backend: Optional[str] = None) -> Union[None, List[str]]:
236
279
  """
237
280
  These are features that appear to be optional in the feature model, but due to the
238
281
  constraints and dependencies, must be included in every valid product. Like dead features,
239
282
  false optional features are usually a sign of an error in the feature model.
283
+
284
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
240
285
  """
241
286
  try:
242
- self._transform_to_sat()
243
- operation = self.discover_metamodel.get_operation(
244
- self.sat_model, "PySATFalseOptionalFeatures"
245
- )
246
- operation.feature_model = self.fm_model
247
- operation.execute(self.sat_model)
248
- features = operation.get_result()
249
- return features
287
+ model = self._select_backend(backend, Backend.SAT)
288
+ return self.discover_metamodel.use_operation(
289
+ model, "FalseOptionalFeatures"
290
+ ).get_result()
250
291
  except FlamaException as exception:
251
292
  logger.error("Error: %s", exception)
252
293
  return None
@@ -271,49 +312,35 @@ class FLAMAFeatureModel:
271
312
  logger.error("Error: %s", exception)
272
313
  return None
273
314
 
274
- def configurations_number(self, with_sat: bool = False) -> Union[None, int]:
315
+ def configurations_number(self, backend: Optional[str] = None) -> Union[None, int]:
275
316
  """
276
317
  This is the total number of different full configurations that can be
277
318
  produced from a feature model. It's calculated by considering all possible
278
319
  combinations of features, taking into account the constraints and
279
320
  dependencies between features.
321
+
322
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to bdd.
280
323
  """
281
324
  try:
282
- nop = 0
283
- if with_sat:
284
- self._transform_to_sat()
285
- nop = self.discover_metamodel.use_operation(
286
- self.sat_model, "PySATConfigurationsNumber"
287
- ).get_result()
288
- else:
289
- self._transform_to_bdd()
290
- nop = self.discover_metamodel.use_operation(
291
- self.bdd_model, "BDDConfigurationsNumber"
292
- ).get_result()
293
- return nop
325
+ model = self._select_backend(backend, Backend.BDD)
326
+ return self.discover_metamodel.use_operation(
327
+ model, "ConfigurationsNumber"
328
+ ).get_result()
294
329
  except FlamaException as exception:
295
330
  logger.error("Error: %s", exception)
296
331
  return None
297
332
 
298
- def configurations(self, with_sat: bool = False) -> Union[None, List[Configuration]]:
333
+ def configurations(self, backend: Optional[str] = None) -> Union[None, List[Configuration]]:
299
334
  """
300
335
  These are the individual outcomes that can be produced from a feature model. Each product
301
336
  is a combination of features that satisfies all the constraints and dependencies in the
302
337
  feature model.
338
+
339
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to bdd.
303
340
  """
304
341
  try:
305
- products = []
306
- if with_sat:
307
- self._transform_to_sat()
308
- products = self.discover_metamodel.use_operation(
309
- self.sat_model, "PySATConfigurations"
310
- ).get_result()
311
- else:
312
- self._transform_to_bdd()
313
- products = self.discover_metamodel.use_operation(
314
- self.bdd_model, "BDDConfigurations"
315
- ).get_result()
316
- return products
342
+ model = self._select_backend(backend, Backend.BDD)
343
+ return self.discover_metamodel.use_operation(model, "Configurations").get_result()
317
344
  except FlamaException as exception:
318
345
  logger.error("Error: %s", exception)
319
346
  return None
@@ -339,46 +366,46 @@ class FLAMAFeatureModel:
339
366
  return None
340
367
 
341
368
  def satisfiable_configuration(
342
- self, configuration_path: str, full_configuration: bool = False
369
+ self,
370
+ configuration_path: str,
371
+ full_configuration: bool = False,
372
+ backend: Optional[str] = None,
343
373
  ) -> Union[None, bool]:
344
374
  """
345
375
  This is a product that is produced from a valid configuration of features. A valid
346
376
  product satisfies all the constraints and dependencies in the feature model.
377
+
378
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
347
379
  """
348
380
  try:
349
- self._transform_to_sat()
381
+ model = self._select_backend(backend, Backend.SAT)
350
382
  configuration = self.discover_metamodel.use_transformation_t2m(
351
383
  configuration_path, "configuration"
352
384
  )
353
385
  operation = self.discover_metamodel.get_operation(
354
- self.sat_model, "PySATSatisfiableConfiguration"
386
+ model, "SatisfiableConfiguration"
355
387
  )
356
388
 
357
- if full_configuration:
358
- configuration.is_full = True
359
- else:
360
- configuration.is_full = False
389
+ configuration.is_full = bool(full_configuration)
361
390
 
362
391
  operation.set_configuration(configuration)
363
- operation.execute(self.sat_model)
364
- result = operation.get_result()
365
- return result
392
+ operation.execute(model)
393
+ return operation.get_result()
366
394
  except FlamaException as exception:
367
395
  logger.error("Error: %s", exception)
368
396
  return None
369
397
 
370
- def satisfiable(self) -> Union[None, bool]:
398
+ def satisfiable(self, backend: Optional[str] = None) -> Union[None, bool]:
371
399
  """
372
400
  In the context of feature models, this usually refers to whether the feature model itself
373
401
  satisfies all the constraints and dependencies. A a valid feature model is one that
374
402
  does encodes at least a single valid product.
403
+
404
+ ``backend`` selects the analysis plugin ("sat", "bdd" or "z3"); defaults to sat.
375
405
  """
376
406
  try:
377
- self._transform_to_sat()
378
- result = self.discover_metamodel.use_operation(
379
- self.sat_model, "PySATSatisfiable"
380
- ).get_result()
381
- return result
407
+ model = self._select_backend(backend, Backend.SAT)
408
+ return self.discover_metamodel.use_operation(model, "Satisfiable").get_result()
382
409
  except FlamaException as exception:
383
410
  logger.error("Error: %s", exception)
384
411
  return None
@@ -551,18 +578,21 @@ class FLAMAFeatureModel:
551
578
  return None
552
579
 
553
580
  def sampling(
554
- self, size: int, with_replacement: bool = False
581
+ self, size: int, with_replacement: bool = False, backend: Optional[str] = None
555
582
  ) -> Union[None, List[Configuration]]:
556
583
  """
557
584
  Returns a random sample of valid configurations of the given size. When
558
585
  with_replacement is True, the same configuration may appear more than once.
586
+
587
+ ``backend`` selects the analysis plugin; defaults to bdd. Only sat and bdd
588
+ implement sampling.
559
589
  """
560
590
  try:
561
- self._transform_to_bdd()
562
- operation = self.discover_metamodel.get_operation(self.bdd_model, "BDDSampling")
591
+ model = self._select_backend(backend, Backend.BDD)
592
+ operation = self.discover_metamodel.get_operation(model, "Sampling")
563
593
  operation.set_sample_size(size)
564
594
  operation.set_with_replacement(with_replacement)
565
- operation.execute(self.bdd_model)
595
+ operation.execute(model)
566
596
  return operation.get_result()
567
597
  except FlamaException as exception:
568
598
  logger.error("Error: %s", exception)
@@ -1,18 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flamapy
3
- Version: 2.6.0.dev0
3
+ Version: 2.6.0.dev2
4
4
  Summary: Flamapy feature model is a distribution of the flama framework containing all plugins required to analyze feature models. It also offers a richer API and a complete command line interface and documentation.
5
5
  Author-email: Flamapy <flamapy@us.es>
6
6
  License-Expression: GPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/flamapy/flamapy-feature-model
8
- Requires-Python: >=3.9
8
+ Requires-Python: >=3.11
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: flamapy-fw~=2.6.0.dev0
12
- Requires-Dist: flamapy-fm~=2.6.0.dev0
13
- Requires-Dist: flamapy-sat~=2.6.0.dev0
14
- Requires-Dist: flamapy-bdd~=2.6.0.dev0
15
- Requires-Dist: flamapy-z3~=2.6.0.dev0
11
+ Requires-Dist: flamapy-fw~=2.6.0.dev2
12
+ Requires-Dist: flamapy-fm~=2.6.0.dev2
13
+ Requires-Dist: flamapy-sat~=2.6.0.dev2
14
+ Requires-Dist: flamapy-bdd~=2.6.0.dev2
15
+ Requires-Dist: flamapy-z3~=2.6.0.dev2
16
16
  Provides-Extra: dev
17
17
  Requires-Dist: pytest; extra == "dev"
18
18
  Requires-Dist: pytest-mock; extra == "dev"
@@ -0,0 +1,12 @@
1
+ flamapy-fw~=2.6.0.dev2
2
+ flamapy-fm~=2.6.0.dev2
3
+ flamapy-sat~=2.6.0.dev2
4
+ flamapy-bdd~=2.6.0.dev2
5
+ flamapy-z3~=2.6.0.dev2
6
+
7
+ [dev]
8
+ pytest
9
+ pytest-mock
10
+ prospector
11
+ mypy
12
+ coverage
@@ -4,18 +4,18 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flamapy"
7
- version = "2.6.0.dev0"
7
+ version = "2.6.0.dev2"
8
8
  description = "Flamapy feature model is a distribution of the flama framework containing all plugins required to analyze feature models. It also offers a richer API and a complete command line interface and documentation."
9
9
  readme = "README.md"
10
10
  license = "GPL-3.0-or-later"
11
11
  authors = [{ name = "Flamapy", email = "flamapy@us.es" }]
12
- requires-python = ">=3.9"
12
+ requires-python = ">=3.11"
13
13
  dependencies = [
14
- "flamapy-fw~=2.6.0.dev0",
15
- "flamapy-fm~=2.6.0.dev0",
16
- "flamapy-sat~=2.6.0.dev0",
17
- "flamapy-bdd~=2.6.0.dev0",
18
- "flamapy-z3~=2.6.0.dev0",
14
+ "flamapy-fw~=2.6.0.dev2",
15
+ "flamapy-fm~=2.6.0.dev2",
16
+ "flamapy-sat~=2.6.0.dev2",
17
+ "flamapy-bdd~=2.6.0.dev2",
18
+ "flamapy-z3~=2.6.0.dev2",
19
19
  ]
20
20
 
21
21
  [project.optional-dependencies]
@@ -38,7 +38,7 @@ include = ["flamapy.*"]
38
38
 
39
39
  [tool.ruff]
40
40
  line-length = 100 # Matches max-line-length from pycodestyle
41
- target-version = "py39"
41
+ target-version = "py311"
42
42
 
43
43
  [tool.ruff.lint]
44
44
  select = ["E", "W", "F", "C", "PL", "RUF"] # Includes pycodestyle, pylint, and McCabe complexity
@@ -1,12 +0,0 @@
1
- flamapy-fw~=2.6.0.dev0
2
- flamapy-fm~=2.6.0.dev0
3
- flamapy-sat~=2.6.0.dev0
4
- flamapy-bdd~=2.6.0.dev0
5
- flamapy-z3~=2.6.0.dev0
6
-
7
- [dev]
8
- pytest
9
- pytest-mock
10
- prospector
11
- mypy
12
- coverage
File without changes
File without changes
File without changes
File without changes