PyEmofUC

Entorno MDE/EMOF implementado en Python

J.M. Drake, P. López Martínez y C. Cuevas

Grupo de Ingeniería Software y Tiempo Real (ISTR) - Universidad de Cantabria

Tabla de Contenidos

Objetivos

PyEmofUC es un entorno ligero para la creación, procesamiento y visualización de información en base al paradigma de ingeniería basada en modelos MDE, en el que todos los elementos (modelo y metamodelos) se formulan de acuerdo a la especificación EMOF 2.4 de la organización OMG.

El entorno ha sido implementado utilizando el lenguaje Python, y con ello, se ha conseguido que sea un entorno:


El entorno PyEmofUC ha sido diseñado con el objetivo de que tenga las siguientes características específicas:


  • Espacio de modelado: Es el espacio tecnológico nativo del entorno. La información se representa con estructuras de datos (Python) que implementan fielmente la estructura de datos que establece la especificación EMOF. Constituye la forma básica de representación de la información que es accesible desde programas.
  • Espacio de serialización: La información se representa mediante texto etiquetado XMI, y con referencia formuladas como URIs textuales estándares. El entorno lo utiliza para el almacenamiento persistente de los modelos y para el intercambio de información con otros entornos.
  • Espacio de dominio: La información se representa mediante un lenguaje de dominio que el entorno genera automáticamente a partir del correspondiente metamodelo. Su objetivo es facilitar la interacción con el experto de dominio que crea la información desde un editor inteligente o la supervisa en una consola enriquecida.
  • No requiere generación de código complementario: Una aplicación opera directamente sobre los modelos del entorno, sin requerir para su instanciación la generación previa de código fuente de soporte.
    Las estructuras de datos que requiere la aplicación la genera dinámicamente en tiempo de ejecución, al cargar el metamodelo. Para hacer esto posible, antes de que un modelo pueda creado/cargado/gestionado en el entorno, debe estar cargado en el entorno su metamodelo.

  • Las transformaciones pueden ser formuladas con estilo imperativo o declarativo: Python es un lenguaje procedural, orientado a objetos y funcional, y las transformaciones de modelos formuladas como script Python, pueden ser formuladas utilizando un estilo imperativo, declarativo o híbrido.

  • EmofUC extiende EMOF con nuevas capacidades: Los modelos son formulados de acuerdo con el metametamodelo EmofUC que extiende la especificación EMOF 2.4 de OMG para su implementación con Python.
    Haciendo uso de tag, implementa las siguientes extensiones de EMOF:

    • Herencia de tipos primitivos: Define tags con nombre “PT” para declarar herencia entre tipos primitivos.
    • Restricción del rango de tipos primitivos: Define tags con nombre “RE”, “MAX” y “MIN”, para formular restricciones en los rangos de los valores en los tipos primitivos que se definan.
    • Definición de las propiedades derivadas: Define tags con nombre “DPE” que  permite definir la regla por la que se evalúan las propiedades derivadas.
    • Asignación de código a las operaciones: Define tags con nombre “OPE” que permiten asociar el código que implementa el comportamiento de las operaciones de las clases.
    • Asocia la documentación a los elementos de los metamodelos: Define tags con nombre “DOC” que permite asociar la documentación que establece la semántica de los metamodelos.
  • Ofrece reflexividad completa: Cualquier elemento de un modelo cargado en el entono tiene siempre acceso directo a su metaclase, y a partir de ella obtiene la información que describe sus características. La reflexividad es clave para la generación de las estructuras de datos en la fase de ejecución de la aplicación. Así mismo, permite desarrollar herramientas genéricas de gestión de modelos con capacidad de operar sobre modelos cuyos metamodelos no están definidos cuando se desarrollaron las herramientas.





Interfaz pública de usuario

El entorno PyEmofUC ofrece una interfaz pública a las aplicaciones y herramientas que procesan la información contenida en los modelos. La interfaz está  constituida por las propiedades y las operaciones de las clases AppRepository, MdlRepository, PyEmofObject y Element (y sus clases especializadas en cada modelo).


Serialización textual de los modelos

Cuando un modelo va a ser gestionado fuera del espacio de memoria de una aplicación, se representa mediante una secuencia de caracteres alfanuméricos estándares. En la formulación textual, las referencias son hiper-referencias textuales ( URIs textuales estándares) y el acceso a los modelos se realiza a través de localizadores (URL). La formulación textual de los modelos se utiliza en los siguientes casos:

