Skip to content
This repository has been archived by the owner on Mar 16, 2024. It is now read-only.

Commit

Permalink
Backport new features and bugfixes
Browse files Browse the repository at this point in the history
* Add special deserialization method for EEnum
* Change the EEnum implementation for default values
* Remove systematic serialization of EClass reference
  • Loading branch information
aranega committed Oct 27, 2017
1 parent 92d0bc3 commit 0a22eb7
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 11 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ Changelog
Note that for the ``resolve_eclass`` method, the use of a cache like
``lru_cache`` is often a good idea.

- Remove systematic serialization of ``EClass`` reference in JSON serializer. In
some cases where the containing feature type is the same than the serialized
object, the ``eClass`` entry in the JSON resource is not required. This allows
to reduce the resource size a little bit more.

- Change the ``EEnum`` implementation for default values. The default value of
an ``EENum`` is computed from the first element in the ``eLiterals``. The
change of a ``default_value`` is performed by 'reordering' the ``eLiterals``
list.


**Bugfixes**

- Refactor ``EProxy`` implementation. The new ``EProxy`` implementation get rid
Expand All @@ -34,6 +45,12 @@ Changelog
they are part of the same level than the ``Resource`` which is serialized.
However, they are part of something "greater".

- Add special deserialization method for ``EEnum``. The basic deserialization
method for ``EEnum`` instance was the same than the one for ``EDataType``.
They only takes the string and put it in the feature instance. Instead, when
a string is found for an ``EEnum`` feature, the ``EEnumLiteral`` must be
searched. This new ``from_string`` implementation just does this.


0.7.3
+++++
Expand Down
27 changes: 22 additions & 5 deletions pyecore/ecore.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,13 +811,27 @@ def __init__(self, name=None, default_value=None, literals=None, **kwargs):
lit_name = '_' + lit_name if (unicode(lit_name[:1], 'utf-8')
.isnumeric()) \
else lit_name
literal = EEnumLiteral(i, lit_name)
literal = EEnumLiteral(value=i, name=lit_name)
self.eLiterals.append(literal)
self.__setattr__(lit_name, literal)
if default_value:
self.default_value = self.__getattribute__(default_value)
elif not default_value and literals:
self.default_value = self.eLiterals[0]
self.default_value = default_value

@property
def default_value(self):
return self.eLiterals[0] if self.eLiterals else None

@default_value.setter
def default_value(self, value):
if value in self:
literal = (value if isinstance(value, EEnumLiteral)
else self.getEEnumLiteral(value))
literals = self.eLiterals
i = literals.index(literal)
literals.insert(0, literals.pop(i))
else:
raise AttributeError('Enumeration literal {} does not exist '
'in {}'.format(value, self))

def __contains__(self, key):
if isinstance(key, EEnumLiteral):
Expand All @@ -838,13 +852,16 @@ def getEEnumLiteral(self, name=None, value=0):
except StopIteration:
return None

def from_string(self, value):
return self.getEEnumLiteral(name=value)

def __repr__(self):
name = self.name or ''
return '{}[{}]'.format(name, str(self.eLiterals))


class EEnumLiteral(ENamedElement):
def __init__(self, value=0, name=None, **kwargs):
def __init__(self, name=None, value=0, **kwargs):
super(EEnumLiteral, self).__init__(name, **kwargs)
self.value = value

Expand Down
17 changes: 11 additions & 6 deletions pyecore/resources/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ def _to_ref_from_obj(self, obj):
return ref

def _to_dict_from_obj(self, obj):
uri = self.serialize_eclass(obj.eClass)
d = {'eClass': uri}
d = {}
containingFeature = obj.eContainmentFeature()
if not containingFeature or obj.eClass is not containingFeature.eType:
uri = self.serialize_eclass(obj.eClass)
d['eClass'] = uri
for attr in obj._isset:
is_ereference = isinstance(attr, Ecore.EReference)
is_ref = is_ereference and not attr.containment
Expand Down Expand Up @@ -95,12 +97,15 @@ def resolve_eclass(self, uri_eclass):
return decoders.resolve(uri_eclass, self)

