|
2 | 2 | import numpy as np |
3 | 3 | import pandas as pd |
4 | 4 |
|
5 | | -from hdmf.utils import docval, getargs, ExtenderMeta, call_docval_func, popargs, get_docval, fmt_docval_args |
6 | 5 | from hdmf import Container, Data, DataRegion, get_region_slicer |
7 | | -from hdmf.container import AbstractContainer |
| 6 | +from hdmf.container import AbstractContainer, MultiContainerInterface as hdmf_MultiContainerInterface |
8 | 7 | from hdmf.common import DynamicTable, DynamicTableRegion # noqa: F401 |
9 | 8 | from hdmf.common import VectorData, VectorIndex, ElementIdentifiers # noqa: F401 |
| 9 | +from hdmf.utils import docval, getargs, ExtenderMeta, call_docval_func, popargs |
| 10 | +from hdmf.utils import LabelledDict # noqa: F401 |
10 | 11 |
|
11 | 12 | from . import CORE_NAMESPACE, register_class |
12 | 13 |
|
@@ -39,28 +40,11 @@ class NWBContainer(NWBMixin, Container): |
39 | 40 |
|
40 | 41 | __nwbfields__ = tuple() |
41 | 42 |
|
42 | | - @docval({'name': 'name', 'type': str, 'doc': 'the name of this container'}) |
43 | | - def __init__(self, **kwargs): |
44 | | - call_docval_func(super(NWBContainer, self).__init__, kwargs) |
45 | | - |
46 | | - def _to_dict(self, arg, label="NULL"): |
47 | | - return_dict = LabelledDict(label) |
48 | | - if arg is None: |
49 | | - return return_dict |
50 | | - else: |
51 | | - for i in arg: |
52 | | - assert i.name is not None # If a container doesn't have a name, it gets lost! |
53 | | - assert i.name not in return_dict |
54 | | - return_dict[i.name] = i |
55 | | - return return_dict |
56 | | - |
57 | 43 |
|
58 | 44 | @register_class('NWBDataInterface', CORE_NAMESPACE) |
59 | 45 | class NWBDataInterface(NWBContainer): |
60 | 46 |
|
61 | | - @docval(*get_docval(NWBContainer.__init__)) |
62 | | - def __init__(self, **kwargs): |
63 | | - call_docval_func(super(NWBDataInterface, self).__init__, kwargs) |
| 47 | + pass |
64 | 48 |
|
65 | 49 |
|
66 | 50 | @register_class('NWBData', CORE_NAMESPACE) |
@@ -332,309 +316,7 @@ def __getitem__(self, idx): |
332 | 316 | return self.__regionslicer[idx] |
333 | 317 |
|
334 | 318 |
|
335 | | -class MultiContainerInterface(NWBDataInterface): |
336 | | - ''' |
337 | | - A class for dynamically defining a API classes that |
338 | | - represent NWBDataInterfaces that contain multiple Containers |
339 | | - of the same type |
340 | | -
|
341 | | - To use, extend this class, and create a dictionary as a class |
342 | | - attribute with the following keys: |
343 | | -
|
344 | | - * 'add' to name the method for adding Container instances |
345 | | -
|
346 | | - * 'create' to name the method fo creating Container instances |
347 | | -
|
348 | | - * 'get' to name the method for getting Container instances |
349 | | -
|
350 | | - * 'attr' to name the attribute that stores the Container instances |
351 | | -
|
352 | | - * 'type' to provide the Container object type |
353 | | -
|
354 | | - See LFP or Position for an example of how to use this. |
355 | | - ''' |
| 319 | +class MultiContainerInterface(NWBDataInterface, hdmf_MultiContainerInterface): |
| 320 | + """Defined in PyNWB for API backward compatibility. See HDMF MultiContainterInterface for details.""" |
356 | 321 |
|
357 | | - @docval(*get_docval(NWBDataInterface.__init__)) |
358 | | - def __init__(self, **kwargs): |
359 | | - call_docval_func(super(MultiContainerInterface, self).__init__, kwargs) |
360 | | - if isinstance(self.__clsconf__, dict): |
361 | | - attr_name = self.__clsconf__['attr'] |
362 | | - self.fields[attr_name] = LabelledDict(attr_name) |
363 | | - else: |
364 | | - for d in self.__clsconf__: |
365 | | - attr_name = d['attr'] |
366 | | - self.fields[attr_name] = LabelledDict(attr_name) |
367 | | - |
368 | | - @staticmethod |
369 | | - def __add_article(noun): |
370 | | - if isinstance(noun, tuple): |
371 | | - noun = noun[0] |
372 | | - if isinstance(noun, type): |
373 | | - noun = noun.__name__ |
374 | | - if noun[0] in ('aeiouAEIOU'): |
375 | | - return 'an %s' % noun |
376 | | - return 'a %s' % noun |
377 | | - |
378 | | - @staticmethod |
379 | | - def __join(argtype): |
380 | | - def tostr(x): |
381 | | - return x.__name__ if isinstance(x, type) else x |
382 | | - if isinstance(argtype, (list, tuple)): |
383 | | - args = [tostr(x) for x in argtype] |
384 | | - if len(args) == 1: |
385 | | - return args[0].__name__ |
386 | | - else: |
387 | | - ", ".join(tostr(x) for x in args[:-1]) + ' or ' + args[-1] |
388 | | - else: |
389 | | - return tostr(argtype) |
390 | | - |
391 | | - @classmethod |
392 | | - def __make_get(cls, func_name, attr_name, container_type): |
393 | | - doc = "Get %s from this %s" % (cls.__add_article(container_type), cls.__name__) |
394 | | - |
395 | | - @docval({'name': 'name', 'type': str, 'doc': 'the name of the %s' % cls.__join(container_type), |
396 | | - 'default': None}, |
397 | | - rtype=container_type, returns='the %s with the given name' % cls.__join(container_type), |
398 | | - func_name=func_name, doc=doc) |
399 | | - def _func(self, **kwargs): |
400 | | - name = getargs('name', kwargs) |
401 | | - d = getattr(self, attr_name) |
402 | | - ret = None |
403 | | - if name is None: |
404 | | - if len(d) > 1: |
405 | | - msg = "more than one element in %s of %s '%s' -- must specify a name" % \ |
406 | | - (attr_name, cls.__name__, self.name) |
407 | | - raise ValueError(msg) |
408 | | - elif len(d) == 0: |
409 | | - msg = "%s of %s '%s' is empty" % (attr_name, cls.__name__, self.name) |
410 | | - raise ValueError(msg) |
411 | | - elif len(d) == 1: |
412 | | - for v in d.values(): |
413 | | - ret = v |
414 | | - else: |
415 | | - ret = d.get(name) |
416 | | - if ret is None: |
417 | | - msg = "'%s' not found in %s of %s '%s'" % (name, attr_name, cls.__name__, self.name) |
418 | | - raise KeyError(msg) |
419 | | - return ret |
420 | | - |
421 | | - return _func |
422 | | - |
423 | | - @classmethod |
424 | | - def __make_add(cls, func_name, attr_name, container_type): |
425 | | - doc = "Add %s to this %s" % (cls.__add_article(container_type), cls.__name__) |
426 | | - |
427 | | - @docval({'name': attr_name, 'type': (list, tuple, dict, container_type), |
428 | | - 'doc': 'the %s to add' % cls.__join(container_type)}, |
429 | | - func_name=func_name, doc=doc) |
430 | | - def _func(self, **kwargs): |
431 | | - container = getargs(attr_name, kwargs) |
432 | | - if isinstance(container, container_type): |
433 | | - containers = [container] |
434 | | - elif isinstance(container, dict): |
435 | | - containers = container.values() |
436 | | - else: |
437 | | - containers = container |
438 | | - d = getattr(self, attr_name) |
439 | | - for tmp in containers: |
440 | | - if not isinstance(tmp.parent, Container): |
441 | | - tmp.parent = self |
442 | | - # else, the ObjectMapper will create a link from self (parent) to tmp (child with existing parent) |
443 | | - if tmp.name in d: |
444 | | - msg = "'%s' already exists in '%s'" % (tmp.name, self.name) |
445 | | - raise ValueError(msg) |
446 | | - d[tmp.name] = tmp |
447 | | - return container |
448 | | - return _func |
449 | | - |
450 | | - @classmethod |
451 | | - def __make_create(cls, func_name, add_name, container_type): |
452 | | - doc = "Create %s and add it to this %s" % (cls.__add_article(container_type), cls.__name__) |
453 | | - |
454 | | - @docval(*filter(_not_parent, get_docval(container_type.__init__)), func_name=func_name, doc=doc, |
455 | | - returns="the %s object that was created" % cls.__join(container_type), rtype=container_type) |
456 | | - def _func(self, **kwargs): |
457 | | - cargs, ckwargs = fmt_docval_args(container_type.__init__, kwargs) |
458 | | - ret = container_type(*cargs, **ckwargs) |
459 | | - getattr(self, add_name)(ret) |
460 | | - return ret |
461 | | - return _func |
462 | | - |
463 | | - @classmethod |
464 | | - def __make_constructor(cls, clsconf): |
465 | | - args = list() |
466 | | - for conf in clsconf: |
467 | | - attr_name = conf['attr'] |
468 | | - container_type = conf['type'] |
469 | | - args.append({'name': attr_name, 'type': (list, tuple, dict, container_type), |
470 | | - 'doc': '%s to store in this interface' % cls.__join(container_type), 'default': dict()}) |
471 | | - |
472 | | - args.append({'name': 'name', 'type': str, 'doc': 'the name of this container', 'default': cls.__name__}) |
473 | | - |
474 | | - @docval(*args, func_name='__init__') |
475 | | - def _func(self, **kwargs): |
476 | | - call_docval_func(super(cls, self).__init__, kwargs) |
477 | | - for conf in clsconf: |
478 | | - attr_name = conf['attr'] |
479 | | - add_name = conf['add'] |
480 | | - container = popargs(attr_name, kwargs) |
481 | | - add = getattr(self, add_name) |
482 | | - add(container) |
483 | | - return _func |
484 | | - |
485 | | - @classmethod |
486 | | - def __make_getitem(cls, attr_name, container_type): |
487 | | - doc = "Get %s from this %s" % (cls.__add_article(container_type), cls.__name__) |
488 | | - |
489 | | - @docval({'name': 'name', 'type': str, 'doc': 'the name of the %s' % cls.__join(container_type), |
490 | | - 'default': None}, |
491 | | - rtype=container_type, returns='the %s with the given name' % cls.__join(container_type), |
492 | | - func_name='__getitem__', doc=doc) |
493 | | - def _func(self, **kwargs): |
494 | | - name = getargs('name', kwargs) |
495 | | - d = getattr(self, attr_name) |
496 | | - if len(d) == 0: |
497 | | - msg = "%s '%s' is empty" % (cls.__name__, self.name) |
498 | | - raise ValueError(msg) |
499 | | - if len(d) > 1 and name is None: |
500 | | - msg = "more than one %s in this %s -- must specify a name" % cls.__join(container_type), cls.__name__ |
501 | | - raise ValueError(msg) |
502 | | - ret = None |
503 | | - if len(d) == 1: |
504 | | - for v in d.values(): |
505 | | - ret = v |
506 | | - else: |
507 | | - ret = d.get(name) |
508 | | - if ret is None: |
509 | | - msg = "'%s' not found in %s '%s'" % (name, cls.__name__, self.name) |
510 | | - raise KeyError(msg) |
511 | | - return ret |
512 | | - |
513 | | - return _func |
514 | | - |
515 | | - @classmethod |
516 | | - def __make_setter(cls, nwbfield, add_name): |
517 | | - |
518 | | - @docval({'name': 'val', 'type': (list, tuple, dict), 'doc': 'the sub items to add', 'default': None}) |
519 | | - def nwbbt_setter(self, **kwargs): |
520 | | - val = getargs('val', kwargs) |
521 | | - if val is None: |
522 | | - return |
523 | | - getattr(self, add_name)(val) |
524 | | - |
525 | | - return nwbbt_setter |
526 | | - |
527 | | - @ExtenderMeta.pre_init |
528 | | - def __build_class(cls, name, bases, classdict): |
529 | | - ''' |
530 | | - This classmethod will be called during class declaration in the metaclass to automatically |
531 | | - create setters and getters for NWB fields that need to be exported |
532 | | - ''' |
533 | | - if not hasattr(cls, '__clsconf__'): |
534 | | - return |
535 | | - multi = False |
536 | | - if isinstance(cls.__clsconf__, dict): |
537 | | - clsconf = [cls.__clsconf__] |
538 | | - elif isinstance(cls.__clsconf__, list): |
539 | | - multi = True |
540 | | - clsconf = cls.__clsconf__ |
541 | | - else: |
542 | | - raise TypeError("'__clsconf__' must be a dict or a list of dicts") |
543 | | - |
544 | | - for i, d in enumerate(clsconf): |
545 | | - # get add method name |
546 | | - add = d.get('add') |
547 | | - if add is None: |
548 | | - msg = "MultiContainerInterface subclass '%s' is missing 'add' key in __clsconf__" % cls.__name__ |
549 | | - if multi: |
550 | | - msg += " at element %d" % i |
551 | | - raise ValueError(msg) |
552 | | - |
553 | | - # get container attribute name |
554 | | - attr = d.get('attr') |
555 | | - if attr is None: |
556 | | - msg = "MultiContainerInterface subclass '%s' is missing 'attr' key in __clsconf__" % cls.__name__ |
557 | | - if multi: |
558 | | - msg += " at element %d" % i |
559 | | - raise ValueError(msg) |
560 | | - |
561 | | - # get container type |
562 | | - container_type = d.get('type') |
563 | | - if container_type is None: |
564 | | - msg = "MultiContainerInterface subclass '%s' is missing 'type' key in __clsconf__" % cls.__name__ |
565 | | - if multi: |
566 | | - msg += " at element %d" % i |
567 | | - raise ValueError(msg) |
568 | | - |
569 | | - # create property with the name given in 'attr' |
570 | | - if not hasattr(cls, attr): |
571 | | - aconf = cls._check_field_spec(attr) |
572 | | - getter = cls._getter(aconf) |
573 | | - doc = "a dictionary containing the %s in this %s container" % \ |
574 | | - (cls.__join(container_type), cls.__name__) |
575 | | - setattr(cls, attr, property(getter, cls.__make_setter(aconf, add), None, doc)) |
576 | | - |
577 | | - # create the add method |
578 | | - setattr(cls, add, cls.__make_add(add, attr, container_type)) |
579 | | - |
580 | | - # get create method name |
581 | | - create = d.get('create') |
582 | | - if create is not None: |
583 | | - setattr(cls, create, cls.__make_create(create, add, container_type)) |
584 | | - |
585 | | - get = d.get('get') |
586 | | - if get is not None: |
587 | | - setattr(cls, get, cls.__make_get(get, attr, container_type)) |
588 | | - |
589 | | - if len(clsconf) == 1: |
590 | | - setattr(cls, '__getitem__', cls.__make_getitem(attr, container_type)) |
591 | | - |
592 | | - # create the constructor, only if it has not been overridden |
593 | | - # i.e. it is the same method as the parent class constructor |
594 | | - if cls.__init__ == MultiContainerInterface.__init__: |
595 | | - setattr(cls, '__init__', cls.__make_constructor(clsconf)) |
596 | | - |
597 | | - |
598 | | -class LabelledDict(dict): |
599 | | - ''' |
600 | | - A dict wrapper class for aggregating Timeseries |
601 | | - from the standard locations |
602 | | - ''' |
603 | | - |
604 | | - @docval({'name': 'label', 'type': str, 'doc': 'the label on this dictionary'}, |
605 | | - {'name': 'def_key_name', 'type': str, 'doc': 'the default key name', 'default': 'name'}) |
606 | | - def __init__(self, **kwargs): |
607 | | - label, def_key_name = getargs('label', 'def_key_name', kwargs) |
608 | | - self.__label = label |
609 | | - self.__defkey = def_key_name |
610 | | - |
611 | | - @property |
612 | | - def label(self): |
613 | | - return self.__label |
614 | | - |
615 | | - def __getitem__(self, args): |
616 | | - key = args |
617 | | - if '==' in args: |
618 | | - key, val = args.split("==") |
619 | | - key = key.strip() |
620 | | - val = val.strip() |
621 | | - if key != self.__defkey: |
622 | | - ret = list() |
623 | | - for item in self.values(): |
624 | | - if getattr(item, key, None) == val: |
625 | | - ret.append(item) |
626 | | - return ret if len(ret) else None |
627 | | - key = val |
628 | | - return super(LabelledDict, self).__getitem__(key) |
629 | | - |
630 | | - @docval({'name': 'container', 'type': (NWBData, NWBContainer), 'doc': 'the container to add to this LabelledDict'}) |
631 | | - def add(self, **kwargs): |
632 | | - ''' |
633 | | - Add a container to this LabelledDict |
634 | | - ''' |
635 | | - container = getargs('container', kwargs) |
636 | | - key = getattr(container, self.__defkey, None) |
637 | | - if key is None: |
638 | | - msg = "container '%s' does not have attribute '%s'" % (container.name, self.__defkey) |
639 | | - raise ValueError(msg) |
640 | | - self[key] = container |
| 322 | + pass |
0 commit comments