Skip to main content

M2M and M2T Models Transformations framework for PyEcore

Project description

Motra is a librairie providing model transformations facilities to PyEcore. The goal of the librairie is to propose a set of embedded DSLs in Python for models to models transformations (M2M) and model to text transformations (M2T) with advanced traceability mechanism. Here are some characteristics about Motra M2M:

  • it proposes a semantic close to QVTo, imperative, based on mappings where the execution order is defined by the developer in each mapping,

  • it supports multiple input and multiple outputs,

  • each mapping result is cached and when a mapping is called twice with a same set of parameters, the exact same created object is returned,

  • by default, any object created in a mapping that is not explicitaly placed in a container is automatically added as model root,

  • used metamodels are automatically registered for smooth load/save of any models.

  • it supports mapping polymorphism without having to rely on manual coding a dispatch with a disjunct (if the mappings own the same name)

Documentation

WIP, at the moment, please refer to transformations examples in examples. To avoid the need to load/install special metamodels, all the transformations examples are given directly over Ecore. The transformations are gathered in simple modules depending to their characteristics: in-place, in-out, endogenous or exogenous.

M2M Quick start

Each transformation must be defined in it’s own Python module (even if multiple transformations can be defined in one module).

# import the input and output metamodels
import ghmde  # based on https://github.com/kolovos/datasets/blob/master/github-mde/ghmde.ecore
import graph  # based on a simple graph metamodel

# import motra for utils and for M2M transformation definition
import motra
from motra import m2m

# M2M transformation "signature" definition
ghmde2graph = m2m.Transformation('ghmde2graph',
                                 inputs=['ghmde_model'],
                                 outputs=['graph_model'])


# defines the entry point of the transformation
@ghmde2graph.main
def main(ghmde_model, graph_model):
    print('Transforming repository to graph', graph_model)
    for f in motra.objects_of_kind(ghmde_model, ghmde.File):
        file2node(f)
    for repository in motra.objects_of_kind(ghmde_model, ghmde.Repository):
        repository2graph(repository, postfix='_graph')
    # m2m.objects_of_kind

# defines a first mapping transforming Files in Node
@ghmde2graph.mapping
def file2node(self: ghmde.File) -> Node:
    result.name = self.path   # The "result" variable is automatically created and injected in the current context


# defines a conditional mapping from Repository to Graph
def does_not_starts_with(self, postfix):
    return not self.name.startswith(postfix)

@ghmde2graph.mapping(when=does_not_starts_with)
def repository2graph(self: ghmde.Repository, postfix: str) -> Graph:
    result.name = self.name + postfix
    for repo_file in self.files:
        result.nodes.append(file2node(repo_file))

Then, it can be imported and directly used from another module. Currently, there is no default runner, but there will be in the future, a way of defining models transformations chains.

# Import the transformation
from transfo_example import ghmde2graph

# Just run it. Input can be a "Resource" or directly a file
result_context = ghmde2graph.run(ghmde_model="input_model.xmi")*

# A result context gives access to:
# * the inputs
# * the outputs
# * the execution trace (still WIP)
# * the transformation definition
# * the used resource set for this transformation
result_context.inputs.ghmde_model.save(output="input_copy.xmi")
result_context.outputs.graph_model.save(output="test.xmi")

M2T Quick start

As for M2M, a M2T transformation must be defined in it’s own Python module (even if multiple transformations can be defined in one module as defined in the module examples/m2t/sample.py). Each template code is written as __doc__ of template functions.

from motra import m2t
import pyecore.ecore as ecore

# M2T transformation "signature" definition
ecore2simplejava = m2t.Transformation("ecore2simplejava")

# Definition of the main entry point.
# At the moment, entry-point cannot have "when=" parameter
# The special <%motra:file ><%/motra:file> is used to specify blocs
# where the code must be written. Multiple "file" tags can be introduced by template.
@ecore2simplejava.main
def eclass2class(self: ecore.EClass):
    """
<%motra:file path="examples/outputs/${self.ePackage.name}/${self.name}.java">
public class ${self.name.capitalize()} {
    % for feature in self.eStructuralFeatures:
    // ${override(feature)}
    ${feature2attribute(feature)}
    % endfor
}
</%motra:file>
"""

@ecore2simplejava.template(
    when=lambda self: self.many
)
def feature2attribute(self: ecore.EAttribute):
    """List<${self.eType.name}> ${self.name}; // many attribute"""


@ecore2simplejava.template
def feature2attribute(self: ecore.EAttribute):
    """${self.eType.name} ${self.name}; // single attribute"""


@ecore2simplejava.template(
    when=lambda self: self.many
)
def feature2attribute(self: ecore.EReference):
    """List<${self.eType.name}> ${self.name}; // many reference"""


@ecore2simplejava.template
def feature2attribute(self: ecore.EReference):
    """List<${self.eType.name}> ${self.name}; // single reference"""


@ecore2simplejava.template
def override(self: ecore.EAttribute):
    """Attribut ${self.name}: ${self.eType.name} [${self.lowerBound}..${upper2symbol(self)}]"""


@ecore2simplejava.template
def override(self: ecore.EReference):
    """Reference ${self.name}: ${self.eType.name} [${self.lowerBound}..${upper2symbol(self)}]"""


@ecore2simplejava.helper
def upper2symbol(self: ecore.EStructuralFeature):
    return '*' if self.many else self.upperBound

Then, it can be imported and directly used from another module. Currently, there is no default runner, but there will be in the future, a way of defining models transformations chains.

# Import the transformation
from examples.m2t.sample import ecore2simplejava

# Just run it. Input can be a "Resource", a model or directly a file
ecore2simplejava.run('examples/inputs/input.ecore')

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

motra-0.1.1.tar.gz (12.0 kB view details)

Uploaded Source

Built Distribution

motra-0.1.1-py3-none-any.whl (10.7 kB view details)

Uploaded Python 3

File details

Details for the file motra-0.1.1.tar.gz.

File metadata

  • Download URL: motra-0.1.1.tar.gz
  • Upload date:
  • Size: 12.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.2 CPython/3.9.1

File hashes

Hashes for motra-0.1.1.tar.gz
Algorithm Hash digest
SHA256 484921b53449ff5f616b6e1f2eae8fd217b6424f8a0e4bb835e9257c5e81be68
MD5 83aa7d905c6fc6e34865cd4a3e4b5128
BLAKE2b-256 73932f6f71c692d442a1c0b25f7777dde2811920ee706eb7a85afa98ed6f53d9

See more details on using hashes here.

File details

Details for the file motra-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: motra-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 10.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.2 CPython/3.9.1

File hashes

Hashes for motra-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1c97e4f7f678e81a755882f888b63459670a52183b79f05ced3053f0b8a28292
MD5 c2d325809c627d7146d0aa5668dd17cc
BLAKE2b-256 d32cd5716fab006e568ebf6c286108cef78f5fe596cf42890fa37dafaf943540

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page