@@ -22,9 +22,9 @@ def __init__(self, name, value=None, parent=None, unit=None,
2222 dependency = None , dependency_value = None , dtype = None ,
2323 value_origin = None , id = None ):
2424 """
25- Create a new Property with a single value. The method will try to infer
26- the value's dtype from the type of the value if not explicitly stated .
27- Example for a property with
25+ Create a new Property. If a value without an explicitly stated dtype
26+ has been provided, the method will try to infer the value's dtype .
27+ Example:
2828 >>> p = Property("property1", "a string")
2929 >>> p.dtype
3030 >>> str
@@ -34,21 +34,25 @@ def __init__(self, name, value=None, parent=None, unit=None,
3434 >>> p = Property("prop", [2, 3, 4])
3535 >>> p.dtype
3636 >>> int
37- :param name: The name of the property
38- :param value: Some data value, this may be a list of homogeneous values
37+ :param name: The name of the property.
38+ :param value: Some data value, it can be a single value or
39+ a list of homogeneous values.
3940 :param unit: The unit of the stored data.
40- :param uncertainty: the uncertainty (e.g. the standard deviation)
41+ :param uncertainty: The uncertainty (e.g. the standard deviation)
4142 associated with a measure value.
4243 :param reference: A reference (e.g. an URL) to an external definition
4344 of the value.
4445 :param definition: The definition of the property.
4546 :param dependency: Another property this property depends on.
4647 :param dependency_value: Dependency on a certain value.
47- :param dtype: the data type of the values stored in the property,
48- if dtype is not given, the type is deduced from the values
48+ :param dtype: The data type of the values stored in the property,
49+ if dtype is not given, the type is deduced from the values.
50+ Check odml.DType for supported data types.
4951 :param value_origin: Reference where the value originated from e.g. a file name.
52+ :param id: UUID string as specified in RFC 4122. If no id is provided,
53+ an id will be generated and assigned. An id has to be unique
54+ within an odML Document.
5055 """
51- # TODO validate arguments
5256 try :
5357 if id is not None :
5458 self ._id = str (uuid .UUID (id ))
@@ -84,7 +88,7 @@ def id(self):
8488
8589 def new_id (self , id = None ):
8690 """
87- new_id sets the id of the current object to a RFC 4122 compliant UUID.
91+ new_id sets the id of the current object to an RFC 4122 compliant UUID.
8892 If an id was provided, it is assigned if it is RFC 4122 UUID format compliant.
8993 If no id was provided, a new UUID is generated and assigned.
9094 :param id: UUID string as specified in RFC 4122.
@@ -108,19 +112,17 @@ def __repr__(self):
108112 @property
109113 def dtype (self ):
110114 """
111- The data type of the value
115+ The data type of the value. Check odml.DType for supported data types.
112116 """
113117 return self ._dtype
114118
115119 @dtype .setter
116120 def dtype (self , new_type ):
117121 """
118122 If the data type of a property value is changed, it is tried
119- to convert the value to the new type.
120- If this doesn't work, the change is refused.
121-
122- This behaviour can be overridden by directly accessing the *_dtype*
123- attribute and adjusting the *data* attribute manually.
123+ to convert existing values to the new type. If this doesn't work,
124+ the change is refused. The dtype can always be changed, if
125+ a Property does not contain values.
124126 """
125127 # check if this is a valid type
126128 if not dtypes .valid_type (new_type ):
@@ -139,7 +141,7 @@ def dtype(self, new_type):
139141 @property
140142 def parent (self ):
141143 """
142- The section containing this property
144+ The section containing this property.
143145 """
144146 return self ._parent
145147
@@ -170,29 +172,30 @@ def _validate_parent(new_parent):
170172 @property
171173 def value (self ):
172174 """
173- Returns the value(s) stored in this property. Method always returns a list that
174- is a copy (!) of the stored value. Changing this list will NOT change the property.
175- For manipulation of the stored values use the append, extend, and direct access methods
176- (using brackets).
175+ Returns the value(s) stored in this property. Method always returns a list
176+ that is a copy (!) of the stored value. Changing this list will NOT change
177+ the property.
178+ For manipulation of the stored values use the append, extend, and direct
179+ access methods (using brackets).
177180
178181 For example:
179- >> p = odml.Property("prop", value=[1, 2, 3])
180- >> print(p.value)
182+ >>> p = odml.Property("prop", value=[1, 2, 3])
183+ >>> print(p.value)
181184 [1, 2, 3]
182- >> p.value.append(4)
183- >> print(p.value)
185+ >>> p.value.append(4)
186+ >>> print(p.value)
184187 [1, 2, 3]
185188
186189 Individual values can be accessed and manipulated like this:
187190 >>> print(p[0])
188191 [1]
189- >> p[0] = 4
190- >> print(p[0])
192+ >>> p[0] = 4
193+ >>> print(p[0])
191194 [4]
192195
193196 The values can be iterated e.g. with a loop:
194- >> for v in p.value:
195- print(v)
197+ >>> for v in p.value:
198+ >>> print(v)
196199 4
197200 2
198201 3
@@ -201,18 +204,18 @@ def value(self):
201204
202205 def value_str (self , index = 0 ):
203206 """
204- Used to access typed data of the value as a string.
205- Use data to access the raw type, i.e.:
207+ Used to access typed data of the value at a specific
208+ index position as a string.
206209 """
207210 return dtypes .set (self ._value [index ], self ._dtype )
208211
209212 def _validate_values (self , values ):
210213 """
211- Method ensures that the passed value(s) can be cast to the
212- same dtype, i.e. that associated with this property or the
213- inferred dtype of the first entry of the values list.
214+ Method ensures that the passed value(s) can be cast to the
215+ same dtype, i.e. that are associated with this property or the
216+ inferred dtype of the first entry of the values list.
214217
215- :param values an iterable that contains the values
218+ :param values: an iterable that contains the values.
216219 """
217220 for v in values :
218221 try :
@@ -227,7 +230,7 @@ def _convert_value_input(self, new_value):
227230 If new_value is a string, it will convert it to a list of
228231 strings if the new_value contains embracing brackets.
229232
230- returns list of new_value
233+ :return: list of new_value
231234 """
232235 if isinstance (new_value , str ):
233236 if new_value [0 ] == "[" and new_value [- 1 ] == "]" :
@@ -241,21 +244,22 @@ def _convert_value_input(self, new_value):
241244 elif not isinstance (new_value , list ):
242245 new_value = [new_value ]
243246 else :
244- raise ValueError ("odml.Property._convert_value_input: unsupported data type for values: %s" % type (new_value ))
247+ raise ValueError ("odml.Property._convert_value_input: "
248+ "unsupported data type for values: %s" % type (new_value ))
245249 return new_value
246250
247251 @value .setter
248252 def value (self , new_value ):
249253 """
250-
251254 Set the value of the property discarding any previous information.
252255 Method will try to convert the passed value to the dtype of
253- the property and raise an ValueError, if not possible
256+ the property and raise an ValueError if not possible.
254257
255- :param new_value a single value or list of values.
258+ :param new_value: a single value or list of values.
256259 """
257260 # Make sure boolean value 'False' gets through as well...
258- if new_value is None or (isinstance (new_value , (list , tuple , str )) and len (new_value ) == 0 ):
261+ if new_value is None or \
262+ (isinstance (new_value , (list , tuple , str )) and len (new_value ) == 0 ):
259263 self ._value = []
260264 return
261265
@@ -285,6 +289,8 @@ def uncertainty(self):
285289
286290 @uncertainty .setter
287291 def uncertainty (self , new_value ):
292+ if new_value == "" :
293+ new_value = None
288294 self ._uncertainty = new_value
289295
290296 @property
@@ -339,9 +345,9 @@ def dependency_value(self, new_value):
339345
340346 def remove (self , value ):
341347 """
342- Remove a value from this property and unset its parent.
343- Raises a TypeError if this would cause the property not to hold any
344- value at all. This can be circumvented by using the *_values* property .
348+ Remove a value from this property. Only the first encountered
349+ occurrence of the passed in value is removed from the properties
350+ list of values .
345351 """
346352 if value in self ._value :
347353 self ._value .remove (value )
@@ -358,6 +364,7 @@ def get_path(self):
358364 def clone (self ):
359365 """
360366 Clone this object to copy it independently to another document.
367+ The id of the cloned object will be set to a different uuid.
361368 """
362369 obj = super (BaseProperty , self ).clone ()
363370 obj ._parent = None
@@ -367,23 +374,23 @@ def clone(self):
367374 return obj
368375
369376 def merge (self , other , strict = True ):
370- """Merges the property 'other' into self, if possible. Information
371- will be synchronized. Method will raise an ValueError when the
377+ """
378+ Merges the property 'other' into self, if possible. Information
379+ will be synchronized. Method will raise a ValueError when the
372380 information in this property and the passed property are in
373381 conflict.
374382
375- :param other a Property
376- :param strict Bool value to indicate whether types should be
377- 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.
378-
383+ :param other: an odML Property.
384+ :param strict: Bool value to indicate whether types should be implicitly converted
385+ even when information may be lost. Default is True, i.e. no conversion,
386+ and a ValueError will be raised if types do not match.
379387 """
380- assert (isinstance (other , ( BaseProperty ) ))
388+ assert (isinstance (other , BaseProperty ))
381389 if strict and self .dtype != other .dtype :
382390 raise ValueError ("odml.Property.merge: src and dest dtypes do not match!" )
383391
384392 if self .unit is not None and other .unit is not None and self .unit != other .unit :
385- raise ValueError ("odml.Property.merge: src and dest units (%s, %s) do not match!"
386- % (other .unit , self .unit ))
393+ raise ValueError ("odml.Property.merge: src and dest units (%s, %s) do not match!" % (other .unit , self .unit ))
387394
388395 if self .definition is not None and other .definition is not None :
389396 self_def = '' .join (map (str .strip , self .definition .split ())).lower ()
@@ -422,14 +429,14 @@ def merge(self, other, strict=True):
422429
423430 def unmerge (self , other ):
424431 """
425- Stub that doesn't do anything for this class
432+ Stub that doesn't do anything for this class.
426433 """
427434 pass
428435
429436 def get_merged_equivalent (self ):
430437 """
431- Return the merged object (i.e. if the section is linked to another one,
432- return the corresponding property of the linked section) or None
438+ Return the merged object (i.e. if the parent section is linked to another one,
439+ return the corresponding property of the linked section) or None.
433440 """
434441 if self .parent is None or self .parent ._merged is None :
435442 return None
@@ -466,17 +473,18 @@ def __setitem__(self, key, item):
466473
467474 def extend (self , obj , strict = True ):
468475 """
469- Extend the list of values stored in this property by the passed values. Method will
470- raise an ValueError, if values cannot be converted to the current dtype. One can also pass
471- another Property to append all values stored in that one. In this case units must match!
476+ Extend the list of values stored in this property by the passed values. Method
477+ will raise a ValueError, if values cannot be converted to the current dtype.
478+ One can also pass another Property to append all values stored in that one.
479+ In this case units must match!
472480
473- :param obj single value, list of values or Property
474- :param strict a Bool that controls whether dtypes must match. Default is True.
481+ :param obj: single value, list of values or a Property.
482+ :param strict: a Bool that controls whether dtypes must match. Default is True.
475483 """
476484 if isinstance (obj , BaseProperty ):
477- if ( obj .unit != self .unit ) :
478- raise ValueError ("odml.Property.append : src and dest units (%s, %s) do not match! "
479- % (obj .unit , self .unit ))
485+ if obj .unit != self .unit :
486+ raise ValueError ("odml.Property.extend : src and dest units (%s, %s) "
487+ "do not match!" % (obj .unit , self .unit ))
480488 self .extend (obj .value )
481489 return
482490
@@ -486,29 +494,41 @@ def extend(self, obj, strict=True):
486494
487495 new_value = self ._convert_value_input (obj )
488496 if len (new_value ) > 0 and strict and dtypes .infer_dtype (new_value [0 ]) != self .dtype :
489- raise ValueError ("odml.Property.extend: passed value data type does not match dtype!" );
497+ raise ValueError ("odml.Property.extend: "
498+ "passed value data type does not match dtype!" )
490499
491500 if not self ._validate_values (new_value ):
492- raise ValueError ("odml.Property.append : passed value(s) cannot be converted to "
493- "data type \' %s\' !" % self ._dtype )
501+ raise ValueError ("odml.Property.extend : passed value(s) cannot be converted "
502+ "to data type \' %s\' !" % self ._dtype )
494503 self ._value .extend ([dtypes .get (v , self .dtype ) for v in new_value ])
495504
496505 def append (self , obj , strict = True ):
497506 """
498- Append a single value to the list of stored values. Method will raise an ValueError if
499- the passed value cannot be converted to the current dtype.
507+ Append a single value to the list of stored values. Method will raise
508+ a ValueError if the passed value cannot be converted to the current dtype.
500509
501- :param obj the additional value.
502- :param strict a Bool that controls whether dtypes must match. Default is True.
510+ :param obj: the additional value.
511+ :param strict: a Bool that controls whether dtypes must match. Default is True.
503512 """
513+ # Ignore empty values before nasty stuff happens, but make sure
514+ # 0 and False get through.
515+ if obj in [None , "" , [], {}]:
516+ return
517+
518+ if not self .value :
519+ self .value = obj
520+ return
521+
504522 new_value = self ._convert_value_input (obj )
505523 if len (new_value ) > 1 :
506524 raise ValueError ("odml.property.append: Use extend to add a list of values!" )
525+
507526 if len (new_value ) > 0 and strict and dtypes .infer_dtype (new_value [0 ]) != self .dtype :
508- raise ValueError ("odml.Property.extend: passed value data type does not match dtype!" );
527+ raise ValueError ("odml.Property.append: "
528+ "passed value data type does not match dtype!" )
509529
510530 if not self ._validate_values (new_value ):
511- raise ValueError ("odml.Property.append: passed value(s) cannot be converted to "
512- "data type \' %s\' !" % self ._dtype )
513- self ._value .append (dtypes .get (new_value [0 ], self .dtype ))
531+ raise ValueError ("odml.Property.append: passed value(s) cannot be converted "
532+ "to data type \' %s\' !" % self ._dtype )
514533
534+ self ._value .append (dtypes .get (new_value [0 ], self .dtype ))
0 commit comments