def to_obj(self, d, owning_feature=None, first=False):
uri_eclass = d['eClass']
is_ref = '$ref' in d
if is_ref:
return Ecore.EProxy(path=d['$ref'], resource=self)
excludes = ['eClass', '$ref', 'uuid']
eclass = self.resolve_eclass(uri_eclass)
if 'eClass' in d:
uri_eclass = d['eClass']
eclass = self.resolve_eclass(uri_eclass)
else:
eclass = owning_feature.eType
if not eclass:
raise ValueError('Unknown metaclass for uri "{}"'
.format(uri_eclass))
Expand Down Expand Up @@ -155,8 +160,8 @@ def process_inst(self, inst, features, owning_feature=None):
feature.eOpposite is not owning_feature:
collection.extend(elements)
elif isinstance(value, unicode):
inst.eSet(feature, value.encode())
elif not isinstance(value, str):
inst.eSet(feature, feature.eType.from_string(value.encode()))
elif isinstance(value, str):
inst.eSet(feature, feature.eType.from_string(value))
else:
inst.eSet(feature, value)
21 changes: 21 additions & 0 deletions tests/json/data/minimal.ecore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="mytest" nsURI="http://mytest/1.0" nsPrefix="myprefix">
<eClassifiers xsi:type="ecore:EClass" name="A">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="B">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="enumatt" eType="#//MyEnum"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="MyRoot">
<eStructuralFeatures xsi:type="ecore:EReference" name="aContainer" upperBound="-1"
eType="#//A" containment="true"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="bContainer" upperBound="-1"
eType="#//B" containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EEnum" name="MyEnum">
<eLiterals name="ABC"/>
<eLiterals name="DEF"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="C" eSuperTypes="#//B"/>
</ecore:EPackage>
8 changes: 8 additions & 0 deletions tests/json/data/minimal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"eClass" : "http://mytest/1.0#//MyRoot",
"aContainer" : [ { } ],
"bContainer" : [ { }, {
"eClass" : "http://mytest/1.0#//C",
"enumatt" : "DEF"
} ]
}
23 changes: 23 additions & 0 deletions tests/json/test_json_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,26 @@ def test__jsonresource_load_mm_errors(rset, mm):
json_file = path.join('tests', 'json', 'data', 'e2.json')
with pytest.raises(ValueError):
rset.get_resource(json_file)


def test__jsonresource_load_enum_incomplete_eclassrefs(rset):
mm_file = path.join('tests', 'json', 'data', 'minimal.ecore')
mm = rset.get_resource(mm_file).contents[0]
rset.metamodel_registry[mm.nsURI] = mm

A = mm.getEClassifier('A')
B = mm.getEClassifier('B')
C = mm.getEClassifier('C')

MyEnum = mm.getEClassifier('MyEnum')

json_file = path.join('tests', 'json', 'data', 'minimal.json')
root = rset.get_resource(json_file).contents[0]

assert len(root.aContainer) == 1
assert len(root.bContainer) == 2
assert root.aContainer[0].eClass is A
assert root.bContainer[0].eClass is B
assert root.bContainer[1].eClass is C
assert root.bContainer[0].enumatt is MyEnum.getEEnumLiteral('ABC')
assert root.bContainer[1].enumatt is MyEnum.getEEnumLiteral('DEF')
25 changes: 25 additions & 0 deletions tests/test_dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,3 +892,28 @@ def test_eobject_kargs_init():

with pytest.raises(BadValueError):
a.test = 4


def test_eenum_defaultvalue_computed():
E = EEnum('enum')
A = EEnumLiteral(name='A')
E.eLiterals.append(A)
B = EEnumLiteral(name='B')
E.eLiterals.append(B)
C = EEnumLiteral(name='C')
E.eLiterals.append(C)
assert E.default_value is A

E.default_value = 'B'
assert E.default_value is B
assert E.eLiterals[0] is B

with pytest.raises(AttributeError):
E.default_value = 'D'

D = EEnumLiteral(name='D')
E.eLiterals.insert(0, D)
assert E.default_value is D

assert E.from_string('A') is A
assert E.to_string(A) is 'A'

0 comments on commit 0a22eb7

Please sign in to comment.