Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
mavaylon1 committed Jan 25, 2024
1 parent 8353092 commit ed140c9
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 135 deletions.
31 changes: 0 additions & 31 deletions src/hdmf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,10 @@
from .container import Container, Data, DataRegion, HERDManager
from .region import ListSlicer
from .utils import docval, getargs
from .term_set import TermSet, TermSetWrapper, TermSetConfigurator

import os


# a global TermSetConfigurator
CUR_DIR = os.path.dirname(os.path.realpath(__file__))
path_to_config = os.path.join(CUR_DIR, 'hdmf_config.yaml')
TS_CONFIG = TermSetConfigurator(path=path_to_config)
TS_CONFIG.unload_termset_config()

@docval({'name': 'config_path', 'type': str, 'doc': 'Path to the configuartion file.',
'default': None})
def load_termset_config(config_path: str):
"""
If a user does not provide a config_path, then this method will unload any present configuration
and load the default curated configuration.
If a user provides a config_path, then this method will:
- Search the current configuation for data_types that are already present. These data_types will be
replaced with the new configuration.
- If the data_type is not present, then they will be loaded alongside the default curated configuration.
"""
if config_path is None:
TS_CONFIG.unload_termset_config()
TS_CONFIG.load_termset_config()
else:
TS_CONFIG.load_termset_config(config_path)

def unload_termset_config():
"""
Remove validation.
"""
return TS_CONFIG.unload_termset_config()

@docval(
{"name": "dataset", "type": None, "doc": "the HDF5 dataset to slice"},
{"name": "region", "type": None, "doc": "the region reference to use to slice"},
Expand Down
17 changes: 12 additions & 5 deletions src/hdmf/build/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .builders import DatasetBuilder, GroupBuilder, LinkBuilder, Builder, BaseBuilder
from .classgenerator import ClassGenerator, CustomClassGenerator, MCIClassGenerator
from ..container import AbstractContainer, Container, Data
from ..term_set import TermSetConfigurator
from ..spec import DatasetSpec, GroupSpec, NamespaceCatalog
from ..spec.spec import BaseStorageSpec
from ..utils import docval, getargs, ExtenderMeta, get_docval
Expand Down Expand Up @@ -391,13 +392,15 @@ def data_type(self):


class TypeMap:
''' A class to maintain the map between ObjectMappers and AbstractContainer classes
'''
"""
A class to maintain the map between ObjectMappers and AbstractContainer classes
"""

@docval({'name': 'namespaces', 'type': NamespaceCatalog, 'doc': 'the NamespaceCatalog to use', 'default': None},
{'name': 'mapper_cls', 'type': type, 'doc': 'the ObjectMapper class to use', 'default': None})
{'name': 'mapper_cls', 'type': type, 'doc': 'the ObjectMapper class to use', 'default': None},
{'name': 'config_path', 'type': str, 'doc': 'The path to the TermSet config yaml.', 'default': None})
def __init__(self, **kwargs):
namespaces, mapper_cls = getargs('namespaces', 'mapper_cls', kwargs)
namespaces, mapper_cls, config_path = getargs('namespaces', 'mapper_cls', 'config_path', kwargs)
if namespaces is None:
namespaces = NamespaceCatalog()
if mapper_cls is None:
Expand All @@ -410,14 +413,18 @@ def __init__(self, **kwargs):
self.__data_types = dict()
self.__default_mapper_cls = mapper_cls
self.__class_generator = ClassGenerator()
self.__load_termset_config = True
self.ts_config = TermSetConfigurator(path=config_path)
self.register_generator(CustomClassGenerator)
self.register_generator(MCIClassGenerator)

@property
def namespace_catalog(self):
return self.__ns_catalog

@property
def data_types(self):
return self.__data_types

@property
def container_types(self):
return self.__container_types
Expand Down
12 changes: 12 additions & 0 deletions src/hdmf/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
# a global type map
global __TYPE_MAP

def load_termset_config(config_path: str):
"""
This method will:
- Search the current configuration for data_types that are already present. These data_types will be
replaced with the new configuration.
- If the data_type is not present, then they will be loaded alongside the default curated configuration.
"""
__TYPE_MAP.ts_config.load_termset_config(config_path)