La serialización y des-serialización se realiza a través de las operaciónes dump() y loadModel() de la interfaz pública. Se utilizan tres formatos de serialización:



Procesos básicos en el el entorno PyEmofUC

Paradescribir la gestion de los modelos dentro del entorno se utiliza el ejemplo CountyCity. Cuando se instala el entorno PyEmofUC, también se instalan los elementos que constituyen este ejemplo.
La ubicación de los recursos del ejemplo depende de la carpeta en la que se instale el entorno. Para los ejemplos que se muestran en esta página, se ha considerado que la carpeta raíz del ejemplo es "c:\temp\PyEmofUC".

1. Instalación del entorno

El código completo de PyEmofUC se puede descargar con el fichero de PyEmofUC_0_0.zip.
Incluye el código del entorno (carpeta src), un conjunto de W3C-schemas de apoyo para la edición de metamodelos (carpeta schemas), y así mismo, los modelos y scripts del ejemplo CountyCity (carpeta CountyCity).

En la siguiente figura se muestra el árbol de directorios con los elementos del entorno:


PyEmofUC => Carpeta raíz que resulta al desempaquetar el fichero PyEmofUC_0_0.zip. Es el origen de los path relativos. 
CountyCity => Carpeta raíz del ejemplo CountyCity, que se utiliza para mostrar las capacidades de PyEmofUC.
src => Carpeta raíz del código Python del entorno PyEmofUC. Su path debe ser incluido en la variable de entorno PYTHONPATH.
pyEmofUC => Paquete Python con el código del entorno PyEmofUC.
__init__.py => Inicialización del módulo del paquete python.
core.py => Código relativo al soporte del metametamodelo EmofUC.
dumper.py => Código de serialización de modelos y metamodelos PyEmosUC a formulaciones textuales.
GLOBAL.py => Módulo python con constantes y recursos generales al entorno.
loader.py => Código de carga de modelos y metamodelos a partir de sus formulaciones textuales.
resource.py => Código de implementación de la interfaz pública del entorno.
xmlcore.py => Código auxiliar de localización y gestión de formulaciones XML de modelos y metamodelos.
PyRTF => Paquete legado con recursos para la gestión de textos en formato RTF.
toolScripts =>
Paquete con scripts generales proporcionados por el entorno.


2. Acceso al entorno desde una aplicación.

Una aplicación que va a operar con el entorno PyEmofUC debe crear, como primer paso, un repositorio de aplicación (instancia de la clases AppRepository). Con los métodos de su interfaz pública se cargan, transforman y visualizan los modelos. En una aplicación se pueden crear varios repositorios de aplicación, pero todos los modelos que tengan referencias cruzadas entre ellos han de estar cargados en el mismo repositorio. La creación del repositorio se realiza invocando su constructor AppRepository().
Si se quiere utilizar URL relativos (platform:...) para localizar los modelos, debe establecerse en la variable xmlcore.PLATFORM la dirección absoluta de la carpeta raíz de las direcciones relativas. En los siguientes ejemplos se establece como raíz la dirección "c:\temp\
PyEmofUC":

>>> import emofUC.resource as resource

>>> import EmofUC.xmlcore as xmlcore

>>> aRep=resource.AppRepository()

>>> xmlcore.PLATFORM='c:/temp/PyEmofUC'

>>> 


3. Carga de un modelo

La carga de un modelo en el entorno se realiza invocando el método loadModel(«modelURL») en el repositorio de aplicación en el que se desea cargar.
La carga de un modelo conlleva la carga de su meta-modelo (obligatorio) y de otros modelos que estén referenciados por él (sólo si los respectivos URL son válidos desde el punto en que se utilizan).


>>> aRep.loadModel('platform:CountyCity/EagleCounty.xmi')

<emofUC.resource.MdlRepository object at 0x0000000002E8D9E8>

>>> 

Con la carga del modelo EagleCounty, también se cargan: el metametamodelo EmofUC, y los metamodelos County y City, ya que entre ellos existen las siguientes interdependencias:



Los modelos que se encuentran cargados en el repositorio pueden visualizarse obteniendo la lista de sus URI a través del método getMdlRepositories(), que retorna un diccionario con todos los modelos cargados en el entorno referenciados como clave por su URI.

>>> for key in aRep.getMdlRepositories().keys(): print "- "+key

 

- http://unican.es/istr/PyEmofUC/CountyCity/EagleCounty

- http://unican.es/istr/PyEmofUC/EmofUC

- http://unican.es/istr/PyEmofUC/CountyCity/City

