Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c31148e
[Section] Add nix '__repr__' style
mpsonntag Nov 6, 2018
d4c5d90
[Section] Add property count to __repr__
mpsonntag Nov 6, 2018
a22bea0
[Property] Add nix '__repr__' style
mpsonntag Nov 6, 2018
31ce79a
[Property] Change attribute from _value to _values
mpsonntag Nov 20, 2018
eb48876
[Property] Add 'values' accessor methods
mpsonntag Nov 20, 2018
13d1f64
[Property] Add 'value' deprecation warnings
mpsonntag Nov 20, 2018
73e230e
[Property] Use 'values' instead of 'value'
mpsonntag Nov 20, 2018
35bc91c
[tools/dumper] Use 'values' instead of 'value'
mpsonntag Nov 20, 2018
73edc26
[validation] Use prop 'values' instead of 'value'
mpsonntag Nov 20, 2018
3986581
[tools/dictparser] Prop 'values' replaces 'value'
mpsonntag Nov 20, 2018
8fb4422
[base] Use prop 'values' instead of 'value'
mpsonntag Nov 20, 2018
ac7e0f7
[Property] Provide 'values' and 'value' on init
mpsonntag Nov 21, 2018
b0a0b25
[format] Add Property values to value mapping
mpsonntag Nov 21, 2018
7a3a5b0
[tools/xmlreader] Proper 'values' handling on load
mpsonntag Nov 21, 2018
8156605
[tools/dictparser] Proper 'values' handling on save
mpsonntag Nov 21, 2018
2d1a796
[test] Adapt tests to new Property.values
mpsonntag Nov 21, 2018
bcf5357
[Property] Add pprint method
mpsonntag Nov 22, 2018
3b8d475
[Section] Add pprint method
mpsonntag Nov 22, 2018
66784ae
[doc/tutorial] Add Property.values updates
mpsonntag Nov 22, 2018
62ca6d8
[CHANGELOG] Add latest changes
mpsonntag Nov 22, 2018
25391bb
[Section] Switch '/' to '|' in '__repr__' style
mpsonntag Nov 23, 2018
db866f1
[Property] Move '__repr__' to magic method block
mpsonntag Nov 23, 2018
f6422bd
[Document] Change '__repr__' to new style
mpsonntag Nov 23, 2018
3c0208c
[doc/tutorial] Add new '__repr__' style changes
mpsonntag Nov 23, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ until the next release.

# Latest changes in master

...

# Version 1.4.2

## Print methods