# a function to register a container classes with the global map
@docval({'name': 'data_type', 'type': str, 'doc': 'the data_type to get the spec for'},
Expand All @@ -37,10 +45,14 @@ def register_class(**kwargs):
def _dec(cls):
_set_exp(cls)
__TYPE_MAP.register_container_type(namespace, data_type, cls)
cls.type_map = __TYPE_MAP
cls.namespace = namespace
return cls
else:
def _dec(cls):
__TYPE_MAP.register_container_type(namespace, data_type, cls)
cls.type_map = __TYPE_MAP
cls.namespace = namespace
return cls

if container_cls is None:
Expand Down
91 changes: 36 additions & 55 deletions src/hdmf/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,68 +234,49 @@ def __init__(self, **kwargs):
self.__read_io = None
self.__obj = None

def get_config(self):
from . import TS_CONFIG #update path
return TS_CONFIG

def get_type_map(self):
from .common import get_type_map
tm = get_type_map()
return tm

@docval({'name': 'constructor_args', 'type': dict,
'doc': 'The fields/parameters/attibutes for the object.'})

Check failure on line 238 in src/hdmf/container.py

View workflow job for this annotation

GitHub Actions / Check for spelling errors

attibutes ==> attributes
def init_validation(self, constructor_args):
"""
"""
# load termset configuartion file from global Config

Check failure on line 243 in src/hdmf/container.py

View workflow job for this annotation

GitHub Actions / Check for spelling errors

configuartion ==> configuration
config = self.get_config()
termset_config = config.config
configurator = self.type_map.ts_config
termset_config = configurator.config
if termset_config is not None:
object_name = self.__class__.__name__

# Check that the object data_type is in the loaded namespace
tm = self.get_type_map()

container_types_dict = tm.container_types
object_exists = False
for namespace in container_types_dict:
if object_name in container_types_dict[namespace]:
object_exists = True
else:
continue

if not object_exists:
msg = "%s is not in the loaded Namespace(s)." % object_name
# check to see that the namespace for the container is in the config
if self.namespace not in self.type_map.container_types:
breakpoint()
msg = "%s not found within loaded configuration." % self.namespace
warn(msg)
else:
# check to see that the container type is in the config under the namespace
config_namespace = termset_config['namespaces'][self.namespace]
object_name = self.__class__.__name__

# Wrap supported fields with TermSetWrapper
obj_wrapped = False
if object_name in termset_config:
obj_wrapped = True
for attr in termset_config[object_name]['fields']:
obj_mapper = tm.get_map(self)
# get the spec according to attr name in schema
# Note: this is the name for the field in the config
spec = obj_mapper.get_attr_spec(attr)

# In the case of dealing with datasets directly or not defined in the spec.
# (Data/VectorData/DynamicTable/etc)
if spec is None:
# constr_name= attr
msg = "Spec not found for %s" % attr
warn(msg)
# From the spec, get the corresponding constructor name
else:
constr_name = obj_mapper.get_const_arg(spec)

if constr_name in constructor_args: # make sure any custom fields are not handled (i.e., make an extension)
termset_path = termset_config[object_name]['fields'][attr]
termset = TermSet(term_schema_path=termset_path)
constructor_args[attr] = TermSetWrapper(value=constructor_args[attr], termset=termset)

# Even if the data_type is in the namespace, it might not be in the configuration.
if object_exists and not obj_wrapped:
msg = "%s is not in the loaded TermSet Configuration." % object_name
warn(msg)
if object_name not in config_namespace['data_types']:
breakpoint()
msg = '%s not found within the configuration for %s' % (object_name, self.namespace)
else:
for attr in config_namespace['data_types'][object_name]:
obj_mapper = self.type_map.get_map(self)
# get the spec according to attr name in schema
# Note: this is the name for the field in the config
spec = obj_mapper.get_attr_spec(attr)

# In the case of dealing with datasets directly or not defined in the spec.
# (Data/VectorData/DynamicTable/etc)
breakpoint()
if spec is None:
msg = "Spec not found for %s" % attr
warn(msg)
# From the spec, get the corresponding constructor name
else:
constr_name = obj_mapper.get_const_arg(spec)
if constr_name in constructor_args: # make sure any custom fields are not handled (i.e., make an extension)
termset_path = config_namespace['data_types'][object_name][attr]
termset = TermSet(term_schema_path=termset_path)
constructor_args[attr] = TermSetWrapper(value=constructor_args[attr], termset=termset)

@property
def read_io(self):
Expand Down
32 changes: 16 additions & 16 deletions src/hdmf/term_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,35 +310,35 @@ class TermSetConfigurator:
"""
"""
@docval({'name': 'path', 'type': str, 'doc': 'Path to the configuartion file.'})
@docval({'name': 'path', 'type': str, 'doc': 'Path to the configuration file.', 'default': None})
def __init__(self, **kwargs):
self.path = [kwargs['path']]
self.config = None
self.load_termset_config()
if kwargs['path'] is None:
self.path = []
else:
self.path = [kwargs['path']]
self.load_termset_config(config_path=self.path[0])

@docval({'name': 'config_path', 'type': str, 'doc': 'Path to the configuartion file.',
'default': None})
@docval({'name': 'config_path', 'type': str, 'doc': 'Path to the configuartion file.'})

Check failure on line 322 in src/hdmf/term_set.py

View workflow job for this annotation

GitHub Actions / Check for spelling errors

configuartion ==> configuration
def load_termset_config(self,config_path):
"""
Load the configuration file for validation on the fields defined for the objects within the file.
"""
# Set self.config for __init__
if self.config is None:
with open(self.path[0], 'r') as config:
termset_config = yaml.safe_load(config)
self.config = termset_config
else:
# Check data_types within new config to see if they already exist in the current config
with open(config_path, 'r') as config:
termset_config = yaml.safe_load(config)
with open(config_path, 'r') as config:
termset_config = yaml.safe_load(config)
if self.config is None: # set the initial config/load after config has been unloaded
self.config = termset_config
if len(self.path)==0: # for loading after an unloaded config
self.path.append(config_path)
else: # append to the existing config
for data_type in termset_config:
if data_type in self.config:
self.config[data_type] = termset_config[data_type]
termset_config.pop(data_type)
self.config.update(termset_config)

# append path to new config to self.path
self.path.append(config_path)
# append path to new config to self.path
self.path.append(config_path)

def unload_termset_config(self):
"""
Expand Down
14 changes: 8 additions & 6 deletions tests/unit/common/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
DynamicTableRegion,
get_manager,
SimpleMultiContainer,
load_termset_config
)
from hdmf.testing import TestCase, H5RoundTripMixin, remove_test_file
from hdmf.utils import StrDataset
Expand All @@ -38,13 +39,14 @@

class TestVDConfig(TestCase):
def test_init_config(self):
load_termset_config(config_path='/Users/mavaylon/Research/NWB/hdmf2/hdmf/tests/unit/test_config2.yaml')
vd = VectorData(name='data', description='',data=['Homo sapiens'])
tb = DynamicTable(name="with_table_columns", description='a test table', columns=[vd])
from hdmf.common import get_type_map
htm = get_type_map()
om = htm.get_map(tb)
from datetime import datetime
from uuid import uuid4
# tb = DynamicTable(name="with_table_columns", description='a test table', columns=[vd])
# from hdmf.common import get_type_map
# htm = get_type_map()
# om = htm.get_map(tb)
# from datetime import datetime
# from uuid import uuid4

# import numpy as np
# from dateutil.tz import tzlocal
Expand Down
26 changes: 7 additions & 19 deletions tests/unit/test_config2.yaml
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
namespaces:
core:
version:
hdmf-common:
version: TBD
data_types:
VectorData:
data: '...'
data: '/Users/mavaylon/Research/NWB/hdmf2/hdmf/tests/unit/example_test_term_set.yaml'
field2: '...'
experimental:
version:

#
# VectorData:
# namespace:
# namespace_version:
# fields:
# data: /Users/mavaylon/Research/NWB/hdmf2/hdmf/tests/unit/example_test_term_set.yaml
# field2: ...
# DynamicTable:
# namespace:
# namespace_version:
# fields:
# data: /Users/mavaylon/Research/NWB/hdmf2/hdmf/tests/unit/example_test_term_set.yaml
# field2: ...
type2:
field1: '...'
hdmf-experimental:
version: TBD
12 changes: 9 additions & 3 deletions tests/unit/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,15 +190,21 @@ def test_set_modified_parent(self):
child_obj.set_modified()
self.assertTrue(child_obj.parent.modified)

def test_vd(self):
col1 = VectorData(
name='Species_1',
description='...',
data=['Homo sapiens'],
)
def test_all_children(self):
col1 = VectorData(
name='Species_1',
description='...',
data=['Homo sapiens'],
)
species = DynamicTable(name='species', description='My species', columns=[col1])
obj = species.all_objects
self.assertEqual(sorted(list(obj.keys())), sorted([species.object_id, species.id.object_id, col1.object_id]))
# species = DynamicTable(name='species', description='My species', columns=[col1])
# obj = species.all_objects
# self.assertEqual(sorted(list(obj.keys())), sorted([species.object_id, species.id.object_id, col1.object_id]))

def test_add_child(self):
"""Test that add child creates deprecation warning and also properly sets child's parent and modified
Expand Down

0 comments on commit ed140c9

Please sign in to comment.