# The pydop Library
**pydop** is a small python library for Delta-Oriented Software Product Lines development, that provides classes for
- feature models
- modular and transformation-based SPL (a generalization of Delta-Oriented Programming)
- modular Multi-SPL
- standard transformation operations, like the addition or the removal of a field or a method in a class
### Installation
This library is available using the `pip` command:
```bash
$ pip install pydop
```
Alternatively, it is possible to simply clone this repository:
this library is implemented in pure python and only depends on [networkx](https://networkx.org/):
installing networkx using `pip` and cloning this repository is enough to install it:
```bash
$ pip install networkx
$ git clone https://github.com/onera/pydop.git
```
### An Hello World Example
This example is taken from [[1]](#1) and consists of a *Multi-Lingual Hello World Product Line*,
i.e., an SPL that generates a class named **Greater** that can say *Hello World* in multiple languages, and possibly multiple times.
In pydop, a product line is split in 5 parts:
- the feature model, that states the variability of the SPL
- the base artifact factory, that generates the initial artifact for every variant generation
- the SPL object itself
- a list of deltas, that are python functions that modify the base artifact
- the configuration knowledge, that links the deltas to the SPL by stating which delta execute for which combination of features, and in which order
#### Part 0: the imports
In this Example, we have 5 imports:
```python
from pydop.fm_constraint import *
```
This line loads all the classes to declare cross-tree constraints, i.e., boolean constraints over feature names.
```python
from pydop.fm_diagram import *
```
This line loads all the classes used to declare a feature model.
```python
from pydop.spl import SPL, RegistryGraph
```
This line loads the `SPL` class and `RegistryGraph`.
The later is a class that allows to specify the ordering between delta as a generic dependency graph.
```python
from pydop.operations.modules import *
```
This line loads all the basic delta operation on standard python objects (including classes and modules)
```python
import sys
```
This line loads the `sys` module, to access `sys.exit` in case of problems.
#### Part 1: the Feature Model
```python
fm = FD("HelloWorld",
FDMandatory(
FD("Language",
FDAlternative(
FD("English"),
FD("Dutch"),
FD("German")
))),
FDOptional(
FD("Repeat", times=Int(0,1000))
))
```
Here, `FD` introduces a new feature.
Hence the root feature of this Feature Model is `HelloWorld`, with one mandatory feature `Language`
(with the languages `English`, `Dutch` and `German` available);
and with one optional feature `Repeat`.
That last feature has an attribute `times`, that states that the number of repetition must be in the interval `[0, 1000[`.
#### Part 2: the Base Artifact Factory
```python
def gen_base_artifact():
class Greeter(object):
def sayHello(self): return "Hello World"
return Greeter
```
The factory is a python function with no parameter, that returns the initial artifact of a variant generation process.
In our case, the initial variant is the `Greeter` class with the method `sayHello` that returns `"Hello World"` said in english.
#### Part 3: the SPL Object
```python
spl = SPL(fm, RegistryGraph(), gen_base_artifact)
```
The `SPL` constructor takes three parameters:
- the feature model of the SPL
- an object stating how the ordering between delta can be specified (in our case, that object is an instance of the `RegistryGraph` class)
- and the base artifact factory
**Note:** it is possible in pydop to use a *Pure* Delta-Oriented Programming approach [[2]](#2) by putting `None` in place of the base artifact factory.
In that case, the parameter of the first delta being executed will be `None`, and this delta would be in charge of providing the base artifact.
#### Part 4: the Deltas
```python
def Nl(Greeter):
@modify(Greeter)
def sayHello(self): return "Hallo wereld"
def De(Greeter):
@modify(Greeter)
def sayHello(self): return "Hallo Welt"
def Rpt(Greeter, product):
@modify(Greeter)
def sayHello(self):
tmp_str = self.original()
return " ".join(tmp_str for _ in range(product["times"]))
```
Deltas are python functions that modify the base artifact to construct the expected variant.
The first delta implements the `Dutch` feature.
It is a function that takes one argument: the artifact to be modified.
This delta modifies that artifact (in our case, the class `Greeter`) by replacing its `sayHello` method and making it return `"hello World` in dutch.
The second delta implements the `German` feature and is implemented like the `Dutch` one.
The third delta implements the `Repeat` feature and takes two parameters:
the first one is the artifact to be modified (like for the two previous deltas),
and the second one is the product that triggered the variant generation.
This delta uses this additional argument to retrieve the number of repetition requested by the user, with `product["times"]` in the last line.
#### Part 5: the Configuration Knowledge
```python
spl.delta("Dutch")(Nl)
spl.delta("German")(De)
spl.delta("Repeat", after=["Nl", "De"])(Rpt)
```
This Configuration Knowledge (CK) simply states that
the `Nl` delta is activated by the `Dutch` feature,
the `De` delta is activated by the `German` feature,
and the `Rpt` delta is activated by the `Repeat` feature.
Moreover, the statement `after=["Nl", "De"]` means that the `Nl` delta cannot be executed before the `Nl` or the `De` delta,
to ensure that the repeated sentence is the correct one.
**Note:** it is possible in pydop to declare the CK together with the delta.
In this case, the delta declaration would look like
```python
@spl.delta("Dutch")
def Nl(Greeter): ...
@spl.delta("German")
def De(Greeter): ...
@spl.delta("Repeat", after=["Nl", "De"])
def Rpt(Greeter, product): ...
```
#### Using the SPL
Now that the SPL is constructed, we can generate variant right away.
For instance, the following generates a variant corresponding to selecting the `Dutch` language, with 4 repetition,
and calls the `sayHello` method on an instance of that variant:
```python
# 1. create a full configuration from a partial specification of features
conf, err = spl.close_configuration({"Dutch": True, "Repeat": True, "times": 4})
if(err): # possibly the partial specification was erroneous
print(err); sys.exit(-1)
# 2. create the variant by simply calling the SPL with the configuration in parameter
Greater = spl(conf)
# 3. use the variant
print(Greater().sayHello())
```
Moreover, other variants can also be created in the same python program, like this second one, with the `German` language selected and no repetition:
```python
conf, err = spl.close_configuration({"German": True, "Repeat": False})
if(err):
print(err); sys.exit(-1)
Greater = spl(conf)
print(Greater().sayHello())
```
### Other Examples
Other examples are available in the [examples](https://github.com/onera/pydop/tree/master/examples) folder.
## References
<a name="1">[1]</a>
Dave Clarke, Radu Muschevici, José Proença, Ina Schaefer, and Rudolf Schlatte.
2010. Variability Modelling in the ABS Language.
In FMCO (LNCS, Vol. 6957). Springer, 204–224.
*doi: 10.1007/978-3-642-25271-6_11*
<a name="2">[2]</a>
Ina Schaefer, and Ferruccio Damiani.
2010. Pure delta-oriented programming.
In FOSD'10. ACM, 49--56.
*doi: 10.1145/1868688.1868696*
Raw data
{
"_id": null,
"home_page": "",
"name": "pydop",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "",
"keywords": "Software Product Lines,Multi-Software Product Lines,Delta-Oriented Programming,Variability,Software Engineering",
"author": "",
"author_email": "Michael Lienhardt <michael.lienhardt@onera.fr>",
"download_url": "https://files.pythonhosted.org/packages/f6/38/be8c5a938e75888b2388e09583c3cdf8a9c7fcb604fc9e498b15500c503f/pydop-1.0.tar.gz",
"platform": "Linux",
"description": "\n# The pydop Library\n\n\n**pydop** is a small python library for Delta-Oriented Software Product Lines development, that provides classes for\n - feature models\n - modular and transformation-based SPL (a generalization of Delta-Oriented Programming)\n - modular Multi-SPL\n - standard transformation operations, like the addition or the removal of a field or a method in a class\n\n### Installation\n\nThis library is available using the `pip` command:\n```bash\n$ pip install pydop\n```\n\nAlternatively, it is possible to simply clone this repository:\n this library is implemented in pure python and only depends on [networkx](https://networkx.org/):\n installing networkx using `pip` and cloning this repository is enough to install it:\n```bash\n$ pip install networkx\n$ git clone https://github.com/onera/pydop.git\n```\n\n### An Hello World Example\n\nThis example is taken from [[1]](#1) and consists of a *Multi-Lingual Hello World Product Line*, \n i.e., an SPL that generates a class named **Greater** that can say *Hello World* in multiple languages, and possibly multiple times.\n\nIn pydop, a product line is split in 5 parts:\n - the feature model, that states the variability of the SPL\n - the base artifact factory, that generates the initial artifact for every variant generation\n - the SPL object itself\n - a list of deltas, that are python functions that modify the base artifact\n - the configuration knowledge, that links the deltas to the SPL by stating which delta execute for which combination of features, and in which order\n\n#### Part 0: the imports\n\nIn this Example, we have 5 imports:\n```python\nfrom pydop.fm_constraint import *\n```\nThis line loads all the classes to declare cross-tree constraints, i.e., boolean constraints over feature names.\n```python\nfrom pydop.fm_diagram import *\n```\nThis line loads all the classes used to declare a feature model.\n```python\nfrom pydop.spl import SPL, RegistryGraph\n```\nThis line loads the `SPL` class and `RegistryGraph`.\nThe later is a class that allows to specify the ordering between delta as a generic dependency graph.\n```python\nfrom pydop.operations.modules import *\n```\nThis line loads all the basic delta operation on standard python objects (including classes and modules)\n```python\nimport sys\n```\nThis line loads the `sys` module, to access `sys.exit` in case of problems.\n\n\n#### Part 1: the Feature Model\n\n```python\nfm = FD(\"HelloWorld\",\n FDMandatory(\n FD(\"Language\",\n FDAlternative(\n FD(\"English\"),\n FD(\"Dutch\"),\n FD(\"German\")\n ))),\n FDOptional(\n FD(\"Repeat\", times=Int(0,1000))\n))\n```\n\nHere, `FD` introduces a new feature.\nHence the root feature of this Feature Model is `HelloWorld`, with one mandatory feature `Language`\n (with the languages `English`, `Dutch` and `German` available);\n and with one optional feature `Repeat`.\nThat last feature has an attribute `times`, that states that the number of repetition must be in the interval `[0, 1000[`.\n\n#### Part 2: the Base Artifact Factory\n\n```python\ndef gen_base_artifact():\n class Greeter(object):\n def sayHello(self): return \"Hello World\"\n return Greeter\n```\n\nThe factory is a python function with no parameter, that returns the initial artifact of a variant generation process.\nIn our case, the initial variant is the `Greeter` class with the method `sayHello` that returns `\"Hello World\"` said in english.\n\n#### Part 3: the SPL Object\n\n```python\nspl = SPL(fm, RegistryGraph(), gen_base_artifact)\n```\n\nThe `SPL` constructor takes three parameters:\n - the feature model of the SPL\n - an object stating how the ordering between delta can be specified (in our case, that object is an instance of the `RegistryGraph` class)\n - and the base artifact factory\n\n\n**Note:** it is possible in pydop to use a *Pure* Delta-Oriented Programming approach [[2]](#2) by putting `None` in place of the base artifact factory.\nIn that case, the parameter of the first delta being executed will be `None`, and this delta would be in charge of providing the base artifact.\n\n\n#### Part 4: the Deltas\n\n```python\ndef Nl(Greeter):\n @modify(Greeter)\n def sayHello(self): return \"Hallo wereld\"\n\ndef De(Greeter):\n @modify(Greeter)\n def sayHello(self): return \"Hallo Welt\"\n\ndef Rpt(Greeter, product):\n @modify(Greeter)\n def sayHello(self):\n tmp_str = self.original()\n return \" \".join(tmp_str for _ in range(product[\"times\"]))\n```\n\nDeltas are python functions that modify the base artifact to construct the expected variant.\nThe first delta implements the `Dutch` feature.\nIt is a function that takes one argument: the artifact to be modified.\nThis delta modifies that artifact (in our case, the class `Greeter`) by replacing its `sayHello` method and making it return `\"hello World` in dutch.\nThe second delta implements the `German` feature and is implemented like the `Dutch` one.\nThe third delta implements the `Repeat` feature and takes two parameters:\n the first one is the artifact to be modified (like for the two previous deltas),\n and the second one is the product that triggered the variant generation.\nThis delta uses this additional argument to retrieve the number of repetition requested by the user, with `product[\"times\"]` in the last line.\n\n#### Part 5: the Configuration Knowledge\n\n```python\nspl.delta(\"Dutch\")(Nl)\nspl.delta(\"German\")(De)\nspl.delta(\"Repeat\", after=[\"Nl\", \"De\"])(Rpt)\n```\n\nThis Configuration Knowledge (CK) simply states that\n the `Nl` delta is activated by the `Dutch` feature,\n the `De` delta is activated by the `German` feature,\n and the `Rpt` delta is activated by the `Repeat` feature.\nMoreover, the statement `after=[\"Nl\", \"De\"]` means that the `Nl` delta cannot be executed before the `Nl` or the `De` delta,\n to ensure that the repeated sentence is the correct one.\n\n\n**Note:** it is possible in pydop to declare the CK together with the delta.\nIn this case, the delta declaration would look like\n\n```python\n@spl.delta(\"Dutch\")\ndef Nl(Greeter): ...\n\n@spl.delta(\"German\")\ndef De(Greeter): ...\n\n@spl.delta(\"Repeat\", after=[\"Nl\", \"De\"])\ndef Rpt(Greeter, product): ...\n```\n\n#### Using the SPL\n\nNow that the SPL is constructed, we can generate variant right away.\nFor instance, the following generates a variant corresponding to selecting the `Dutch` language, with 4 repetition,\n and calls the `sayHello` method on an instance of that variant:\n\n```python\n# 1. create a full configuration from a partial specification of features\nconf, err = spl.close_configuration({\"Dutch\": True, \"Repeat\": True, \"times\": 4})\nif(err): # possibly the partial specification was erroneous\n print(err); sys.exit(-1)\n\n# 2. create the variant by simply calling the SPL with the configuration in parameter\nGreater = spl(conf)\n\n# 3. use the variant\nprint(Greater().sayHello())\n```\n\nMoreover, other variants can also be created in the same python program, like this second one, with the `German` language selected and no repetition:\n\n```python\nconf, err = spl.close_configuration({\"German\": True, \"Repeat\": False})\nif(err):\n print(err); sys.exit(-1)\n\nGreater = spl(conf)\nprint(Greater().sayHello())\n```\n\n### Other Examples\n\nOther examples are available in the [examples](https://github.com/onera/pydop/tree/master/examples) folder.\n\n\n\n## References\n\n<a name=\"1\">[1]</a> \nDave Clarke, Radu Muschevici, Jos\u00e9 Proen\u00e7a, Ina Schaefer, and Rudolf Schlatte.\n2010. Variability Modelling in the ABS Language.\nIn FMCO (LNCS, Vol. 6957). Springer, 204\u2013224.\n*doi: 10.1007/978-3-642-25271-6_11*\n\n<a name=\"2\">[2]</a>\nIna Schaefer, and Ferruccio Damiani.\n2010. Pure delta-oriented programming.\nIn FOSD'10. ACM, 49--56.\n*doi: 10.1145/1868688.1868696*\n\n",
"bugtrack_url": null,
"license": "",
"summary": "Python package for the creation of delta-oriented Software Product Lines.",
"version": "1.0",
"project_urls": {
"Bug Tracker": "https://github.com/onera/pydop/issues",
"Documentation": "https://github.com/onera/pydop",
"Homepage": "https://github.com/onera/pydop",
"Source Code": "https://github.com/onera/pydop"
},
"split_keywords": [
"software product lines",
"multi-software product lines",
"delta-oriented programming",
"variability",
"software engineering"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "64e1d8f778f22b6c48bc377caec06bfae8c5c8ef22260bd20aa8fee1f2959296",
"md5": "c7777a850886062117ef92d77fb57225",
"sha256": "51527fe08d931c5083671c6146be073388e163ca1469d9e415a87adba90e3be6"
},
"downloads": -1,
"filename": "pydop-1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c7777a850886062117ef92d77fb57225",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 37664,
"upload_time": "2024-03-07T01:44:12",
"upload_time_iso_8601": "2024-03-07T01:44:12.388674Z",
"url": "https://files.pythonhosted.org/packages/64/e1/d8f778f22b6c48bc377caec06bfae8c5c8ef22260bd20aa8fee1f2959296/pydop-1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "f638be8c5a938e75888b2388e09583c3cdf8a9c7fcb604fc9e498b15500c503f",
"md5": "91b3656be382b9eb64008c52ba2f7e84",
"sha256": "099bd0cf73702538b9df3576f9ebc7601a914cb8bd13b2bf32abc13774d35dbf"
},
"downloads": -1,
"filename": "pydop-1.0.tar.gz",
"has_sig": false,
"md5_digest": "91b3656be382b9eb64008c52ba2f7e84",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 39656,
"upload_time": "2024-03-07T01:44:14",
"upload_time_iso_8601": "2024-03-07T01:44:14.633283Z",
"url": "https://files.pythonhosted.org/packages/f6/38/be8c5a938e75888b2388e09583c3cdf8a9c7fcb604fc9e498b15500c503f/pydop-1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-03-07 01:44:14",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "onera",
"github_project": "pydop",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "pydop"
}