{ "info": { "author": "Vincent Aranega", "author_email": "vincent.aranega@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Topic :: Software Development", "Topic :: Software Development :: Libraries" ], "description": "====================================================================\nPyEcore: A Pythonic Implementation of the Eclipse Modeling Framework\n====================================================================\n\n|pypi-version| |master-build| |coverage| |license|\n\n.. |master-build| image:: https://travis-ci.org/pyecore/pyecore-py2.svg?branch=master\n :target: https://travis-ci.org/pyecore/pyecore-py2\n\n.. |develop-build| image:: https://travis-ci.org/pyecore/pyecore-py2.svg?branch=develop\n :target: https://travis-ci.org/pyecore/pyecore-py2\n\n.. |pypi-version| image:: https://badge.fury.io/py/pyecore-py2.svg\n :target: https://badge.fury.io/py/pyecore-py2\n\n.. |coverage| image:: https://coveralls.io/repos/github/pyecore/pyecore-py2/badge.svg?branch=master\n :target: https://coveralls.io/github/pyecore/pyecore-py2?branch=master\n\n.. |license| image:: https://img.shields.io/badge/license-New%20BSD-blue.svg\n :target: https://raw.githubusercontent.com/pyecore/pyecore-py2/master/LICENSE\n\nPyEcore is a Model Driven Engineering (MDE) framework written for Python 2.7 \n(backport of the `PyEcore for Python 3\n`_ version).\nPrecisely, it is an implementation of `EMF/Ecore\n`_ for Python, and it tries to give an\nAPI which is compatible with the original EMF Java implementation.\n\nPyEcore allows you to handle models and metamodels (structured data model), and\ngives the key you need for building MDE-based tools and other applications based\non a structured data model. It supports out-of-the-box:\n\n* Data inheritance,\n* Two-ways relationship management (opposite references),\n* XMI (de)serialization,\n* JSON (de)serialization,\n* Notification system,\n* Reflexive API...\n\nLet see how to create on a very simple \"dynamic\" metamodel (in opposite to\nstatic ones, see the `documentation `_\nfor more details):\n\n.. code-block:: python\n\n >>> from pyecore.ecore import EClass, EAttribute, EString, EObject\n >>> Graph = EClass('Graph') # We create a 'Graph' concept\n >>> Node = EClass('Node') # We create a 'Node' concept\n >>>\n >>> # We add a \"name\" attribute to the Graph concept\n >>> Graph.eStructuralFeatures.append(EAttribute('name', EString,\n default_value='new_name'))\n >>> # And one on the 'Node' concept\n >>> Node.eStructuralFeatures.append(EAttribute('name', EString))\n >>>\n >>> # We now introduce a containment relation between Graph and Node\n >>> contains_nodes = EReference('nodes', Node, upper=-1, containment=True)\n >>> Graph.eStructuralFeatures.append(contains_nodes)\n >>> # We add an opposite relation between Graph and Node\n >>> Node.eStructuralFeatures.append(EReference('owned_by', Graph, eOpposite=contains_nodes))\n\nWith this code, we have defined two concepts: ``Graph`` and ``Node``. Both have\na ``name``, and it exists a containment relationship between them. This relation\nis bi-directionnal, which means that each time a ``Node`` object is added to the\n``nodes`` relationship of a ``Graph``, the ``owned_by`` relation of the ``Node``\nis updated also (it also work in the other way).\n\nLet's create some instances of our freshly created metamodel:\n\n.. code-block:: python\n\n >>> # We create a Graph\n >>> g1 = Graph(name='Graph 1')\n >>> g1\n \n >>>\n >>> # And two node instances\n >>> n1 = Node(name='Node 1')\n >>> n2 = Node(name='Node 2')\n >>> n1, n2\n (,\n )\n >>>\n >>> # We add them to the Graph\n >>> g1.nodes.extend([n1, n2])\n >>> g1.nodes\n EOrderedSet([,\n ])\n >>>\n >>> # bi-directional references are updated\n >>> n1.owned_by\n \n\n\nThis example gives a quick overview of some of the features you get for free\nwhen using PyEcore.\n\n*The project slowly grows and it still requires more love.*\n\nInstallation\n============\n\nPyEcore is available on ``pypi``, you can simply install it using ``pip``:\n\n.. code-block:: bash\n\n $ pip install pyecore\n\nThe installation can also be performed manually (better in a virtualenv):\n\n.. code-block:: bash\n\n $ python setup.py install\n\n\nDocumentation\n=============\n\nYou can read the documentation (for Python 3), which sums up pretty much everything you can\nfind in this page, and more:\n\nhttps://pyecore.readthedocs.io/en/latest/\n\nYou can also find on this page a part of the Documentation:\n\n.. contents:: :depth: 2\n\n\nDynamic Metamodels\n==================\n\nDynamic metamodels reflects the ability to create metamodels \"on-the-fly\". You\ncan create metaclass hierarchie, add ``EAttribute`` and ``EReference``.\n\nIn order to create a new metaclass, you need to create an ``EClass`` instance:\n\n.. code-block:: python\n\n >>> import pyecore.ecore as Ecore\n >>> MyMetaclass = Ecore.EClass('MyMetaclass')\n\nYou can then create instances of your metaclass:\n\n.. code-block:: python\n\n >>> instance1 = MyMetaclass()\n >>> instance2 = MyMetaclass()\n >>> assert instance1 is not instance2\n\nFrom the created instances, we can go back to the metaclasses:\n\n.. code-block:: python\n\n >>> instance1.eClass\n \n\nThen, we can add metaproperties to the freshly created metaclass:\n\n.. code-block:: python\n\n >>> instance1.eClass.eAttributes\n []\n >>> MyMetaclass.eStructuralFeatures.append(Ecore.EAttribute('name', Ecore.EString)) # We add a 'name' which is a string\n >>> instance1.eClass.eAttributes # Is there a new feature?\n [] # Yep, the new feature is here!\n >>> str(instance1.name) # There is a default value for the new attribute\n 'None'\n >>> instance1.name = 'mystuff'\n >>> instance1.name\n 'mystuff'\n >>> # As the feature exists in the metaclass, the name of the feature can be used in the constructor\n >>> instance3 = MyMetaclass(name='myname')\n >>> instance3.name\n 'myname'\n\nWe can also create a new metaclass ``B`` and a new meta-references towards\n``B``:\n\n.. code-block:: python\n\n >>> B = Ecore.EClass('B')\n >>> MyMetaclass.eStructuralFeatures.append(Ecore.EReference('toB', B, containment=True))\n >>> b1 = B()\n >>> instance1.toB = b1\n >>> instance1.toB\n \n >>> b1.eContainer() is instance1 # because 'toB' is a containment reference\n True\n\nOpposite and 'collection' meta-references are also managed:\n\n.. code-block:: python\n\n >>> C = Ecore.EClass('C')\n >>> C.eStructuralFeatures.append(Ecore.EReference('toMy', MyMetaclass))\n >>> MyMetaclass.eStructuralFeatures.append(Ecore.EReference('toCs', C, upper=-1, eOpposite=C.eStructuralFeatures[0]))\n >>> instance1.toCs\n []\n >>> c1 = C()\n >>> c1.toMy = instance1\n >>> instance1.toCs # 'toCs' should contain 'c1' because 'toMy' is opposite relation of 'toCs'\n []\n\nExplore Dynamic metamodel/model objects\n---------------------------------------\n\nIt is possible, when you are handling an object in the Python console, to ask\nfor all the meta-attributes/meta-references and meta-operations that can\nbe called on it using ``dir()`` on, either a dynamic metamodel object or a\nmodel instance. This allows you to quickly experiment and find the information\nyou are looking for:\n\n.. code-block:: python\n\n >>> A = EClass('A')\n >>> dir(A)\n ['abstract',\n 'delete',\n 'eAllContents',\n 'eAllOperations',\n 'eAllReferences',\n 'eAllStructuralFeatures',\n 'eAllSuperTypes',\n 'eAnnotations',\n 'eAttributes',\n 'eContainer',\n # ... there is many others\n 'findEOperation',\n 'findEStructuralFeature',\n 'getEAnnotation',\n 'instanceClassName',\n 'interface',\n 'name']\n >>> a = A()\n >>> dir(a)\n []\n >>> A.eStructuralFeatures.append(EAttribute('myname', EString))\n >>> dir(a)\n ['myname']\n\n\nEnhance the Dynamic metamodel\n-----------------------------\n\nEven if you define or use a dynamic metamodel, you can add dedicated methods\n(e.g: ``__repr__``) to the equivalent Python class. Each ``EClass`` instance is\nlinked to a Python class which can be reached using the ``python_class`` field:\n\n.. code-block:: python\n\n >>> A = EClass('A')\n >>> A.python_class\n pyecore.ecore.A\n\nYou can directly add new \"non-metamodel\" related method to this class:\n\n.. code-block:: python\n\n >>> a = A()\n >>> a\n \n >>> A.python_class.__repr__ = lambda self: 'My repr for real'\n >>> a\n My repr for real\n\n\nStatic Metamodels\n=================\n\nThe static definition of a metamodel using PyEcore mostly relies on the\nclassical classes definitions in Python. Each Python class is linked to an\n``EClass`` instance and has a special metaclass. The static code for metamodel\nalso provides a model layer and the ability to easily refer/navigate inside the\ndefined meta-layer.\n\n.. code-block:: python\n\n $ ls library\n __init__.py library.py\n\n $ cat library/library.py\n # ... stuffs here\n class Writer(EObject):\n __metaclass__ = MetaEClass\n name = EAttribute(eType=EString)\n books = EReference(upper=-1)\n\n def __init__(self, name=None, books=None, **kwargs):\n if kwargs:\n raise AttributeError('unexpected arguments: {}'.format(kwargs))\n\n super().__init__()\n if name is not None:\n self.name = name\n if books:\n self.books.extend(books)\n # ... Other stuffs here\n\n $ python\n ...\n >>> import library\n >>> # we can create elements and handle the model level\n >>> smith = library.Writer(name='smith')\n >>> book1 = library.Book(title='Ye Old Book1')\n >>> book1.pages = 100\n >>> smith.books.append(book1)\n >>> assert book1 in smith.books\n >>> assert smith in book1.authors\n >>> # ...\n >>> # We can also navigate the meta-level\n >>> import pyecore.ecore as Ecore # We import the Ecore metamodel only for tests\n >>> assert isinstance(library.Book.authors, Ecore.EReference)\n >>> library.Book.authors.upperBound\n -1\n >>> assert isinstance(library.Writer.name, Ecore.EAttribute)\n\n\nThere is two main ways of creating static ``EClass`` with PyEcore. The first\none relies on automatic code generation while the second one uses manual\ndefinition.\n\nThe automatic code generator defines a Python package hierarchie instead of\nonly a Python module. This allows more freedom for dedicated operations and\nreferences between packages.\n\nHow to Generate the Static Metamodel Code\n-----------------------------------------\n\nThe static code is generated from a ``.ecore`` where your metamodel is defined\n(the EMF ``.genmodel`` files are not yet supported (probably in future version).\n\nThere is currently two ways of generating the code for your metamodel. The first\none is to use a MTL generator (in ``/generator``) and the second one is to use a\ndedicated command line tool written in Python, using Pymultigen, Jinja and PyEcore.\n\nUsing the Accelo/MTL Generator\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo use this generator, you need Eclipse and the right Acceleo plugins. Once\nEclipse is installed with the right plugins, you need to create a new Acceleo\nproject, copy the PyEcore generator in it, configure a new Acceleo runner,\nselect your ``.ecore`` and your good to go. There is plenty of documentation\nover the Internet for Acceleo/MTL project creation/management...\n\n**WARNING:** the Acceleo generator is now deprecated, use pyecoregen instead!\n\nUsing the Dedicated CLI Generator (PyEcoregen)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor simple generation, the Acceleo generator will still do the job, but for more\ncomplex metamodel and a more robust generation, pyecoregen is significantly better.\nIts use is the prefered solution for the static metamodel code generation.\nAdvantages over the Acceleo generator are the following:\n\n* it gives the ability to deal with generation from the command line,\n* it gives the ability to launch generation programmatically (and very simply),\n* it introduces into PyEcore a framework for code generation,\n* it allows you to code dedicated behavior in mixin classes,\n* it can be installed from `pip`.\n\nThis generator source code can be found at this address with mode details:\nhttps://github.com/pyecore/pyecoregen and is available on Pypi, so you can\ninstall it quite symply using:\n\n.. code-block:: bash\n\n $ pip install pyecoregen\n\nThis will automatically install all the required dependencies and give you a new\nCLI tool: ``pyecoregen``.\n\nUsing this tool, your static code generation is very simple:\n\n.. code-block:: bash\n\n $ pyecoregen -e your_ecore_file.ecore -o your_output_path\n\nThe generated code is automatically formatted using ``autopep8``. Once the code\nis generated, your can import it and use it in your Python code.\n\n\nManually defines static ``EClass``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo manually defines static ``EClass``, it is simply a matter of creating a\nPython class, and adding to it the ``@EMetaclass`` class decorator. This\ndecorator will automatically add the righ metaclass to the defined class, and\nintroduce the missing classes in it's inheritance tree. Defining simple\nmetaclass is thus fairly easy:\n\n.. code-block:: python\n\n @EMetaclass\n class Person(object):\n name = EAttribute(eType=EString)\n age = EAttribute(eType=EInt)\n children = EReference(upper=-1, containment=True)\n\n def __init__(self, name):\n self.name = name\n\n Person.children.eType = Person # As the relation is reflexive, it must be set AFTER the metaclass creation\n\n p1 = Person('Parent')\n p1.children.append(Person('Child'))\n\n\nWithout more information, all the created metaclass will be added to a default\n``EPackage``, generated on the fly. If the ``EPackage`` must be controlled, a\nglobal variable of ``EPackage`` type, named ``eClass``, must be created in the\nmodule.\n\n.. code-block:: python\n\n eClass = EPackage(name='pack', nsURI='http://pack/1.0', nsPrefix='pack')\n\n @EMetaclass\n class TestMeta(object):\n pass\n\n assert TestMeta.eClass.ePackage is eClass\n\nHowever, when ``@EMetaclass`` is used, the direct ``super()`` call in\nthe ``__init__`` constructor cannot be directly called. Instead,\n``super(x, self)`` must be called:\n\n.. code-block:: python\n\n class Stuff(object):\n def __init__(self):\n self.stuff = 10\n\n\n @EMetaclass\n class A(Stuff):\n def __init__(self, tmp):\n super(A, self).__init__()\n self.tmp = tmp\n\n\n a = A()\n assert a.stuff == 10\n\nIf you want to directly extends the PyEcore metamodel, the ``@EMetaclass`` is\nnot required, and ``super()`` can be used.\n\n.. code-block:: python\n\n class MyNamedElement(ENamedElement):\n def __init__(self, tmp=None, **kwargs):\n super().__init__(**kwargs)\n self.tmp = tmp\n\n\nStatic/Dynamic ``EOperation``\n=============================\n\nPyEcore also support ``EOperation`` definition for static and dynamic metamodel.\nFor static metamodel, the solution is simple, a simple method with the code is\nadded inside the defined class. The corresponding ``EOperation`` is created on\nthe fly. Theire is still some \"requirements\" for this. In order to be understood\nas an ``EOperation`` candidate, the defined method must have at least one\nparameter and the first parameter must always be named ``self``.\n\nFor dynamic metamodels, the simple fact of adding an ``EOperation`` instance in\nthe ``EClass`` instance, adds an \"empty\" implementation:\n\n.. code-block:: python\n\n >>> import pyecore.ecore as Ecore\n >>> A = Ecore.EClass('A')\n >>> operation = Ecore.EOperation('myoperation')\n >>> param1 = Ecore.EParameter('param1', eType=Ecore.EString, required=True)\n >>> operation.eParameters.append(param1)\n >>> A.eOperations.append(operation)\n >>> a = A()\n >>> help(a.myoperation)\n Help on method myoperation:\n\n myoperation(param1) method of pyecore.ecore.A instance\n >>> a.myoperation('test')\n ...\n NotImplementedError: Method myoperation(param1) is not yet implemented\n\nFor each ``EParameter``, the ``required`` parameter express the fact that the\nparameter is required or not in the produced operation:\n\n.. code-block:: python\n\n >>> operation2 = Ecore.EOperation('myoperation2')\n >>> p1 = Ecore.EParameter('p1', eType=Ecore.EString)\n >>> operation2.eParameters.append(p1)\n >>> A.eOperations.append(operation2)\n >>> a = A()\n >>> a.operation2(p1='test') # Will raise a NotImplementedError exception\n\nYou can then create an implementation for the eoperation and link it to the\nEClass:\n\n.. code-block:: python\n\n >>> def myoperation(self, param1):\n ... print(self, param1)\n ...\n >>> A.python_class.myoperation = myoperation\n\nTo be able to propose a dynamic empty implementation of the operation, PyEcore\nrelies on Python code generation at runtime.\n\n\nNotifications\n=============\n\nPyEcore gives you the ability to listen to modifications performed on an\nelement. The ``EObserver`` class provides a basic observer which can receive\nnotifications from the ``EObject`` it is register in:\n\n.. code-block:: python\n\n >>> import library as lib # we use the wikipedia library example\n >>> from pyecore.notification import EObserver, Kind\n >>> smith = lib.Writer()\n >>> b1 = lib.Book()\n >>> observer = EObserver(smith, notifyChanged=lambda x: print(x))\n >>> b1.authors.append(smith) # observer receive the notification from smith because 'authors' is eOpposite or 'books'\n\nThe ``EObserver`` notification method can be set using a lambda as in the\nprevious example, using a regular function or by class inheritance:\n\n.. code-block:: python\n\n >>> def print_notif(notification):\n ... print(notification)\n ...\n >>> observer = EObserver()\n >>> observer.observe(b1)\n >>> observer.notifyChanged = print_notif\n >>> b1.authors.append(smith) # observer receive the notification from b1\n\nUsing inheritance:\n\n.. code-block:: python\n\n >>> class PrintNotification(EObserver):\n ... def __init__(self, notifier=None):\n ... super().__init__(notifier=notifier)\n ...\n ... def notifyChanged(self, notification):\n ... print(notification)\n ...\n ...\n >>> observer = PrintNotification(b1)\n >>> b1.authors.append(smith) # observer receive the notification from b1\n\nThe ``Notification`` object contains information about the performed\nmodification:\n\n* ``new`` -> the new added value (can be a collection) or ``None`` is remove or unset\n* ``old`` -> the replaced value (always ``None`` for collections)\n* ``feature`` -> the ``EStructuralFeature`` modified\n* ``notifer`` -> the object that have been modified\n* ``kind`` -> the kind of modification performed\n\nThe different kind of notifications that can be currently received are:\n\n* ``ADD`` -> when an object is added to a collection\n* ``ADD_MANY`` -> when many objects are added to a collection\n* ``REMOVE`` -> when an object is removed from a collection\n* ``SET`` -> when a value is set in an attribute/reference\n* ``UNSET`` -> when a value is removed from an attribute/reference\n\n\nDeep Journey Inside PyEcore\n===========================\n\nThis section will provide some explanation of how PyEcore works.\n\nEClasse Instances as Factories\n------------------------------\n\nThe most noticeable difference between PyEcore and Java-EMF implementation is\nthe fact that there is no factories (as you probably already seen). Each EClass\ninstance is in itself a factory. This allows you to do this kind of tricks:\n\n.. code-block:: python\n\n >>> A = EClass('A')\n >>> eobject = A() # We create an A instance\n >>> eobject.eClass\n \n >>> eobject2 = eobject.eClass() # We create another A instance\n >>> assert isinstance(eobject2, eobject.__class__)\n >>> from pyecore.ecore import EcoreUtils\n >>> assert EcoreUtils.isinstance(eobject2, A)\n\n\nIn fact, each EClass instance create a new Python ``class`` named after the\nEClass name and keep a strong relationship towards it. Moreover, EClass\nimplements is a ``callable`` and each time ``()`` is called on an EClass\ninstance, an instance of the associated Python ``class`` is created. Here is a\nsmall example:\n\n.. code-block:: python\n\n >>> MyClass = EClass('MyClass') # We create an EClass instance\n >>> type(MyClass)\n pyecore.ecore.EClass\n >>> MyClass.python_class\n pyecore.ecore.MyClass\n >>> myclass_instance = MyClass() # MyClass is callable, creates an instance of the 'python_class' class\n >>> myclass_instance\n \n >>> type(myclass_instance)\n pyecore.ecore.MyClass\n # We can access the EClass instance from the created instance and go back\n >>> myclass_instance.eClass\n \n >>> assert myclass_instance.eClass.python_class is MyClass.python_class\n >>> assert myclass_instance.eClass.python_class.eClass is MyClass\n >>> assert myclass_instance.__class__ is MyClass.python_class\n >>> assert myclass_instance.__class__.eClass is MyClass\n >>> assert myclass_instance.__class__.eClass is myclass_instance.eClass\n\n\nThe Python class hierarchie (inheritance tree) associated to the EClass instance\n\n.. code-block:: python\n\n >>> B = EClass('B') # in complement, we create a new B metaclass\n >>> list(B.eAllSuperTypes())\n []\n >>> B.eSuperTypes.append(A) # B inherits from A\n >>> list(B.eAllSuperTypes())\n {}\n >>> B.python_class.mro()\n [pyecore.ecore.B,\n pyecore.ecore.A,\n pyecore.ecore.EObject,\n pyecore.ecore.ENotifier,\n object]\n >>> b_instance = B()\n >>> assert isinstance(b_instance, A.python_class)\n >>> assert EcoreUtils.isinstance(b_instance, A)\n\n\nImporting an Existing XMI Metamodel/Model\n=========================================\n\nXMI support is still a little rough on the edges, but the XMI import is on good tracks.\nCurrently, only basic XMI metamodel (``.ecore``) and model instances can be\nloaded:\n\n.. code-block:: python\n\n >>> from pyecore.resources import ResourceSet, URI\n >>> rset = ResourceSet()\n >>> resource = rset.get_resource(URI('path/to/mm.ecore'))\n >>> mm_root = resource.contents[0]\n >>> rset.metamodel_registry[mm_root.nsURI] = mm_root\n >>> # At this point, the .ecore is loaded in the 'rset' as a metamodel\n >>> resource = rset.get_resource(URI('path/to/instance.xmi'))\n >>> model_root = resource.contents[0]\n >>> # At this point, the model instance is loaded!\n\nThe ``ResourceSet/Resource/URI`` will evolve in the future. At the moment, only\nbasic operations are enabled: ``create_resource/get_resource/load/save...``.\n\n\nDynamic Metamodels Helper\n-------------------------\n\nOnce a metamodel is loaded from an XMI metamodel (from a ``.ecore`` file), the\nmetamodel root that is gathered is an ``EPackage`` instance. To access each\n``EClass`` from the loaded resource, a ``getEClassifier(...)`` call must be\nperformed:\n\n.. code-block:: python\n\n >>> #...\n >>> resource = rset.get_resource(URI('path/to/mm.ecore'))\n >>> mm_root = resource.contents[0]\n >>> A = mm_root.getEClassifier('A')\n >>> a_instance = A()\n\nWhen a big metamodel is loaded, this operation can be cumbersome. To ease this\noperation, PyEcore proposes an helper that gives a quick access to each\n``EClass`` contained in the ``EPackage`` and its subpackages. This helper is the\n``DynamicEPackage`` class. Its creation is performed like so:\n\n.. code-block:: python\n\n >>> #...\n >>> resource = rset.get_resource(URI('path/to/mm.ecore'))\n >>> mm_root = resource.contents[0]\n >>> from pyecore.utils import DynamicEPackage\n >>> MyMetamodel = DynamicEPackage(mm_root) # We create a DynamicEPackage for the loaded root\n >>> a_instance = MyMetamodel.A()\n >>> b_instance = MyMetamodel.B()\n\n\nAdding External Metamodel Resources\n-----------------------------------\n\nExternal resources for metamodel loading should be added in the resource set.\nFor example, some metamodels use the XMLType instead of the Ecore one.\nThe resource creation should be done by hand first:\n\n.. code-block:: python\n\n int_conversion = lambda x: int(x) # translating str to int durint load()\n String = Ecore.EDataType('String', str)\n Double = Ecore.EDataType('Double', int, 0, from_string=int_conversion)\n Int = Ecore.EDataType('Int', int, from_string=int_conversion)\n IntObject = Ecore.EDataType('IntObject', int, None,\n from_string=int_conversion)\n Boolean = Ecore.EDataType('Boolean', bool, False,\n from_string=lambda x: x in ['True', 'true'],\n to_string=lambda x: str(x).lower())\n Long = Ecore.EDataType('Long', int, 0, from_string=int_conversion)\n EJavaObject = Ecore.EDataType('EJavaObject', object)\n xmltype = Ecore.EPackage()\n xmltype.eClassifiers.extend([String,\n Double,\n Int,\n EJavaObject,\n Long,\n Boolean,\n IntObject])\n xmltype.nsURI = 'http://www.eclipse.org/emf/2003/XMLType'\n xmltype.nsPrefix = 'xmltype'\n xmltype.name = 'xmltype'\n rset.metamodel_registry[xmltype.nsURI] = xmltype\n\n # Then the resource can be loaded (here from an http address)\n resource = rset.get_resource(HttpURI('http://myadress.ecore'))\n root = resource.contents[0]\n\n\nMetamodel References by 'File Path'\n-----------------------------------\n\nIf a metamodel references others in a 'file path' manner (for example, a\nmetamodel ``A`` had some relationship towards a ``B`` metamodel like this:\n``../metamodelb.ecore`` ), PyEcore requires that the ``B`` metamodel is loaded\nfirst and registered against the metamodel path URI like (in the example, against\nthe ``../metamodelb.ecore`` URI).\n\n.. code-block:: python\n\n >>> # We suppose that the metamodel A.ecore has references towards B.ecore\n ... # '../../B.ecore'. Path of A.ecore is 'a/b/A.ecore' and B.ecore is '.'\n >>> resource = rset.get_resource(URI('B.ecore')) # We load B.ecore first\n >>> root = resource.contents[0]\n >>> rset.metamodel_registry['../../B.ecore'] = root # We register it against the 'file path' uri\n >>> resource = rset.get_resource(URI('a/b/A.ecore')) # A.ecore now loads just fine\n\n\nAdding External resources\n-------------------------\n\nWhen a model reference another one, they both need to be added inside the same\nResourceSet.\n\n.. code-block:: python\n\n rset.get_resource(URI('uri/towards/my/first/resource'))\n resource = rset.get_resource(URI('uri/towards/my/secon/resource'))\n\nIf for some reason, you want to dynamically create the resource which is\nrequired for XMI deserialization of another one, you need to create an empty\nresource first:\n\n.. code-block:: python\n\n # Other model is 'external_model'\n resource = rset.create_resource(URI('the/wanted/uri'))\n resource.append(external_model)\n\n\nExporting an Existing XMI Resource\n==================================\n\nAs for the XMI import, the XMI export (serialization) is still somehow very\nbasic. Here is an example of how you could save your objects in a file:\n\n.. code-block:: python\n\n >>> # we suppose we have an already existing model in 'root'\n >>> from pyecore.resources.xmi import XMIResource\n >>> from pyecore.resources import URI\n >>> resource = XMIResource(URI('my/path.xmi'))\n >>> resource.append(root) # We add the root to the resource\n >>> resource.save() # will save the result in 'my/path.xmi'\n >>> resource.save(output=URI('test/path.xmi')) # save the result in 'test/path.xmi'\n\n\nYou can also use a ``ResourceSet`` to deal with this:\n\n.. code-block:: python\n\n >>> # we suppose we have an already existing model in 'root'\n >>> from pyecore.resources import ResourceSet, URI\n >>> rset = ResourceSet()\n >>> resource = rset.create_resource(URI('my/path.xmi'))\n >>> resource.append(root)\n >>> resource.save()\n\n\nDealing with JSON Resources\n===========================\n\nPyEcore is also able to load/save JSON models/metamodels. The JSON format it uses\ntries to be close from the one described in the `emfjson-jackson `_ project.\nThe way the JSON serialization/deserialization works, on a user point of view,\nis pretty much the same than the XMI resources, but as the JSON resource factory\nis not loaded by default (for XMI, it is), you have to manually register it\nfirst. The registration can be performed globally or at a ``ResourceSet`` level.\nHere is how to register the JSON resource factory for a given ``ResourceSet``.\n\n.. code-block:: python\n\n >>> from pyecore.resources import ResourceSet\n >>> from pyecore.resources.json import JsonResource\n >>> rset = ResourceSet() # We have a resource set\n >>> rset.resource_factory['json'] = lambda uri: JsonResource(uri) # we register the factory for '.json' extensions\n\n\nAnd here is how to register the factory at a global level:\n\n.. code-block:: python\n\n >>> from pyecore.resources import ResourceSet\n >>> from pyecore.resources.json import JsonResource\n >>> ResourceSet.resource_factory['json'] = lambda uri: JsonResource(uri)\n\n\nOnce the resource factory is registered, you can load/save models/metamodels\nexactly the same way you would have done it for XMI. Check the XMI section to\nsee how to load/save resources using a ``ResourceSet``.\n\n**NOTE:** Currently, the Json serialization is performed using the defaut Python\n``json`` lib. It means that your PyEcore model is first translated to a huge\n``dict`` before the export/import. For huge models, this could implies a memory\nand a performance cost.\n\n\nCreating Your own URI\n=====================\n\nPyEcore uses ``URI`` to deal with 'stream' opening/reading/writing/closing.\nAn ``URI`` is used to give a file-like object to a ``Resource``. By default,\nthe basic ``URI`` provides a way to read and write to files on your system (if\nthe path used is a file system path, abstract paths or logical ones are not\nserialized onto the disk). Another, ``HttpURI`` opens a file-like object from\na remote URL, but does not give the ability to write to a remote URL.\n\nAs example, in this section, we will create a ``StringURI`` that gives the\nresource the ability to read/write from/to a Python String.\n\n.. code-block:: python\n\n class StringURI(URI):\n def __init__(self, uri, text=None):\n super(StringURI, self).__init__(uri)\n if text is not None:\n self.__stream = StringIO(text)\n\n def getvalue(self):\n return self.__stream.getvalue()\n\n def create_instream(self):\n return self.__stream\n\n def create_outstream(self):\n self.__stream = StringIO()\n return self.__stream\n\n\nThe ``StringURI`` class inherits from ``URI``, and adds a new parameter to the\nconstructor: ``text``. In this class, the ``__stream`` attribute is handled in\nthe ``URI`` base class, and inherited from it.\n\nThe constructor builds a new ``StringIO`` instance if a text is passed to this\n``URI``. This parameter is used when a string must be decoded. In this context,\nthe ``create_instream()`` method is used to provide the ``__stream`` to read\nfrom. In this case, it only returns the stream created in the constructor.\n\nThe ``create_outstream()`` methods is used to create the output stream. In this\ncase, a simple ``StringIO`` instance is created.\n\nIn complement, the ``getvalue()`` method provides a way of getting the result\nof the load/save operation. The following code illustrate how the ``StringURI``\ncan be used:\n\n.. code-block:: python\n\n # we have a model in memory in 'root'\n uri = StringURI('myuri')\n resource = rset.create_resource(uri)\n resource.append(root)\n resource.save()\n print(uri.getvalue()) # we get the result of the serialization\n\n mystr = uri.getvalue() # we assume this is a new string\n uri = StringURI('newuri', text=mystr)\n resource = rset.create_resource(uri)\n resource.load()\n root = resource.contents[0] # we get the root of the loaded resource\n\n\nDeleting Elements\n=================\n\nDeleting elements in EMF is still a sensible point because of all the special\nmodel \"shape\" that can impact the deletion algorithm. PyEcore supports two main\nway of deleting elements: one is a real kind of deletion, while the other is\nmore less direct.\n\nThe ``delete()`` method\n-----------------------\n\nThe first way of deleting element is to use the ``delete()`` method which is\nowned by every ``EObject/EProxy``:\n\n.. code-block:: python\n\n >>> # we suppose we have an already existing element in 'elem'\n >>> elem.delete()\n\nThis call is also recursive by default: every sub-object of the deleted element\nis also deleted. This behavior is the one by default as a \"containment\"\nreference is a strong constraint.\n\nThe behavior of the ``delete()`` method can be confusing when there is many\n``EProxy`` in the game. As the ``EProxy`` only gives a partial view of the\nobject while it is not resolved, the ``delete()`` can only correctly remove\nresolved proxies. If a resource or many elements that are referenced in many\nother resources must be destroyed, in order to be sure to not introduce broken\nproxies, the best solution is to resolve all the proxies first, then to delete\nthem.\n\n\nRemoving an element from it's container\n---------------------------------------\n\nYou can also, in a way, removing elements from a model using the XMI\nserialization. If you want to remove an element from a Resource, you have to\nremove it from its container. PyEcore does not serialize elements that are not\ncontained by a ``Resource`` and each reference to this 'not-contained' element\nis not serialized.\n\nModifying Elements Using Commands\n=================================\n\nPyEcore objects can be modified as shown previously, using basic Python\noperators, but these mofifications cannot be undone. To do so, it is required to\nuse ``Command`` and a ``CommandStack``. Each command represent a basic action\nthat can be performed on an element (set/add/remove/move/delete):\n\n.. code-block:: python\n\n >>> from pyecore.commands import Set\n >>> # we assume have a metamodel with an A EClass that owns a 'name' feature\n >>> a = A()\n >>> set = Set(owner=a, feature='name', value='myname')\n >>> if set.can_execute:\n ... set.execute()\n >>> a.name\n myname\n\nIf you use a simple command withtout ``CommandStack``, the ``can_execute`` call\nis mandatory! It performs some prior computation before the actual command\nexecution. Each executed command also supports 'undo' and 'redo':\n\n.. code-block:: python\n\n >>> if set.can_undo:\n ... set.undo()\n >>> assert a.name is None\n >>> set.redo()\n >>> assert a.name == 'myname'\n\nAs for the ``execute()`` method, the ``can_undo`` call must be done before\ncalling the ``undo()`` method. However, there is no ``can_redo``, the ``redo()``\ncall can be mad right away after an undo.\n\nTo compose all of these commands, a ``Compound`` can be used. Basically, a\n``Compound`` acts as a list with extra methods (``execute``, ``undo``,\n``redo``...):\n\n.. code-block:: python\n\n >>> from pyecore.commands import Compound\n >>> a = A() # we use a new A instance\n >>> c = Compound(Set(owner=a, feature='name', value='myname'),\n ... Set(owner=a, feature='name', value='myname2'))\n >>> len(c)\n 2\n >>> if c.can_execute:\n ... c.execute()\n >>> a.name\n myname2\n >>> if c.can_undo:\n ... c.undo()\n >>> assert a.name is None\n\nIn order to organize and keep a stack of each played command, a ``CommandStack``\ncan be used:\n\n.. code-block:: python\n\n >>> from pyecore.commands import CommandStack\n >>> a = A()\n >>> stack = CommandStack()\n >>> stack.execute(Set(owner=a, feature='name', value='myname'))\n >>> stack.execute(Set(owner=a, feature='name', value='myname2'))\n >>> stack.undo()\n >>> assert a.name == 'myname'\n >>> stack.redo()\n >>> assert a.name == 'myname2'\n\n\nHere is a quick review of each command:\n\n* ``Set`` --> sets a ``feature`` to a ``value`` for an ``owner``\n* ``Add`` --> adds a ``value`` object to a ``feature`` collection from an ``owner`` object (``Add(owner=a, feature='collection', value=b)``). This command can also add a ``value`` at a dedicated ``index`` (``Add(owner=a, feature='collection', value=b, index=0)``)\n* ``Remove`` --> removes a ``value`` object from a ``feature`` collection from an ``owner`` (``Remove(owner=a, feature='collection', value=b)``). This command can also remove an object located at an ``index`` (``Remove(owner=a, feature='collection', index=0)``)\n* ``Move`` --> moves a ``value`` to a ``to_index`` position inside a ``feature`` collection (``Move(owner=a, feature='collection', value=b, to_index=1)``). This command can also move an element from a ``from_index`` to a ``to_index`` in a collection (``Move(owner=a, feature='collection', from_index=0, to_index=1)``)\n* ``Delete`` --> deletes an elements and its contained elements (``Delete(owner=a)``)\n\n\nDynamically Extending PyEcore Base Classes\n==========================================\n\nPyEcore is extensible and there is two ways of modifying it: either by extending\nthe basic concepts (as ``EClass``, ``EStructuralFeature``...), or by directly\nmodifying the same concepts.\n\nExtending PyEcore Base Classes\n------------------------------\n\nTo extend the PyEcore base classes, the only thing to do is to create new\n``EClass`` instances that have some base classes as ``superclass``.\nThe following excerpt shows how you can create an ``EClass`` instance that\nwill add support ``EAnnotation`` to each created instance:\n\n.. code-block:: python\n\n >>> from pyecore.ecore import *\n >>> A = EClass('A', superclass=(EModelElement.eClass)) # we need to use '.eClass' to stay in the PyEcore EClass instance level\n >>> a = A() # we create an instance that has 'eAnnotations' support\n >>> a.eAnnotations\n EOrderedSet()\n >>> annotation = EAnnotation(source='testSource')\n >>> annotation.details['mykey'] = 33\n >>> a.eAnnotations.append(annotation)\n >>> EOrderedSet([])\n\nIf you want to extend ``EClass``, the process is mainly the same, but there is a\ntwist:\n\n.. code-block:: python\n\n >>> from pyecore.ecore import *\n >>> NewEClass = EClass('NewEClass', superclass=(EClass.eClass)) # NewEClass is an EClass instance and an EClass\n >>> A = NewEClass('A') # here is the twist, currently, EClass instance MUST be named\n >>> a = A() # we can create 'A' instance\n >>> a\n \n\n\nModifying PyEcore Base Classes\n------------------------------\n\nPyEcore let you dynamically add new features to the base class and thus\nintroduce new feature for base classes instances:\n\n.. code-block:: python\n\n >>> from pyecore.ecore import *\n >>> EClass.new_feature = EAttribute('new_feature', EInt) # EClass has now a new EInt feature\n >>> A = EClass('A')\n >>> A.new_feature\n 0\n >>> A.new_feature = 5\n >>> A.new_feature\n 5\n\n\nDependencies\n============\n\nThe dependencies required by pyecore are:\n\n* ordered-set which is used for the ``ordered`` and ``unique`` collections expressed in the metamodel,\n* lxml which is used for the XMI parsing.\n\nThese dependencies are directly installed if you choose to use ``pip``.\n\n\nRun the Tests\n=============\n\nTests uses `py.test` and 'coverage'. Everything is driven by `Tox`, so in order\nto run the tests simply run:\n\n.. code-block:: bash\n\n $ tox\n\n\nLiberty Regarding the Java EMF Implementation\n=============================================\n\n* There is some meta-property that could be missing inside PyEcore. If you see one missing, please open a new ticket!\n* Proxies are not \"removed\" once resolved as in the the Java version, instead they acts as transparent proxies and redirect each calls to the 'proxied' object.\n* PyEcore is able to automatically load some model/metamodel dependencies on its own.\n\nState\n=====\n\nIn the current state, the project implements:\n\n* the dynamic/static metamodel definitions,\n* reflexive API,\n* inheritance,\n* enumerations,\n* abstract metaclasses,\n* runtime typechecking,\n* attribute/reference creations,\n* collections (attribute/references with upper bound set to ``-1``),\n* reference eopposite,\n* containment reference,\n* introspection,\n* select/reject on collections,\n* Eclipse XMI import (partially, only single root models),\n* Eclipse XMI export (partially, only single root models),\n* simple notification/Event system,\n* EOperations support,\n* code generator for the static part,\n* EMF proxies (first version),\n* object deletion (first version),\n* EMF commands (first version),\n* EMF basic command stack,\n* EMF very basic Editing Domain,\n* JSON import (simple JSON format),\n* JSON export (simple JSON format),\n* introduce behavior @runtime,\n* resources auto-load for some cross-references,\n* derived collections,\n* multiple roots ressources,\n* ``xsi:schemaLocation`` support for XMI resources,\n* URI mapper like.\n\nThe things that are in the roadmap:\n\n* new implementation of ``EOrderedSet``, ``EList``, ``ESet`` and ``EBag``,\n* new implementation of ``EStringToStringMapEntry`` and ``EFeatureMapEntry``,\n* improve documentation,\n* copy/paste (?).\n\nExisting Projects\n=================\n\nThere is not so much projects proposing to handle model and metamodel in Python.\nThe only projects I found are:\n\n* PyEMOF (http://www.lifl.fr/~marvie/software/pyemof.html)\n* EMF4CPP (https://github.com/catedrasaes-umu/emf4cpp)\n* PyEMOFUC (http://www.istr.unican.es/pyemofuc/index_En.html)\n\nPyEMOF proposes an implementation of the OMG's EMOF in Python. The project\ntargets Python2, only supports Class/Primitive Types (no Enumeration), XMI\nimport/export and does not provide a reflexion layer. The project didn't move\nsince 2005.\n\nEMF4CPP proposes a C++ implementation of EMF. This implementation also\nintroduces Python scripts to call the generated C++ code from a Python\nenvironment. It seems that the EMF4CPP does not provide a reflexive layer\neither.\n\nPyEMOFUC proposes, like PyEMOF, a pure Python implementation of the OMG's EMOF.\nIf we stick to a kind of EMF terminology, PyEMOFUC only supports dynamic\nmetamodels and seems to provide a reflexive layer. The project does not appear\nseems to have moved since a while.\n\nContributors\n============\n\nThanks for making PyEcore better!\n\n* Mike Pagel (`@moltob `_), which is also the author\n of `pyecoregen `_ and `pymultigen `_ (on which pyecoregen is based)\n* Terry Kingston (`@TerryKingston `_)\n* Afonso Pinto (`@afonsobspinto `_)\n* Andy (`@CFAndy `_)\n\n\nAdditional Resources\n====================\n\n* `This article `_\n on the blog of the Professor Jordi Cabot gives more information and\n implementations details about PyEcore.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/pyecore/pyecore-py2", "keywords": "model metamodel EMF Ecore MDE", "license": "BSD 3-Clause", "maintainer": "", "maintainer_email": "", "name": "pyecore-py2", "package_url": "https://pypi.org/project/pyecore-py2/", "platform": "", "project_url": "https://pypi.org/project/pyecore-py2/", "project_urls": { "Homepage": "https://github.com/pyecore/pyecore-py2" }, "release_url": "https://pypi.org/project/pyecore-py2/0.9.0/", "requires_dist": [ "enum34", "chainmap", "backports.functools-lru-cache", "ordered-set", "singledispatch", "lxml", "defusedxml", "restrictedpython (>=4.0b6)" ], "requires_python": "", "summary": "A Python(ic) Implementation of the Eclipse Modeling Framework (EMF/Ecore), Python 2.7 backport", "version": "0.9.0" }, "last_serial": 4425322, "releases": { "0.7.11": [ { "comment_text": "", "digests": { "md5": "e4e4c42107bd8b402945d00388aadeb7", "sha256": "18107f6fd4c82fdfceb265846835b8e1b37f6bb7b25416432907396d948c37fa" }, "downloads": -1, "filename": "pyecore_py2-0.7.11-py2-none-any.whl", "has_sig": false, "md5_digest": "e4e4c42107bd8b402945d00388aadeb7", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 58557, "upload_time": "2018-02-01T13:16:15", "url": "https://files.pythonhosted.org/packages/f0/97/41246b4a7567d8a13bd54a76041fdc36c96c5435183e38f6ddda699b4226/pyecore_py2-0.7.11-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f05f5c0ca0add35a211b19d4dd4f28d9", "sha256": "0b794010a777cac1f6a2ff1a451fca9e84b6bd7b2f3dc73614b7e61ee20d39f5" }, "downloads": -1, "filename": "pyecore-py2-0.7.11.tar.gz", "has_sig": false, "md5_digest": "f05f5c0ca0add35a211b19d4dd4f28d9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 74646, "upload_time": "2018-02-01T13:16:16", "url": "https://files.pythonhosted.org/packages/a8/88/e454dd8dddb7622661d04d295ee45a3c2278b25ebab5913ab0afa7ff3f2a/pyecore-py2-0.7.11.tar.gz" } ], "0.7.12": [ { "comment_text": "", "digests": { "md5": "f1f56b48c526d46cf94046786de57ae2", "sha256": "0e69cac93b04d9bff379fd7dd69cb453dbb00998d202a5eeaed1fd13aa2adfb9" }, "downloads": -1, "filename": "pyecore_py2-0.7.12-py2-none-any.whl", "has_sig": false, "md5_digest": "f1f56b48c526d46cf94046786de57ae2", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 58784, "upload_time": "2018-02-09T10:03:38", "url": "https://files.pythonhosted.org/packages/db/20/3b90a8d6cb454790f2cec8862d659f321bf87158c3a0cb95d20b88504ffa/pyecore_py2-0.7.12-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "35123ae8058b9cf51f9bdb87fdbcd7f1", "sha256": "fcbefed7b6fb040fcb0af29e97fa6a9b6c965514c305af125b31c8662b23fc09" }, "downloads": -1, "filename": "pyecore-py2-0.7.12.tar.gz", "has_sig": false, "md5_digest": "35123ae8058b9cf51f9bdb87fdbcd7f1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 75125, "upload_time": "2018-02-09T10:03:39", "url": "https://files.pythonhosted.org/packages/d5/97/ce441af11f0a042b00837996f575c86395a74eac0e4f009331b17005bbe2/pyecore-py2-0.7.12.tar.gz" } ], "0.7.13": [ { "comment_text": "", "digests": { "md5": "d1d0e838d0e627286674f7917ebc778c", "sha256": "924c30191081f8add8bec2a2f3d32f1b48ce0b4bfd30163286118863dfa86455" }, "downloads": -1, "filename": "pyecore_py2-0.7.13-py2-none-any.whl", "has_sig": false, "md5_digest": "d1d0e838d0e627286674f7917ebc778c", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 58813, "upload_time": "2018-02-11T15:46:41", "url": "https://files.pythonhosted.org/packages/14/73/f6051002b4eba2ec7424fb86549115543540c89517ebc202be0190e36806/pyecore_py2-0.7.13-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8a02f27a6695ad15e07bd1c363d6cf5c", "sha256": "6a1fa9c675bba0b6eefa8acdc096b091794f11598423851ea199af5905716563" }, "downloads": -1, "filename": "pyecore-py2-0.7.13.tar.gz", "has_sig": false, "md5_digest": "8a02f27a6695ad15e07bd1c363d6cf5c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 75240, "upload_time": "2018-02-11T15:46:43", "url": "https://files.pythonhosted.org/packages/8d/9d/760f4fcf93f344d843451ee366e9b9743dbdf4ce00d2223edef9cfce8d99/pyecore-py2-0.7.13.tar.gz" } ], "0.7.2": [ { "comment_text": "", "digests": { "md5": "5256f8dbe54242d1d0bb64374a4dea55", "sha256": "f169f154d20e6a596d7325e6c132f9396ebc478c46adffdb6464d3d5f2c2413a" }, "downloads": -1, "filename": "pyecore_py2-0.7.2-py2-none-any.whl", "has_sig": false, "md5_digest": "5256f8dbe54242d1d0bb64374a4dea55", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 54810, "upload_time": "2017-10-21T08:22:40", "url": "https://files.pythonhosted.org/packages/31/0a/910b3e29eb2c6251a9b5d21f9575889cf289aa618591f9a4c3aa8e7677cc/pyecore_py2-0.7.2-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d1202314f3525ab3dc7b51e37dccbecc", "sha256": "0eae4426963207cf2ac5df011d97083d5a7883d0850d2d9c1699ecf02a95ee3e" }, "downloads": -1, "filename": "pyecore-py2-0.7.2.tar.gz", "has_sig": false, "md5_digest": "d1202314f3525ab3dc7b51e37dccbecc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 68451, "upload_time": "2017-10-21T08:22:41", "url": "https://files.pythonhosted.org/packages/4c/a3/e47e57e62d76fb5eb2f3c6f3bbe7644271524f3c12b4518cd2eac0a2050d/pyecore-py2-0.7.2.tar.gz" } ], "0.7.3": [ { "comment_text": "", "digests": { "md5": "2de22515b5bed27f99c6567a152dd5b2", "sha256": "f3f4bc30c2211c4f6ef2ce9eec1d40b6897bc901721b8f59905ef74d4de2ff09" }, "downloads": -1, "filename": "pyecore_py2-0.7.3-py2-none-any.whl", "has_sig": false, "md5_digest": "2de22515b5bed27f99c6567a152dd5b2", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 56274, "upload_time": "2017-10-25T14:16:22", "url": "https://files.pythonhosted.org/packages/99/d8/80edbc6d84fa297c5afda30d3149a74474d1c2068a47556dee6311dad0c9/pyecore_py2-0.7.3-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c419cc55b8846bcd790cf784041a9c31", "sha256": "8a32ae71e05d3ed3ca10b619de119c0429e5bbe5a5b2ea7ad25e7f34919b3ab5" }, "downloads": -1, "filename": "pyecore-py2-0.7.3.tar.gz", "has_sig": false, "md5_digest": "c419cc55b8846bcd790cf784041a9c31", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 70835, "upload_time": "2017-10-25T14:16:23", "url": "https://files.pythonhosted.org/packages/c4/7b/1ec9474288f6a2149e25f0ba31d95fb3781f7592b094d3511eef3c00455c/pyecore-py2-0.7.3.tar.gz" } ], "0.7.4": [ { "comment_text": "", "digests": { "md5": "377a4ffab013ddf380ac4fb69f41109f", "sha256": "f4ed7f234b4e498ac801d62c27bf6273c7b6690283eeeaa3f1a9c6978e8eb32e" }, "downloads": -1, "filename": "pyecore_py2-0.7.4-py2-none-any.whl", "has_sig": false, "md5_digest": "377a4ffab013ddf380ac4fb69f41109f", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 56359, "upload_time": "2017-10-27T12:15:13", "url": "https://files.pythonhosted.org/packages/e1/be/8db7bc64616af65ddb9cdfec6c523fe2ed7e696302a29322d95cea9e2422/pyecore_py2-0.7.4-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c44acee6e43c257222f6fc4635eb2430", "sha256": "b9896b30d765c793d0ca1d5e62d7aede82de592ebe70edc892e886ed905f77a5" }, "downloads": -1, "filename": "pyecore-py2-0.7.4.tar.gz", "has_sig": false, "md5_digest": "c44acee6e43c257222f6fc4635eb2430", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 71610, "upload_time": "2017-10-27T12:15:15", "url": "https://files.pythonhosted.org/packages/93/a0/a3269f95d6499dfc43630a23a4916a660da36a335e51f4d6466bf440fdda/pyecore-py2-0.7.4.tar.gz" } ], "0.7.5": [ { "comment_text": "", "digests": { "md5": "ec0d18514a771c79ab60a3216eefae05", "sha256": "72ec88f98d2d8b71de0d1bf816a7d81b3db789944d93696adf21a93e3365d740" }, "downloads": -1, "filename": "pyecore_py2-0.7.5-py2-none-any.whl", "has_sig": false, "md5_digest": "ec0d18514a771c79ab60a3216eefae05", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 58319, "upload_time": "2017-11-13T19:09:19", "url": "https://files.pythonhosted.org/packages/3e/f1/636ec3a75e1496513973532f2d6e02c23e77c5dfbdcc562a02653fb6665b/pyecore_py2-0.7.5-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "856009a7aba1387d74b5406789db308d", "sha256": "e806400b7cae66a084b885e67c01182b3efce5702e5795b7b4dcf3b2c695e665" }, "downloads": -1, "filename": "pyecore-py2-0.7.5.tar.gz", "has_sig": false, "md5_digest": "856009a7aba1387d74b5406789db308d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 73863, "upload_time": "2017-11-13T19:09:22", "url": "https://files.pythonhosted.org/packages/c4/c1/151146c63387d32e834e7e4cf95e4cab607dd13455027496cf49b0ed832d/pyecore-py2-0.7.5.tar.gz" } ], "0.7.6": [ { "comment_text": "", "digests": { "md5": "c88d9d95631c05c071ec0549b9b53ade", "sha256": "4b200fabc3604e0be20435692df2fb9034c7e48c1225473ac60fe7cd9177a85c" }, "downloads": -1, "filename": "pyecore_py2-0.7.6-py2-none-any.whl", "has_sig": false, "md5_digest": "c88d9d95631c05c071ec0549b9b53ade", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 58579, "upload_time": "2017-11-25T15:34:03", "url": "https://files.pythonhosted.org/packages/79/d6/9c2d8a9c7509e44161366c1fb38ee75bbb597ba2dc2d0fb86453656411fd/pyecore_py2-0.7.6-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c0688129ac80f9b3f4ff2ab236e7cd10", "sha256": "410781860e033a20be400bfbd2b7e6c9cfa58ccaccc118a067b12e21b3cca6ca" }, "downloads": -1, "filename": "pyecore-py2-0.7.6.tar.gz", "has_sig": false, "md5_digest": "c0688129ac80f9b3f4ff2ab236e7cd10", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 74373, "upload_time": "2017-11-25T15:34:04", "url": "https://files.pythonhosted.org/packages/2d/9c/ac6b1138c9b7e72c251a56949a0e98a6ebab3d02004f454dd0ed7c27c79b/pyecore-py2-0.7.6.tar.gz" } ], "0.7.7": [ { "comment_text": "", "digests": { "md5": "174c07254fe1addd126ea3f39aad11fc", "sha256": "933bb047d6eab57f453926e3b66fe04994273c311ee360aa210a904a5aef504c" }, "downloads": -1, "filename": "pyecore_py2-0.7.7-py2-none-any.whl", "has_sig": false, "md5_digest": "174c07254fe1addd126ea3f39aad11fc", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 58578, "upload_time": "2017-12-06T13:07:45", "url": "https://files.pythonhosted.org/packages/c5/33/30d61f282709921eb8279b5e1ec9b1b4b5b3353d224d4c409d46cf2b9c9f/pyecore_py2-0.7.7-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "9d0699701bb69b81ece0e74b5f8e02cc", "sha256": "1a24a211591ce546dffa1204e3bc9c2dda4162e0670132a99d96471443102399" }, "downloads": -1, "filename": "pyecore-py2-0.7.7.tar.gz", "has_sig": false, "md5_digest": "9d0699701bb69b81ece0e74b5f8e02cc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 74555, "upload_time": "2017-12-06T13:07:46", "url": "https://files.pythonhosted.org/packages/f6/5a/bb098d1f31a8151fe62adf9c4aee2d5b4a3ee0867572ce2352fa67b10624/pyecore-py2-0.7.7.tar.gz" } ], "0.8.0": [ { "comment_text": "", "digests": { "md5": "94a5d604808af9a6618fbb0bed96cb80", "sha256": "f5c97e26b984166f2bd6b1d4260d05c9b7b472bbedd6b66c0c6e317701447923" }, "downloads": -1, "filename": "pyecore_py2-0.8.0-py2-none-any.whl", "has_sig": false, "md5_digest": "94a5d604808af9a6618fbb0bed96cb80", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 46525, "upload_time": "2018-04-07T14:30:03", "url": "https://files.pythonhosted.org/packages/8a/6a/f2e0cdc53a4e9df06021aacf6e2590d023a930b42b9606e246120a78838f/pyecore_py2-0.8.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ce4896946e8e7a4bc8f0929701378e72", "sha256": "5ec985eabb262c566667e7207c0b47322699986755ef3b7a10995d371040c81e" }, "downloads": -1, "filename": "pyecore-py2-0.8.0.tar.gz", "has_sig": false, "md5_digest": "ce4896946e8e7a4bc8f0929701378e72", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 77137, "upload_time": "2018-04-07T14:30:04", "url": "https://files.pythonhosted.org/packages/48/75/f92fed7c9955fcaa39800a316f1e116cc5a63bf4e157cfdd7db2528127de/pyecore-py2-0.8.0.tar.gz" } ], "0.8.1": [ { "comment_text": "", "digests": { "md5": "54497e5e622e10c0cb391f9c89365377", "sha256": "8432b850d3e0ecf76acfb53d677ee8eadf7f459733d9a511dec0067c1ab1271f" }, "downloads": -1, "filename": "pyecore_py2-0.8.1-py2-none-any.whl", "has_sig": false, "md5_digest": "54497e5e622e10c0cb391f9c89365377", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 46943, "upload_time": "2018-04-17T11:33:46", "url": "https://files.pythonhosted.org/packages/98/35/dcf6065c93ed8c6b002b81706bb81cbef137b35126d338664b918b2c45e9/pyecore_py2-0.8.1-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e5a903317d93386876c8ce73086c8497", "sha256": "b438680014f1c343ac90ab665ad4cf0cc9c5d884465ed94e1f5c8881031f5319" }, "downloads": -1, "filename": "pyecore-py2-0.8.1.tar.gz", "has_sig": false, "md5_digest": "e5a903317d93386876c8ce73086c8497", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 77486, "upload_time": "2018-04-17T11:33:47", "url": "https://files.pythonhosted.org/packages/fd/00/44b73405bd281adc6da72491bade87274a58276b5140d42cdf4bf4e354d1/pyecore-py2-0.8.1.tar.gz" } ], "0.8.2": [ { "comment_text": "", "digests": { "md5": "0c9d954d4ab26a1ababa10d123392b72", "sha256": "54ebfff27f74873322c9424e55bfa2197d5ef2219320a467d30df7e99e96be47" }, "downloads": -1, "filename": "pyecore_py2-0.8.2-py2-none-any.whl", "has_sig": false, "md5_digest": "0c9d954d4ab26a1ababa10d123392b72", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 47040, "upload_time": "2018-06-10T15:00:37", "url": "https://files.pythonhosted.org/packages/ed/f9/d4740d3d6ec8389bd2b719e632a5406b66957b82b00b9f696adf5c8c648b/pyecore_py2-0.8.2-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6b5b5431e12063ca1ef822a4638bf308", "sha256": "91741e7b56ee75c5b7496d3f21effb719a0a96bda7cd8607500c4cf4df7a4a22" }, "downloads": -1, "filename": "pyecore-py2-0.8.2.tar.gz", "has_sig": false, "md5_digest": "6b5b5431e12063ca1ef822a4638bf308", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 77972, "upload_time": "2018-06-10T15:00:38", "url": "https://files.pythonhosted.org/packages/05/13/ced2be1ef083261ee2222ac266ac56027680c986b710fa62d944d352af3d/pyecore-py2-0.8.2.tar.gz" } ], "0.8.3": [ { "comment_text": "", "digests": { "md5": "9acfe3a3cd6963ed233ada6d25562e23", "sha256": "f5842005be5d1649d912f6b0df21ae6bf1f7d1a356efee292e40e89a5314dd0a" }, "downloads": -1, "filename": "pyecore_py2-0.8.3-py2-none-any.whl", "has_sig": false, "md5_digest": "9acfe3a3cd6963ed233ada6d25562e23", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 47748, "upload_time": "2018-06-23T09:22:53", "url": "https://files.pythonhosted.org/packages/9e/29/f67df8acfe019c09961b8666383e06f7918f1bc20685e536a73e5c8d7a8e/pyecore_py2-0.8.3-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4c0d26b94d8f0ea30d4e3b42717a688b", "sha256": "17ffd65a5b94cc456e8b9ce90bcba2476a9638bb1f29fb44c6af5838bf8e0d20" }, "downloads": -1, "filename": "pyecore-py2-0.8.3.tar.gz", "has_sig": false, "md5_digest": "4c0d26b94d8f0ea30d4e3b42717a688b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 78854, "upload_time": "2018-06-23T09:22:54", "url": "https://files.pythonhosted.org/packages/78/09/53a0c851e2a33be37e4fdfcc5dfd7dc134bb1538ced497f86224e9c27465/pyecore-py2-0.8.3.tar.gz" } ], "0.8.4": [ { "comment_text": "", "digests": { "md5": "89fa21109269e41ecb5f2c4fc88e5cfc", "sha256": "a1319b43a8b59a88096f5f078407196f780179fd2cbb61cc5189d5ef075d5346" }, "downloads": -1, "filename": "pyecore_py2-0.8.4-py2-none-any.whl", "has_sig": false, "md5_digest": "89fa21109269e41ecb5f2c4fc88e5cfc", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 47802, "upload_time": "2018-06-26T05:00:40", "url": "https://files.pythonhosted.org/packages/e7/0b/227d25d0b9a6826fffd2e03edc173c8567cd9c007e60b673dd1874cc4743/pyecore_py2-0.8.4-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "32d9518c578e2558fd263d579886ad90", "sha256": "77c46151c995dd4ea362b9f0567380943154b61cc658912224122e6e857e21e3" }, "downloads": -1, "filename": "pyecore-py2-0.8.4.tar.gz", "has_sig": false, "md5_digest": "32d9518c578e2558fd263d579886ad90", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 78981, "upload_time": "2018-06-26T05:00:41", "url": "https://files.pythonhosted.org/packages/b7/02/a22aec2d4eed94dd7ceae08fd3857e9aacd128be276ae47861cb309c5a0d/pyecore-py2-0.8.4.tar.gz" } ], "0.8.5": [ { "comment_text": "", "digests": { "md5": "0a5f41dc7449263e499d8b8b719f9cc1", "sha256": "08c4a75e7a5cebe71a8be82a83548391df54f7606b4e8a67945b8daca1a99027" }, "downloads": -1, "filename": "pyecore_py2-0.8.5-py2-none-any.whl", "has_sig": false, "md5_digest": "0a5f41dc7449263e499d8b8b719f9cc1", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 48012, "upload_time": "2018-06-27T12:06:39", "url": "https://files.pythonhosted.org/packages/4c/c7/073ce1ecc3b745384bee7ae3e387a86c9fc89e728102b015ae6e6a4ab293/pyecore_py2-0.8.5-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6da2971852063dccad429a7e30e1f858", "sha256": "f695f199f23c2582f0f8da3479a84b37dfd2222902c7fa09a0cda210ee728d78" }, "downloads": -1, "filename": "pyecore-py2-0.8.5.tar.gz", "has_sig": false, "md5_digest": "6da2971852063dccad429a7e30e1f858", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 79169, "upload_time": "2018-06-27T12:06:40", "url": "https://files.pythonhosted.org/packages/0a/d7/d986334f370692085e8c18527a550d328af5ca48b49c9d4960b2bc09caa8/pyecore-py2-0.8.5.tar.gz" } ], "0.8.6": [ { "comment_text": "", "digests": { "md5": "a01d9f29bfcebaa32927c693ec1a6266", "sha256": "f6599847191f7b54e2ae01364339d080a1646723ea4332f0b4bc25e8ad505fc5" }, "downloads": -1, "filename": "pyecore_py2-0.8.6-py2-none-any.whl", "has_sig": false, "md5_digest": "a01d9f29bfcebaa32927c693ec1a6266", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 47994, "upload_time": "2018-07-24T12:18:11", "url": "https://files.pythonhosted.org/packages/e9/1b/f6f29b180b55fb7d44bdc294bc69b63c29b554658b3a342516baa7c06e82/pyecore_py2-0.8.6-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b43b4424c7e6a2443755ecc332eebea9", "sha256": "89a4dd9fab327cf4ff55ed6ed837bb73abe368298b064eac6784d931f194c7c7" }, "downloads": -1, "filename": "pyecore-py2-0.8.6.tar.gz", "has_sig": false, "md5_digest": "b43b4424c7e6a2443755ecc332eebea9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 79404, "upload_time": "2018-07-24T12:18:12", "url": "https://files.pythonhosted.org/packages/ab/a4/1eadefe81f0403bab11a10e34c7665bc9ffdc3efd060e2adae987d6db938/pyecore-py2-0.8.6.tar.gz" } ], "0.8.7": [ { "comment_text": "", "digests": { "md5": "b68e32999894dd5f2528044ec6ff4ff4", "sha256": "8e29595d936401a7b698daa6a86a6a414950c95161f56e132ea6c1574b576d21" }, "downloads": -1, "filename": "pyecore_py2-0.8.7-py2-none-any.whl", "has_sig": false, "md5_digest": "b68e32999894dd5f2528044ec6ff4ff4", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 48143, "upload_time": "2018-08-23T18:00:09", "url": "https://files.pythonhosted.org/packages/1e/bb/e36d9d510573ef350ee67e86e84bc3e413768a589a81c34ed215c0cf4271/pyecore_py2-0.8.7-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c5029122ab740efa8412356521957d30", "sha256": "fd1293634da519d1639991e96e291e5f5e0212da542fabb4fe07c519f7e4559c" }, "downloads": -1, "filename": "pyecore-py2-0.8.7.tar.gz", "has_sig": false, "md5_digest": "c5029122ab740efa8412356521957d30", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 79893, "upload_time": "2018-08-23T18:00:10", "url": "https://files.pythonhosted.org/packages/29/13/07dfa0df985b458ebed5271b07148380a323f486c0398b078086e93a7345/pyecore-py2-0.8.7.tar.gz" } ], "0.8.8": [ { "comment_text": "", "digests": { "md5": "8a357ad17fd54d4ce3e6d43a0d47c757", "sha256": "fab411e359212a9692e46eba96f37eebe74e6db3155a28711667b75bcfa24105" }, "downloads": -1, "filename": "pyecore_py2-0.8.8-py2-none-any.whl", "has_sig": false, "md5_digest": "8a357ad17fd54d4ce3e6d43a0d47c757", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 48174, "upload_time": "2018-08-30T07:54:21", "url": "https://files.pythonhosted.org/packages/fd/be/53ed98c364b50f61fa7b094beea8d61a2f7500943bee595567f53a5baf3a/pyecore_py2-0.8.8-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c8420b2557141e3037609e1d5d89c967", "sha256": "f730419022aff84be498cd2f48bc2f76bf236fec9f954875e5d0e54f91e842ce" }, "downloads": -1, "filename": "pyecore-py2-0.8.8.tar.gz", "has_sig": false, "md5_digest": "c8420b2557141e3037609e1d5d89c967", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 80054, "upload_time": "2018-08-30T07:54:23", "url": "https://files.pythonhosted.org/packages/72/1b/95295f87dba614362ef61fe046d9488005199e90bead8c8853caffc5f2c5/pyecore-py2-0.8.8.tar.gz" } ], "0.9.0": [ { "comment_text": "", "digests": { "md5": "a82c14515b7ee40e75540430bbf5adf9", "sha256": "d3cb93024bba7afe7ddd06b5f51a400550f69828a0a9952837109595ce205411" }, "downloads": -1, "filename": "pyecore_py2-0.9.0-py2-none-any.whl", "has_sig": false, "md5_digest": "a82c14515b7ee40e75540430bbf5adf9", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 49698, "upload_time": "2018-10-28T21:26:25", "url": "https://files.pythonhosted.org/packages/82/5f/052fdea8c4563a2b911d6691a9a36b0f7597a90e5c8295a7f5690cdc9724/pyecore_py2-0.9.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "983e041a0ce76eb66485207986dd3c3f", "sha256": "1699c41a08c11516548a580351c2a695b6bb1bfebf29eabbba67fc820c44fab2" }, "downloads": -1, "filename": "pyecore-py2-0.9.0.tar.gz", "has_sig": false, "md5_digest": "983e041a0ce76eb66485207986dd3c3f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 81225, "upload_time": "2018-10-28T21:26:27", "url": "https://files.pythonhosted.org/packages/8f/63/d9fb8b3f1b2b9993cd2773a0a71537f274ce36599850b3645da924ce8b22/pyecore-py2-0.9.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "a82c14515b7ee40e75540430bbf5adf9", "sha256": "d3cb93024bba7afe7ddd06b5f51a400550f69828a0a9952837109595ce205411" }, "downloads": -1, "filename": "pyecore_py2-0.9.0-py2-none-any.whl", "has_sig": false, "md5_digest": "a82c14515b7ee40e75540430bbf5adf9", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 49698, "upload_time": "2018-10-28T21:26:25", "url": "https://files.pythonhosted.org/packages/82/5f/052fdea8c4563a2b911d6691a9a36b0f7597a90e5c8295a7f5690cdc9724/pyecore_py2-0.9.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "983e041a0ce76eb66485207986dd3c3f", "sha256": "1699c41a08c11516548a580351c2a695b6bb1bfebf29eabbba67fc820c44fab2" }, "downloads": -1, "filename": "pyecore-py2-0.9.0.tar.gz", "has_sig": false, "md5_digest": "983e041a0ce76eb66485207986dd3c3f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 81225, "upload_time": "2018-10-28T21:26:27", "url": "https://files.pythonhosted.org/packages/8f/63/d9fb8b3f1b2b9993cd2773a0a71537f274ce36599850b3645da924ce8b22/pyecore-py2-0.9.0.tar.gz" } ] }