diff --git a/.travis.yml b/.travis.yml index 64226cd3..5e16de67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,23 +30,34 @@ before_install: install: - export PYVER=${TRAVIS_PYTHON_VERSION:0:1} + - if [ $PYVER = 3 ]; then + export PYCMD=python3; + export PIPCMD=pip3; + else + export PYCMD=python; + export PIPCMD=pip; + fi; + - if [ $COVERALLS = 1 ]; then - pip install --upgrade coveralls; + $PIPCMD install --upgrade coveralls; fi; + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - which python; + which $PYCMD; source venv/bin/activate; - which python; - fi - - pip install lxml enum34 pyyaml rdflib + brew upgrade python; + which $PYCMD; + fi; + + - $PIPCMD install lxml enum34 pyyaml rdflib script: - - which python - - python setup.py build + - which $PYCMD + - $PYCMD setup.py build - if [ $COVERALLS = 1 ]; then coverage${PYVER} run --source=odml setup.py test && coverage${PYVER} report -m; else - python setup.py test; + $PYCMD setup.py test; fi; after_success: diff --git a/odml/property.py b/odml/property.py index b02bb1fa..79c9e373 100644 --- a/odml/property.py +++ b/odml/property.py @@ -150,7 +150,7 @@ def _validate_parent(new_parent): @property def value(self): - return self._value + return tuple(self._value) def value_str(self, index=0): """ @@ -172,18 +172,32 @@ def _validate_values(self, values): return False return True - @value.setter - def value(self, new_value): - # Make sure boolean value 'False' gets through as well... - if new_value is None or new_value == "": - return + def _convert_value_input(self, new_value): + """ + This method ensures, that the passed new value is a list. + If new_value is a string, it will convert it to a list of + strings if the new_value contains embracing brackets. + + returns list of new_value + """ if isinstance(new_value, str): if new_value[0] == "[" and new_value[-1] == "]": new_value = new_value[1:-1].split(",") if not isinstance(new_value, list): new_value = [new_value] + return new_value + + @value.setter + def value(self, new_value): + # Make sure boolean value 'False' gets through as well... + if new_value is None or new_value == "": + return + + new_value = self._convert_value_input(new_value) + if self._dtype is None: self._dtype = dtypes.infer_dtype(new_value[0]) + if not self._validate_values(new_value): raise ValueError("odml.Property.value: passed values are not of " "consistent type!") @@ -267,14 +281,14 @@ def clone(self): """ obj = super(BaseProperty, self).clone() obj._section = None - obj.value = self.value + obj.value = self._value return obj def merge(self, property): """ - Stub that doesn't do anything for this class + Merges the values in 'property' into self, if possible. """ - pass + self.append(list(property.value)) def unmerge(self, property): """ @@ -308,3 +322,17 @@ def __len__(self): def __getitem__(self, key): return self._value[key] + + def append(self, obj): + if isinstance(obj, BaseProperty): + self.merge(obj) + return + if self._value == []: + self.value = obj + else: + new_value = self._convert_value_input(obj) + if not self._validate_values(new_value): + raise ValueError("odml.Property.append: passed value(s) cannot be converted to " + "data type \'%s\'!" % self._dtype) + self._value.extend([dtypes.get(v, self.dtype) for v in new_value]) + diff --git a/odml/tools/dict_parser.py b/odml/tools/dict_parser.py index 47ea641c..0d2cdf01 100644 --- a/odml/tools/dict_parser.py +++ b/odml/tools/dict_parser.py @@ -80,8 +80,9 @@ def get_properties(props_list): if hasattr(prop, attr): tag = getattr(prop, attr) - - if (tag == []) or tag: # Even if 'value' is empty, allow '[]' + if isinstance(tag, tuple): + prop_dict[attr] = list(tag) + elif (tag == []) or tag: # Even if 'value' is empty, allow '[]' prop_dict[attr] = tag props_seq.append(prop_dict) @@ -187,7 +188,7 @@ def parse_properties(self, props_list): prop_attrs[attr] = _property[attr] prop = odmlfmt.Property.create(**prop_attrs) - prop._value = values + prop.value = values odml_props.append(prop) return odml_props diff --git a/odml/tools/dumper.py b/odml/tools/dumper.py index aa01b7af..dfb34a7c 100644 --- a/odml/tools/dumper.py +++ b/odml/tools/dumper.py @@ -10,7 +10,7 @@ def get_props(obj, props): if hasattr(obj, p): x = getattr(obj, p) if x is not None: - if isinstance(x, list): + if isinstance(x, list) or isinstance(x, tuple): out.append("%s=%s" % (p, to_csv(x))) else: out.append("%s=%s" % (p, repr(x))) diff --git a/test/test_property.py b/test/test_property.py index debe542c..bbb07602 100644 --- a/test/test_property.py +++ b/test/test_property.py @@ -13,6 +13,19 @@ def setUp(self): def test_value(self): p = Property("property", 100) self.assertEqual(p.value[0], 100) + self.assertEqual(type(p.value), tuple) + + p.append(10) + self.assertEqual(len(p.value), 2) + p.append([20, 30, '40']) + self.assertEqual(len(p.value), 5) + with self.assertRaises(ValueError): + p.append('invalid') + p.append(('5', 6, 7)) + + p2 = Property("property 2", 3) + p.append(p2) + self.assertEqual(len(p.value), 6) def test_bool_conversion(self): @@ -21,13 +34,13 @@ def test_bool_conversion(self): assert(p.dtype == 'int') p.dtype = DType.boolean assert(p.dtype == 'boolean') - assert(p.value == [True, False, True, False, True]) + assert(p.value == (True, False, True, False, True)) q = Property(name='sent', value=['False', True, 'TRUE', '0', 't', 'F', '1']) assert(q.dtype == 'string') q.dtype = DType.boolean assert(q.dtype == 'boolean') - assert(q.value == [False, True, True, False, True, False, True]) + assert(q.value == (False, True, True, False, True, False, True)) # Failure tests curr_val = [3, 0, 1, 0, 8] @@ -37,7 +50,7 @@ def test_bool_conversion(self): with self.assertRaises(ValueError): p.dtype = DType.boolean assert(p.dtype == curr_type) - assert(p.value == curr_val) + assert(p.value == tuple(curr_val)) curr_type = 'string' q = Property(name='sent', value=['False', True, 'TRUE', '0', 't', '12', 'Ft']) @@ -53,7 +66,7 @@ def test_str_to_int_convert(self): assert(p.dtype == 'string') p.dtype = DType.int assert(p.dtype == 'int') - assert(p.value == [3, 0, 1, 0, 8]) + assert(p.value == (3, 0, 1, 0, 8)) # Failure Test p = Property(name='dogs_onboard', value=['7', '20', '1 Dog', 'Seven']) @@ -63,7 +76,7 @@ def test_str_to_int_convert(self): p.dtype = DType.int assert(p.dtype == 'string') - assert(p.value == ['7', '20', '1 Dog', 'Seven']) + assert(p.value == ('7', '20', '1 Dog', 'Seven')) def test_name(self): pass diff --git a/test/test_rdf_writer.py b/test/test_rdf_writer.py index dca03c3b..74fe195f 100644 --- a/test/test_rdf_writer.py +++ b/test/test_rdf_writer.py @@ -100,7 +100,7 @@ def test_adding_values(self): w.convert_to_rdf() self.assertEqual(len(list(w.g.subjects(predicate=RDF.li, object=Literal("val")))), 1) - doc.sections[0].properties[0].value.append("val2") + doc.sections[0].properties[0].append("val2") w = RDFWriter([doc]) w.convert_to_rdf() self.assertEqual(len(list(w.g.subject_objects(predicate=RDF.li))), 2)