- http://unican.es/istr/PyEmofUC/CountyCity/County

- http://unican.es/istr/PyEmofUC/CountyCity/EagleCity

>>> 


4. Creación de un modelo desde un programa.

Un modelo puede crearse mediante un programa Python, siempre que esté cargado en el repositorio de aplicación el correspondiente metamodelo .

En el siguiente ejemplo se crea un modelo newCity conforme al metamodelo City del proyecto CountyCity.

>>> print aRep.hasMdlRepository('http://unican.es/istr/PyEmofUC/CountyCity/City')

True

>>> newModel = aRep.createMdlRepository(

                'http://unican.es/istr/PyEmofUC/CountyCity/Example/newModel',

                'platform:CountyCity/City.xmi',

                'nme')

>>> 

>>> newModel.rootPackage = newModel.createElement(

                'cty.CompanyDirectory',

                'Id_' + str(newModel.nextXmiId()))

>>> 

>>> aCompany = newModel.createElement('cty.Company', 'Id_' + str(newModel.nextXmiId()))

>>> newModel.rootPackage.company.append(aCompany)

>>> aCompany.name = 'TransAtlantic Ltd.'

>>>

>>> aEmploy = newModel.createElement('cty.Employ', 'Id_' + str(newModel.nextXmiId()))

>>> aEmploy.name = 'Director'

>>> aEmploy.salary = 87300.00

>>> aEmploy.employer = aCompany

>>> aEmploy.worker = None

>>> aCompany.employ.append(aEmploy)

>>>

>>> aEmploy = newModel.createElement('cty.Employ', 'Id_' + str(newModel.nextXmiId()))

>>> aEmploy.name = 'Sailor'

>>> aEmploy.salary = 16100.00

>>> aEmploy.employer = aCompany

>>> aEmploy.worker = None

>>> aCompany.employ.append(aEmploy)

>>>

>>> newModel.dump('platform:CountyCity/DumpedFiles/newModel.rtf')

>>> 

# Comprueba que el mm está cargado

 

# Se crea un nuevo modelo:

#  - se le asigna el URI

#  - El URL del metamodelo

#  - El identificador del espacio de nombres

 

# Se crea el elemento repositorio raíz

 

 

 

# Se crea un elemento tipo Company

# Se agrega al contenedor raíz

#Se asigna valor al atributo name

 

# Se crea un elemento tipo empleo

# Se le asigna valor a sus atributos

 

 

 

# Se agrega al elemento propietario

 

# Secrea un segundo elemento Employ

 

 

 

 

 

 

# El modelo newModel se guarda en la URL

 



El modelo creado  ('platform:CountyCity/DumpedFiles/newModel.rtf') representado en formato .rtf es el siguiente:

MODEL date:2015-03-16T10:44

uri:http://unican.es/istr/PyEmofUC/CountyCity/Example/newModel

mmUrl:platform:CountyCity/City.xmi

nsPrefix:nme

rootType:

:CompanyDirectory[Id_0]{

TransAtlantic Ltd.:Company[Id_1]{

Director:Employ[Id_2]{

salary:87300.0

worker:None

employer:#Id_1

} # Director

Sailor:Employ[Id_3]{

salary:16100.0

worker:None

employer:#Id_1

} # Sailor

} # TransAtlantic Ltd.

} #


5. Creación de un modelo a partir del W3C-Schema.

Siempre que se almacena un metamodelo en formato .xmi (por ejemplo City.xmi), el sistema genera también el correspondiente W3C-Schema (por ejemplo City.xsd) que describe el contenido de los modelos que son conformes con él.
Haciendo uso de él en un editor inteligente XML, el operador puede introducir de forma asistida modelos que realicen el metamodelo.

ElW3C-schema que se genera es laxo y su funcióm es asistir al operador durante la introducción de los modelos. Algunos de los aspectos en los que ayuda el schema son:

En la versión actual, el editor no ayuda en la asignación de las referencias, ya que estas se implementan con tipos genéricos xsd:anyURI.

En la siguiente figura se muestra el uso de un editor XMLSpy de Altova en la generación de un modelo por el operador.



6.Creación de un modelo por edición del lenguaje de dominio.

Cuando se almacena un metamodelo (por ejemplo City.xmi) en formato .xmi haciendo uso del método dump(url, schemaUrl), el sistema genera la gramática que corresponde al metamodelo. El entorno proporciona un editor basado en la gramática que asiste al operador a introducir el modelo de forma textual. Este editor está aún pendiente de implementar. (TODO)



