Skip to content

Commit 23f9181

Browse files
authored
Merge pull request #277 from mpsonntag/refDoc
Document updates and tests
2 parents 40d088a + b4fedcd commit 23f9181

9 files changed

Lines changed: 414 additions & 113 deletions

File tree

README.rst

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@
55
.. image:: https://coveralls.io/repos/github/G-Node/python-odml/badge.svg?branch=master
66
:target: https://coveralls.io/github/G-Node/python-odml?branch=master
77

8-
odML libraries and editor
9-
=========================
8+
odML (Open metaData Markup Language) core library
9+
=================================================
1010

11-
The Python-odML library (including the odML-Editor) is available on
12-
`GitHub <https://github.com/G-Node/python-odml>`_. If you are not familiar with
13-
the version control system **git**, but still want to use it, have a look at
14-
the documentation available on the `git-scm website <https://git-scm.com/>`_.
11+
The open metadata Markup Language is a file based format (XML, JSON, YAML) for storing
12+
metadata in an organised human- and machine-readable way. odML is an initiative to define
13+
and establish an open, flexible, and easy-to-use format to transport metadata.
14+
15+
The Python-odML library can be easily installed via :code:`pip`. The source code is freely
16+
available on `GitHub <https://github.com/G-Node/python-odml>`_. If you are not familiar
17+
with the version control system **git**, but still want to use it, have a look at the
18+
documentation available on the `git-scm website <https://git-scm.com/>`_.
1519

1620
Dependencies
1721
------------
@@ -85,6 +89,5 @@ Bugs & Questions
8589
Should you find a behaviour that is likely a bug, please file a bug report at
8690
`the github bug tracker <https://github.com/G-Node/python-odml/issues>`_.
8791

88-
If you have questions regarding the use of the library or the editor, feel free to
89-
join the `#gnode <http://webchat.freenode.net?channels=%23gnode>`_ IRC channel
90-
on freenode.
92+
If you have questions regarding the use of the library, feel free to join the
93+
`#gnode <http://webchat.freenode.net?channels=%23gnode>`_ IRC channel on freenode.

odml/base.py

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"""
33
Collects common base functionality
44
"""
5-
5+
import collections
66
import posixpath
77

88
from . import terminology
9-
from .tools.doc_inherit import inherit_docstring, allow_inherit_docstring
9+
from .tools.doc_inherit import allow_inherit_docstring
1010

1111

1212
class _baseobj(object):
@@ -169,20 +169,20 @@ def append(self, *obj_tuple):
169169
@allow_inherit_docstring
170170
class sectionable(baseobject):
171171
def __init__(self):
172-
from odml.section import Section
173-
self._sections = SmartList(Section)
172+
from odml.section import BaseSection
173+
self._sections = SmartList(BaseSection)
174174
self._repository = None
175175

176176
@property
177177
def document(self):
178178
"""
179179
Returns the parent-most node (if its a document instance) or None
180180
"""
181+
from odml.doc import BaseDocument
181182
p = self
182183
while p.parent:
183184
p = p.parent
184-
import odml.doc as doc
185-
if isinstance(p, doc.Document):
185+
if isinstance(p, BaseDocument):
186186
return p
187187

188188
@property
@@ -192,29 +192,57 @@ def sections(self):
192192

193193
def insert(self, position, section):
194194
"""
195-
Adds the section to the section-list and makes this document the
196-
section’s parent.
195+
Insert a Section at the child-list position. A ValueError will be raised,
196+
if a Section with the same name already exists in the child-list.
197197
198-
Currently just appends the section and does not insert at the
199-
specified *position*
198+
:param position: index at which the object should be inserted.
199+
:param section: odML Section object.
200200
"""
201-
self._sections.append(section)
202-
section._parent = self
201+
from odml.section import BaseSection
202+
if isinstance(section, BaseSection):
203+
if section.name in self._sections:
204+
raise ValueError("Section with name '%s' already exists." % section.name)
203205

204-
def append(self, *vsection_tuple):
206+
self._sections.insert(position, section)
207+
section._parent = self
208+
else:
209+
raise ValueError("Can only insert objects of type Section.")
210+
211+
def append(self, section):
205212
"""
206-
Adds the section to the section-list and makes this document the
207-
section’s parent.
213+
Method appends a single Section to the section child-lists of the current Object.
214+
215+
:param section: odML Section object.
208216
"""
209217
from odml.section import BaseSection
210-
from odml.doc import BaseDocument
211-
for vsection in vsection_tuple:
212-
if (not isinstance(vsection, BaseSection)) & \
213-
isinstance(self, BaseDocument):
214-
raise KeyError("Object " + str(vsection) +
215-
" is not a Section.")
216-
self._sections.append(vsection)
217-
vsection._parent = self
218+
if isinstance(section, BaseSection):
219+
self._sections.append(section)
220+
section._parent = self
221+
elif isinstance(section, collections.Iterable) and not isinstance(section, str):
222+
raise ValueError("Use extend to add a list of Sections.")
223+
else:
224+
raise ValueError("Can only append objects of type Section.")
225+
226+
def extend(self, sec_list):
227+
"""
228+
Method adds Sections to the section child-list of the current object.
229+
230+
:param sec_list: Iterable containing odML Section entries.
231+
"""
232+
from odml.section import BaseSection
233+
if not isinstance(sec_list, collections.Iterable):
234+
raise TypeError("'%s' object is not iterable" % type(sec_list).__name__)
235+
236+
# Make sure only Sections with unique names will be added.
237+
for sec in sec_list:
238+
if not isinstance(sec, BaseSection):
239+
raise ValueError("Can only extend objects of type Section.")
240+
241+
elif isinstance(sec, BaseSection) and sec.name in self._sections:
242+
raise KeyError("Section with name '%s' already exists." % sec.name)
243+
244+
for sec in sec_list:
245+
self.append(sec)
218246

