Skip to content

Commit ac805c5

Browse files
authored
Merge pull request #313 from mpsonntag/nixvalues
nixpy style values and printing
2 parents 25330df + 3c0208c commit ac805c5

18 files changed

+400
-296
lines changed

CHANGELOG.md

+20
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,26 @@ until the next release.
55

66
# Latest changes in master
77

8+
...
9+
10+
# Version 1.4.2
11+
12+
## Print methods
13+
14+
`pprint` methods have been added to both `Section` and `Property`
15+
to print whole Section trees with their child sections and properties.
16+
The `__repr__` style of `Section` and `Property` has been changed to
17+
be more similar to the [nixpy](https://github.com/G-Node/nixpy) `__repr__` style.
18+
Printing a `Section` now also features the immediate `Property` child count
19+
in addition to the immediate `Section` child count. See #309.
20+
21+
## Deprecation of 'Property.value' in favor of 'Property.values'
22+
23+
To make working with odML more similar to working with the
24+
metadata part of [nixpy](https://github.com/G-Node/nixpy), the `Property.value`
25+
attribute has been marked deprecated and the `Property.values`
26+
attribute has been added. See #308.
27+
828
## Uncertainty changes
929

1030
Uncertainty is now limited to float only. See #294.

doc/tutorial.rst

+80-81
Large diffs are not rendered by default.

odml/base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,8 @@ def itervalues(self, max_depth=None, filter_func=lambda x: True):
357357
:type filter_func: function
358358
"""
359359
for prop in [p for p in self.iterproperties(max_depth=max_depth)]:
360-
if filter_func(prop.value):
361-
yield prop.value
360+
if filter_func(prop.values):
361+
yield prop.values
362362

363363
def contains(self, obj):
364364
"""

odml/doc.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ def __init__(self, author=None, date=None, version=None, repository=None, oid=No
4242
self._origin_file_name = None
4343

4444
def __repr__(self):
45-
return "<Doc %s by %s (%d sections)>" % (self._version, self._author,
46-
len(self._sections))
45+
return "Document %s {author = %s, %d sections}" % \
46+
(self._version, self._author, len(self._sections))
4747

4848
@property
4949
def oid(self):

odml/format.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ class Property(Format):
109109
_map = {
110110
'dependencyvalue': 'dependency_value',
111111
'type': 'dtype',
112-
'id': 'oid'
112+
'id': 'oid',
113+
'value': 'values'
113114
}
114115
_rdf_map = {
115116
'id': _ns.hasId,

odml/property.py

+113-59
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ class BaseProperty(base.BaseObject):
1313
"""An odML Property"""
1414
_format = frmt.Property
1515

16-
def __init__(self, name=None, value=None, parent=None, unit=None,
16+
def __init__(self, name=None, values=None, parent=None, unit=None,
1717
uncertainty=None, reference=None, definition=None,
1818
dependency=None, dependency_value=None, dtype=None,
19-
value_origin=None, oid=None):
19+
value_origin=None, oid=None, value=None):
2020
"""
2121
Create a new Property. If a value without an explicitly stated dtype
2222
has been provided, the method will try to infer the value's dtype.
@@ -31,8 +31,8 @@ def __init__(self, name=None, value=None, parent=None, unit=None,
3131
>>> p.dtype
3232
>>> int
3333
:param name: The name of the property.
34-
:param value: Some data value, it can be a single value or
35-
a list of homogeneous values.
34+
:param values: Some data value, it can be a single value or
35+
a list of homogeneous values.
3636
:param unit: The unit of the stored data.
3737
:param uncertainty: The uncertainty (e.g. the standard deviation)
3838
associated with a measure value.
@@ -48,6 +48,8 @@ def __init__(self, name=None, value=None, parent=None, unit=None,
4848
:param oid: object id, UUID string as specified in RFC 4122. If no id is provided,
4949
an id will be generated and assigned. An id has to be unique
5050
within an odML Document.
51+
:param value: Legacy code to the 'values' attribute. If 'values' is provided,
52+
any data provided via 'value' will be ignored.
5153
"""
5254
try:
5355
if oid is not None:
@@ -78,28 +80,33 @@ def __init__(self, name=None, value=None, parent=None, unit=None,
7880
else:
7981
print("Warning: Unknown dtype '%s'." % dtype)
8082

81-
self._value = []
82-
self.value = value
83+
self._values = []
84+
self.values = values
85+
if not values and (value or isinstance(value, bool)):
86+
self.values = value
8387

8488
self.parent = parent
8589

8690
def __len__(self):
87-
return len(self._value)
91+
return len(self._values)
8892

8993
def __getitem__(self, key):
90-
return self._value[key]
94+
return self._values[key]
9195

9296
def __setitem__(self, key, item):
9397
if int(key) < 0 or int(key) > self.__len__():
9498
raise IndexError("odml.Property.__setitem__: key %i invalid for "
9599
"array of length %i" % (int(key), self.__len__()))
96100
try:
97101
val = dtypes.get(item, self.dtype)
98-
self._value[int(key)] = val
102+
self._values[int(key)] = val
99103
except Exception:
100104
raise ValueError("odml.Property.__setitem__: passed value cannot be "
101105
"converted to data type \'%s\'!" % self._dtype)
102106

107+
def __repr__(self):
108+
return "Property: {name = %s}" % self._name
109+
103110
@property
104111
def oid(self):
105112
"""
@@ -143,9 +150,6 @@ def name(self, new_name):
143150

144151
self._name = new_name
145152

146-
def __repr__(self):
147-
return "<Property %s>" % self._name
148-
149153
@property
150154
def dtype(self):
151155
"""
@@ -166,10 +170,10 @@ def dtype(self, new_type):
166170
raise AttributeError("'%s' is not a valid type." % new_type)
167171
# we convert the value if possible
168172
old_type = self._dtype
169-
old_values = self._value
173+
old_values = self._values
170174
try:
171175
self._dtype = new_type
172-
self.value = old_values
176+
self.values = old_values
173177
except:
174178
self._dtype = old_type # If conversion failed, restore old dtype
175179
raise ValueError("cannot convert from '%s' to '%s'" %
@@ -209,42 +213,27 @@ def _validate_parent(new_parent):
209213
@property
210214
def value(self):
211215
"""
212-
Returns the value(s) stored in this property. Method always returns a list
213-
that is a copy (!) of the stored value. Changing this list will NOT change
214-
the property.
215-
For manipulation of the stored values use the append, extend, and direct
216-
access methods (using brackets).
217-
218-
For example:
219-
>>> p = odml.Property("prop", value=[1, 2, 3])
220-
>>> print(p.value)
221-
[1, 2, 3]
222-
>>> p.value.append(4)
223-
>>> print(p.value)
224-
[1, 2, 3]
216+
Deprecated alias of 'values'. Will be removed with the next minor release.
217+
"""
218+
print("The attribute 'value' is deprecated. Please use 'values' instead.")
219+
return self.values
225220

226-
Individual values can be accessed and manipulated like this:
227-
>>> print(p[0])
228-
[1]
229-
>>> p[0] = 4
230-
>>> print(p[0])
231-
[4]
221+
@value.setter
222+
def value(self, new_value):
223+
"""
224+
Deprecated alias of 'values'. Will be removed with the next minor release.
232225
233-
The values can be iterated e.g. with a loop:
234-
>>> for v in p.value:
235-
>>> print(v)
236-
4
237-
2
238-
3
226+
:param new_value: a single value or list of values.
239227
"""
240-
return list(self._value)
228+
print("The attribute 'value' is deprecated. Please use 'values' instead.")
229+
self.values = new_value
241230

242231
def value_str(self, index=0):
243232
"""
244233
Used to access typed data of the value at a specific
245234
index position as a string.
246235
"""
247-
return dtypes.set(self._value[index], self._dtype)
236+
return dtypes.set(self._values[index], self._dtype)
248237

249238
def _validate_values(self, values):
250239
"""
@@ -285,19 +274,52 @@ def _convert_value_input(self, new_value):
285274
"unsupported data type for values: %s" % type(new_value))
286275
return new_value
287276

288-
@value.setter
289-
def value(self, new_value):
277+
@property
278+
def values(self):
290279
"""
291-
Set the value of the property discarding any previous information.
280+
Returns the value(s) stored in this property. Method always returns a list
281+
that is a copy (!) of the stored value. Changing this list will NOT change
282+
the property.
283+
For manipulation of the stored values use the append, extend, and direct
284+
access methods (using brackets).
285+
286+
For example:
287+
>>> p = odml.Property("prop", values=[1, 2, 3])
288+
>>> print(p.values)
289+
[1, 2, 3]
290+
>>> p.values.append(4)
291+
>>> print(p.values)
292+
[1, 2, 3]
293+
294+
Individual values can be accessed and manipulated like this:
295+
>>> print(p[0])
296+
[1]
297+
>>> p[0] = 4
298+
>>> print(p[0])
299+
[4]
300+
301+
The values can be iterated e.g. with a loop:
302+
>>> for v in p.values:
303+
>>> print(v)
304+
4
305+
2
306+
3
307+
"""
308+
return list(self._values)
309+
310+
@values.setter
311+
def values(self, new_value):
312+
"""
313+
Set the values of the property discarding any previous information.
292314
Method will try to convert the passed value to the dtype of
293-
the property and raise an ValueError if not possible.
315+
the property and raise a ValueError if not possible.
294316
295317
:param new_value: a single value or list of values.
296318
"""
297319
# Make sure boolean value 'False' gets through as well...
298320
if new_value is None or \
299321
(isinstance(new_value, (list, tuple, str)) and len(new_value) == 0):
300-
self._value = []
322+
self._values = []
301323
return
302324

303325
new_value = self._convert_value_input(new_value)
@@ -306,9 +328,9 @@ def value(self, new_value):
306328
self._dtype = dtypes.infer_dtype(new_value[0])
307329

308330
if not self._validate_values(new_value):
309-
raise ValueError("odml.Property.value: passed values are not of "
331+
raise ValueError("odml.Property.values: passed values are not of "
310332
"consistent type!")
311-
self._value = [dtypes.get(v, self.dtype) for v in new_value]
333+
self._values = [dtypes.get(v, self.dtype) for v in new_value]
312334

313335
@property
314336
def value_origin(self):
@@ -394,8 +416,8 @@ def remove(self, value):
394416
occurrence of the passed in value is removed from the properties
395417
list of values.
396418
"""
397-
if value in self._value:
398-
self._value.remove(value)
419+
if value in self._values:
420+
self._values.remove(value)
399421

400422
def get_path(self):
401423
"""
@@ -417,7 +439,7 @@ def clone(self, keep_id=False):
417439
"""
418440
obj = super(BaseProperty, self).clone()
419441
obj._parent = None
420-
obj.value = self._value
442+
obj.values = self._values
421443
if not keep_id:
422444
obj.new_id()
423445

@@ -441,7 +463,7 @@ def merge_check(self, source, strict=True):
441463

442464
# Catch unmerge-able values at this point to avoid
443465
# failing Section tree merges which cannot easily be rolled back.
444-
new_value = self._convert_value_input(source.value)
466+
new_value = self._convert_value_input(source.values)
445467
if not self._validate_values(new_value):
446468
raise ValueError("odml.Property.merge: passed value(s) cannot "
447469
"be converted to data type '%s'!" % self._dtype)
@@ -512,7 +534,7 @@ def merge(self, other, strict=True):
512534
if self.unit is None and other.unit is not None:
513535
self.unit = other.unit
514536

515-
to_add = [v for v in other.value if v not in self._value]
537+
to_add = [v for v in other.values if v not in self._values]
516538
self.extend(to_add, strict=strict)
517539

518540
def unmerge(self, other):
@@ -557,11 +579,11 @@ def extend(self, obj, strict=True):
557579
if obj.unit != self.unit:
558580
raise ValueError("odml.Property.extend: src and dest units (%s, %s) "
559581
"do not match!" % (obj.unit, self.unit))
560-
self.extend(obj.value)
582+
self.extend(obj.values)
561583
return
562584

563585
if self.__len__() == 0:
564-
self.value = obj
586+
self.values = obj
565587
return
566588

567589
new_value = self._convert_value_input(obj)
@@ -572,7 +594,7 @@ def extend(self, obj, strict=True):
572594
if not self._validate_values(new_value):
573595
raise ValueError("odml.Property.extend: passed value(s) cannot be converted "
574596
"to data type \'%s\'!" % self._dtype)
575-
self._value.extend([dtypes.get(v, self.dtype) for v in new_value])
597+
self._values.extend([dtypes.get(v, self.dtype) for v in new_value])
576598

577599
def append(self, obj, strict=True):
578600
"""
@@ -587,8 +609,8 @@ def append(self, obj, strict=True):
587609
if obj in [None, "", [], {}]:
588610
return
589611

590-
if not self.value:
591-
self.value = obj
612+
if not self.values:
613+
self.values = obj
592614
return
593615

594616
new_value = self._convert_value_input(obj)
@@ -603,4 +625,36 @@ def append(self, obj, strict=True):
603625
raise ValueError("odml.Property.append: passed value(s) cannot be converted "
604626
"to data type \'%s\'!" % self._dtype)
605627

606-
self._value.append(dtypes.get(new_value[0], self.dtype))
628+
self._values.append(dtypes.get(new_value[0], self.dtype))
629+
630+
def pprint(self, indent=2, max_length=80, current_depth=-1):
631+
"""
632+
Pretty print method to visualize Properties and Section-Property trees.
633+
634+
:param indent: number of leading spaces for every child Property.
635+
:param max_length: maximum number of characters printed in one line.
636+
:param current_depth: number of hierarchical levels printed from the
637+
starting Section.
638+
"""
639+
property_spaces = ""
640+
prefix = ""
641+
if current_depth >= 0:
642+
property_spaces = " " * ((current_depth + 2) * indent)
643+
prefix = "|-"
644+
645+
if self.unit is None:
646+
value_string = str(self.values)
647+
else:
648+
value_string = "{}{}".format(self.values, self.unit)
649+
650+
p_len = len(property_spaces) + len(self.name) + len(value_string)
651+
if p_len >= max_length - 4:
652+
split_len = int((max_length - len(property_spaces)
653+
+ len(self.name) - len(prefix))/2)
654+
str1 = value_string[0: split_len]
655+
str2 = value_string[-split_len:]
656+
print(("{}{} {}: {} ... {}".format(property_spaces, prefix,
657+
self.name, str1, str2)))
658+
else:
659+
print(("{}{} {}: {}".format(property_spaces, prefix, self.name,
660+
value_string)))

0 commit comments

Comments
 (0)