From 66f642b6a742ae8097a1c9732e8b4292a83c5e51 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Wed, 29 Apr 2020 17:59:51 +0200 Subject: [PATCH 01/18] Augment Section parent test with indirect access --- nixio/test/test_section.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nixio/test/test_section.py b/nixio/test/test_section.py index 2f950ac7..ba77aae1 100644 --- a/nixio/test/test_section.py +++ b/nixio/test/test_section.py @@ -231,6 +231,9 @@ def test_parent(self): grp.metadata = child self.assertEqual(grp.metadata.parent, self.section) + # indirect access parent check + self.assertEqual(block.groups["group"].metadata.parent, self.section) + def test_inverse_search(self): block = self.file.create_block("a block", "block with metadata") block.metadata = self.section From 90abd221067536eb5457613aacc84202d30fc9f7 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Wed, 29 Apr 2020 19:19:11 +0200 Subject: [PATCH 02/18] Common method rename: _create_new -> create_new The creator method is meant to be used by parent classes. I had originally made it "private" to imply it's for internal library use only. In the future, we might fold the creation functionality into the __init__ function along with the instantiation of objects from a file. --- nixio/block.py | 28 ++++++++++++++-------------- nixio/data_array.py | 24 ++++++++++++------------ nixio/data_frame.py | 8 ++++---- nixio/dimensions.py | 22 +++++++++++----------- nixio/entity.py | 2 +- nixio/feature.py | 2 +- nixio/file.py | 4 ++-- nixio/group.py | 6 +++--- nixio/multi_tag.py | 6 +++--- nixio/property.py | 4 ++-- nixio/section.py | 12 ++++++------ nixio/source.py | 8 ++++---- nixio/tag.py | 8 ++++---- nixio/test/test_validator.py | 4 ++-- 14 files changed, 69 insertions(+), 69 deletions(-) diff --git a/nixio/block.py b/nixio/block.py index 1bf849d9..326d4032 100644 --- a/nixio/block.py +++ b/nixio/block.py @@ -49,9 +49,9 @@ def __init__(self, nixparent, h5group, compression=Compression.Auto): self._data_frames = None @classmethod - def _create_new(cls, nixparent, h5parent, name, type_, compression): - newentity = super(Block, cls)._create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixparent, h5parent, name, type_, compression): + newentity = super(Block, cls).create_new(nixparent, h5parent, + name, type_) newentity._compr = compression return newentity @@ -101,8 +101,8 @@ def create_multi_tag(self, name="", type_="", positions=None, "{}-extents".format(type_), data=extents) extcreated = True - mtag = MultiTag._create_new(self, multi_tags, - name, type_, positions) + mtag = MultiTag.create_new(self, multi_tags, + name, type_, positions) except Exception as e: msg = "MultiTag Creation Failed" if poscreated: @@ -150,7 +150,7 @@ def create_tag(self, name="", type_="", position=0, tags = self._h5group.open_group("tags") if name in tags: raise exceptions.DuplicateName("create_tag") - tag = Tag._create_new(self, tags, name, type_, position) + tag = Tag.create_new(self, tags, name, type_, position) return tag # Source @@ -170,7 +170,7 @@ def create_source(self, name, type_): sources = self._h5group.open_group("sources") if name in sources: raise exceptions.DuplicateName("create_source") - src = Source._create_new(self, sources, name, type_) + src = Source.create_new(self, sources, name, type_) return src # Group @@ -190,7 +190,7 @@ def create_group(self, name, type_): groups = self._h5group.open_group("groups") if name in groups: raise exceptions.DuplicateName("open_group") - grp = Group._create_new(self, groups, name, type_) + grp = Group.create_new(self, groups, name, type_) return grp def create_data_array(self, name="", array_type="", dtype=None, shape=None, @@ -250,8 +250,8 @@ def create_data_array(self, name="", array_type="", dtype=None, shape=None, raise exceptions.DuplicateName("create_data_array") if compression == Compression.Auto: compression = self._compr - da = DataArray._create_new(self, data_arrays, name, array_type, - dtype, shape, compression) + da = DataArray.create_new(self, data_arrays, name, array_type, + dtype, shape, compression) if data is not None: da.write_direct(data) return da @@ -328,7 +328,7 @@ def create_data_frame(self, name="", type_="", col_dict=None, ) else: # col_dtypes is None and data is None raise ValueError( - "The data type of each column have to be specified" + "The data type of each column have to be specified" ) if len(col_names) != len(col_dict): raise exceptions.DuplicateColumnName @@ -348,7 +348,7 @@ def create_data_frame(self, name="", type_="", col_dict=None, # data is None or type(data[0]) != np.void # data_type doesnt matter raise ValueError( - "No information about column names is provided!" + "No information about column names is provided!" ) if col_dict is not None: @@ -362,8 +362,8 @@ def create_data_frame(self, name="", type_="", col_dict=None, dt_arr = list(col_dict.items()) col_dtype = np.dtype(dt_arr) - df = DataFrame._create_new(self, data_frames, name, - type_, shape, col_dtype, compression) + df = DataFrame.create_new(self, data_frames, name, + type_, shape, col_dtype, compression) if data is not None: if type(data[0]) == np.void: diff --git a/nixio/data_array.py b/nixio/data_array.py index 7384f51a..5a25be78 100644 --- a/nixio/data_array.py +++ b/nixio/data_array.py @@ -38,10 +38,10 @@ def __init__(self, nixparent, h5group): self._dimensions = None @classmethod - def _create_new(cls, nixparent, h5parent, name, type_, data_type, shape, - compression): - newentity = super(DataArray, cls)._create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixparent, h5parent, name, type_, data_type, shape, + compression): + newentity = super(DataArray, cls).create_new(nixparent, h5parent, + name, type_) datacompr = False if compression == Compression.DeflateNormal: datacompr = True @@ -87,7 +87,7 @@ def append_set_dimension(self, labels=None): """ dimgroup = self._h5group.open_group("dimensions") index = len(dimgroup) + 1 - setdim = SetDimension._create_new(dimgroup, index) + setdim = SetDimension.create_new(dimgroup, index) if labels: setdim.labels = labels if self._parent._parent.time_auto_update: @@ -109,8 +109,8 @@ def append_sampled_dimension(self, sampling_interval, label=None, """ dimgroup = self._h5group.open_group("dimensions") index = len(dimgroup) + 1 - smpldim = SampledDimension._create_new(dimgroup, index, - sampling_interval) + smpldim = SampledDimension.create_new(dimgroup, index, + sampling_interval) if label: smpldim.label = label if unit: @@ -134,7 +134,7 @@ def append_range_dimension(self, ticks, label=None, unit=None): """ dimgroup = self._h5group.open_group("dimensions") index = len(dimgroup) + 1 - rdim = RangeDimension._create_new(dimgroup, index, ticks) + rdim = RangeDimension.create_new(dimgroup, index, ticks) if label: rdim.label = label rdim.unit = unit @@ -160,8 +160,8 @@ def append_data_frame_dimension(self, data_frame, column_idx=None): """ dimgroup = self._h5group.open_group("dimensions") index = len(dimgroup) + 1 - dfdim = DataFrameDimension._create_new(dimgroup, index, - data_frame, column_idx) + dfdim = DataFrameDimension.create_new(dimgroup, index, + data_frame, column_idx) if self._parent._parent.time_auto_update: self.force_updated_at() return dfdim @@ -194,7 +194,7 @@ def append_alias_range_dimension(self): "Current SI unit is {}".format(u), "DataArray.append_alias_range_dimension" ) - return RangeDimension._create_new_alias(dimgroup, 1, self) + return RangeDimension.create_new_alias(dimgroup, 1, self) def delete_dimensions(self): """ @@ -315,7 +315,7 @@ def unit(self, u): u = None util.check_attr_type(u, str) if (self._dimension_count() == 1 and - self.dimensions[0].dimension_type == DimensionType.Range and + self.dimensions[0].dimension_type == DimensionType.Range and self.dimensions[0].is_alias and u is not None): if not (util.units.is_si(u) or util.units.is_compound(u)): raise InvalidUnit( diff --git a/nixio/data_frame.py b/nixio/data_frame.py index ab5fea36..016fd48b 100644 --- a/nixio/data_frame.py +++ b/nixio/data_frame.py @@ -33,10 +33,10 @@ def __init__(self, nixparent, h5group): self._rows = None @classmethod - def _create_new(cls, nixparent, h5parent, - name, type_, shape, col_dtype, compression): - newentity = super(DataFrame, cls)._create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixparent, h5parent, name, type_, + shape, col_dtype, compression): + newentity = super(DataFrame, cls).create_new(nixparent, h5parent, + name, type_) newentity._h5group.create_dataset("data", (shape, ), col_dtype) return newentity diff --git a/nixio/dimensions.py b/nixio/dimensions.py index 7f1e2e8b..1413e6c9 100644 --- a/nixio/dimensions.py +++ b/nixio/dimensions.py @@ -42,7 +42,7 @@ def __init__(self, h5group, index): self.dim_index = int(index) @classmethod - def _create_new(cls, parent, index): + def create_new(cls, parent, index): h5group = parent.open_group(str(index)) newdim = cls(h5group, index) return newdim @@ -76,8 +76,8 @@ def __init__(self, h5group, index): super(SampledDimension, self).__init__(h5group, index) @classmethod - def _create_new(cls, parent, index, sample): - newdim = super(SampledDimension, cls)._create_new(parent, index) + def create_new(cls, parent, index, sample): + newdim = super(SampledDimension, cls).create_new(parent, index) newdim._set_dimension_type(DimensionType.Sample) newdim.sampling_interval = sample return newdim @@ -172,15 +172,15 @@ def __init__(self, h5group, index): super(RangeDimension, self).__init__(h5group, index) @classmethod - def _create_new(cls, parent, index, ticks): - newdim = super(RangeDimension, cls)._create_new(parent, index) + def create_new(cls, parent, index, ticks): + newdim = super(RangeDimension, cls).create_new(parent, index) newdim._set_dimension_type(DimensionType.Range) newdim._h5group.write_data("ticks", ticks, dtype=DataType.Double) return newdim @classmethod - def _create_new_alias(cls, parent, index, da): - newdim = super(RangeDimension, cls)._create_new(parent, index) + def create_new_alias(cls, parent, index, da): + newdim = super(RangeDimension, cls).create_new(parent, index) newdim._set_dimension_type(DimensionType.Range) newdim._h5group.create_link(da, da.id) return newdim @@ -301,8 +301,8 @@ def __init__(self, h5group, index): super(SetDimension, self).__init__(h5group, index) @classmethod - def _create_new(cls, parent, index): - newdim = super(SetDimension, cls)._create_new(parent, index) + def create_new(cls, parent, index): + newdim = super(SetDimension, cls).create_new(parent, index) newdim._set_dimension_type(DimensionType.Set) return newdim @@ -325,7 +325,7 @@ def __init__(self, h5group, index): super(DataFrameDimension, self).__init__(h5group, index) @classmethod - def _create_new(cls, parent, index, data_frame, column): + def create_new(cls, parent, index, data_frame, column): """ Create a new Dimension that points to a DataFrame @@ -338,7 +338,7 @@ def _create_new(cls, parent, index, data_frame, column): :return: The new DataFrameDimension """ - newdim = super(DataFrameDimension, cls)._create_new(parent, index) + newdim = super(DataFrameDimension, cls).create_new(parent, index) newdim.data_frame = data_frame newdim.column_idx = column newdim._set_dimension_type(DimensionType.DataFrame) diff --git a/nixio/entity.py b/nixio/entity.py index fc8b99dd..ebdf6d79 100644 --- a/nixio/entity.py +++ b/nixio/entity.py @@ -18,7 +18,7 @@ def __init__(self, nixparent, h5group): self._parent = nixparent @classmethod - def _create_new(cls, nixparent, h5parent, name=None, type_=None): + def create_new(cls, nixparent, h5parent, name=None, type_=None): if name and type_: util.check_entity_name_and_type(name, type_) id_ = util.create_id() diff --git a/nixio/feature.py b/nixio/feature.py index bedca5ea..865ec308 100644 --- a/nixio/feature.py +++ b/nixio/feature.py @@ -20,7 +20,7 @@ def __init__(self, nixparent, h5group): self._parent = nixparent @classmethod - def _create_new(cls, nixparent, h5parent, data, link_type): + def create_new(cls, nixparent, h5parent, data, link_type): id_ = util.create_id() h5group = h5parent.open_group(id_) h5group.set_attr("entity_id", id_) diff --git a/nixio/file.py b/nixio/file.py index 3f415aa5..cbf0c33d 100644 --- a/nixio/file.py +++ b/nixio/file.py @@ -416,7 +416,7 @@ def create_block(self, name="", type_="", compression=Compression.Auto, raise ValueError("Block with the given name already exists!") if compression == Compression.Auto: compression = self._compr - block = Block._create_new(self, self._data, name, type_, compression) + block = Block.create_new(self, self._data, name, type_, compression) return block # Section @@ -437,7 +437,7 @@ def create_section(self, name, type_="undefined", oid=None): """ if name in self.sections: raise DuplicateName("create_section") - sec = Section._create_new(self, self._metadata, name, type_, oid) + sec = Section.create_new(self, self._metadata, name, type_, oid) return sec @property diff --git a/nixio/group.py b/nixio/group.py index 628aaff8..a7a03309 100644 --- a/nixio/group.py +++ b/nixio/group.py @@ -28,9 +28,9 @@ def __init__(self, nixparent, h5group): self._sources = None @classmethod - def _create_new(cls, nixparent, h5parent, name, type_): - newentity = super(Group, cls)._create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixparent, h5parent, name, type_): + newentity = super(Group, cls).create_new(nixparent, h5parent, + name, type_) return newentity @property diff --git a/nixio/multi_tag.py b/nixio/multi_tag.py index 7ad3402b..2cb9a0d8 100644 --- a/nixio/multi_tag.py +++ b/nixio/multi_tag.py @@ -29,9 +29,9 @@ def __init__(self, nixparent, h5group): self._features = None @classmethod - def _create_new(cls, nixparent, h5parent, name, type_, positions): - newentity = super(MultiTag, cls)._create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixparent, h5parent, name, type_, positions): + newentity = super(MultiTag, cls).create_new(nixparent, h5parent, + name, type_) newentity.positions = positions return newentity diff --git a/nixio/property.py b/nixio/property.py index 0145126d..38981c21 100644 --- a/nixio/property.py +++ b/nixio/property.py @@ -97,8 +97,8 @@ def __init__(self, nixparent, h5dataset): self._h5dataset = self._h5group @classmethod - def _create_new(cls, nixparent, h5parent, name, - dtype, shape=None, oid=None): + def create_new(cls, nixparent, h5parent, name, + dtype, shape=None, oid=None): if shape is None or shape[0] == 0: shape = (8, ) util.check_entity_name(name) diff --git a/nixio/section.py b/nixio/section.py index 8837f2f0..48d82a15 100644 --- a/nixio/section.py +++ b/nixio/section.py @@ -55,9 +55,9 @@ def __init__(self, nixparent, h5group): self._properties = None @classmethod - def _create_new(cls, nixparent, h5parent, name, type_, oid=None): - newentity = super(Section, cls)._create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixparent, h5parent, name, type_, oid=None): + newentity = super(Section, cls).create_new(nixparent, h5parent, + name, type_) if util.is_uuid(oid): newentity._h5group.set_attr("entity_id", oid) @@ -83,7 +83,7 @@ def create_section(self, name, type_="undefined", oid=None): sections = self._h5group.open_group("sections", True) if name in sections: raise exceptions.DuplicateName("create_section") - sec = Section._create_new(self, sections, name, type_, oid) + sec = Section.create_new(self, sections, name, type_, oid) sec._sec_parent = self return sec @@ -169,7 +169,7 @@ def create_property(self, name="", values_or_dtype=0, oid=None, raise TypeError("Array contains inconsistent values.") shape = (len(vals),) - prop = Property._create_new(self, properties, name, dtype, shape, oid) + prop = Property.create_new(self, properties, name, dtype, shape, oid) prop.values = vals return prop @@ -293,7 +293,7 @@ def parent(self): if self._sec_parent is not None: return self._sec_parent rootmd = self._h5group.file.open_group("metadata") - # Assuming most metadata trees are shallow---doing BFS + # BFS sections = [Section(None, sg) for sg in rootmd] if self in sections: # Top-level section diff --git a/nixio/source.py b/nixio/source.py index ec2e7ab0..2ffe338c 100644 --- a/nixio/source.py +++ b/nixio/source.py @@ -23,9 +23,9 @@ def __init__(self, nixparent, h5group): self._sources = None @classmethod - def _create_new(cls, nixparent, h5parent, name, type_): - newentity = super(Source, cls)._create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixparent, h5parent, name, type_): + newentity = super(Source, cls).create_new(nixparent, h5parent, + name, type_) return newentity # Source @@ -45,7 +45,7 @@ def create_source(self, name, type_): sources = self._h5group.open_group("sources", True) if name in sources: raise exceptions.DuplicateName("create_source") - src = Source._create_new(self, sources, name, type_) + src = Source.create_new(self, sources, name, type_) return src @property diff --git a/nixio/tag.py b/nixio/tag.py index 7e5ba127..78c6669d 100644 --- a/nixio/tag.py +++ b/nixio/tag.py @@ -105,7 +105,7 @@ def create_feature(self, data, link_type): link_type = link_type.lower() link_type = LinkType(link_type) features = self._h5group.open_group("features") - feat = Feature._create_new(self, features, data, link_type) + feat = Feature.create_new(self, features, data, link_type) return feat @staticmethod @@ -173,9 +173,9 @@ def __init__(self, nixparent, h5group): self._features = None @classmethod - def _create_new(cls, nixparent, h5parent, name, type_, position): - newentity = super(Tag, cls)._create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixparent, h5parent, name, type_, position): + newentity = super(Tag, cls).create_new(nixparent, h5parent, + name, type_) newentity.position = position return newentity diff --git a/nixio/test/test_validator.py b/nixio/test/test_validator.py index ad8486ae..77d34b2d 100644 --- a/nixio/test/test_validator.py +++ b/nixio/test/test_validator.py @@ -247,7 +247,7 @@ def test_incorrect_dim_index(self): da.delete_dimensions() dimgroup = da._h5group.open_group("dimensions") # This wont work if we ever change the internals - nix.SetDimension._create_new(dimgroup, "10") + nix.SetDimension.create_new(dimgroup, "10") res = self.file.validate() assert VE.IncorrectDimensionIndex.format(1, 10) in res["errors"][da] @@ -256,7 +256,7 @@ def test_invalid_dim_index(self): da.delete_dimensions() dimgroup = da._h5group.open_group("dimensions") # This wont work if we ever change the internals - nix.SetDimension._create_new(dimgroup, "-1") + nix.SetDimension.create_new(dimgroup, "-1") res = self.file.validate() assert VE.InvalidDimensionIndex.format(1) in res["errors"][da] From 9e0eeb4f2214bdc242a2c45e7cbaba2adc6de880 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Wed, 29 Apr 2020 19:38:17 +0200 Subject: [PATCH 03/18] Add file read-only property to Entity --- nixio/entity.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/nixio/entity.py b/nixio/entity.py index ebdf6d79..e8ccbac8 100644 --- a/nixio/entity.py +++ b/nixio/entity.py @@ -16,6 +16,7 @@ def __init__(self, nixparent, h5group): util.check_entity_id(h5group.get_attr("entity_id")) self._h5group = h5group self._parent = nixparent + self._file = None @classmethod def create_new(cls, nixparent, h5parent, name=None, type_=None): @@ -54,6 +55,16 @@ def created_at(self): """ return util.str_to_time(self._h5group.get_attr("created_at")) + @property + def file(self): + """ + Reference to the NIX File object. + This is a read-only property. + + :rtype: nixio.File + """ + return self._file + def force_created_at(self, t=None): """ Sets the creation time `created_at` to the given time From 92bb2fb0173d80946fcb472cff49897b88d71b63 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Wed, 29 Apr 2020 21:43:45 +0200 Subject: [PATCH 04/18] Remove old unused function --- nixio/multi_tag.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/nixio/multi_tag.py b/nixio/multi_tag.py index 2cb9a0d8..de5c9c93 100644 --- a/nixio/multi_tag.py +++ b/nixio/multi_tag.py @@ -109,11 +109,6 @@ def features(self): self._features = FeatureContainer("features", self, Feature) return self._features - def _get_slice(self, data, index): - offset, count = self._get_offset_and_count(data, index) - sl = tuple(slice(o, o + c) for o, c in zip(offset, count)) - return sl - def _calc_data_slices(self, data, index): positions = self.positions extents = self.extents From 1cfec74d635033ecf2748b968b9092e3ca6d6683 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 00:21:36 +0200 Subject: [PATCH 05/18] Initialise all Entity objects with reference to File All Entities are initialised with and keep a reference to the File object. Containers keep the same reference to initialise objects that it instantiates from the file. Non-Entity objects that also require this reference: - Feature - Dimension objects --- nixio/block.py | 38 ++++++++++++--------- nixio/container.py | 10 +++--- nixio/data_array.py | 43 +++++++++++------------- nixio/data_frame.py | 13 ++++---- nixio/dimensions.py | 64 ++++++++++++++++++++---------------- nixio/entity.py | 9 ++--- nixio/feature.py | 9 ++--- nixio/file.py | 6 ++-- nixio/group.py | 11 ++++--- nixio/multi_tag.py | 24 +++++++------- nixio/property.py | 8 ++--- nixio/section.py | 29 ++++++++-------- nixio/source.py | 14 ++++---- nixio/tag.py | 18 +++++----- nixio/test/test_validator.py | 6 ++-- 15 files changed, 159 insertions(+), 143 deletions(-) diff --git a/nixio/block.py b/nixio/block.py index 326d4032..622f7b04 100644 --- a/nixio/block.py +++ b/nixio/block.py @@ -38,8 +38,9 @@ class Block(Entity): - def __init__(self, nixparent, h5group, compression=Compression.Auto): - super(Block, self).__init__(nixparent, h5group) + def __init__(self, nixfile, nixparent, h5group, + compression=Compression.Auto): + super(Block, self).__init__(nixfile, nixparent, h5group) self._groups = None self._data_arrays = None self._tags = None @@ -50,7 +51,8 @@ def __init__(self, nixparent, h5group, compression=Compression.Auto): @classmethod def create_new(cls, nixparent, h5parent, name, type_, compression): - newentity = super(Block, cls).create_new(nixparent, h5parent, + nixfile = nixparent # file is parent + newentity = super(Block, cls).create_new(nixfile, nixparent, h5parent, name, type_) newentity._compr = compression return newentity @@ -101,7 +103,7 @@ def create_multi_tag(self, name="", type_="", positions=None, "{}-extents".format(type_), data=extents) extcreated = True - mtag = MultiTag.create_new(self, multi_tags, + mtag = MultiTag.create_new(self.file, self, multi_tags, name, type_, positions) except Exception as e: msg = "MultiTag Creation Failed" @@ -150,7 +152,7 @@ def create_tag(self, name="", type_="", position=0, tags = self._h5group.open_group("tags") if name in tags: raise exceptions.DuplicateName("create_tag") - tag = Tag.create_new(self, tags, name, type_, position) + tag = Tag.create_new(self.file, self, tags, name, type_, position) return tag # Source @@ -170,7 +172,7 @@ def create_source(self, name, type_): sources = self._h5group.open_group("sources") if name in sources: raise exceptions.DuplicateName("create_source") - src = Source.create_new(self, sources, name, type_) + src = Source.create_new(self.file, self, sources, name, type_) return src # Group @@ -190,7 +192,7 @@ def create_group(self, name, type_): groups = self._h5group.open_group("groups") if name in groups: raise exceptions.DuplicateName("open_group") - grp = Group.create_new(self, groups, name, type_) + grp = Group.create_new(self.file, self, groups, name, type_) return grp def create_data_array(self, name="", array_type="", dtype=None, shape=None, @@ -250,7 +252,7 @@ def create_data_array(self, name="", array_type="", dtype=None, shape=None, raise exceptions.DuplicateName("create_data_array") if compression == Compression.Auto: compression = self._compr - da = DataArray.create_new(self, data_arrays, name, array_type, + da = DataArray.create_new(self.file, self, data_arrays, name, array_type, dtype, shape, compression) if data is not None: da.write_direct(data) @@ -362,7 +364,7 @@ def create_data_frame(self, name="", type_="", col_dict=None, dt_arr = list(col_dict.items()) col_dtype = np.dtype(dt_arr) - df = DataFrame.create_new(self, data_frames, name, + df = DataFrame.create_new(self.file, self, data_frames, name, type_, shape, col_dtype, compression) if data is not None: @@ -510,7 +512,7 @@ def sources(self): This is a read only attribute. """ if self._sources is None: - self._sources = SourceContainer("sources", self, Source) + self._sources = SourceContainer("sources", self.file, self, Source) return self._sources @property @@ -522,7 +524,8 @@ def multi_tags(self): This is a read only attribute. """ if self._multi_tags is None: - self._multi_tags = Container("multi_tags", self, MultiTag) + self._multi_tags = Container("multi_tags", self.file, + self, MultiTag) return self._multi_tags @property @@ -534,7 +537,7 @@ def tags(self): This is a read only attribute. """ if self._tags is None: - self._tags = Container("tags", self, Tag) + self._tags = Container("tags", self.file, self, Tag) return self._tags @property @@ -547,13 +550,15 @@ def data_arrays(self): This is a read only attribute. """ if self._data_arrays is None: - self._data_arrays = Container("data_arrays", self, DataArray) + self._data_arrays = Container("data_arrays", self.file, + self, DataArray) return self._data_arrays @property def data_frames(self): if self._data_frames is None: - self._data_frames = Container("data_frames", self, DataFrame) + self._data_frames = Container("data_frames", self.file, + self, DataFrame) return self._data_frames @property @@ -565,7 +570,7 @@ def groups(self): This is a read only attribute. """ if self._groups is None: - self._groups = Container("groups", self, Group) + self._groups = Container("groups", self.file, self, Group) return self._groups # metadata @@ -579,7 +584,8 @@ def metadata(self): :type: Section """ if "metadata" in self._h5group: - return Section(None, self._h5group.open_group("metadata")) + return Section(self.file, None, + self._h5group.open_group("metadata")) else: return None diff --git a/nixio/container.py b/nixio/container.py index bd2e2a79..ac4febb9 100644 --- a/nixio/container.py +++ b/nixio/container.py @@ -23,14 +23,15 @@ class Container(object): checking and instantiations) """ - def __init__(self, name, parent, itemclass): + def __init__(self, name, nixfile, parent, itemclass): self._backend = parent._h5group.open_group(name) self._itemclass = itemclass + self._file = nixfile self._parent = parent self._name = name def _inst_item(self, item): - return self._itemclass(self._parent, item) + return self._itemclass(self._file, self._parent, item) def __len__(self): return len(self._backend) @@ -180,7 +181,8 @@ class LinkContainer(Container): """ def __init__(self, name, parent, itemclass, itemstore): - super(LinkContainer, self).__init__(name, parent, itemclass) + super(LinkContainer, self).__init__(name, parent.file, + parent, itemclass) self._itemstore = itemstore def __delitem__(self, item): @@ -249,7 +251,7 @@ def __contains__(self, item): return False def _inst_item(self, item): - return self._itemclass(self._itemstore._parent, item) + return self._itemclass(self._file, self._itemstore._parent, item) @staticmethod def _item_key(item): diff --git a/nixio/data_array.py b/nixio/data_array.py index 5a25be78..5bd5c118 100644 --- a/nixio/data_array.py +++ b/nixio/data_array.py @@ -32,16 +32,16 @@ class DataSliceMode(Enum): class DataArray(Entity, DataSet): - def __init__(self, nixparent, h5group): - super(DataArray, self).__init__(nixparent, h5group) + def __init__(self, nixfile, nixparent, h5group): + super(DataArray, self).__init__(nixfile, nixparent, h5group) self._sources = None self._dimensions = None @classmethod - def create_new(cls, nixparent, h5parent, name, type_, data_type, shape, - compression): - newentity = super(DataArray, cls).create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixfile, nixparent, h5parent, name, type_, + data_type, shape, compression): + newentity = super(DataArray, cls).create_new(nixfile, nixparent, + h5parent, name, type_) datacompr = False if compression == Compression.DeflateNormal: datacompr = True @@ -85,9 +85,8 @@ def append_set_dimension(self, labels=None): :returns: The newly created SetDimension. :rtype: SetDimension """ - dimgroup = self._h5group.open_group("dimensions") - index = len(dimgroup) + 1 - setdim = SetDimension.create_new(dimgroup, index) + index = len(self.dimensions) + 1 + setdim = SetDimension.create_new(self, index) if labels: setdim.labels = labels if self._parent._parent.time_auto_update: @@ -107,10 +106,8 @@ def append_sampled_dimension(self, sampling_interval, label=None, :returns: The newly created SampledDimension. :rtype: SampledDimension """ - dimgroup = self._h5group.open_group("dimensions") - index = len(dimgroup) + 1 - smpldim = SampledDimension.create_new(dimgroup, index, - sampling_interval) + index = len(self.dimensions) + 1 + smpldim = SampledDimension.create_new(self, index, sampling_interval) if label: smpldim.label = label if unit: @@ -132,9 +129,8 @@ def append_range_dimension(self, ticks, label=None, unit=None): :returns: The newly created RangeDimension. :rtype: RangeDimension """ - dimgroup = self._h5group.open_group("dimensions") - index = len(dimgroup) + 1 - rdim = RangeDimension.create_new(dimgroup, index, ticks) + index = len(self.dimensions) + 1 + rdim = RangeDimension.create_new(self, index, ticks) if label: rdim.label = label rdim.unit = unit @@ -158,9 +154,8 @@ def append_data_frame_dimension(self, data_frame, column_idx=None): :returns: Thew newly created DataFrameDimension. :rtype: DataFrameDimension """ - dimgroup = self._h5group.open_group("dimensions") - index = len(dimgroup) + 1 - dfdim = DataFrameDimension.create_new(dimgroup, index, + index = len(self.dimensions) + 1 + dfdim = DataFrameDimension.create_new(self, index, data_frame, column_idx) if self._parent._parent.time_auto_update: self.force_updated_at() @@ -183,7 +178,6 @@ def append_alias_range_dimension(self): if self._dimension_count() > 0: raise ValueError("Cannot append additional alias dimension. " "There must only be one!") - dimgroup = self._h5group.open_group("dimensions") # check if existing unit is SI if self.unit: u = self.unit @@ -194,7 +188,7 @@ def append_alias_range_dimension(self): "Current SI unit is {}".format(u), "DataArray.append_alias_range_dimension" ) - return RangeDimension.create_new_alias(dimgroup, 1, self) + return RangeDimension.create_new_alias(self, 1) def delete_dimensions(self): """ @@ -390,8 +384,8 @@ def dimensions(self): :type: Container of dimension descriptors. """ if self._dimensions is None: - self._dimensions = DimensionContainer("dimensions", self, - Dimension) + self._dimensions = DimensionContainer("dimensions", self.file, + self, Dimension) return self._dimensions # metadata @@ -406,7 +400,8 @@ def metadata(self): :type: Section """ if "metadata" in self._h5group: - return Section(None, self._h5group.open_group("metadata")) + return Section(self.file, None, + self._h5group.open_group("metadata")) else: return None diff --git a/nixio/data_frame.py b/nixio/data_frame.py index 016fd48b..983a9d09 100644 --- a/nixio/data_frame.py +++ b/nixio/data_frame.py @@ -26,17 +26,17 @@ class DataFrame(Entity, DataSet): - def __init__(self, nixparent, h5group): - super(DataFrame, self).__init__(nixparent, h5group) + def __init__(self, nixfile, nixparent, h5group): + super(DataFrame, self).__init__(nixfile, nixparent, h5group) self._sources = None self._columns = None self._rows = None @classmethod - def create_new(cls, nixparent, h5parent, name, type_, + def create_new(cls, nixfile, nixparent, h5parent, name, type_, shape, col_dtype, compression): - newentity = super(DataFrame, cls).create_new(nixparent, h5parent, - name, type_) + newentity = super(DataFrame, cls).create_new(nixfile, nixparent, + h5parent, name, type_) newentity._h5group.create_dataset("data", (shape, ), col_dtype) return newentity @@ -423,7 +423,8 @@ def metadata(self): :type: Section """ if "metadata" in self._h5group: - return Section(None, self._h5group.open_group("metadata")) + return Section(self.file, None, + self._h5group.open_group("metadata")) else: return None diff --git a/nixio/dimensions.py b/nixio/dimensions.py index 1413e6c9..0eda9d22 100644 --- a/nixio/dimensions.py +++ b/nixio/dimensions.py @@ -32,20 +32,18 @@ def _inst_item(self, item): DimensionType.DataFrame: DataFrameDimension, }[DimensionType(item.get_attr("dimension_type"))] idx = item.name - return cls(item, idx) + return cls(self._parent, idx) class Dimension(object): - def __init__(self, h5group, index): + def __init__(self, nixfile, data_array, index): + dimgroup = data_array._h5group.open_group("dimensions") + h5group = dimgroup.open_group(str(index)) self._h5group = h5group self.dim_index = int(index) - - @classmethod - def create_new(cls, parent, index): - h5group = parent.open_group(str(index)) - newdim = cls(h5group, index) - return newdim + self._parent = data_array + self._file = nixfile @property def dimension_type(self): @@ -72,12 +70,13 @@ def __repr__(self): class SampledDimension(Dimension): - def __init__(self, h5group, index): - super(SampledDimension, self).__init__(h5group, index) + def __init__(self, data_array, index): + nixfile = data_array.file + super(SampledDimension, self).__init__(nixfile, data_array, index) @classmethod - def create_new(cls, parent, index, sample): - newdim = super(SampledDimension, cls).create_new(parent, index) + def create_new(cls, data_array, index, sample): + newdim = cls(data_array, index) newdim._set_dimension_type(DimensionType.Sample) newdim.sampling_interval = sample return newdim @@ -168,21 +167,22 @@ def offset(self, o): class RangeDimension(Dimension): - def __init__(self, h5group, index): - super(RangeDimension, self).__init__(h5group, index) + def __init__(self, data_array, index): + nixfile = data_array.file + super(RangeDimension, self).__init__(nixfile, data_array, index) @classmethod - def create_new(cls, parent, index, ticks): - newdim = super(RangeDimension, cls).create_new(parent, index) + def create_new(cls, data_array, index, ticks): + newdim = cls(data_array, index) newdim._set_dimension_type(DimensionType.Range) newdim._h5group.write_data("ticks", ticks, dtype=DataType.Double) return newdim @classmethod - def create_new_alias(cls, parent, index, da): - newdim = super(RangeDimension, cls).create_new(parent, index) + def create_new_alias(cls, data_array, index): + newdim = cls(data_array, index) newdim._set_dimension_type(DimensionType.Range) - newdim._h5group.create_link(da, da.id) + newdim._h5group.create_link(data_array, data_array.id) return newdim @property @@ -297,12 +297,13 @@ def axis(self, count, start=0): class SetDimension(Dimension): - def __init__(self, h5group, index): - super(SetDimension, self).__init__(h5group, index) + def __init__(self, data_array, index): + nixfile = data_array.file + super(SetDimension, self).__init__(nixfile, data_array, index) @classmethod - def create_new(cls, parent, index): - newdim = super(SetDimension, cls).create_new(parent, index) + def create_new(cls, data_array, index): + newdim = cls(data_array, index) newdim._set_dimension_type(DimensionType.Set) return newdim @@ -321,15 +322,18 @@ def labels(self, labels): class DataFrameDimension(Dimension): - def __init__(self, h5group, index): - super(DataFrameDimension, self).__init__(h5group, index) + def __init__(self, data_array, index): + nixfile = data_array.file + super(DataFrameDimension, self).__init__(nixfile, data_array, index) @classmethod - def create_new(cls, parent, index, data_frame, column): + def create_new(cls, data_array, index, data_frame, column): """ Create a new Dimension that points to a DataFrame - :param parent: DataArray the dimension will be attached to + :param data_array: The DataArray this Dimension belongs to + + :param parent: The H5Group for the dimensions :param data_frame: the referenced DataFrame for this Dimension @@ -338,7 +342,7 @@ def create_new(cls, parent, index, data_frame, column): :return: The new DataFrameDimension """ - newdim = super(DataFrameDimension, cls).create_new(parent, index) + newdim = cls(data_array, index) newdim.data_frame = data_frame newdim.column_idx = column newdim._set_dimension_type(DimensionType.DataFrame) @@ -419,7 +423,9 @@ def get_label(self, index=None): def data_frame(self): dfname = self._h5group.get_by_pos(0).name grp = self._h5group.open_group(dfname) - df = DataFrame(grp.h5root.group["data_frames"], grp) + nixblock = self._parent._parent + nixfile = self._file + df = DataFrame(nixfile, nixblock, grp) return df @data_frame.setter diff --git a/nixio/entity.py b/nixio/entity.py index e8ccbac8..e51f32fc 100644 --- a/nixio/entity.py +++ b/nixio/entity.py @@ -12,14 +12,14 @@ class Entity(object): - def __init__(self, nixparent, h5group): + def __init__(self, nixfile, nixparent, h5group): util.check_entity_id(h5group.get_attr("entity_id")) self._h5group = h5group self._parent = nixparent - self._file = None + self._file = nixfile @classmethod - def create_new(cls, nixparent, h5parent, name=None, type_=None): + def create_new(cls, nixfile, nixparent, h5parent, name=None, type_=None): if name and type_: util.check_entity_name_and_type(name, type_) id_ = util.create_id() @@ -30,7 +30,8 @@ def create_new(cls, nixparent, h5parent, name=None, type_=None): h5group.set_attr("name", name) h5group.set_attr("type", type_) h5group.set_attr("entity_id", id_) - newentity = cls(nixparent, h5group) + + newentity = cls(nixfile, nixparent, h5group) newentity.force_created_at() newentity.force_updated_at() return newentity diff --git a/nixio/feature.py b/nixio/feature.py index 865ec308..d5e3fc9c 100644 --- a/nixio/feature.py +++ b/nixio/feature.py @@ -14,17 +14,18 @@ class Feature(object): - def __init__(self, nixparent, h5group): + def __init__(self, nixfile, nixparent, h5group): util.check_entity_id(h5group.get_attr("entity_id")) self._h5group = h5group self._parent = nixparent + self._file = nixfile @classmethod - def create_new(cls, nixparent, h5parent, data, link_type): + def create_new(cls, nixfile, nixparent, h5parent, data, link_type): id_ = util.create_id() h5group = h5parent.open_group(id_) h5group.set_attr("entity_id", id_) - newfeature = cls(nixparent, h5group) + newfeature = cls(nixfile, nixparent, h5group) newfeature.link_type = link_type newfeature.data = data newfeature._h5group.set_attr("created_at", @@ -56,7 +57,7 @@ def link_type(self, lt): def data(self): if "data" not in self._h5group: raise RuntimeError("Feature.data: DataArray not found!") - return DataArray(self._parent._parent, + return DataArray(self._file, self._parent._parent, self._h5group.open_group("data")) @data.setter diff --git a/nixio/file.py b/nixio/file.py index cbf0c33d..9f7873dd 100644 --- a/nixio/file.py +++ b/nixio/file.py @@ -437,7 +437,7 @@ def create_section(self, name, type_="undefined", oid=None): """ if name in self.sections: raise DuplicateName("create_section") - sec = Section.create_new(self, self._metadata, name, type_, oid) + sec = Section.create_new(self, self, self._metadata, name, type_, oid) return sec @property @@ -450,7 +450,7 @@ def blocks(self): create_block method of File. This is a read-only attribute. """ if self._blocks is None: - self._blocks = Container("data", self, Block) + self._blocks = Container("data", self, self, Block) return self._blocks def find_sections(self, filtr=lambda _: True, limit=None): @@ -486,7 +486,7 @@ def sections(self): This is a read-only property. """ if self._sections is None: - self._sections = SectionContainer("metadata", self, Section) + self._sections = SectionContainer("metadata", self, self, Section) return self._sections diff --git a/nixio/group.py b/nixio/group.py index a7a03309..9360509f 100644 --- a/nixio/group.py +++ b/nixio/group.py @@ -19,8 +19,8 @@ class Group(Entity): - def __init__(self, nixparent, h5group): - super(Group, self).__init__(nixparent, h5group) + def __init__(self, nixfile, nixparent, h5group): + super(Group, self).__init__(nixfile, nixparent, h5group) self._data_arrays = None self._data_frames = None self._tags = None @@ -28,8 +28,8 @@ def __init__(self, nixparent, h5group): self._sources = None @classmethod - def create_new(cls, nixparent, h5parent, name, type_): - newentity = super(Group, cls).create_new(nixparent, h5parent, + def create_new(cls, nixfile, nixparent, h5parent, name, type_): + newentity = super(Group, cls).create_new(nixfile, nixparent, h5parent, name, type_) return newentity @@ -115,7 +115,8 @@ def metadata(self): :type: Section """ if "metadata" in self._h5group: - return Section(None, self._h5group.open_group("metadata")) + return Section(self.file, None, + self._h5group.open_group("metadata")) else: return None diff --git a/nixio/multi_tag.py b/nixio/multi_tag.py index de5c9c93..6d1be56d 100644 --- a/nixio/multi_tag.py +++ b/nixio/multi_tag.py @@ -22,16 +22,16 @@ class MultiTag(BaseTag): - def __init__(self, nixparent, h5group): - super(MultiTag, self).__init__(nixparent, h5group) + def __init__(self, nixfile, nixparent, h5group): + super(MultiTag, self).__init__(nixfile, nixparent, h5group) self._sources = None self._references = None self._features = None @classmethod - def create_new(cls, nixparent, h5parent, name, type_, positions): - newentity = super(MultiTag, cls).create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixfile, nixparent, h5parent, name, type_, positions): + newentity = super(MultiTag, cls).create_new(nixfile, nixparent, + h5parent, name, type_) newentity.positions = positions return newentity @@ -44,7 +44,8 @@ def positions(self): """ if "positions" not in self._h5group: raise RuntimeError("MultiTag.positions: DataArray not found!") - return DataArray(self._parent, self._h5group.open_group("positions")) + return DataArray(self.file, self._parent, + self._h5group.open_group("positions")) @positions.setter def positions(self, da): @@ -65,9 +66,9 @@ def extents(self): :type: DataArray or None """ if "extents" in self._h5group: - return DataArray(self._parent, self._h5group.open_group("extents")) - else: - return None + return DataArray(self.file, self._parent, + self._h5group.open_group("extents")) + return None @extents.setter def extents(self, da): @@ -106,7 +107,8 @@ def features(self): :type: Container of Feature. """ if self._features is None: - self._features = FeatureContainer("features", self, Feature) + self._features = FeatureContainer("features", self.file, + self, Feature) return self._features def _calc_data_slices(self, data, index): @@ -261,7 +263,7 @@ def metadata(self): :type: Section """ if "metadata" in self._h5group: - return Section(None, self._h5group.open_group("metadata")) + return Section(self.file, None, self._h5group.open_group("metadata")) else: return None diff --git a/nixio/property.py b/nixio/property.py index 38981c21..bf18729a 100644 --- a/nixio/property.py +++ b/nixio/property.py @@ -92,12 +92,12 @@ def get_odml_type(cls, dtype): class Property(Entity): """An odML Property""" - def __init__(self, nixparent, h5dataset): - super(Property, self).__init__(nixparent, h5dataset) + def __init__(self, nixfile, nixparent, h5dataset): + super(Property, self).__init__(nixfile, nixparent, h5dataset) self._h5dataset = self._h5group @classmethod - def create_new(cls, nixparent, h5parent, name, + def create_new(cls, nixfile, nixparent, h5parent, name, dtype, shape=None, oid=None): if shape is None or shape[0] == 0: shape = (8, ) @@ -112,7 +112,7 @@ def create_new(cls, nixparent, h5parent, name, h5dataset.set_attr("entity_id", oid) - newentity = cls(nixparent, h5dataset) + newentity = cls(nixfile, nixparent, h5dataset) newentity.force_created_at() newentity.force_updated_at() diff --git a/nixio/section.py b/nixio/section.py index 48d82a15..6ec0df3f 100644 --- a/nixio/section.py +++ b/nixio/section.py @@ -48,16 +48,16 @@ def __getattribute__(self, item): class Section(Entity): - def __init__(self, nixparent, h5group): - super(Section, self).__init__(nixparent, h5group) + def __init__(self, nixfile, nixparent, h5group): + super(Section, self).__init__(nixfile, nixparent, h5group) self._sec_parent = None self._sections = None self._properties = None @classmethod - def create_new(cls, nixparent, h5parent, name, type_, oid=None): - newentity = super(Section, cls).create_new(nixparent, h5parent, - name, type_) + def create_new(cls, nixfile, nixparent, h5parent, name, type_, oid=None): + newentity = super(Section, cls).create_new(nixfile, nixparent, + h5parent, name, type_) if util.is_uuid(oid): newentity._h5group.set_attr("entity_id", oid) @@ -83,7 +83,7 @@ def create_section(self, name, type_="undefined", oid=None): sections = self._h5group.open_group("sections", True) if name in sections: raise exceptions.DuplicateName("create_section") - sec = Section.create_new(self, sections, name, type_, oid) + sec = Section.create_new(self.file, self, sections, name, type_, oid) sec._sec_parent = self return sec @@ -169,7 +169,8 @@ def create_property(self, name="", values_or_dtype=0, oid=None, raise TypeError("Array contains inconsistent values.") shape = (len(vals),) - prop = Property.create_new(self, properties, name, dtype, shape, oid) + prop = Property.create_new(self.file, self, properties, + name, dtype, shape, oid) prop.values = vals return prop @@ -240,7 +241,7 @@ def link(self): if "link" not in self._h5group: return None - return Section(self, self._h5group.open_group("link")) + return Section(self.file, self, self._h5group.open_group("link")) @link.setter def link(self, id_or_sec): @@ -249,7 +250,7 @@ def link(self, id_or_sec): if isinstance(id_or_sec, Section): sec = id_or_sec else: - rootsec = Section(self, self._h5group.h5root) + rootsec = Section(self.file, self, self._h5group.h5root) sec = rootsec.find_sections(filtr=lambda x: x.id == id_or_sec) self._h5group.create_link(sec, "link") @@ -258,7 +259,7 @@ def link(self, id_or_sec): def inherited_properties(self): properties = self._h5group.open_group("properties") - inhprops = [Property(self, h5prop) for h5prop in properties] + inhprops = [Property(self.file, self, h5prop) for h5prop in properties] if self.link: inhprops.extend(self.link.inherited_properties()) return inhprops @@ -294,7 +295,7 @@ def parent(self): return self._sec_parent rootmd = self._h5group.file.open_group("metadata") # BFS - sections = [Section(None, sg) for sg in rootmd] + sections = [Section(self.file, None, sg) for sg in rootmd] if self in sections: # Top-level section return None @@ -444,7 +445,8 @@ def sections(self): :type: Container of Section """ if self._sections is None: - self._sections = SectionContainer("sections", self, Section) + self._sections = SectionContainer("sections", self.file, + self, Section) return self._sections def __len__(self): @@ -504,7 +506,8 @@ def props(self): :type: Container of Property """ if self._properties is None: - self._properties = Container("properties", self, Property) + self._properties = Container("properties", self.file, + self, Property) return self._properties def pprint(self, indent=2, max_depth=1, max_length=80, current_depth=0): diff --git a/nixio/source.py b/nixio/source.py index 2ffe338c..dc33987c 100644 --- a/nixio/source.py +++ b/nixio/source.py @@ -18,13 +18,13 @@ class Source(Entity): - def __init__(self, nixparent, h5group): - super(Source, self).__init__(nixparent, h5group) + def __init__(self, nixfile, nixparent, h5group): + super(Source, self).__init__(nixfile, nixparent, h5group) self._sources = None @classmethod - def create_new(cls, nixparent, h5parent, name, type_): - newentity = super(Source, cls).create_new(nixparent, h5parent, + def create_new(cls, nixfile, nixparent, h5parent, name, type_): + newentity = super(Source, cls).create_new(nixfile, nixparent, h5parent, name, type_) return newentity @@ -45,7 +45,7 @@ def create_source(self, name, type_): sources = self._h5group.open_group("sources", True) if name in sources: raise exceptions.DuplicateName("create_source") - src = Source.create_new(self, sources, name, type_) + src = Source.create_new(self.file, self, sources, name, type_) return src @property @@ -99,7 +99,7 @@ def sources(self): This is a read only attribute. """ if self._sources is None: - self._sources = SourceContainer("sources", self, Source) + self._sources = SourceContainer("sources", self.file, self, Source) return self._sources # metadata @@ -114,7 +114,7 @@ def metadata(self): :type: Section """ if "metadata" in self._h5group: - return Section(None, self._h5group.open_group("metadata")) + return Section(self.file, None, self._h5group.open_group("metadata")) else: return None diff --git a/nixio/tag.py b/nixio/tag.py index 78c6669d..e4a8c19a 100644 --- a/nixio/tag.py +++ b/nixio/tag.py @@ -105,7 +105,7 @@ def create_feature(self, data, link_type): link_type = link_type.lower() link_type = LinkType(link_type) features = self._h5group.open_group("features") - feat = Feature.create_new(self, features, data, link_type) + feat = Feature.create_new(self.file, self, features, data, link_type) return feat @staticmethod @@ -166,15 +166,15 @@ def _pos_to_idx(pos, unit, dim): class Tag(BaseTag): - def __init__(self, nixparent, h5group): - super(Tag, self).__init__(nixparent, h5group) + def __init__(self, nixfile, nixparent, h5group): + super(Tag, self).__init__(nixfile, nixparent, h5group) self._sources = None self._references = None self._features = None @classmethod - def create_new(cls, nixparent, h5parent, name, type_, position): - newentity = super(Tag, cls).create_new(nixparent, h5parent, + def create_new(cls, nixfile, nixparent, h5parent, name, type_, position): + newentity = super(Tag, cls).create_new(nixfile, nixparent, h5parent, name, type_) newentity.position = position return newentity @@ -347,7 +347,8 @@ def features(self): :type: Container of Feature. """ if self._features is None: - self._features = FeatureContainer("features", self, Feature) + self._features = FeatureContainer("features", self.file, + self, Feature) return self._features @property @@ -375,9 +376,8 @@ def metadata(self): :type: Section """ if "metadata" in self._h5group: - return Section(None, self._h5group.open_group("metadata")) - else: - return None + return Section(self.file, None, self._h5group.open_group("metadata")) + return None @metadata.setter def metadata(self, sect): diff --git a/nixio/test/test_validator.py b/nixio/test/test_validator.py index 77d34b2d..ac5f5893 100644 --- a/nixio/test/test_validator.py +++ b/nixio/test/test_validator.py @@ -245,18 +245,16 @@ def test_check_data_array_invalid_unit(self): def test_incorrect_dim_index(self): da = self.file.blocks[1].data_arrays["data-1d"] da.delete_dimensions() - dimgroup = da._h5group.open_group("dimensions") # This wont work if we ever change the internals - nix.SetDimension.create_new(dimgroup, "10") + nix.SetDimension.create_new(da, "10") res = self.file.validate() assert VE.IncorrectDimensionIndex.format(1, 10) in res["errors"][da] def test_invalid_dim_index(self): da = self.file.blocks[1].data_arrays["data-1d"] da.delete_dimensions() - dimgroup = da._h5group.open_group("dimensions") # This wont work if we ever change the internals - nix.SetDimension.create_new(dimgroup, "-1") + nix.SetDimension.create_new(da, "-1") res = self.file.validate() assert VE.InvalidDimensionIndex.format(1) in res["errors"][da] From 9aaefe316c544891f13ec79dc97ad5adce07dc24 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Wed, 29 Apr 2020 21:39:01 +0200 Subject: [PATCH 06/18] Test object file references Testing in container for convenience (has all objects) and because instantiating objects through containers is where an object might lose references to parent objects. --- nixio/test/test_container.py | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/nixio/test/test_container.py b/nixio/test/test_container.py index d8ed5b79..21e5b393 100644 --- a/nixio/test/test_container.py +++ b/nixio/test/test_container.py @@ -73,13 +73,53 @@ def test_link_container_index_getter(self): self.assertEqual(self.multi_tag, self.group.multi_tags[0]) + def test_file_references(self): + # add some sources + self.source = self.block.create_source("test source", "containertest") + self.child_source = self.source.create_source("test source 2", + "containertest") + # using assertIs since the reference should be the same instance as + # the original + + # created objects + self.assertIs(self.block.file, self.file) + self.assertIs(self.group.file, self.file) + self.assertIs(self.dataarray.file, self.file) + self.assertIs(self.tag.file, self.file) + self.assertIs(self.multi_tag.file, self.file) + self.assertIs(self.positions.file, self.file) + self.assertIs(self.source.file, self.file) + self.assertIs(self.child_source.file, self.file) + + # instantiated through container getters + blk = self.file.blocks[0] + self.assertIs(blk.file, self.file) + self.assertIs(blk.groups[0].file, self.file) + self.assertIs(blk.data_arrays[0].file, self.file) + self.assertIs(blk.tags[0].file, self.file) + self.assertIs(blk.multi_tags[0].file, self.file) + self.assertIs(blk.sources[0].file, self.file) + self.assertIs(blk.sources[0].sources[0].file, self.file) + + # linked through group + self.assertIs(self.group.data_arrays[0].file, self.file) + self.assertIs(self.group.tags[0].file, self.file) + self.assertIs(self.group.multi_tags[0].file, self.file) + def test_parent_references(self): + # add some sources + self.source = self.block.create_source("test source", "containertest") + self.child_source = self.source.create_source("test source 2", + "containertest") + self.assertEqual(self.block._parent, self.file) self.assertEqual(self.group._parent, self.block) self.assertEqual(self.dataarray._parent, self.block) self.assertEqual(self.tag._parent, self.block) self.assertEqual(self.multi_tag._parent, self.block) self.assertEqual(self.positions._parent, self.block) + self.assertEqual(self.source._parent, self.block) + self.assertEqual(self.child_source._parent, self.source) def test_link_parent_references(self): self.assertEqual(self.group.data_arrays[0]._parent, self.block) From da44eb7a9a05f510541661c74a2ca3f4762a6ecc Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 02:10:29 +0200 Subject: [PATCH 07/18] Use file property to read time_auto_update setting --- nixio/data_array.py | 16 ++++++++-------- nixio/data_frame.py | 2 +- nixio/entity.py | 10 ++-------- nixio/feature.py | 4 ++-- nixio/multi_tag.py | 4 ++-- nixio/tag.py | 6 +++--- 6 files changed, 18 insertions(+), 24 deletions(-) diff --git a/nixio/data_array.py b/nixio/data_array.py index 5bd5c118..770db9fb 100644 --- a/nixio/data_array.py +++ b/nixio/data_array.py @@ -89,7 +89,7 @@ def append_set_dimension(self, labels=None): setdim = SetDimension.create_new(self, index) if labels: setdim.labels = labels - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() return setdim @@ -114,7 +114,7 @@ def append_sampled_dimension(self, sampling_interval, label=None, smpldim.unit = unit if offset: smpldim.offset = offset - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() return smpldim @@ -134,7 +134,7 @@ def append_range_dimension(self, ticks, label=None, unit=None): if label: rdim.label = label rdim.unit = unit - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() return rdim @@ -157,7 +157,7 @@ def append_data_frame_dimension(self, data_frame, column_idx=None): index = len(self.dimensions) + 1 dfdim = DataFrameDimension.create_new(self, index, data_frame, column_idx) - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() return dfdim @@ -252,7 +252,7 @@ def polynom_coefficients(self, coeff): else: dtype = DataType.Double self._h5group.write_data("polynom_coefficients", coeff, dtype) - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() @property @@ -270,7 +270,7 @@ def expansion_origin(self): def expansion_origin(self, eo): util.check_attr_type(eo, Number) self._h5group.set_attr("expansion_origin", eo) - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() @property @@ -288,7 +288,7 @@ def label(self): def label(self, l): util.check_attr_type(l, str) self._h5group.set_attr("label", l) - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() @property @@ -318,7 +318,7 @@ def unit(self, u): "DataArray.unit" ) self._h5group.set_attr("unit", u) - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() def get_slice(self, positions, extents=None, mode=DataSliceMode.Index): diff --git a/nixio/data_frame.py b/nixio/data_frame.py index 983a9d09..aa3309bc 100644 --- a/nixio/data_frame.py +++ b/nixio/data_frame.py @@ -348,7 +348,7 @@ def units(self, u): util.check_attr_type(i, str) unit = np.array(u, util.vlen_str_dtype) self._h5group.set_attr("units", unit) - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() @property diff --git a/nixio/entity.py b/nixio/entity.py index e51f32fc..94608b90 100644 --- a/nixio/entity.py +++ b/nixio/entity.py @@ -120,10 +120,7 @@ def definition(self): def definition(self, d): util.check_attr_type(d, str) self._h5group.set_attr("definition", d) - par = self._parent - while isinstance(par, Entity): - par = par._parent - if par.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() @property @@ -154,10 +151,7 @@ def type(self, t): raise AttributeError("type can't be None") util.check_attr_type(t, str) self._h5group.set_attr("type", t) - par = self._parent - while isinstance(par, Entity): - par = par._parent - if par.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() def __eq__(self, other): diff --git a/nixio/feature.py b/nixio/feature.py index d5e3fc9c..d88e4cf8 100644 --- a/nixio/feature.py +++ b/nixio/feature.py @@ -49,7 +49,7 @@ def link_type(self, lt): lt = lt.lower() lt = LinkType(lt) self._h5group.set_attr("link_type", lt.value) - if self._parent._parent._parent.time_auto_update: + if self._file.time_auto_update: t = util.now_int() self._h5group.set_attr("updated_at", util.time_to_str(t)) @@ -70,7 +70,7 @@ def data(self, da): if "data" in self._h5group: del self._h5group["data"] self._h5group.create_link(da, "data") - if self._parent._parent._parent.time_auto_update: + if self._file.time_auto_update: t = util.now_int() self._h5group.set_attr("updated_at", util.time_to_str(t)) diff --git a/nixio/multi_tag.py b/nixio/multi_tag.py index 6d1be56d..a230766c 100644 --- a/nixio/multi_tag.py +++ b/nixio/multi_tag.py @@ -54,7 +54,7 @@ def positions(self, da): if "positions" in self._h5group: del self._h5group["positions"] self._h5group.create_link(da, "positions") - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() @property @@ -76,7 +76,7 @@ def extents(self, da): del self._h5group["extents"] else: self._h5group.create_link(da, "extents") - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() @property diff --git a/nixio/tag.py b/nixio/tag.py index e4a8c19a..364c7e46 100644 --- a/nixio/tag.py +++ b/nixio/tag.py @@ -86,7 +86,7 @@ def units(self, units): dtype = DataType.String self._h5group.write_data("units", sanitized, dtype) - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() def create_feature(self, data, link_type): @@ -196,7 +196,7 @@ def position(self, pos): else: dtype = DataType.Double self._h5group.write_data("position", pos, dtype) - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() @property @@ -217,7 +217,7 @@ def extent(self, ext): else: dtype = DataType.Double self._h5group.write_data("extent", ext, dtype) - if self._parent._parent.time_auto_update: + if self.file.time_auto_update: self.force_updated_at() def _calc_data_slices(self, data): From 3602171aa110976588f56db0294919e2bca1f319 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 02:13:45 +0200 Subject: [PATCH 08/18] Feature.file property (read only) --- nixio/feature.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/nixio/feature.py b/nixio/feature.py index d88e4cf8..efc2adbc 100644 --- a/nixio/feature.py +++ b/nixio/feature.py @@ -39,6 +39,10 @@ def create_new(cls, nixfile, nixparent, h5parent, data, link_type): def id(self): return self._h5group.get_attr("entity_id") + @property + def file(self): + return self._file + @property def link_type(self): return LinkType(self._h5group.get_attr("link_type")) @@ -49,7 +53,7 @@ def link_type(self, lt): lt = lt.lower() lt = LinkType(lt) self._h5group.set_attr("link_type", lt.value) - if self._file.time_auto_update: + if self.file.time_auto_update: t = util.now_int() self._h5group.set_attr("updated_at", util.time_to_str(t)) @@ -57,7 +61,7 @@ def link_type(self, lt): def data(self): if "data" not in self._h5group: raise RuntimeError("Feature.data: DataArray not found!") - return DataArray(self._file, self._parent._parent, + return DataArray(self.file, self._parent._parent, self._h5group.open_group("data")) @data.setter @@ -70,7 +74,7 @@ def data(self, da): if "data" in self._h5group: del self._h5group["data"] self._h5group.create_link(da, "data") - if self._file.time_auto_update: + if self.file.time_auto_update: t = util.now_int() self._h5group.set_attr("updated_at", util.time_to_str(t)) From 32e765bb9010ac8b4ffe6ec0ddf69c57c15805d7 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 02:16:52 +0200 Subject: [PATCH 09/18] Test auto_update_time setting Type and Definition changes on Block. --- nixio/test/test_file.py | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/nixio/test/test_file.py b/nixio/test/test_file.py index 2266b5de..e386ae7e 100644 --- a/nixio/test/test_file.py +++ b/nixio/test/test_file.py @@ -10,6 +10,7 @@ import unittest import h5py import numpy as np +import time import nixio as nix import nixio.file as filepy @@ -166,6 +167,60 @@ def test_copy_on_file(self): assert self.file.blocks[0] == self.file.blocks[1] # ID stays the same assert self.file.blocks[0].name != self.file.blocks[1].name + def test_timestamp_autoupdate_definition(self): + blk = self.file.create_block("block", "timetest") + blktime = blk.updated_at + blk.definition = "updated" + # no update + self.assertEqual(blk.updated_at, blktime) + + rblk = self.file.blocks["block"] # read through container + rblk.definition = "updated again" + self.assertEqual(rblk.updated_at, blktime) + + # close and recreate file with auto_update_time enabled + self.file.close() + self.file = nix.File(self.testfilename, nix.FileMode.Overwrite, + auto_update_time=True) + blk = self.file.create_block("block", "timetest") + blktime = blk.updated_at + time.sleep(1) # wait for time to change + blk.definition = "update" + self.assertNotEqual(blk.updated_at, blktime) + + rblk = self.file.blocks["block"] # read through container + rblktime = rblk.updated_at + time.sleep(1) # wait for time to change + rblk.definition = "time should change" + self.assertNotEqual(rblk.updated_at, rblktime) + + def test_timestamp_autoupdate_type(self): + blk = self.file.create_block("block", "timetest") + blktime = blk.updated_at + blk.type = "updated" + # no update + self.assertEqual(blk.updated_at, blktime) + + rblk = self.file.blocks["block"] # read through container + rblk.type = "updated again" + self.assertEqual(rblk.updated_at, blktime) + + # close and recreate file with auto_update_time enabled + self.file.close() + self.file = nix.File(self.testfilename, nix.FileMode.Overwrite, + auto_update_time=True) + blk = self.file.create_block("block", "timetest") + blktime = blk.updated_at + time.sleep(1) # wait for time to change + blk.type = "update" + self.assertNotEqual(blk.updated_at, blktime) + + rblk = self.file.blocks["block"] # read through container + rblktime = rblk.updated_at + time.sleep(1) # wait for time to change + rblk.type = "time should change" + self.assertNotEqual(rblk.updated_at, rblktime) + class TestFileVer(unittest.TestCase): From e8eaaa1c97dab99e623a2ee881d5fcee7f14cb65 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 17:21:46 +0200 Subject: [PATCH 10/18] Rename auto update timestamp option and true by default - Rename 'time_auto_update' to 'auto_update_timestamp' - Property and constructor argument are the same - Enabled by default: Updating timestamps should be the default. The overhead is probably minimal (perhaps even negligible) and should only be disabled for performance when required. --- nixio/data_array.py | 16 ++++++++-------- nixio/data_frame.py | 2 +- nixio/entity.py | 4 ++-- nixio/feature.py | 4 ++-- nixio/file.py | 33 +++++++++++++++++++++------------ nixio/multi_tag.py | 4 ++-- nixio/section.py | 6 +++--- nixio/tag.py | 6 +++--- nixio/test/test_file.py | 34 ++++++++++++++++------------------ 9 files changed, 58 insertions(+), 51 deletions(-) diff --git a/nixio/data_array.py b/nixio/data_array.py index 770db9fb..2b0d85b8 100644 --- a/nixio/data_array.py +++ b/nixio/data_array.py @@ -89,7 +89,7 @@ def append_set_dimension(self, labels=None): setdim = SetDimension.create_new(self, index) if labels: setdim.labels = labels - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() return setdim @@ -114,7 +114,7 @@ def append_sampled_dimension(self, sampling_interval, label=None, smpldim.unit = unit if offset: smpldim.offset = offset - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() return smpldim @@ -134,7 +134,7 @@ def append_range_dimension(self, ticks, label=None, unit=None): if label: rdim.label = label rdim.unit = unit - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() return rdim @@ -157,7 +157,7 @@ def append_data_frame_dimension(self, data_frame, column_idx=None): index = len(self.dimensions) + 1 dfdim = DataFrameDimension.create_new(self, index, data_frame, column_idx) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() return dfdim @@ -252,7 +252,7 @@ def polynom_coefficients(self, coeff): else: dtype = DataType.Double self._h5group.write_data("polynom_coefficients", coeff, dtype) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property @@ -270,7 +270,7 @@ def expansion_origin(self): def expansion_origin(self, eo): util.check_attr_type(eo, Number) self._h5group.set_attr("expansion_origin", eo) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property @@ -288,7 +288,7 @@ def label(self): def label(self, l): util.check_attr_type(l, str) self._h5group.set_attr("label", l) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property @@ -318,7 +318,7 @@ def unit(self, u): "DataArray.unit" ) self._h5group.set_attr("unit", u) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() def get_slice(self, positions, extents=None, mode=DataSliceMode.Index): diff --git a/nixio/data_frame.py b/nixio/data_frame.py index aa3309bc..a4ce238e 100644 --- a/nixio/data_frame.py +++ b/nixio/data_frame.py @@ -348,7 +348,7 @@ def units(self, u): util.check_attr_type(i, str) unit = np.array(u, util.vlen_str_dtype) self._h5group.set_attr("units", unit) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property diff --git a/nixio/entity.py b/nixio/entity.py index 94608b90..6babde5f 100644 --- a/nixio/entity.py +++ b/nixio/entity.py @@ -120,7 +120,7 @@ def definition(self): def definition(self, d): util.check_attr_type(d, str) self._h5group.set_attr("definition", d) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property @@ -151,7 +151,7 @@ def type(self, t): raise AttributeError("type can't be None") util.check_attr_type(t, str) self._h5group.set_attr("type", t) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() def __eq__(self, other): diff --git a/nixio/feature.py b/nixio/feature.py index efc2adbc..9feccdf0 100644 --- a/nixio/feature.py +++ b/nixio/feature.py @@ -53,7 +53,7 @@ def link_type(self, lt): lt = lt.lower() lt = LinkType(lt) self._h5group.set_attr("link_type", lt.value) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: t = util.now_int() self._h5group.set_attr("updated_at", util.time_to_str(t)) @@ -74,7 +74,7 @@ def data(self, da): if "data" in self._h5group: del self._h5group["data"] self._h5group.create_link(da, "data") - if self.file.time_auto_update: + if self.file.auto_update_timestamps: t = util.now_int() self._h5group.set_attr("updated_at", util.time_to_str(t)) diff --git a/nixio/file.py b/nixio/file.py index 9f7873dd..a2985549 100644 --- a/nixio/file.py +++ b/nixio/file.py @@ -85,7 +85,8 @@ def make_fcpl(): class File(object): def __init__(self, path, mode=FileMode.ReadWrite, - compression=Compression.Auto, auto_update_time=False): + compression=Compression.Auto, + auto_update_timestamps=True): """ Open a NIX file, or create it if it does not exist. @@ -93,6 +94,9 @@ def __init__(self, path, mode=FileMode.ReadWrite, :param mode: FileMode ReadOnly, ReadWrite, or Overwrite. (default: ReadWrite) :param compression: No, DeflateNormal, Auto (default: Auto) + :param auto_update_timestamps: Enable/disable automatic updating of + 'updated_at' timestamp. (default: True) + :return: nixio.File object """ try: @@ -120,7 +124,7 @@ def __init__(self, path, mode=FileMode.ReadWrite, self._root = H5Group(self._h5file, "/") self._h5group = self._root # to match behaviour of other objects - self._time_auto_update = True + self._auto_update_timestamps = auto_update_timestamps self._check_header(mode) self.mode = mode self._data = self._root.open_group("data", create=True) @@ -129,7 +133,6 @@ def __init__(self, path, mode=FileMode.ReadWrite, self.force_created_at() if "updated_at" not in self._h5file.attrs: self.force_updated_at() - self.time_auto_update = auto_update_time if compression == Compression.Auto: compression = Compression.No self._compr = compression @@ -139,10 +142,10 @@ def __init__(self, path, mode=FileMode.ReadWrite, @classmethod def open(cls, path, mode=FileMode.ReadWrite, compression=Compression.Auto, - backend=None, auto_update_time=False): + backend=None, auto_update_timestamps=True): if backend is not None: warn("Backend selection is deprecated. Ignoring value.") - return cls(path, mode, compression, auto_update_time) + return cls(path, mode, compression, auto_update_timestamps) def _create_header(self): self._set_format() @@ -218,18 +221,24 @@ def _set_format(self): self._root.set_attr("format", FILE_FORMAT.encode("ascii")) @property - def time_auto_update(self): + def auto_update_timestamps(self): """ - A user defined flag which decided if time should always be updated - when properties are changed. + If enabled, automatically updates the 'updated_at' attribute when an + object's data or attributes are changed. :type: bool """ - return self._time_auto_update + return self._auto_update_timestamps + + @auto_update_timestamps.setter + def auto_update_timestamps(self, enable): + """ + If enabled, automatically updates the 'updated_at' attribute when an + object's data or attributes are changed. - @time_auto_update.setter - def time_auto_update(self, auto_update_flag): - self._time_auto_update = auto_update_flag + :type: bool + """ + self._auto_update_timestamps = enable @property def created_at(self): diff --git a/nixio/multi_tag.py b/nixio/multi_tag.py index a230766c..2d208667 100644 --- a/nixio/multi_tag.py +++ b/nixio/multi_tag.py @@ -54,7 +54,7 @@ def positions(self, da): if "positions" in self._h5group: del self._h5group["positions"] self._h5group.create_link(da, "positions") - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property @@ -76,7 +76,7 @@ def extents(self, da): del self._h5group["extents"] else: self._h5group.create_link(da, "extents") - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property diff --git a/nixio/section.py b/nixio/section.py index 6ec0df3f..85d1cdad 100644 --- a/nixio/section.py +++ b/nixio/section.py @@ -226,7 +226,7 @@ def reference(self): def reference(self, ref): util.check_attr_type(ref, str) self._h5group.set_attr("reference", ref) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property @@ -254,7 +254,7 @@ def link(self, id_or_sec): sec = rootsec.find_sections(filtr=lambda x: x.id == id_or_sec) self._h5group.create_link(sec, "link") - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() def inherited_properties(self): @@ -278,7 +278,7 @@ def repository(self): def repository(self, r): util.check_attr_type(r, str) self._h5group.set_attr("repository", r) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property diff --git a/nixio/tag.py b/nixio/tag.py index 364c7e46..d38fae22 100644 --- a/nixio/tag.py +++ b/nixio/tag.py @@ -86,7 +86,7 @@ def units(self, units): dtype = DataType.String self._h5group.write_data("units", sanitized, dtype) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() def create_feature(self, data, link_type): @@ -196,7 +196,7 @@ def position(self, pos): else: dtype = DataType.Double self._h5group.write_data("position", pos, dtype) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() @property @@ -217,7 +217,7 @@ def extent(self, ext): else: dtype = DataType.Double self._h5group.write_data("extent", ext, dtype) - if self.file.time_auto_update: + if self.file.auto_update_timestamps: self.force_updated_at() def _calc_data_slices(self, data): diff --git a/nixio/test/test_file.py b/nixio/test/test_file.py index e386ae7e..f6f5996b 100644 --- a/nixio/test/test_file.py +++ b/nixio/test/test_file.py @@ -170,56 +170,54 @@ def test_copy_on_file(self): def test_timestamp_autoupdate_definition(self): blk = self.file.create_block("block", "timetest") blktime = blk.updated_at + time.sleep(1) # wait for time to change blk.definition = "updated" # no update - self.assertEqual(blk.updated_at, blktime) + self.assertNotEqual(blk.updated_at, blktime) rblk = self.file.blocks["block"] # read through container + time.sleep(1) # wait for time to change rblk.definition = "updated again" - self.assertEqual(rblk.updated_at, blktime) + self.assertNotEqual(rblk.updated_at, blktime) - # close and recreate file with auto_update_time enabled - self.file.close() - self.file = nix.File(self.testfilename, nix.FileMode.Overwrite, - auto_update_time=True) - blk = self.file.create_block("block", "timetest") + # disable timestamp autoupdating + self.file.auto_update_timestamps = False blktime = blk.updated_at time.sleep(1) # wait for time to change blk.definition = "update" - self.assertNotEqual(blk.updated_at, blktime) + self.assertEqual(blk.updated_at, blktime) rblk = self.file.blocks["block"] # read through container rblktime = rblk.updated_at time.sleep(1) # wait for time to change rblk.definition = "time should change" - self.assertNotEqual(rblk.updated_at, rblktime) + self.assertEqual(rblk.updated_at, rblktime) def test_timestamp_autoupdate_type(self): blk = self.file.create_block("block", "timetest") blktime = blk.updated_at + time.sleep(1) # wait for time to change blk.type = "updated" # no update - self.assertEqual(blk.updated_at, blktime) + self.assertNotEqual(blk.updated_at, blktime) rblk = self.file.blocks["block"] # read through container + time.sleep(1) # wait for time to change rblk.type = "updated again" - self.assertEqual(rblk.updated_at, blktime) + self.assertNotEqual(rblk.updated_at, blktime) - # close and recreate file with auto_update_time enabled - self.file.close() - self.file = nix.File(self.testfilename, nix.FileMode.Overwrite, - auto_update_time=True) - blk = self.file.create_block("block", "timetest") + # disable timestamp autoupdating + self.file.auto_update_timestamps = False blktime = blk.updated_at time.sleep(1) # wait for time to change blk.type = "update" - self.assertNotEqual(blk.updated_at, blktime) + self.assertEqual(blk.updated_at, blktime) rblk = self.file.blocks["block"] # read through container rblktime = rblk.updated_at time.sleep(1) # wait for time to change rblk.type = "time should change" - self.assertNotEqual(rblk.updated_at, rblktime) + self.assertEqual(rblk.updated_at, rblktime) class TestFileVer(unittest.TestCase): From fa40d413db704972c4f0b5632d26fdadfb1ec6c0 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 17:46:48 +0200 Subject: [PATCH 11/18] Split tests for enabled and disabled timestamp update Instead of separating based on property, separate based on option state. --- nixio/test/test_file.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/nixio/test/test_file.py b/nixio/test/test_file.py index f6f5996b..dc37ffae 100644 --- a/nixio/test/test_file.py +++ b/nixio/test/test_file.py @@ -167,7 +167,8 @@ def test_copy_on_file(self): assert self.file.blocks[0] == self.file.blocks[1] # ID stays the same assert self.file.blocks[0].name != self.file.blocks[1].name - def test_timestamp_autoupdate_definition(self): + def test_timestamp_autoupdate(self): + # Using Block to test Entity.definition blk = self.file.create_block("block", "timetest") blktime = blk.updated_at time.sleep(1) # wait for time to change @@ -180,21 +181,7 @@ def test_timestamp_autoupdate_definition(self): rblk.definition = "updated again" self.assertNotEqual(rblk.updated_at, blktime) - # disable timestamp autoupdating - self.file.auto_update_timestamps = False - blktime = blk.updated_at - time.sleep(1) # wait for time to change - blk.definition = "update" - self.assertEqual(blk.updated_at, blktime) - - rblk = self.file.blocks["block"] # read through container - rblktime = rblk.updated_at - time.sleep(1) # wait for time to change - rblk.definition = "time should change" - self.assertEqual(rblk.updated_at, rblktime) - - def test_timestamp_autoupdate_type(self): - blk = self.file.create_block("block", "timetest") + # Using Block to test Entity.type blktime = blk.updated_at time.sleep(1) # wait for time to change blk.type = "updated" @@ -206,8 +193,23 @@ def test_timestamp_autoupdate_type(self): rblk.type = "updated again" self.assertNotEqual(rblk.updated_at, blktime) + def test_timestamp_noautoupdate(self): + # Using Block to test Entity.definition + blk = self.file.create_block("block", "timetest") + # disable timestamp autoupdating self.file.auto_update_timestamps = False + blktime = blk.updated_at + time.sleep(1) # wait for time to change + blk.definition = "update" + self.assertEqual(blk.updated_at, blktime) + + rblk = self.file.blocks["block"] # read through container + rblktime = rblk.updated_at + time.sleep(1) # wait for time to change + rblk.definition = "time should change" + self.assertEqual(rblk.updated_at, rblktime) + blktime = blk.updated_at time.sleep(1) # wait for time to change blk.type = "update" From 1fbc6a9cd40f6e78b307d8d20198b743c1ae986c Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 18:08:40 +0200 Subject: [PATCH 12/18] Test timestamp autoupdate for DataArray properties --- nixio/data_array.py | 5 +- nixio/test/test_data_array.py | 112 ++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/nixio/data_array.py b/nixio/data_array.py index 2b0d85b8..22d1a9a9 100644 --- a/nixio/data_array.py +++ b/nixio/data_array.py @@ -188,7 +188,10 @@ def append_alias_range_dimension(self): "Current SI unit is {}".format(u), "DataArray.append_alias_range_dimension" ) - return RangeDimension.create_new_alias(self, 1) + aliasdim = RangeDimension.create_new_alias(self, 1) + if self.file.auto_update_timestamps: + self.force_updated_at() + return aliasdim def delete_dimensions(self): """ diff --git a/nixio/test/test_data_array.py b/nixio/test/test_data_array.py index 0cd41913..49fe0b54 100644 --- a/nixio/test/test_data_array.py +++ b/nixio/test/test_data_array.py @@ -7,6 +7,7 @@ # modification, are permitted under the terms of the BSD License. See # LICENSE file in the root of the Project. import os +import time from six import string_types import sys import unittest @@ -476,3 +477,114 @@ def test_dim_one_based(self): for idx, dim in dim_container_one_based: assert self.array.dimensions[idx-1].dimension_type ==\ dim.dimension_type + + def test_timestamp_autoupdate(self): + array = self.block.create_data_array("array.time", "signal", + nix.DataType.Double, (100, )) + # Append dimensions and check time + datime = array.updated_at + time.sleep(1) + array.append_set_dimension() + self.assertNotEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.append_sampled_dimension(sampling_interval=0.1) + self.assertNotEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.append_range_dimension(ticks=[0.1]) + self.assertNotEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + df = self.block.create_data_frame( + "df", "test.data_array.timestamp.data_frame", + col_dict={"idx": int} + ) + array.append_data_frame_dimension(data_frame=df) + self.assertNotEqual(datime, array.updated_at) + + array.delete_dimensions() + datime = array.updated_at + time.sleep(1) + array.append_alias_range_dimension() + self.assertNotEqual(datime, array.updated_at) + + # other properties + datime = array.updated_at + time.sleep(1) + array.polynom_coefficients = [1.1, 2.2] + self.assertNotEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.expansion_origin = -1 + self.assertNotEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.label = "lbl" + self.assertNotEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.unit = "Ms" + self.assertNotEqual(datime, array.updated_at) + + def test_timestamp_noautoupdate(self): + self.file.auto_update_timestamps = False + array = self.block.create_data_array("array.time", "signal", + nix.DataType.Double, (100, )) + # Append dimensions and check time + datime = array.updated_at + time.sleep(1) + array.append_set_dimension() + self.assertEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.append_sampled_dimension(sampling_interval=0.1) + self.assertEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.append_range_dimension(ticks=[0.1]) + self.assertEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + df = self.block.create_data_frame( + "df", "test.data_array.timestamp.data_frame", + col_dict={"idx": int} + ) + array.append_data_frame_dimension(data_frame=df) + self.assertEqual(datime, array.updated_at) + + array.delete_dimensions() + datime = array.updated_at + time.sleep(1) + array.append_alias_range_dimension() + self.assertEqual(datime, array.updated_at) + + # other properties + datime = array.updated_at + time.sleep(1) + array.polynom_coefficients = [1.1, 2.2] + self.assertEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.expansion_origin = -1 + self.assertEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.label = "lbl" + self.assertEqual(datime, array.updated_at) + + datime = array.updated_at + time.sleep(1) + array.unit = "Ms" + self.assertEqual(datime, array.updated_at) From 99bdae79fc4526a358117f391dad5367122f0d2f Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 18:33:41 +0200 Subject: [PATCH 13/18] Test timestamp autoupdate for DataFrame properties --- nixio/test/test_data_frame.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/nixio/test/test_data_frame.py b/nixio/test/test_data_frame.py index 2b2655f0..61445f7b 100644 --- a/nixio/test/test_data_frame.py +++ b/nixio/test/test_data_frame.py @@ -3,6 +3,7 @@ import nixio as nix from .tmp import TempDir import os +import time import numpy as np from six import string_types try: @@ -206,3 +207,21 @@ def test_creation_without_name(self): df = self.block.create_data_frame("without_name", "test", data=data) assert sorted(list(df.column_names)) == sorted(["name", "id", "val"]) assert sorted(list(df["name"])) == ["a", "b", "c"] + + def test_timestamp_autoupdate(self): + self.file.auto_update_timestamps = True + df = self.block.create_data_frame("df.time", "test.time", + col_dict={"idx": int}) + dftime = df.updated_at + time.sleep(1) + df.units = "ly" + self.assertNotEqual(dftime, df.updated_at) + + def test_timestamp_noautoupdate(self): + self.file.auto_update_timestamps = False + df = self.block.create_data_frame("df.time", "test.time", + col_dict={"idx": int}) + dftime = df.updated_at + time.sleep(1) + df.units = "ly" + self.assertEqual(dftime, df.updated_at) From d598d0a09885194aeee0562b098454f12a173669 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 18:33:41 +0200 Subject: [PATCH 14/18] Test timestamp autoupdate for Feature properties --- nixio/test/test_feature.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/nixio/test/test_feature.py b/nixio/test/test_feature.py index 0b83eea2..e2d91f1e 100644 --- a/nixio/test/test_feature.py +++ b/nixio/test/test_feature.py @@ -7,6 +7,7 @@ # modification, are permitted under the terms of the BSD License. See # LICENSE file in the root of the Project. import os +import time import unittest import nixio as nix from .tmp import TempDir @@ -84,3 +85,36 @@ def test_feature_on_group(self): def test_create_diff_link_type_style(self): self.stimuli_tag.create_feature(self.movie1, nix.LinkType.Tagged) + + def test_timestamp_autoupdate(self): + array = self.block.create_data_array("array.time", "signal", + nix.DataType.Double, (100, )) + feature = self.stimuli_tag.create_feature(array, nix.LinkType.Tagged) + + ftime = feature.updated_at + time.sleep(1) + feature.data = self.block.create_data_array("alt.array", "signal", + nix.DataType.Int8, (1,)) + self.assertNotEqual(ftime, feature.updated_at) + + ftime = feature.updated_at + time.sleep(1) + feature.link_type = nix.LinkType.Untagged + self.assertNotEqual(ftime, feature.updated_at) + + def test_timestamp_noautoupdate(self): + self.file.auto_update_timestamps = False + array = self.block.create_data_array("array.time", "signal", + nix.DataType.Double, (100, )) + feature = self.stimuli_tag.create_feature(array, nix.LinkType.Tagged) + + ftime = feature.updated_at + time.sleep(1) + feature.data = self.block.create_data_array("alt.array", "signal", + nix.DataType.Int8, (1,)) + self.assertEqual(ftime, feature.updated_at) + + ftime = feature.updated_at + time.sleep(1) + feature.link_type = nix.LinkType.Untagged + self.assertEqual(ftime, feature.updated_at) From 69f4219d9cf0d2bb6c47783ab669a992b4d26db9 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 18:33:41 +0200 Subject: [PATCH 15/18] Test timestamp autoupdate for MultiTag positions and extents --- nixio/test/test_multi_tag.py | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/nixio/test/test_multi_tag.py b/nixio/test/test_multi_tag.py index c64956f8..628e8894 100644 --- a/nixio/test/test_multi_tag.py +++ b/nixio/test/test_multi_tag.py @@ -7,6 +7,7 @@ # modification, are permitted under the terms of the BSD License. See # LICENSE file in the root of the Project. import os +import time import unittest import numpy as np import nixio as nix @@ -562,3 +563,42 @@ def out_of_bounds(): self.feature_tag.feature_data(2, 1) self.assertRaises(IndexError, out_of_bounds) + + def test_timestamp_autoupdate(self): + pos = self.block.create_data_array("positions.time", "test.time", + nix.DataType.Int16, (0, 0)) + mtag = self.block.create_multi_tag("mtag.time", "test.time", pos) + + mtagtime = mtag.updated_at + time.sleep(1) # wait for time to change + mtag.positions = self.block.create_data_array("pos2.time", + "test.time", + nix.DataType.Int8, (0,)) + self.assertNotEqual(mtag.updated_at, mtagtime) + + mtagtime = mtag.updated_at + time.sleep(1) # wait for time to change + mtag.extents = self.block.create_data_array("extents.time", + "test.time", + nix.DataType.Int8, (0,)) + self.assertNotEqual(mtag.updated_at, mtagtime) + + def test_timestamp_noautoupdate(self): + self.file.auto_update_timestamps = False + pos = self.block.create_data_array("positions.time", "test.time", + nix.DataType.Int16, (0, 0)) + mtag = self.block.create_multi_tag("mtag.time", "test.time", pos) + + mtagtime = mtag.updated_at + time.sleep(1) # wait for time to change + mtag.positions = self.block.create_data_array("pos2.time", + "test.time", + nix.DataType.Int8, (0,)) + self.assertEqual(mtag.updated_at, mtagtime) + + mtagtime = mtag.updated_at + time.sleep(1) # wait for time to change + mtag.extents = self.block.create_data_array("extents.time", + "test.time", + nix.DataType.Int8, (0,)) + self.assertEqual(mtag.updated_at, mtagtime) From 2798d748952f561e87fb5e633868d4daa633ab15 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 18:33:41 +0200 Subject: [PATCH 16/18] Test timestamp autoupdate for Section properties --- nixio/test/test_section.py | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/nixio/test/test_section.py b/nixio/test/test_section.py index ba77aae1..20571432 100644 --- a/nixio/test/test_section.py +++ b/nixio/test/test_section.py @@ -7,6 +7,7 @@ # modification, are permitted under the terms of the BSD License. See # LICENSE file in the root of the Project. import os +import time import unittest import nixio as nix from .tmp import TempDir @@ -317,3 +318,42 @@ def test_copy_on_sections(self): assert sec2 == tarsec.sections[1] assert sec1.sections[0] == tarsec.sections[1] tarfile.close() + + def test_timestamp_autoupdate(self): + section = self.file.create_section("section.time", "test.time") + + sectime = section.updated_at + time.sleep(1) # wait for time to change + section.reference = "whatever" + self.assertNotEqual(sectime, section.updated_at) + + sectime = section.updated_at + linksec = self.file.create_section("link.section.time", "test.time") + time.sleep(1) # wait for time to change + section.link = linksec + self.assertNotEqual(sectime, section.updated_at) + + sectime = section.updated_at + time.sleep(1) # wait for time to change + section.repository = "repo" + self.assertNotEqual(sectime, section.updated_at) + + def test_timestamp_noautoupdate(self): + self.file.auto_update_timestamps = False + section = self.file.create_section("section.time", "test.time") + + sectime = section.updated_at + time.sleep(1) # wait for time to change + section.reference = "whatever" + self.assertEqual(sectime, section.updated_at) + + sectime = section.updated_at + time.sleep(1) # wait for time to change + linksec = self.file.create_section("link.section.time", "test.time") + section.link = linksec + self.assertEqual(sectime, section.updated_at) + + sectime = section.updated_at + time.sleep(1) # wait for time to change + section.repository = "repo" + self.assertEqual(sectime, section.updated_at) From 2fc194ee644fd96f0cc3b15a2085963dcece34e8 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Thu, 30 Apr 2020 18:33:41 +0200 Subject: [PATCH 17/18] Test timestamp autoupdate for Tag properties --- nixio/test/test_tag.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/nixio/test/test_tag.py b/nixio/test/test_tag.py index c48d9d94..b10f4eb4 100644 --- a/nixio/test/test_tag.py +++ b/nixio/test/test_tag.py @@ -7,6 +7,7 @@ # modification, are permitted under the terms of the BSD License. See # LICENSE file in the root of the Project. import os +import time import unittest import numpy as np import nixio as nix @@ -297,3 +298,40 @@ def test_tag_feature_data(self): assert (data1.size == 1) assert (data2.size == 3) + + def test_timestamp_autoupdate(self): + tag = self.block.create_tag("tag.time", "test.time", [-1]) + + tagtime = tag.updated_at + time.sleep(1) # wait for time to change + tag.position = [-100] + self.assertNotEqual(tag.updated_at, tagtime) + + tagtime = tag.updated_at + time.sleep(1) # wait for time to change + tag.extent = [30] + self.assertNotEqual(tag.updated_at, tagtime) + + tagtime = tag.updated_at + time.sleep(1) # wait for time to change + tag.units = "Mm" + self.assertNotEqual(tag.updated_at, tagtime) + + def test_timestamp_noautoupdate(self): + self.file.auto_update_timestamps = False + tag = self.block.create_tag("tag.time", "test.time", [-1]) + + tagtime = tag.updated_at + time.sleep(1) # wait for time to change + tag.position = [-100] + self.assertEqual(tag.updated_at, tagtime) + + tagtime = tag.updated_at + time.sleep(1) # wait for time to change + tag.extent = [30] + self.assertEqual(tag.updated_at, tagtime) + + tagtime = tag.updated_at + time.sleep(1) # wait for time to change + tag.units = "Mm" + self.assertEqual(tag.updated_at, tagtime) From f76a283d5cf63c15e396463366cd78115710e7cb Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Mon, 4 May 2020 22:11:03 +0200 Subject: [PATCH 18/18] Cleanup: Use file references whenever appropriate - Removed hacky parent traversal for getting root objects like the top level metadata container. - Removed H5Group utility functions that are now obsolete. - Containers that need to do a search from the root object to delete objects now use the file reference. --- nixio/container.py | 12 +++------- nixio/hdf5/h5group.py | 53 ------------------------------------------- nixio/section.py | 15 +----------- 3 files changed, 4 insertions(+), 76 deletions(-) diff --git a/nixio/container.py b/nixio/container.py index ac4febb9..0804a81a 100644 --- a/nixio/container.py +++ b/nixio/container.py @@ -57,10 +57,7 @@ def __delitem__(self, item): self._itemclass.__name__) ) - root = self._backend.h5root - if not root: - root = self._parent._h5group - root.delete_all([item.id]) + self._file._h5group.delete_all([item.id]) def __iter__(self): for group in self._backend: @@ -122,8 +119,7 @@ def __delitem__(self, item): # the root block secids = [s.id for s in item.find_sections()] - root = self._backend.file - root.delete_all(secids) + self._file._h5group.delete_all(secids) class SourceContainer(Container): @@ -146,9 +142,7 @@ def __delitem__(self, item): # the root block srcids = [s.id for s in item.find_sources()] srcids.append(item.id) - - root = self._backend.h5root - root.delete_all(srcids) + self._file._h5group.delete_all(srcids) class LinkContainer(Container): diff --git a/nixio/hdf5/h5group.py b/nixio/hdf5/h5group.py index b7d6c703..28195042 100644 --- a/nixio/hdf5/h5group.py +++ b/nixio/hdf5/h5group.py @@ -12,11 +12,8 @@ from .h5dataset import H5DataSet from ..datatype import DataType -from ..block import Block -from ..section import Section from .. import util -from ..exceptions import InvalidEntity class H5Group(object): @@ -297,56 +294,6 @@ def change_id(_, igrp): g.visititems(change_id) return g - @property - def file(self): - """ - An H5Group object which represents the file root. - - :return: H5Group at '/' - """ - return H5Group(self.group.file, "/", create=False) - - @property - def h5root(self): - """ - Returns the H5Group of the Block or top-level Section which contains - this object. Returns None if requested on the file root '/' or the - /data or /metadata groups. - - :return: Top level object containing this group (H5Group) - """ - pathparts = self.group.name.split("/") - if len(pathparts) == 3: - return self - if self.group.name == "/": - return None - if len(pathparts) == 2: - return None - - return self.parent.h5root - - @property - def root(self): - """ - Returns the Block or top-level Section which contains this object. - Returns None if requested on the file root '/' or the /data or - /metadata groups. - - :return: Top level object containing this group (Block or Section) - """ - h5root = self.h5root - if h5root is None: - return None - topgroup = self.group.name.split("/")[1] - if topgroup == "data": - cls = Block - return cls(h5root.parent, h5root) - elif topgroup == "metadata": - cls = Section - return cls(h5root.parent, h5root) - else: - raise InvalidEntity - @property def parent(self): return self.create_from_h5obj(self._parent) diff --git a/nixio/section.py b/nixio/section.py index 85d1cdad..d3a14ab5 100644 --- a/nixio/section.py +++ b/nixio/section.py @@ -293,9 +293,8 @@ def parent(self): """ if self._sec_parent is not None: return self._sec_parent - rootmd = self._h5group.file.open_group("metadata") # BFS - sections = [Section(self.file, None, sg) for sg in rootmd] + sections = list(self.file.sections) if self in sections: # Top-level section return None @@ -309,18 +308,6 @@ def parent(self): return None - @property - def file(self): - """ - Root file object. - - :type: File - """ - par = self._parent - while isinstance(par, Entity): - par = par._parent - return par - @property def referring_objects(self): objs = []