Skip to content

Commit

Permalink
Merge pull request #30 from hdmf-dev/fix/nested_get_class
Browse files Browse the repository at this point in the history
add support for nested get_class
  • Loading branch information
bendichter committed Apr 16, 2019
2 parents 61fc163 + 22401f3 commit 7920c5f
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 31 deletions.
56 changes: 29 additions & 27 deletions src/hdmf/build/map.py
Original file line number Diff line number Diff line change
Expand Up @@ -1290,11 +1290,11 @@ def __get_type(self, spec):
container_type = val.get(tgttype)
if container_type is not None:
return container_type
return (Data, Container)
return Data, Container
elif spec.shape is None and spec.dims is None:
return self._type_map.get(spec.dtype)
else:
return ('array_data',)
return 'array_data',
elif isinstance(spec, LinkSpec):
return Container
else:
Expand All @@ -1306,19 +1306,21 @@ def __get_type(self, spec):
elif spec.shape is None and spec.dims is None:
return self._type_map.get(spec.dtype)
else:
return ('array_data', 'data',)
return 'array_data', 'data'

def __get_constructor(self, base, addl_fields):
def __get_cls_dict(self, base, addl_fields):
# TODO: fix this to be more maintainable and smarter
if base is None:
raise ValueError('cannot generate class without base class')
existing_args = set()
docval_args = list()
new_args = list()
if base is not None:
for arg in get_docval(base.__init__):
existing_args.add(arg['name'])
if arg['name'] in addl_fields:
continue
docval_args.append(arg)
fields = list()
for arg in get_docval(base.__init__):
existing_args.add(arg['name'])
if arg['name'] in addl_fields:
continue
docval_args.append(arg)
for f, field_spec in addl_fields.items():
if not f == 'help':
dtype = self.__get_type(field_spec)
Expand All @@ -1330,20 +1332,19 @@ def __get_constructor(self, base, addl_fields):
docval_args.append(docval_arg)
if f not in existing_args:
new_args.append(f)
if base is None:
@docval(*docval_args)
def __init__(self, **kwargs):
for f in new_args:
setattr(self, f, kwargs.get(f, None))
return __init__
else:
@docval(*docval_args)
def __init__(self, **kwargs):
pargs, pkwargs = fmt_docval_args(base.__init__, kwargs)
super(type(self), self).__init__(*pargs, **pkwargs)
for f in new_args:
setattr(self, f, kwargs.get(f, None))
return __init__
if issubclass(dtype, (Container, Data, DataRegion)):
fields.append({'name': f, 'child': True})
else:
fields.append(f)

@docval(*docval_args)
def __init__(self, **kwargs):
pargs, pkwargs = fmt_docval_args(base.__init__, kwargs)
super(type(self), self).__init__(*pargs, **pkwargs)
for f in new_args:
setattr(self, f, kwargs.get(f, None))

return {'__init__': __init__, base._fieldsname: tuple(fields)}

@docval({"name": "namespace", "type": str, "doc": "the namespace containing the data_type"},
{"name": "data_type", "type": str, "doc": "the data type to create a Container class for"},
Expand All @@ -1364,7 +1365,6 @@ def get_container_cls(self, **kwargs):
parent_cls = self.__get_container_cls(namespace, t)
if parent_cls is not None:
break
bases = tuple()
if parent_cls is not None:
bases = (parent_cls,)
else:
Expand All @@ -1375,14 +1375,16 @@ def get_container_cls(self, **kwargs):
else:
raise ValueError("Cannot generate class from %s" % type(spec))
parent_cls = bases[0]
if type(parent_cls) is not ExtenderMeta:
raise ValueError("parent class %s is not of type ExtenderMeta - %s" % (parent_cls, type(parent_cls)))
name = data_type
attr_names = self.__default_mapper_cls.get_attr_names(spec)
fields = dict()
for k, field_spec in attr_names.items():
if not spec.is_inherited_spec(field_spec):
fields[k] = field_spec
d = {'__init__': self.__get_constructor(parent_cls, fields)}
cls = type(str(name), bases, d)
d = self.__get_cls_dict(parent_cls, fields)
cls = ExtenderMeta(str(name), bases, d)
self.register_container_type(namespace, data_type, cls)
return cls

Expand Down
6 changes: 4 additions & 2 deletions src/hdmf/container.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import abc
from six import with_metaclass
from .utils import docval, getargs
from .utils import docval, getargs, ExtenderMeta


class Container(with_metaclass(abc.ABCMeta, object)):
class Container(with_metaclass(ExtenderMeta, object)):

_fieldsname = '__fields__'

@docval({'name': 'name', 'type': str, 'doc': 'the name of this container'},
{'name': 'parent', 'type': 'Container', 'doc': 'the Container that holds this Container', 'default': None},
Expand Down
4 changes: 2 additions & 2 deletions src/hdmf/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ def get_data_shape(data, strict_no_data_load=False):
:param data: Array for which we should determine the shape.
:type data: List, numpy.ndarray, DataChunkIterator, any object that support __len__ or .shape.
:param strict_no_data_load: In order to determin the shape of nested tuples and lists, this function
:param strict_no_data_load: In order to determine the shape of nested tuples and lists, this function
recursively inspects elements along the dimensions, assuming that the data has a regular,
rectangular shape. In the case of out-of-core iterators this means that the first item
along each dimensions would potentially be loaded into memory. By setting this option
Expand Down Expand Up @@ -580,7 +580,7 @@ def __get_shape_helper(local_data):

def pystr(s):
"""
Cross-version support for convertin a string of characters to Python str object
Cross-version support for converting a string of characters to Python str object
"""
if six.PY2 and isinstance(s, six.text_type):
return s.encode('ascii', 'ignore')
Expand Down

0 comments on commit 7920c5f

Please sign in to comment.