7. Almacenamiento persistente de un modelo

La creación, transformación y procesamiento de los modelos se realiza habitualmente en memoria de las aplicaciones como un repositorio de modelo, y permanece en ella mientras el repositorio de aplicación no se destruya. Cuando se requiere, el modelo se salva persistentemente como un fichero de texto, y de forma nativa como un texto etiquetado con formato XMI.
El almacenamiento se realiza haciendo uso del método dump(«url») que ofrece la interfaz pública de la clase MdlRepository en cuya instancia se almacena el modelo en memoria.
El siguiente código almacena el modelo de uri = ‘http://unican.es/istr/PyEmofUC/CountyCity/ EagleCounty’ en el fichero de url =’platform:CountyCity/DumpedFiles/EagleCounty.xmi’.

>>> model=aRep.getMdlRepository('http://unican.es/istr/PyEmofUC/CountyCity/EagleCounty')

>>> model.dump(url='platform:CountyCity/DumpedFiles/EagleCounty.xmi',schemaUrl='../../County.xsd')

>>> 


8. Visualización de un modelo

Un modelo puede ser visualizado por el operador en tres formatos:

En la versión actual, la visualización se realiza invocando el editor correspondiente sobre el fichero que almacena el modelo en el formato deseado. Éste debe ser almacenado previamente haciendo uso del método dump() ofrecido por la interfaz pública de la clase MdlRepository, y aplicado a la instancia de la misma que contiene el modelo en el espacio de memoria de la aplicación.

El formato de salida es realizado por el método dump() en base a la extensión del URL del fichero en que se almacena la forma textual de modelo: para el formato etiquetado XMI la extensión del URL es .xmi, para el formato texto plano es “.txt” y para el formato texto plano enriquecido con color es “.rtf”.

En la siguiente tabla se muestra el código que generan los ficheros de texto del modelo EagleCity con los tres formatos, y en las siguientes figuras se muestran la visualización  haciendo uso de editores comerciales compatibles con los formatos.

>>> eagleCityModel=aRep.getMdlRepository('http://unican.es/istr/PyEmofUC/CountyCity/EagleCity')

>>> # Store file with XMI format

>>> eagleCityModel.dump(url='platform:CountyCity/DumpedFiles/EagleCity.xmi',schemaUrl='../../City.xsd')

>>> # Store file with flat text format

>>> eagleCityModel.dump(url='platform:CountyCity/DumpedFiles/EagleCity.txt')

>>> # Store file with flat coloured text format

>>> eagleCityModel.dump(url='platform:CountyCity/DumpedFiles/EagleCity.rtf')

>>> 



9. Transformación imperativa de un modelo

En el estilo imperativo, la transformación de un modelo se realiza a través de bucles y sentencias condicionales que exploran el modelo de entrada, generando, en base a los elementos que encuentra, los  elementos del modelo de salida. En PyEmofUC se realiza con un programa Python utilizando su estilo de programación procedural y orientado a objetos.
En el proyecto ejemplo CountyCity se puede ver el seudocódigo de la transformación CountyCity_CountyToTaxCensus_Imperative_script.py.


10. Transformación declarativa de un modelo

En el estilo declarativo, la transformación de un modelo se especifica indicando para cada tipo de elemento de modelo de entrada una regla que define el elemento del modelo de salida  que debe generar, así como los valores de los atributos y referencias que se le ha de asignar en la transformación.

La transformación la realiza un motor de transformación que itera sobre los elementos especificados. En PyEmofUC se realiza con un programa Python en dos fases:

  1. Se generan los elementos del modelo de salida utilizando un estilo de programación funcional, sentencias de compresión de listas y generadores.
  2. Se establecen las referencias Python entre los elementos del modelo de salida generados en la fase primera.

En la página del ejempo CountyCity, se puede ver el código Python de la transformación CountyCity_CountyToTaxCensus_Declarative_script.py. formulada con un estilo declarativo tipo ATL. 

11. Eliminación de un repositorio de aplicación.

En la versión actual de PyEmofUC no se admite la eliminación individual de un modelo dentro del entorno. Esto ocurre ya que la eliminación de un modelo cargado en un repositorio es una operación costosa ya que suele estar fuertemente inter-referenciado con otros modelos que deben permanecer manteniendo su consistencia.
Los modelos en memoria se eliminan cuando desde la aplicación se elimina el repositorio de aplicación que lo contiene.

>>> del aRep

>>> 

Documentos

Enlaces de interés