219247
def remove(self, section):
220248
""" Removes the specified child-section """
@@ -254,7 +282,7 @@ def itersections(self, recursive=True, yield_self=False,
254282
if self == self.document and ((max_depth is None) or (max_depth > 0)):
255283
for sec in self.sections:
256284
stack.append((sec, 1)) # (<section>, <level in a tree>)
257-
elif not self == self.document:
285+
elif self != self.document:
258286
stack.append((self, 0)) # (<section>, <level in a tree>)
259287

260288
while len(stack) > 0:
@@ -319,12 +347,11 @@ def contains(self, obj):
319347
if obj.name == i.name and obj.type == i.type:
320348
return i
321349

322-
# FIXME type arguments renamed to dtype?
323-
def _matches(self, obj, key=None, type=None, include_subtype=False):
350+
def _matches(self, obj, key=None, otype=None, include_subtype=False):
324351
"""
325352
Find out
326353
* if the *key* matches obj.name (if key is not None)
327-
* or if *type* matches obj.type (if type is not None)
354+
* or if *otype* matches obj.type (if type is not None)
328355
* if type does not match exactly, test for subtype.
329356
(e.g.stimulus/white_noise)
330357
comparisons are case-insensitive, however both key and type
@@ -333,18 +360,16 @@ def _matches(self, obj, key=None, type=None, include_subtype=False):
333360
name_match = (key is None or (
334361
key is not None and hasattr(obj, "name") and obj.name == key))
335362

336-
exact_type_match = (type is None or (type is not None and
337-
hasattr(obj, "type") and
338-
obj.type.lower() == type))
363+
exact_type_match = (otype is None or (otype is not None and
364+
hasattr(obj, "type") and
365+
obj.type.lower() == otype))
339366

340367
if not include_subtype:
341368
return name_match and exact_type_match
342369

343-
subtype_match = type is None or (type is not None and
344-
hasattr(obj, "type") and
345-
type in obj.type
346-
.lower().split('/')[:-1])
347-
# TODO : Break the above line more elegantly
370+
subtype_match = (otype is None or
371+
(otype is not None and hasattr(obj, "type") and
372+
otype in obj.type.lower().split('/')[:-1]))
348373

349374
return name_match and (exact_type_match or subtype_match)
350375

@@ -539,10 +564,10 @@ def clone(self, children=True):
539564
Clone this object recursively allowing to copy it independently
540565
to another document
541566
"""
542-
from odml.section import Section
567+
from odml.section import BaseSection
543568
obj = super(sectionable, self).clone(children)
544569
obj._parent = None
545-
obj._sections = SmartList(Section)
570+
obj._sections = SmartList(BaseSection)
546571
if children:
547572
for s in self._sections:
548573
obj.append(s.clone())

odml/doc.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Document(base._baseobj):
1515
@allow_inherit_docstring
1616
class BaseDocument(base.sectionable, Document):
1717
"""
18-
A represenation of an odML document in memory.
18+
A representation of an odML document in memory.
1919
Its odml attributes are: *author*, *date*, *version* and *repository*.
2020
A Document behaves very much like a section, except that it cannot hold
2121
properties.
@@ -69,6 +69,8 @@ def author(self):
6969

7070
@author.setter
7171
def author(self, new_value):
72+
if new_value == "":
73+
new_value = None
7274
self._author = new_value
7375

7476
@property
@@ -81,18 +83,24 @@ def version(self):
8183

8284
@version.setter
8385
def version(self, new_value):
86+
if new_value == "":
87+
new_value = None
8488
self._version = new_value
8589

8690
@property
8791
def date(self):
8892
"""
8993
The date the document was created.
9094
"""
91-
return dtypes.set(self._date, "date")
95+
return self._date
9296

9397
@date.setter
9498
def date(self, new_value):
95-
self._date = dtypes.get(new_value, "date")
99+
if not new_value:
100+
new_value = None
101+
else:
102+
new_value = dtypes.date_set(new_value)
103+
self._date = new_value
96104

97105
@property
98106
def parent(self):
@@ -125,4 +133,4 @@ def get_terminology_equivalent(self):
125133
if self.repository is None:
126134
return None
127135
term = terminology.load(self.repository)
128-
return term
136+
return term

odml/property.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ def __init__(self, name, value=None, parent=None, unit=None,
6161
except ValueError as e:
6262
print(e)
6363
self._id = str(uuid.uuid4())
64+
65+
self._parent = None
6466
self._name = name
6567
self._value_origin = value_origin
6668
self._unit = unit
@@ -79,7 +81,6 @@ def __init__(self, name, value=None, parent=None, unit=None,
7981
self._value = []
8082
self.value = value
8183

82-
self._parent = None
8384
self.parent = parent
8485

8586
@property

odml/section.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def __init__(self, name, type=None, parent=None,
4646
print(e)
4747
self._id = str(uuid.uuid4())
4848

49+
self._parent = None
4950
self._name = name
5051
self._definition = definition
5152
self._reference = reference
@@ -55,7 +56,6 @@ def __init__(self, name, type=None, parent=None,
5556

5657
# this may fire a change event, so have the section setup then
5758
self.type = type
58-
self._parent = None
5959
self.parent = parent
6060

6161
def __repr__(self):

0 commit comments

Comments
 (0)