From 6428752a83ce041977260018ba77c5e7bb1bfbf8 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 22 Mar 2018 10:49:11 +0100 Subject: [PATCH 01/11] [test/property int] Add missing definition check --- test/test_property_integration.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_property_integration.py b/test/test_property_integration.py index 479883f7..cf30d591 100644 --- a/test/test_property_integration.py +++ b/test/test_property_integration.py @@ -106,6 +106,7 @@ def test_simple_attributes(self): self.assertEqual(jprop.unit, p_unit) self.assertEqual(jprop.uncertainty, p_uncertainty) self.assertEqual(jprop.reference, p_ref) + self.assertEqual(jprop.definition, p_def) self.assertEqual(jprop.dependency, p_dep) self.assertEqual(jprop.dependency_value, p_dep_val) @@ -116,6 +117,7 @@ def test_simple_attributes(self): self.assertEqual(xprop.unit, p_unit) self.assertEqual(xprop.uncertainty, p_uncertainty) self.assertEqual(xprop.reference, p_ref) + self.assertEqual(xprop.definition, p_def) self.assertEqual(xprop.dependency, p_dep) self.assertEqual(xprop.dependency_value, p_dep_val) @@ -126,5 +128,6 @@ def test_simple_attributes(self): self.assertEqual(yprop.unit, p_unit) self.assertEqual(yprop.uncertainty, p_uncertainty) self.assertEqual(yprop.reference, p_ref) + self.assertEqual(yprop.definition, p_def) self.assertEqual(yprop.dependency, p_dep) self.assertEqual(yprop.dependency_value, p_dep_val) From b475da37447c3d53036fc06a9b1c86689e64d163 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 22 Mar 2018 12:41:40 +0100 Subject: [PATCH 02/11] [property] Cleanup extend error messages Replaces 'odml.Property.append' with '.extend'. Includes some PEP8 fixes. --- odml/property.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/odml/property.py b/odml/property.py index 74e31f7e..9e45204a 100644 --- a/odml/property.py +++ b/odml/property.py @@ -466,17 +466,18 @@ def __setitem__(self, key, item): def extend(self, obj, strict=True): """ - Extend the list of values stored in this property by the passed values. Method will - raise an ValueError, if values cannot be converted to the current dtype. One can also pass - another Property to append all values stored in that one. In this case units must match! + Extend the list of values stored in this property by the passed values. Method + will raise an ValueError, if values cannot be converted to the current dtype. + One can also pass another Property to append all values stored in that one. + In this case units must match! :param obj single value, list of values or Property :param strict a Bool that controls whether dtypes must match. Default is True. """ if isinstance(obj, BaseProperty): - if (obj.unit != self.unit): - raise ValueError("odml.Property.append: src and dest units (%s, %s) do not match!" - % (obj.unit, self.unit)) + if obj.unit != self.unit: + raise ValueError("odml.Property.extend: src and dest units (%s, %s) " + "do not match!" % (obj.unit, self.unit)) self.extend(obj.value) return @@ -486,11 +487,12 @@ def extend(self, obj, strict=True): new_value = self._convert_value_input(obj) if len(new_value) > 0 and strict and dtypes.infer_dtype(new_value[0]) != self.dtype: - raise ValueError("odml.Property.extend: passed value data type does not match dtype!"); + raise ValueError("odml.Property.extend: " + "passed value data type does not match dtype!") if not self._validate_values(new_value): - raise ValueError("odml.Property.append: passed value(s) cannot be converted to " - "data type \'%s\'!" % self._dtype) + raise ValueError("odml.Property.extend: 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]) def append(self, obj, strict=True): From 11d362eb7ffb701651a3994ed8797c5b2257933a Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 22 Mar 2018 12:43:32 +0100 Subject: [PATCH 03/11] [property] Cleanup append error messages Replaces 'odml.Property.append' with '.extend'. Includes some PEP8 fixes. --- odml/property.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/odml/property.py b/odml/property.py index 9e45204a..ea555520 100644 --- a/odml/property.py +++ b/odml/property.py @@ -497,8 +497,8 @@ def extend(self, obj, strict=True): def append(self, obj, strict=True): """ - Append a single value to the list of stored values. Method will raise an ValueError if - the passed value cannot be converted to the current dtype. + Append a single value to the list of stored values. Method will raise + a ValueError if the passed value cannot be converted to the current dtype. :param obj the additional value. :param strict a Bool that controls whether dtypes must match. Default is True. @@ -506,11 +506,13 @@ def append(self, obj, strict=True): new_value = self._convert_value_input(obj) if len(new_value) > 1: raise ValueError("odml.property.append: Use extend to add a list of values!") + if len(new_value) > 0 and strict and dtypes.infer_dtype(new_value[0]) != self.dtype: - raise ValueError("odml.Property.extend: passed value data type does not match dtype!"); + raise ValueError("odml.Property.append: " + "passed value data type does not match dtype!") if not self._validate_values(new_value): - raise ValueError("odml.Property.append: passed value(s) cannot be converted to " - "data type \'%s\'!" % self._dtype) + raise ValueError("odml.Property.append: passed value(s) cannot be converted " + "to data type \'%s\'!" % self._dtype) self._value.append(dtypes.get(new_value[0], self.dtype)) From b122e22516102f0f44668cf81f64a58676e482f1 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 22 Mar 2018 12:44:59 +0100 Subject: [PATCH 04/11] [property] Set '' uncertainty value to None --- odml/property.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/odml/property.py b/odml/property.py index ea555520..5535af92 100644 --- a/odml/property.py +++ b/odml/property.py @@ -285,6 +285,8 @@ def uncertainty(self): @uncertainty.setter def uncertainty(self, new_value): + if new_value == "": + new_value = None self._uncertainty = new_value @property From 8701c10e7ec0cd16c14f41ce3b3a631d9bdc9e8a Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 22 Mar 2018 12:45:51 +0100 Subject: [PATCH 05/11] [test/property] Add simple attributes test Removes obsolete name and value_origin tests. --- test/test_property.py | 68 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/test/test_property.py b/test/test_property.py index d0dc6737..b881aac7 100644 --- a/test/test_property.py +++ b/test/test_property.py @@ -10,6 +10,63 @@ class TestProperty(unittest.TestCase): def setUp(self): pass + def test_simple_attributes(self): + p_name = "propertyName" + p_origin = "from over there" + p_unit = "pears" + p_uncertainty = "+-12" + p_ref = "4 8 15 16 23" + p_def = "an odml test property" + p_dep = "yes" + p_dep_val = "42" + + prop = Property(name=p_name, value_origin=p_origin, unit=p_unit, + uncertainty=p_uncertainty, reference=p_ref, definition=p_def, + dependency=p_dep, dependency_value=p_dep_val) + + self.assertEqual(prop.name, p_name) + self.assertEqual(prop.value_origin, p_origin) + self.assertEqual(prop.unit, p_unit) + self.assertEqual(prop.uncertainty, p_uncertainty) + self.assertEqual(prop.reference, p_ref) + self.assertEqual(prop.definition, p_def) + self.assertEqual(prop.dependency, p_dep) + self.assertEqual(prop.dependency_value, p_dep_val) + + # Test setting attributes + prop.name = "%s_edit" % p_name + self.assertEqual(prop.name, "%s_edit" % p_name) + prop.value_origin = "%s_edit" % p_origin + self.assertEqual(prop.value_origin, "%s_edit" % p_origin) + prop.unit = "%s_edit" % p_unit + self.assertEqual(prop.unit, "%s_edit" % p_unit) + prop.uncertainty = "%s_edit" % p_uncertainty + self.assertEqual(prop.uncertainty, "%s_edit" % p_uncertainty) + prop.reference = "%s_edit" % p_ref + self.assertEqual(prop.reference, "%s_edit" % p_ref) + prop.definition = "%s_edit" % p_def + self.assertEqual(prop.definition, "%s_edit" % p_def) + prop.dependency = "%s_edit" % p_dep + self.assertEqual(prop.dependency, "%s_edit" % p_dep) + prop.dependency_value = "%s_edit" % p_dep_val + self.assertEqual(prop.dependency_value, "%s_edit" % p_dep_val) + + # Test setting attributes to None when '' is passed. + prop.value_origin = "" + self.assertIsNone(prop.value_origin) + prop.unit = "" + self.assertIsNone(prop.unit) + prop.uncertainty = "" + self.assertIsNone(prop.uncertainty) + prop.reference = "" + self.assertIsNone(prop.reference) + prop.definition = "" + self.assertIsNone(prop.definition) + prop.dependency = "" + self.assertIsNone(prop.dependency) + prop.dependency_value = "" + self.assertIsNone(prop.dependency_value) + def test_value(self): p = Property("property", 100) self.assertEqual(p.value[0], 100) @@ -150,9 +207,6 @@ def test_str_to_int_convert(self): assert(p.dtype == 'string') assert(p.value == ['7', '20', '1 Dog', 'Seven']) - def test_name(self): - pass - def test_parent(self): p = Property("property_section", parent=Section("S")) self.assertIsInstance(p.parent, BaseSection) @@ -218,14 +272,6 @@ def test_get_path(self): prop.parent = sec self.assertEqual("/%s:%s" % (sec.name, prop.name), prop.get_path()) - def test_value_origin(self): - p = Property("P") - self.assertEqual(p.value_origin, None) - p = Property("P", value_origin="V") - self.assertEqual(p.value_origin, "V") - p.value_origin = "" - self.assertEqual(p.value_origin, None) - def test_id(self): p = Property(name="P") self.assertIsNotNone(p.id) From 03d515a3d89ce6cb470b96d2ff3d2ac9cade218f Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 22 Mar 2018 14:52:25 +0100 Subject: [PATCH 06/11] [test/property] Add extend test Adds extend test, moves extend related code from test_value to test_value_extend and adds additional extend test code. --- test/test_property.py | 90 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 12 deletions(-) diff --git a/test/test_property.py b/test/test_property.py index b881aac7..68169634 100644 --- a/test/test_property.py +++ b/test/test_property.py @@ -76,17 +76,12 @@ def test_value(self): self.assertEqual(len(p), 2) self.assertRaises(ValueError, p.append, [1, 2, 3]) - p.extend([20, 30, '40']) - self.assertEqual(len(p), 5) with self.assertRaises(ValueError): p.append('invalid') - with self.assertRaises(ValueError): - p.extend(('5', 6, 7)) p2 = Property("property 2", 3) self.assertRaises(ValueError, p.append, p2) - p.extend(p2) - self.assertEqual(len(p), 6) + self.assertEqual(len(p), 2) p.value = None self.assertEqual(len(p), 0) @@ -116,16 +111,10 @@ def test_value(self): p.append(5.5, strict=False) self.assertEqual(len(p), 1) - self.assertRaises(ValueError, p.extend, [3.14, 6.28]) - p.extend([3.14, 6.28], strict=False) - self.assertEqual(len(p), 3) - p5 = Property("test", value="a string") p5.append("Freude") self.assertEqual(len(p5), 2) self.assertRaises(ValueError, p5.append, "[a, b, c]") - p5.extend("[a, b, c]") - self.assertEqual(len(p5), 5) p6 = Property("test", {"name": "Marie", "name": "Johanna"}) self.assertEqual(len(p6), 1) @@ -140,6 +129,83 @@ def test_value(self): with self.assertRaises(ValueError): _ = Property(name="Public-Key", value='(5689; 1254; 687)', dtype='2-tuple') + def test_value_extend(self): + prop = Property(name="extend") + + # Test extend w/o Property value or dtype. + val = [1, 2, 3] + prop.extend(val) + self.assertEqual(prop.dtype, DType.int) + self.assertEqual(prop.value, val) + + # Extend with single value. + prop.extend(4) + self.assertEqual(prop.value, [1, 2, 3, 4]) + + # Extend with list value. + prop.extend([5, 6]) + self.assertEqual(prop.value, [1, 2, 3, 4, 5, 6]) + + # Test extend w/o Property value + prop = Property(name="extend", dtype="float") + prop.extend([1.0, 2.0, 3.0]) + self.assertEqual(prop.value, [1.0, 2.0, 3.0]) + + # Test extend with Property value + prop = Property(name="extend", value=10) + prop.extend([20, 30, '40']) + self.assertEqual(prop.value, [10, 20, 30, 40]) + + # Test extend fail with mismatching dtype + with self.assertRaises(ValueError): + prop.extend(['5', 6, 7]) + with self.assertRaises(ValueError): + prop.extend([5, 6, 'a']) + + # Test extend via Property + prop = Property(name="extend", value=["a", "b"]) + ext_prop = Property(name="value extend", value="c") + prop.extend(ext_prop) + self.assertEqual(prop.value, ["a", "b", "c"]) + + ext_prop.value = ["d", "e"] + prop.extend(ext_prop) + self.assertEqual(prop.value, ["a", "b", "c", "d", "e"]) + + ext_prop = Property(name="value extend", value=[1, 2 ,3]) + with self.assertRaises(ValueError): + prop.extend(ext_prop) + self.assertEqual(prop.value, ["a", "b", "c", "d", "e"]) + + # Test extend via Property unit check + prop = Property(name="extend", value=[1, 2], unit="mV") + ext_prop = Property(name="extend", value=[3, 4], unit="mV") + prop.extend(ext_prop) + self.assertEqual(prop.value, [1, 2, 3, 4]) + + ext_prop.unit = "kV" + with self.assertRaises(ValueError): + prop.extend(ext_prop) + self.assertEqual(prop.value, [1, 2, 3, 4]) + + ext_prop.unit = "" + with self.assertRaises(ValueError): + prop.extend(ext_prop) + self.assertEqual(prop.value, [1, 2, 3, 4]) + + # Test strict flag + prop = Property(name="extend", value=[1, 2], dtype="int") + with self.assertRaises(ValueError): + prop.extend([3.14, True, "5.927"]) + self.assertEqual(prop.value, [1, 2]) + + prop.extend([3.14, True, "5.927"], strict=False) + self.assertEqual(prop.value, [1, 2, 3, 1, 5]) + + # Make sure non-convertible values still raise an error + with self.assertRaises(ValueError): + prop.extend([6, "some text"]) + def test_get_set_value(self): values = [1, 2, 3, 4, 5] p = Property("property", value=values) From 4a1f5403e0029017225121478317920c166aa752 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 22 Mar 2018 17:28:32 +0100 Subject: [PATCH 07/11] [property] Allow append on unset dtype Closes #266 --- odml/property.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/odml/property.py b/odml/property.py index 5535af92..1ba8d14e 100644 --- a/odml/property.py +++ b/odml/property.py @@ -505,6 +505,10 @@ def append(self, obj, strict=True): :param obj the additional value. :param strict a Bool that controls whether dtypes must match. Default is True. """ + if not self.value: + self.value = obj + return + new_value = self._convert_value_input(obj) if len(new_value) > 1: raise ValueError("odml.property.append: Use extend to add a list of values!") @@ -516,5 +520,6 @@ def append(self, obj, strict=True): 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.append(dtypes.get(new_value[0], self.dtype)) From f308a34621785748e50f51998331f93932c514b2 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 22 Mar 2018 17:29:55 +0100 Subject: [PATCH 08/11] [property] Exit before appending empty value --- odml/property.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/odml/property.py b/odml/property.py index 1ba8d14e..634cc5d5 100644 --- a/odml/property.py +++ b/odml/property.py @@ -505,6 +505,11 @@ def append(self, obj, strict=True): :param obj the additional value. :param strict a Bool that controls whether dtypes must match. Default is True. """ + # Ignore empty values before nasty stuff happens, but make sure + # 0 and False get through. + if obj in [None, "", [], {}]: + return + if not self.value: self.value = obj return From 21d85fb187c7758984a9c71bf54fc77628f81498 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Thu, 22 Mar 2018 17:52:18 +0100 Subject: [PATCH 09/11] [test/property] Add append test Adds append test, moves append related code from test_value to test_value_append and adds additional append test code. --- test/test_property.py | 106 +++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 26 deletions(-) diff --git a/test/test_property.py b/test/test_property.py index 68169634..59bb9abc 100644 --- a/test/test_property.py +++ b/test/test_property.py @@ -72,17 +72,6 @@ def test_value(self): self.assertEqual(p.value[0], 100) self.assertEqual(type(p.value), list) - p.append(10) - self.assertEqual(len(p), 2) - self.assertRaises(ValueError, p.append, [1, 2, 3]) - - with self.assertRaises(ValueError): - p.append('invalid') - - p2 = Property("property 2", 3) - self.assertRaises(ValueError, p.append, p2) - self.assertEqual(len(p), 2) - p.value = None self.assertEqual(len(p), 0) @@ -98,23 +87,8 @@ def test_value(self): p.value = () self.assertEqual(len(p), 0) - p3 = Property("test", value=2, unit="Hz") - p4 = Property("test", value=5.5, unit="s") - - with self.assertRaises(ValueError): - p3.append(p4) - p.value.append(5) self.assertEqual(len(p.value), 0) - self.assertRaises(ValueError, p.append, 5.5) - - p.append(5.5, strict=False) - self.assertEqual(len(p), 1) - - p5 = Property("test", value="a string") - p5.append("Freude") - self.assertEqual(len(p5), 2) - self.assertRaises(ValueError, p5.append, "[a, b, c]") p6 = Property("test", {"name": "Marie", "name": "Johanna"}) self.assertEqual(len(p6), 1) @@ -129,6 +103,86 @@ def test_value(self): with self.assertRaises(ValueError): _ = Property(name="Public-Key", value='(5689; 1254; 687)', dtype='2-tuple') + def test_value_append(self): + # Test append w/o Property value or dtype + prop = Property(name="append") + prop.append(1) + self.assertEqual(prop.dtype, DType.int) + self.assertEqual(prop.value, [1]) + + # Test append with Property dtype. + prop = Property(name="append", dtype="int") + prop.append(3) + self.assertEqual(prop.value, [3]) + + # Test append with Property value + prop = Property(name="append", value=[1, 2]) + prop.append(3) + self.assertEqual(prop.value, [1, 2, 3]) + + # Test append with Property list value + prop = Property(name="append", value=[1, 2]) + prop.append([3]) + self.assertEqual(prop.value, [1, 2, 3]) + + # Test append of empty values, make sure 0 and False are properly handled + prop = Property(name="append") + prop.append(None) + prop.append("") + prop.append([]) + prop.append({}) + self.assertEqual(prop.value, []) + + prop.append(0) + self.assertEqual(prop.value, [0]) + + prop.value = None + prop.dtype = None + prop.append(False) + self.assertEqual(prop.value, [False]) + + prop = Property(name="append", value=[1, 2]) + prop.append(None) + prop.append("") + prop.append([]) + prop.append({}) + self.assertEqual(prop.value, [1, 2]) + + prop.append(0) + self.assertEqual(prop.value, [1, 2, 0]) + + # Test fail append with multiple values + prop = Property(name="append", value=[1, 2, 3]) + with self.assertRaises(ValueError): + prop.append([4, 5]) + self.assertEqual(prop.value, [1, 2, 3]) + + # Test fail append with mismatching dtype + prop = Property(name="append", value=[1, 2], dtype="int") + with self.assertRaises(ValueError): + prop.append([3.14]) + with self.assertRaises(ValueError): + prop.append([True]) + with self.assertRaises(ValueError): + prop.append(["5.927"]) + self.assertEqual(prop.value, [1, 2]) + + # Test strict flag + prop.append(3.14, strict=False) + prop.append(True, strict=False) + prop.append("5.927", strict=False) + self.assertEqual(prop.value, [1, 2, 3, 1, 5]) + + # Make sure non-convertible values still raise an error + with self.assertRaises(ValueError): + prop.append("invalid") + self.assertEqual(prop.value, [1, 2, 3, 1, 5]) + + p5 = Property("test", value="a string") + p5.append("Freude") + self.assertEqual(len(p5), 2) + self.assertRaises(ValueError, p5.append, "[a, b, c]") + def test_value_extend(self): prop = Property(name="extend") From 015b309f54841cc42dfe2670e977d653116e341b Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Fri, 23 Mar 2018 11:48:58 +0100 Subject: [PATCH 10/11] [test/property] Update dtype test --- test/test_property.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/test_property.py b/test/test_property.py index 59bb9abc..f0aa9769 100644 --- a/test/test_property.py +++ b/test/test_property.py @@ -70,7 +70,7 @@ def test_simple_attributes(self): def test_value(self): p = Property("property", 100) self.assertEqual(p.value[0], 100) - self.assertEqual(type(p.value), list) + self.assertIsInstance(p.value, list) p.value = None self.assertEqual(len(p), 0) @@ -90,8 +90,8 @@ def test_value(self): p.value.append(5) self.assertEqual(len(p.value), 0) - p6 = Property("test", {"name": "Marie", "name": "Johanna"}) - self.assertEqual(len(p6), 1) + p2 = Property("test", {"name": "Marie", "name": "Johanna"}) + self.assertEqual(len(p2), 1) # Test tuple dtype value. t = Property(name="Location", value='(39.12; 67.19)', dtype='2-tuple') @@ -380,6 +380,12 @@ def test_dtype(self): with self.assertRaises(AttributeError): prop.dtype = "x-tuple" + # Test not setting None when a property contains values. + prop.value = [1, 2, 3] + self.assertIsNotNone(prop.dtype) + prop.dtype = None + self.assertIsNotNone(prop.dtype) + def test_get_path(self): doc = Document() sec = Section(name="parent", parent=doc) From b2791c6046563a7510833bb36f578fa2d2b3a380 Mon Sep 17 00:00:00 2001 From: "M. Sonntag" Date: Mon, 26 Mar 2018 14:46:51 +0200 Subject: [PATCH 11/11] [property] Update method docstrings --- odml/property.py | 128 ++++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/odml/property.py b/odml/property.py index 634cc5d5..894296d2 100644 --- a/odml/property.py +++ b/odml/property.py @@ -22,9 +22,9 @@ def __init__(self, name, value=None, parent=None, unit=None, dependency=None, dependency_value=None, dtype=None, value_origin=None, id=None): """ - Create a new Property with a single value. The method will try to infer - the value's dtype from the type of the value if not explicitly stated. - Example for a property with + Create a new Property. If a value without an explicitly stated dtype + has been provided, the method will try to infer the value's dtype. + Example: >>> p = Property("property1", "a string") >>> p.dtype >>> str @@ -34,21 +34,25 @@ def __init__(self, name, value=None, parent=None, unit=None, >>> p = Property("prop", [2, 3, 4]) >>> p.dtype >>> int - :param name: The name of the property - :param value: Some data value, this may be a list of homogeneous values + :param name: The name of the property. + :param value: Some data value, it can be a single value or + a list of homogeneous values. :param unit: The unit of the stored data. - :param uncertainty: the uncertainty (e.g. the standard deviation) + :param uncertainty: The uncertainty (e.g. the standard deviation) associated with a measure value. :param reference: A reference (e.g. an URL) to an external definition of the value. :param definition: The definition of the property. :param dependency: Another property this property depends on. :param dependency_value: Dependency on a certain value. - :param dtype: the data type of the values stored in the property, - if dtype is not given, the type is deduced from the values + :param dtype: The data type of the values stored in the property, + if dtype is not given, the type is deduced from the values. + Check odml.DType for supported data types. :param value_origin: Reference where the value originated from e.g. a file name. + :param id: UUID string as specified in RFC 4122. If no id is provided, + an id will be generated and assigned. An id has to be unique + within an odML Document. """ - # TODO validate arguments try: if id is not None: self._id = str(uuid.UUID(id)) @@ -84,7 +88,7 @@ def id(self): def new_id(self, id=None): """ - new_id sets the id of the current object to a RFC 4122 compliant UUID. + new_id sets the id of the current object to an RFC 4122 compliant UUID. If an id was provided, it is assigned if it is RFC 4122 UUID format compliant. If no id was provided, a new UUID is generated and assigned. :param id: UUID string as specified in RFC 4122. @@ -108,7 +112,7 @@ def __repr__(self): @property def dtype(self): """ - The data type of the value + The data type of the value. Check odml.DType for supported data types. """ return self._dtype @@ -116,11 +120,9 @@ def dtype(self): def dtype(self, new_type): """ If the data type of a property value is changed, it is tried - to convert the value to the new type. - If this doesn't work, the change is refused. - - This behaviour can be overridden by directly accessing the *_dtype* - attribute and adjusting the *data* attribute manually. + to convert existing values to the new type. If this doesn't work, + the change is refused. The dtype can always be changed, if + a Property does not contain values. """ # check if this is a valid type if not dtypes.valid_type(new_type): @@ -139,7 +141,7 @@ def dtype(self, new_type): @property def parent(self): """ - The section containing this property + The section containing this property. """ return self._parent @@ -170,29 +172,30 @@ def _validate_parent(new_parent): @property def value(self): """ - Returns the value(s) stored in this property. Method always returns a list that - is a copy (!) of the stored value. Changing this list will NOT change the property. - For manipulation of the stored values use the append, extend, and direct access methods - (using brackets). + Returns the value(s) stored in this property. Method always returns a list + that is a copy (!) of the stored value. Changing this list will NOT change + the property. + For manipulation of the stored values use the append, extend, and direct + access methods (using brackets). For example: - >> p = odml.Property("prop", value=[1, 2, 3]) - >> print(p.value) + >>> p = odml.Property("prop", value=[1, 2, 3]) + >>> print(p.value) [1, 2, 3] - >> p.value.append(4) - >> print(p.value) + >>> p.value.append(4) + >>> print(p.value) [1, 2, 3] Individual values can be accessed and manipulated like this: >>> print(p[0]) [1] - >> p[0] = 4 - >> print(p[0]) + >>> p[0] = 4 + >>> print(p[0]) [4] The values can be iterated e.g. with a loop: - >> for v in p.value: - print(v) + >>> for v in p.value: + >>> print(v) 4 2 3 @@ -201,18 +204,18 @@ def value(self): def value_str(self, index=0): """ - Used to access typed data of the value as a string. - Use data to access the raw type, i.e.: + Used to access typed data of the value at a specific + index position as a string. """ return dtypes.set(self._value[index], self._dtype) def _validate_values(self, values): """ - Method ensures that the passed value(s) can be cast to the - same dtype, i.e. that associated with this property or the - inferred dtype of the first entry of the values list. + Method ensures that the passed value(s) can be cast to the + same dtype, i.e. that are associated with this property or the + inferred dtype of the first entry of the values list. - :param values an iterable that contains the values + :param values: an iterable that contains the values. """ for v in values: try: @@ -227,7 +230,7 @@ def _convert_value_input(self, new_value): 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 + :return: list of new_value """ if isinstance(new_value, str): if new_value[0] == "[" and new_value[-1] == "]": @@ -241,21 +244,22 @@ def _convert_value_input(self, new_value): elif not isinstance(new_value, list): new_value = [new_value] else: - raise ValueError("odml.Property._convert_value_input: unsupported data type for values: %s" % type(new_value)) + raise ValueError("odml.Property._convert_value_input: " + "unsupported data type for values: %s" % type(new_value)) return new_value @value.setter def value(self, new_value): """ - Set the value of the property discarding any previous information. Method will try to convert the passed value to the dtype of - the property and raise an ValueError, if not possible + the property and raise an ValueError if not possible. - :param new_value a single value or list of values. + :param new_value: a single value or list of values. """ # Make sure boolean value 'False' gets through as well... - if new_value is None or (isinstance(new_value, (list, tuple, str)) and len(new_value) == 0): + if new_value is None or \ + (isinstance(new_value, (list, tuple, str)) and len(new_value) == 0): self._value = [] return @@ -341,9 +345,9 @@ def dependency_value(self, new_value): def remove(self, value): """ - Remove a value from this property and unset its parent. - Raises a TypeError if this would cause the property not to hold any - value at all. This can be circumvented by using the *_values* property. + Remove a value from this property. Only the first encountered + occurrence of the passed in value is removed from the properties + list of values. """ if value in self._value: self._value.remove(value) @@ -360,6 +364,7 @@ def get_path(self): def clone(self): """ Clone this object to copy it independently to another document. + The id of the cloned object will be set to a different uuid. """ obj = super(BaseProperty, self).clone() obj._parent = None @@ -369,23 +374,23 @@ def clone(self): return obj def merge(self, other, strict=True): - """Merges the property 'other' into self, if possible. Information - will be synchronized. Method will raise an ValueError when the + """ + Merges the property 'other' into self, if possible. Information + will be synchronized. Method will raise a ValueError when the information in this property and the passed property are in conflict. - :param other a Property - :param strict Bool value to indicate whether types should be - implicitly converted even when information may be lost. Default is True, i.e. no conversion, and error will be raised if types do not match. - + :param other: an odML Property. + :param strict: Bool value to indicate whether types should be implicitly converted + even when information may be lost. Default is True, i.e. no conversion, + and a ValueError will be raised if types do not match. """ - assert(isinstance(other, (BaseProperty))) + assert(isinstance(other, BaseProperty)) if strict and self.dtype != other.dtype: raise ValueError("odml.Property.merge: src and dest dtypes do not match!") if self.unit is not None and other.unit is not None and self.unit != other.unit: - raise ValueError("odml.Property.merge: src and dest units (%s, %s) do not match!" - % (other.unit, self.unit)) + raise ValueError("odml.Property.merge: src and dest units (%s, %s) do not match!" % (other.unit, self.unit)) if self.definition is not None and other.definition is not None: self_def = ''.join(map(str.strip, self.definition.split())).lower() @@ -424,14 +429,14 @@ def merge(self, other, strict=True): def unmerge(self, other): """ - Stub that doesn't do anything for this class + Stub that doesn't do anything for this class. """ pass def get_merged_equivalent(self): """ - Return the merged object (i.e. if the section is linked to another one, - return the corresponding property of the linked section) or None + Return the merged object (i.e. if the parent section is linked to another one, + return the corresponding property of the linked section) or None. """ if self.parent is None or self.parent._merged is None: return None @@ -469,12 +474,12 @@ def __setitem__(self, key, item): def extend(self, obj, strict=True): """ Extend the list of values stored in this property by the passed values. Method - will raise an ValueError, if values cannot be converted to the current dtype. + will raise a ValueError, if values cannot be converted to the current dtype. One can also pass another Property to append all values stored in that one. In this case units must match! - :param obj single value, list of values or Property - :param strict a Bool that controls whether dtypes must match. Default is True. + :param obj: single value, list of values or a Property. + :param strict: a Bool that controls whether dtypes must match. Default is True. """ if isinstance(obj, BaseProperty): if obj.unit != self.unit: @@ -502,8 +507,8 @@ def append(self, obj, strict=True): Append a single value to the list of stored values. Method will raise a ValueError if the passed value cannot be converted to the current dtype. - :param obj the additional value. - :param strict a Bool that controls whether dtypes must match. Default is True. + :param obj: the additional value. + :param strict: a Bool that controls whether dtypes must match. Default is True. """ # Ignore empty values before nasty stuff happens, but make sure # 0 and False get through. @@ -527,4 +532,3 @@ def append(self, obj, strict=True): "to data type \'%s\'!" % self._dtype) self._value.append(dtypes.get(new_value[0], self.dtype)) -