Skip to content
This repository has been archived by the owner on Apr 23, 2021. It is now read-only.

Commit

Permalink
Merge pull request #334 from simphony/remove-create-data-container
Browse files Browse the repository at this point in the history
Removed unused function and introduced special constructor
  • Loading branch information
mehdisadeghi committed Oct 24, 2016
2 parents a3c9e3e + 34d718b commit b43f15a
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 144 deletions.
12 changes: 0 additions & 12 deletions scripts/tests/test_meta_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

from simphony.api import CUBA
from simphony.cuds.meta import api as meta_class
from simphony.core.data_container import create_data_container


class TestMetaClass(unittest.TestCase):
Expand Down Expand Up @@ -331,14 +330,3 @@ def test_not_sharing_mutable(self):
box2 = meta_class.Box()
box1.vector[0][0] = 1.
self.assertNotEqual(box1.vector[0][0], box2.vector[0][0])

def test_assign_data_with_unsupported_parameters(self):
''' Test for assigning unsupported CUBA keys to data '''
RDC = create_data_container(meta_class.Material.supported_parameters())
rdc = RDC()
rdc[CUBA.NAME] = 'test_material'
rdc[CUBA.DESCRIPTION] = 'Just another material'
mat = meta_class.Material(data=rdc)

with self.assertRaises(ValueError):
mat.data[CUBA.SIZE] = 1
103 changes: 31 additions & 72 deletions simphony/core/data_container.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import sys as _sys

from simphony.core.cuba import CUBA


Expand All @@ -20,24 +18,44 @@ class DataContainer(dict):
"""

# Memory usage optimization.
__slots__ = ()

# These are the allowed CUBA keys (faster to convert to set for lookup)
restricted_keys = frozenset(CUBA)

# Map CUBA enum name to CUBA enum
# Used by assigning key using keyword name
_restricted_mapping = CUBA.__members__

def __init__(self, *args, **kwargs):
""" Constructor.
Initialization follows the behaviour of the python dict class.
"""

super(DataContainer, self).__init__()

# These are the allowed CUBA keys (faster to convert to set for lookup)
self.restricted_keys = frozenset(CUBA)

# Map CUBA enum name to CUBA enum
# Used by assigning key using keyword name
self._restricted_mapping = CUBA.__members__

self.update(*args, **kwargs)

@classmethod
def new_with_restricted_keys(cls, restricted_keys):
"""Instantiate a DataContainer with the given restricted keys
Parameters
----------
restricted_keys : sequence
CUBA IntEnum
Returns
-------
new instance of DataContainer.
"""
# Make sure all restricted keys are CUBA keys
if any(not isinstance(key, CUBA) for key in restricted_keys):
raise ValueError('All restricted keys should be CUBA IntEnum')

self = super(DataContainer, cls).__new__(cls)
self.restricted_keys = frozenset(restricted_keys)
self._restricted_mapping = {key.name: key for key in restricted_keys}

return self

def __setitem__(self, key, value):
""" Set/Update the key value only when the key is a CUBA key.
Expand Down Expand Up @@ -101,62 +119,3 @@ def _check_mapping(self, mapping):
if invalid_keys:
message = 'Key(s) {!r} are not in the supported CUBA keywords'
raise ValueError(message.format(invalid_keys))


def create_data_container(restricted_keys,
class_name='RestrictedDataContainer'):
''' Create a DataContainer subclass with the given
restricted keys
Note
----
For pickling to work, the created class needs to be assigned
a name `RestrictedDataContainer` in the module where it is
created
Parameters
----------
restricted_keys : sequence
CUBA IntEnum
class_name : str
Name of the returned class
Returns
-------
RestrictedDataContainer : DataContainer
subclass of DataContainer with the given `restricted_keys`
Examples
--------
>>> RestrictedDataContainer = create_data_container((CUBA.NAME,
CUBA.VELOCITY))
>>> container = RestrictedDataContainer()
>>> container.restricted_keys
frozenset({<CUBA.VELOCITY: 55>, <CUBA.NAME: 61>})
>>> container[CUBA.NAME] = 'name'
>>> container[CUBA.POSITION] = (1.0, 1.0, 1.0)
...
ValueError: Key <CUBA.POSITION: 119> is not in the supported CUBA keywords
'''
# Make sure all restricted keys are CUBA keys
if any(not isinstance(key, CUBA) for key in restricted_keys):
raise ValueError('All restricted keys should be CUBA IntEnum')

