Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/hdmf/spec/catalog.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import copy
from collections import OrderedDict
from pydantic import validate_call

from .spec import BaseStorageSpec, GroupSpec
from ..utils import docval, getargs
Expand Down
10 changes: 10 additions & 0 deletions src/hdmf/spec/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,20 @@ def __convert_spec_cls_keys(self, parent_cls, spec_cls, spec_dict):
"""Replace instances of data_type_def/inc in spec_dict with new values from spec_cls."""
# this is necessary because the def_key and inc_key may be different in each namespace
# NOTE: this does not handle more than one custom set of keys
# breakpoint()
if parent_cls.def_key() in spec_dict:
spec_dict[spec_cls.def_key()] = spec_dict.pop(parent_cls.def_key())
if parent_cls.inc_key() in spec_dict:
spec_dict[spec_cls.inc_key()] = spec_dict.pop(parent_cls.inc_key())
# parent_def_key = parent_cls.__private_attributes__["_def_key"].get_default()
# if parent_def_key in spec_dict:
# spec_def_key = spec_cls.__private_attributes__["_def_key"].get_default()
# spec_dict[spec_def_key] = spec_dict.pop(parent_def_key)
#
# parent_inc_key = parent_cls.__private_attributes__["_inc_key"].get_default()
# if parent_inc_key in spec_dict:
# spec_inc_key = spec_cls.__private_attributes__["_inc_key"].get_default()
# spec_dict[spec_inc_key] = spec_dict.pop(parent_inc_key)

def __resolve_includes(self, spec_cls, spec_dict, catalog):
"""Replace data type inc strings with the spec definition so the new spec is built with included fields.
Expand Down
156 changes: 92 additions & 64 deletions src/hdmf/spec/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from abc import ABCMeta
from collections import OrderedDict
from warnings import warn
from pydantic import validate_call, BaseModel
from typing import Optional, Self

from ..utils import docval, getargs, popargs, get_docval

Expand Down Expand Up @@ -101,45 +103,13 @@ def build_spec(cls, spec_dict):
return cls(**kwargs)


class Spec(ConstructableDict):
class Spec(BaseModel):
''' A base specification class
'''

@docval({'name': 'doc', 'type': str, 'doc': 'a description about what this specification represents'},
{'name': 'name', 'type': str, 'doc': 'The name of this attribute', 'default': None},
{'name': 'required', 'type': bool, 'doc': 'whether or not this attribute is required', 'default': True},
{'name': 'parent', 'type': 'hdmf.spec.spec.Spec', 'doc': 'the parent of this spec', 'default': None})
def __init__(self, **kwargs):
name, doc, required, parent = getargs('name', 'doc', 'required', 'parent', kwargs)
super().__init__()
self['doc'] = doc
if name is not None:
self['name'] = name
if not required:
self['required'] = required
self._parent = parent

@property
def doc(self):
''' Documentation on what this Spec is specifying '''
return self.get('doc', None)

@property
def name(self):
''' The name of the object being specified '''
return self.get('name', None)

@property
def parent(self):
''' The parent specification of this specification '''
return self._parent

@parent.setter
def parent(self, spec):
''' Set the parent of this specification '''
if self._parent is not None:
raise AttributeError('Cannot re-assign parent.')
self._parent = spec
doc: str
name: Optional[str] = None
required: bool = True
parent: Optional[Self] = None

@classmethod
def build_const_args(cls, spec_dict):
Expand Down Expand Up @@ -167,6 +137,69 @@ def path(self):

# def __eq__(self, other):
# return id(self) == id(other)
# class Spec(ConstructableDict):
# ''' A base specification class
# '''
#
# @docval({'name': 'doc', 'type': str, 'doc': 'a description about what this specification represents'},
# {'name': 'name', 'type': str, 'doc': 'The name of this attribute', 'default': None},
# {'name': 'required', 'type': bool, 'doc': 'whether or not this attribute is required', 'default': True},
# {'name': 'parent', 'type': 'hdmf.spec.spec.Spec', 'doc': 'the parent of this spec', 'default': None})
# def __init__(self, **kwargs):
# name, doc, required, parent = getargs('name', 'doc', 'required', 'parent', kwargs)
# super().__init__()
# self['doc'] = doc
# if name is not None:
# self['name'] = name
# if not required:
# self['required'] = required
# self._parent = parent
#
# @property
# def doc(self):
# ''' Documentation on what this Spec is specifying '''
# return self.get('doc', None)
#
# @property
# def name(self):
# ''' The name of the object being specified '''
# return self.get('name', None)
#
# @property
# def parent(self):
# ''' The parent specification of this specification '''
# return self._parent
#
# @parent.setter
# def parent(self, spec):
# ''' Set the parent of this specification '''
# if self._parent is not None:
# raise AttributeError('Cannot re-assign parent.')
# self._parent = spec
#
# @classmethod
# def build_const_args(cls, spec_dict):
# ''' Build constructor arguments for this Spec class from a dictionary '''
# ret = super().build_const_args(spec_dict)
# return ret
#
# def __hash__(self):
# return id(self)
#
# @property
# def path(self):
# stack = list()
# tmp = self
# while tmp is not None:
# name = tmp.name
# if name is None:
# name = tmp.data_type_def
# if name is None:
# name = tmp.data_type_inc
# stack.append(name)
# tmp = tmp.parent
# return "/".join(reversed(stack))



_target_type_key = 'target_type'
Expand Down Expand Up @@ -301,10 +334,10 @@ def build_const_args(cls, spec_dict):
class BaseStorageSpec(Spec):
''' A specification for any object that can hold attributes. '''

__inc_key = 'data_type_inc'
__def_key = 'data_type_def'
__type_key = 'data_type'
__id_key = 'object_id'
inc_key = 'data_type_inc'
def_key = 'data_type_def'
type_key = 'data_type'
id_key = 'object_id'

@docval(*_attrbl_args)
def __init__(self, **kwargs):
Expand Down Expand Up @@ -460,45 +493,40 @@ def linkable(self):
return self.get('linkable', True)

@classmethod
def id_key(cls):
''' Get the key used to store data ID on an instance

Override this method to use a different name for 'object_id'
def def_key(cls):
'''
return cls.__id_key

@classmethod
def type_key(cls):
''' Get the key used to store data type on an instance

Override this method to use a different name for 'data_type'. HDMF supports combining schema
that uses 'data_type' and at most one different name for 'data_type'.
Get the key used to define a data_type definition.
'''
return cls.__type_key
# Directly access the class attribute to avoid method conflict
return cls.__dict__.get("def_key", "data_type_def")

@classmethod
def inc_key(cls):
''' Get the key used to define a data_type include.

Override this method to use a different keyword for 'data_type_inc'. HDMF supports combining schema
that uses 'data_type_inc' and at most one different name for 'data_type_inc'.
'''
return cls.__inc_key
Get the key used to define a data_type include.
'''
return cls.__dict__.get("inc_key", "data_type_inc")

@classmethod
def def_key(cls):
''' Get the key used to define a data_type definition.
def type_key(cls):
'''
Get the key used to store data type on an instance.
'''
return cls.__dict__.get("type_key", "data_type")

Override this method to use a different keyword for 'data_type_def' HDMF supports combining schema
that uses 'data_type_def' and at most one different name for 'data_type_def'.
@classmethod
def id_key(cls):
'''
Get the key used to store data ID on an instance.
'''
return cls.__def_key
return cls.__dict__.get("id_key", "object_id")

@property
def data_type_inc(self):
''' The data type this specification inherits '''
return self.get(self.inc_key())


@property
def data_type_def(self):
''' The data type this specification defines '''
Expand Down