`pprint` methods have been added to both `Section` and `Property`
to print whole Section trees with their child sections and properties.
The `__repr__` style of `Section` and `Property` has been changed to
be more similar to the [nixpy](https://github.com/G-Node/nixpy) `__repr__` style.
Printing a `Section` now also features the immediate `Property` child count
in addition to the immediate `Section` child count. See #309.

## Deprecation of 'Property.value' in favor of 'Property.values'

To make working with odML more similar to working with the
metadata part of [nixpy](https://github.com/G-Node/nixpy), the `Property.value`
attribute has been marked deprecated and the `Property.values`
attribute has been added. See #308.

## Uncertainty changes

Uncertainty is now limited to float only. See #294.
Expand Down
161 changes: 80 additions & 81 deletions doc/tutorial.rst

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions odml/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,8 @@ def itervalues(self, max_depth=None, filter_func=lambda x: True):
:type filter_func: function
"""
for prop in [p for p in self.iterproperties(max_depth=max_depth)]:
if filter_func(prop.value):
yield prop.value
if filter_func(prop.values):
yield prop.values

def contains(self, obj):
"""
Expand Down
4 changes: 2 additions & 2 deletions odml/doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ def __init__(self, author=None, date=None, version=None, repository=None, oid=No
self._origin_file_name = None

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

@property
def oid(self):
Expand Down
3 changes: 2 additions & 1 deletion odml/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ class Property(Format):
_map = {
'dependencyvalue': 'dependency_value',
'type': 'dtype',
'id': 'oid'
'id': 'oid',
'value': 'values'
}
_rdf_map = {
'id': _ns.hasId,
Expand Down
172 changes: 113 additions & 59 deletions odml/property.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ class BaseProperty(base.BaseObject):
"""An odML Property"""
_format = frmt.Property

def __init__(self, name=None, value=None, parent=None, unit=None,
def __init__(self, name=None, values=None, parent=None, unit=None,
uncertainty=None, reference=None, definition=None,
dependency=None, dependency_value=None, dtype=None,
value_origin=None, oid=None):
value_origin=None, oid=None, value=None):
"""
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.
Expand All @@ -31,8 +31,8 @@ def __init__(self, name=None, value=None, parent=None, unit=None,
>>> p.dtype
>>> int
: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 values: 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)
associated with a measure value.
Expand All @@ -48,6 +48,8 @@ def __init__(self, name=None, value=None, parent=None, unit=None,
:param oid: object 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.
:param value: Legacy code to the 'values' attribute. If 'values' is provided,
any data provided via 'value' will be ignored.
"""
try:
if oid is not None:
Expand Down Expand Up @@ -78,28 +80,33 @@ def __init__(self, name=None, value=None, parent=None, unit=None,
else:
print("Warning: Unknown dtype '%s'." % dtype)

self._value = []
self.value = value
self._values = []
self.values = values
if not values and (value or isinstance(value, bool)):
self.values = value

self.parent = parent

def __len__(self):
return len(self._value)
return len(self._values)

def __getitem__(self, key):
return self._value[key]
return self._values[key]

def __setitem__(self, key, item):
if int(key) < 0 or int(key) > self.__len__():
raise IndexError("odml.Property.__setitem__: key %i invalid for "
"array of length %i" % (int(key), self.__len__()))
try:
val = dtypes.get(item, self.dtype)
self._value[int(key)] = val
self._values[int(key)] = val
except Exception:
raise ValueError("odml.Property.__setitem__: passed value cannot be "
"converted to data type \'%s\'!" % self._dtype)

def __repr__(self):
return "Property: {name = %s}" % self._name

@property
def oid(self):
"""
Expand Down Expand Up @@ -143,9 +150,6 @@ def name(self, new_name):

self._name = new_name

def __repr__(self):
return "<Property %s>" % self._name

@property
def dtype(self):
"""
Expand All @@ -166,10 +170,10 @@ def dtype(self, new_type):
raise AttributeError("'%s' is not a valid type." % new_type)
# we convert the value if possible
old_type = self._dtype
old_values = self._value
old_values = self._values
try:
self._dtype = new_type
self.value = old_values
self.values = old_values
except:
self._dtype = old_type # If conversion failed, restore old dtype
raise ValueError("cannot convert from '%s' to '%s'" %
Expand Down Expand Up @@ -209,42 +213,27 @@ 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).

For example:
>>> p = odml.Property("prop", value=[1, 2, 3])
>>> print(p.value)
[1, 2, 3]
>>> p.value.append(4)
>>> print(p.value)
[1, 2, 3]
Deprecated alias of 'values'. Will be removed with the next minor release.
"""
print("The attribute 'value' is deprecated. Please use 'values' instead.")
return self.values

Individual values can be accessed and manipulated like this:
>>> print(p[0])
[1]
>>> p[0] = 4
>>> print(p[0])
[4]
@value.setter
def value(self, new_value):
"""
Deprecated alias of 'values'. Will be removed with the next minor release.

The values can be iterated e.g. with a loop:
>>> for v in p.value:
>>> print(v)
4
2
3
:param new_value: a single value or list of values.
"""
return list(self._value)
print("The attribute 'value' is deprecated. Please use 'values' instead.")
self.values = new_value

def value_str(self, index=0):
"""
Used to access typed data of the value at a specific
index position as a string.
"""
return dtypes.set(self._value[index], self._dtype)
return dtypes.set(self._values[index], self._dtype)

def _validate_values(self, values):
"""
Expand Down Expand Up @@ -285,19 +274,52 @@ def _convert_value_input(self, new_value):
"unsupported data type for values: %s" % type(new_value))
return new_value

@value.setter
def value(self, new_value):
@property
def values(self):
"""
Set the value of the property discarding any previous information.
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", values=[1, 2, 3])
>>> print(p.values)
[1, 2, 3]
>>> p.values.append(4)
>>> print(p.values)
[1, 2, 3]

Individual values can be accessed and manipulated like this:
>>> print(p[0])
[1]
>>> p[0] = 4
>>> print(p[0])
[4]

The values can be iterated e.g. with a loop:
>>> for v in p.values:
>>> print(v)
4
2
3
"""
return list(self._values)

@values.setter
def values(self, new_value):
"""
Set the values 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 a ValueError if not possible.

: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):
self._value = []
self._values = []
return

new_value = self._convert_value_input(new_value)
Expand All @@ -306,9 +328,9 @@ def value(self, new_value):
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 "
raise ValueError("odml.Property.values: passed values are not of "
"consistent type!")
self._value = [dtypes.get(v, self.dtype) for v in new_value]
self._values = [dtypes.get(v, self.dtype) for v in new_value]

@property
def value_origin(self):
Expand Down Expand Up @@ -394,8 +416,8 @@ def remove(self, value):
occurrence of the passed in value is removed from the properties
list of values.
"""
if value in self._value:
self._value.remove(value)
if value in self._values:
self._values.remove(value)

def get_path(self):
"""
Expand All @@ -417,7 +439,7 @@ def clone(self, keep_id=False):
"""
obj = super(BaseProperty, self).clone()
obj._parent = None
obj.value = self._value
obj.values = self._values
if not keep_id:
obj.new_id()

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

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

to_add = [v for v in other.value if v not in self._value]
to_add = [v for v in other.values if v not in self._values]
self.extend(to_add, strict=strict)

def unmerge(self, other):
Expand Down Expand Up @@ -557,11 +579,11 @@ def extend(self, obj, strict=True):
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)
self.extend(obj.values)
return

if self.__len__() == 0:
self.value = obj
self.values = obj
return

new_value = self._convert_value_input(obj)
Expand All @@ -572,7 +594,7 @@ def extend(self, obj, strict=True):
if not self._validate_values(new_value):
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])
self._values.extend([dtypes.get(v, self.dtype) for v in new_value])

def append(self, obj, strict=True):
"""
Expand All @@ -587,8 +609,8 @@ def append(self, obj, strict=True):
if obj in [None, "", [], {}]:
return

if not self.value:
self.value = obj
if not self.values:
self.values = obj
return

new_value = self._convert_value_input(obj)
Expand All @@ -603,4 +625,36 @@ def append(self, obj, strict=True):
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))
self._values.append(dtypes.get(new_value[0], self.dtype))

def pprint(self, indent=2, max_length=80, current_depth=-1):
"""
Pretty print method to visualize Properties and Section-Property trees.

:param indent: number of leading spaces for every child Property.
:param max_length: maximum number of characters printed in one line.
:param current_depth: number of hierarchical levels printed from the
starting Section.
"""
property_spaces = ""
prefix = ""
if current_depth >= 0:
property_spaces = " " * ((current_depth + 2) * indent)
prefix = "|-"

if self.unit is None:
value_string = str(self.values)
else:
value_string = "{}{}".format(self.values, self.unit)

p_len = len(property_spaces) + len(self.name) + len(value_string)
if p_len >= max_length - 4:
split_len = int((max_length - len(property_spaces)
+ len(self.name) - len(prefix))/2)
str1 = value_string[0: split_len]
str2 = value_string[-split_len:]
print(("{}{} {}: {} ... {}".format(property_spaces, prefix,
self.name, str1, str2)))
else:
print(("{}{} {}: {}".format(property_spaces, prefix, self.name,
value_string)))
Loading