mapping = {key.name: key for key in restricted_keys}

new_class = type(class_name, (DataContainer,),
{'__doc__': DataContainer.__doc__,
'__slots__': (),
'restricted_keys': frozenset(restricted_keys),
'_restricted_mapping': mapping})

# For the dynamically generated class to have a chance to be
# pickleable. Bypass this step for some Python implementations
try:
new_class.__module__ = _sys._getframe(1).f_globals.get(
'__name__', '__main__')
except AttributeError:
pass

return new_class
67 changes: 7 additions & 60 deletions simphony/core/tests/test_data_container.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import cPickle
from io import BytesIO
import unittest

from simphony.core.cuba import CUBA
from simphony.core.data_container import DataContainer, create_data_container
from simphony.core.data_container import DataContainer


class TestDataContainer(unittest.TestCase):
Expand Down Expand Up @@ -150,66 +148,15 @@ def test_setitem_with_non_cuba_key(self):
with self.assertRaises(ValueError):
container[100] = 29

def test_new_with_restricted_keys(self):
restricted_keys = frozenset([CUBA.UUID, CUBA.NAME])
container = DataContainer.new_with_restricted_keys(restricted_keys)
self.assertEqual(container.restricted_keys, restricted_keys)

# Create a container class here for testing pickling
# As with all dynamically created classes, it needs to be
# properly named in a module in order for pickling to work
RestrictedDataContainer = create_data_container(CUBA)
container[CUBA.NAME] = "foo"


class TestRestrictedDataContainer(unittest.TestCase):

def setUp(self):
self.maxDiff = None
iter_cuba = iter(CUBA)
# The first 9 keys are supported keys
self.valid_keys = tuple(iter_cuba.next() for i in range(1, 10))
# The rest are not supported
self.invalid_keys = tuple(key for key in iter_cuba)

def test_setitem_with_valid_key(self):
container = create_data_container(self.valid_keys)()
container[self.valid_keys[0]] = 20
self.assertIsInstance(container.keys()[0], CUBA)
self.assertEqual(container[self.valid_keys[0]], 20)

def test_setitem_with_invalid_key(self):
container = create_data_container(self.valid_keys)()

for key in self.invalid_keys:
with self.assertRaises(ValueError):
container[key] = 1

def test_update_with_valid_keys(self):
data = {key: key+3 for key in self.valid_keys}
container = create_data_container(self.valid_keys)(data)
self.assertTrue(all(key in self.valid_keys for key in container))

def test_update_with_some_invalid_keys(self):
data = {key: key+3 for key in self.valid_keys}
data[self.invalid_keys[0]] = 20

with self.assertRaises(ValueError):
create_data_container(self.valid_keys)(data)

def test_error_with_non_cuba_keys(self):
with self.assertRaises(ValueError):
create_data_container((1, 2))

def test_data_container_can_be_pickled(self):
# Create a container with data
container = RestrictedDataContainer()
for key in CUBA:
container[key] = key+3

# Pickle and write to a buffer
stream = BytesIO()
cPickle.dump(container, stream)
stream.seek(0)

# Restore the data
pickled_data = cPickle.load(stream)
self.assertDictEqual(pickled_data, container)
container[CUBA.DESCRIPTION] = "hello"


if __name__ == '__main__':
Expand Down

0 comments on commit b43f15a

Please sign in to comment.