NeurodataWithoutBorders / pynwb

A Python API for working with Neurodata stored in the NWB Format
https://pynwb.readthedocs.io
Other
178 stars 84 forks source link

Unexpected exception when loading a file with extension not pre-imported and load_namespaces=True #1337

Closed yarikoptic closed 3 years ago

yarikoptic commented 3 years ago

Description

In dandi-cli we try to automagically import needed extensions (from those which we know about) if we detect that there is a missing namespace. We react on KeyError which has "not a namespace" in its message. And I believe it was working ok across the dandisets we have. But trying now, it seems we would get a non-descriptive of the underlying cause exception (IIRC we use load_namespaces= to be able to load files produced with prior versions of pynwb et al, although it seems not even in effect due to those warnings you could see below):

*$> DUECREDIT_ENABLE=1 python -c 'import pynwb; pynwb.NWBHDF5IO("sub-699733573/sub-699733573_ses-715093703.nwb", "r", load_namespaces=True).read()'
/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/spec/namespace.py:484: UserWarning: Ignoring cached namespace 'hdmf-common' version 1.1.3 because version 1.3.0 is already loaded.
  warn("Ignoring cached namespace '%s' version %s because version %s is already loaded."
/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/spec/namespace.py:484: UserWarning: Ignoring cached namespace 'core' version 2.2.2 because version 2.2.5 is already loaded.
  warn("Ignoring cached namespace '%s' version %s because version %s is already loaded."
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/backends/hdf5/h5tools.py", line 420, in read
    return call_docval_func(super().read, kwargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 423, in call_docval_func
    return func(*fargs, **fkwargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call
    return func(args[0], **pargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/backends/io.py", line 41, in read
    container = self.__manager.construct(f_builder)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call
    return func(args[0], **pargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 282, in construct
    result = self.__type_map.construct(builder, self, None)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call
    return func(args[0], **pargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 959, in construct
    return obj_mapper.construct(builder, build_manager, parent)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call
    return func(args[0], **pargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1210, in construct
    subspecs = self.__get_subspec_values(builder, self.spec, manager)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1139, in __get_subspec_values
    self.__get_sub_builders(groups, spec.groups, manager, ret)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1190, in __get_sub_builders
    ret.update(self.__get_subspec_values(sub_builder, subspec, manager))
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1139, in __get_subspec_values
    self.__get_sub_builders(groups, spec.groups, manager, ret)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1190, in __get_sub_builders
    ret.update(self.__get_subspec_values(sub_builder, subspec, manager))
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1139, in __get_subspec_values
    self.__get_sub_builders(groups, spec.groups, manager, ret)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1182, in __get_sub_builders
    sub_builder = self.__flatten(sub_builder, subspec, manager)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1195, in __flatten
    tmp = [manager.construct(b) for b in sub_builder]
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1195, in <listcomp>
    tmp = [manager.construct(b) for b in sub_builder]
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call
    return func(args[0], **pargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 278, in construct
    result = self.__type_map.construct(builder, self, parent)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call
    return func(args[0], **pargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 954, in construct
    obj_mapper = self.get_map(builder)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call
    return func(args[0], **pargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 874, in get_map
    container_cls = self.get_cls(obj)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call
    return func(args[0], **pargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 807, in get_cls
    return self.get_container_cls(namespace, data_type)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call
    return func(args[0], **pargs)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 730, in get_container_cls
    cls = ExtenderMeta(str(data_type), bases, d)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 755, in __init__
    func(name, bases, classdict)
  File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/container.py", line 136, in __gather_fields
    raise ValueError("Field '%s' cannot be defined in %s. It already exists on base class %s."
ValueError: Field 'description' cannot be defined in EcephysProbe. It already exists on base class Device.

DueCredit Report:
- HDMF: Hierarchical Data Modeling Framework for Modern Science Data Standards / hdmf/ (v 2.3.0) [1]
- Scientific tools library / numpy (v 1.18.5) [2]

2 packages cited
0 modules cited
0 functions cited

References
----------

[1] Tritt, A.J. et al., 2019. HDMF: Hierarchical Data Modeling Framework for Modern Science Data Standards. In 2019 IEEE International Conference on Big Data (Big Data). pp. 165–179.
[2] Van Der Walt, S., Colbert, S.C. & Varoquaux, G., 2011. The NumPy array: a structure for efficient numerical computation. Computing in Science & Engineering, 13(2), pp.22–30.

pynwb 1.4.0

it seems to load ok if I import needed extension first ```shell *$> python -c 'import pynwb, allensdk.brain_observatory.ecephys.nwb; pynwb.NWBHDF5IO("sub-699733573/sub-699733573_ses-715093703.nwb", "r", load_namespaces=True).read()' /home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/spec/namespace.py:484: UserWarning: Ignoring cached namespace 'hdmf-common' version 1.1.3 because version 1.3.0 is already loaded. warn("Ignoring cached namespace '%s' version %s because version %s is already loaded." /home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/spec/namespace.py:484: UserWarning: Ignoring cached namespace 'core' version 2.2.2 because version 2.2.5 is already loaded. warn("Ignoring cached namespace '%s' version %s because version %s is already loaded." ```
and if I run without load_namespaces=True, and without pre-importing the extension I do get that expected exception ```shell *$> python -c 'import pynwb; pynwb.NWBHDF5IO("sub-699733573/sub-699733573_ses-715093703.nwb", "r").read()' Traceback (most recent call last): File "", line 1, in File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/backends/hdf5/h5tools.py", line 420, in read return call_docval_func(super().read, kwargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 423, in call_docval_func return func(*fargs, **fkwargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call return func(args[0], **pargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/backends/io.py", line 41, in read container = self.__manager.construct(f_builder) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call return func(args[0], **pargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 282, in construct result = self.__type_map.construct(builder, self, None) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call return func(args[0], **pargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 959, in construct return obj_mapper.construct(builder, build_manager, parent) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call return func(args[0], **pargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1210, in construct subspecs = self.__get_subspec_values(builder, self.spec, manager) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1139, in __get_subspec_values self.__get_sub_builders(groups, spec.groups, manager, ret) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1190, in __get_sub_builders ret.update(self.__get_subspec_values(sub_builder, subspec, manager)) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1139, in __get_subspec_values self.__get_sub_builders(groups, spec.groups, manager, ret) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1182, in __get_sub_builders sub_builder = self.__flatten(sub_builder, subspec, manager) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1195, in __flatten tmp = [manager.construct(b) for b in sub_builder] File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1195, in tmp = [manager.construct(b) for b in sub_builder] File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call return func(args[0], **pargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 278, in construct result = self.__type_map.construct(builder, self, parent) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call return func(args[0], **pargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/manager.py", line 959, in construct return obj_mapper.construct(builder, build_manager, parent) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call return func(args[0], **pargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1210, in construct subspecs = self.__get_subspec_values(builder, self.spec, manager) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1139, in __get_subspec_values self.__get_sub_builders(groups, spec.groups, manager, ret) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/build/objectmapper.py", line 1168, in __get_sub_builders for parent_dt in manager.namespace_catalog.get_hierarchy(ns, dt): File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/utils.py", line 580, in func_call return func(args[0], **pargs) File "/home/yoh/miniconda3/lib/python3.8/site-packages/hdmf/spec/namespace.py", line 327, in get_hierarchy raise KeyError("'%s' not a namespace" % namespace) KeyError: "'ndx-aibs-ecephys' not a namespace" ```

Overall -- it relates to a discussion of exceptions we had somewhere -- could somehow there be consistent identification of needing an extension loaded and raising a dedicated exception (e.g. MissingNWBExtensionError) raised?

Related question: should we or should we not generally use load_namespaces=True in our attempt to open some .nwb file which might have been produced with some prior version of pynwb?

How "safe" it is to not load some of the older namespaces (those core and hdmf-common as informed about in the warnings above) while loading the others?

Steps to Reproduce

Download a file from https://girder.dandiarchive.org/api/v1/item/5eda859399f25d97bd27985d/download (a sample file from DANDI:000021), which was likely produced by some prior pynwb (or matnwb, @bendichter might know better ;) ) and try the above

Environment

Python Executable: Conda
Python Version: Python 3.8
Operating System: The Best Linux
HDMF Version: Version of PyNWB used -- hm... me confused, `2.3.0: 1.4.0`? ;-) consider #1336
rly commented 3 years ago

Thanks for the bug report. I'll look into this. It will likely result in a bug fix release in HDMF.

yarikoptic commented 3 years ago

cool, thank you @rly -- I will give it a shot. Cheers