local_backend.py 26.7 KB
Newer Older
1
from __future__ import print_function
2
from future import standard_library
3
from enum import Enum
4
from operator import itemgetter
5 6
from builtins import range
from builtins import object
7
from builtins import str
8 9 10
import numpy as np
from collections import defaultdict

11 12
standard_library.install_aliases()

13

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
class ParserEvent(Enum):
    """Enumerations for the different parser events when traversing the
    results.
    """
    open_section = 1
    close_section = 2
    add_value = 3
    add_array_value = 4


class ParserKeyError(Exception):
    """A custom exception for all cases where a certain metainfo can not be
    found in the results.
    """
    pass


31
class DummyFile(object):
32 33 34 35 36 37
    """Mimics a file object by defininf a write interface, but actually does
    not write anything. This allows one to used this object in functions which
    require a file object as input, but you dont want to actually write
    anything.
    """
    def write(self, input):
38 39
        pass

40

41
class LocalBackend(object):
Lauri Himanen's avatar
Lauri Himanen committed
42 43
    """A backend that outputs results into a regular python dictionary. This is
    useful if you wan't to run the parser with python only.
44
    """
45 46 47 48 49 50 51 52 53 54 55
    def __init__(self, metaInfoEnv, debug=True, store=True):
        """
        Args:
            metaInfoEnv: The description of the metainfo environment.
            debug: Boolean indicating whether some debugging should be done
                (check that correct types and shapes are pushed into backend)
            store: Boolean indicating whether the parsed results should be
                stored in memory. Useful to skip the storing if you just want
                to quickly check that everything is runnning fine and don't
                want to waste RAM in doing so.
        """
56
        self.__metaInfoEnv = metaInfoEnv
57
        self.fileOut = DummyFile()
58 59 60 61 62 63
        self.__gIndex = -1
        self.__openSections = set()
        self.__lastIndex = {}
        self.stats = {}
        self.dataManagers = {}
        self.sectionManagers = {}
64
        self.results = Results(metaInfoEnv, self.dataManagers, self.sectionManagers)
65
        self.debug = debug
66
        self.store = store
67 68 69 70 71 72 73

        for ikNames, ik in metaInfoEnv.infoKinds.items():
            if ik.kindStr == "type_section":
                parentS = list(metaInfoEnv.firstAncestorsByType(ik.name).get("type_section", [[]])[0])
                parentS.sort()
                self.sectionManagers[ik.name] = SectionManager(
                    metaInfo=ik,
74
                    parentSectionNames=parentS, debug=self.debug)
75 76 77 78 79 80 81 82 83 84 85 86 87 88
        for ikNames, ik in metaInfoEnv.infoKinds.items():
            if ik.kindStr == "type_document_content" or ik.kindStr == "type_dimension":
                superSectionNames = metaInfoEnv.firstAncestorsByType(ik.name).get("type_section", [[]])[0]
                if not superSectionNames:
                    raise Exception("MetaInfo of conrete value %s is not in any superSection" % ik.name)
                elif len(superSectionNames) > 1:
                    raise Exception("MetaInfo of concrete value %s has multiple superSections (%s)" %
                                    (ik.name, superSectionNames))
                self.dataManagers[ik.name] = DataManager(ik, self.sectionManagers[superSectionNames[0]])

    def openSection(self, metaName):
        """opens a new section and returns its new unique gIndex"""
        manager = self.sectionManagers[metaName]
        newIndex = manager.openSection(self)
89
        self.__openSections.add((metaName, newIndex))
90 91
        return newIndex

92 93 94 95 96 97
    def openNonOverlappingSection(self, metaName):
        """opens a new non overlapping section"""
        if any(x[0] == metaName for x in self.__openSections):
            raise Exception("Section %s is not supposed to overlap" % metaName)
        return self.openSection(metaName)

98 99 100
    def closeSection(self, metaName, gIndex):
        manager = self.sectionManagers[metaName]
        manager.closeSection(self, gIndex)
101 102 103 104 105 106 107 108 109 110 111 112
        if (metaName, gIndex) in self.__openSections:
            self.__openSections.remove((metaName, gIndex))

    def closeNonOverlappingSection(self, metaName):
        """closes a non overlapping section"""
        openGIndexes = [x for x in self.__openSections if x[0] == metaName]
        if len(openGIndexes) != 1:
            if not openGIndexes:
                raise Exception("Call to closeNonOverlapping(%s) with no open section" % metaName)
            else:
                raise Exception("Section %s was not supposed to overlap, found %s open when closing" % (metaName, openGIndexes))
        self.closeSection(metaName, openGIndexes[0][1])
113 114

    def addValue(self, metaName, value, gIndex=-1):
115 116
        """override will cause the backend to rewrite the
        first value of MetaName in gindex in the backend"""
117
        dataManager = self.dataManagers[metaName]
118 119 120 121 122

        if self.debug:

            # Check that the value is actually of scalar type
            value_type = type(value)
123
            if value_type not in [float, int, bool, type(b""), type(u""), str, np.float64]:
124
                raise TypeError("Could not use function 'addValue' to push value '{}' with type '{}' for metainfo '{}'.".format(value, value_type, metaName))
125 126 127 128 129 130 131 132 133

            # Check that the metainfo should be scalar
            metainfo_shape = dataManager.metaInfo.shape
            if metainfo_shape is not None:
                if len(metainfo_shape) != 0:
                    raise TypeError("The metainfo '{}' does not support scalar values. Check the shape attribute of the metainfo and use the function addArrayValues() instead if the value should be an array.".format(metaName))

            # Check the type
            dtype_str = dataManager.metaInfo.dtypeStr
134 135
            if dtype_str is None:
                raise TypeError("The metainfo '{}' does not define a dtypeStr".format(metaName))
136
            single_types = self.single_value_type_for_metainfo_type(dtype_str)
137
            actual_numpy_type = type(value)
138 139
            if actual_numpy_type not in single_types:
                raise TypeError("The given value for metainfo '{}' is of incorrrect type. The type was '{}' when it should be one of '{}'".format(metaName, actual_numpy_type, single_types))
140

141 142
        dataManager.superSectionManager.addValue(dataManager.metaInfo, value, gIndex)

Lauri Himanen's avatar
Lauri Himanen committed
143 144 145
    def addRealValue(self, metaName, value, gIndex=-1):
        self.addValue(metaName, value, gIndex)

146
    def addArrayValues(self, metaName, values, gIndex=-1, **kwargs):
147

148
        dataManager = self.dataManagers[metaName]
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

        if self.debug:

            # Check that the value is actually a numpy array
            if not isinstance(values, np.ndarray):
                raise TypeError("The value provided for '{}' is not a valid numpy array. Please only push numpy arrays with the backend function addArrayValues().".format(metaName))

            # Check that the metainfo should be an array
            metainfo_shape = dataManager.metaInfo.shape
            if len(metainfo_shape) == 0:
                raise TypeError("The metainfo '{}' does not support arrays. Check the shape attribute of the metainfo and use the function addValue() instead if the value should be scalar.".format(metaName))

            # Check the number of dimensions
            array_shape = values.shape
            len_meta_dim = len(metainfo_shape)
            len_array_dim = len(array_shape)
            if len_array_dim != len_meta_dim:
                raise TypeError("Incompatible shape provided for metainfo '{}'. The shape was '{}' whereas it should be '{}'. Check the shape attribute of the metainfo".format(metaName, array_shape, metainfo_shape))

            # If the shapes are given as integers in the metainfo we can also
            # check the number of values in each dimension
            try:
                [int(x) for x in metainfo_shape]
            except Exception:
                pass
            else:
                for index in range(len_meta_dim):
                    array_dim = array_shape[index]
                    metainfo_dim = metainfo_shape[index]
                    if array_dim != metainfo_dim:
                        raise TypeError("Incompatible shape provided for metainfo '{}'. The shape was '{}' whereas it should be '{}'. Check the shape attribute of the metainfo".format(metaName, array_shape, metainfo_shape))

            # Check the type
            dtype_str = dataManager.metaInfo.dtypeStr
183
            array_types = self.array_type_for_metainfo_type(dtype_str)
184
            actual_numpy_type = values.dtype.type
185
            if actual_numpy_type not in array_types:
186
                raise TypeError("The given array for metainfo '{}' has incorrect type of values in it. The values given are '{}', whereas the datatype given in metainfo is '{}'".format(metaName, actual_numpy_type, dtype_str))
187

188
        dataManager.superSectionManager.addArrayValues(dataManager.metaInfo, values, gIndex=gIndex, **kwargs)
189

190 191 192 193 194 195 196 197
    def setSectionInfo(self, metaName, gIndex, references):
        """
        Sets info values of an open section references should be a dictionary with the
        gIndexes of the root sections this section refers to.
        """
        # TODO needs to be implemented !!!
        pass

198
    def array_type_for_metainfo_type(self, dtypeStr):
199 200 201 202
        """Returns a list of numpy types correspoding to the dtypeStr of a
        metainfo.
        """
        if dtypeStr == "f":
203
            return [np.float_, np.float64, np.float32, np.int_, np.int64, np.int32, np.int16, np.int8]
204
        elif dtypeStr == "i":
205
            return [np.int_, np.int64, np.int32, np.int16, np.int8]
206 207 208 209 210
        elif dtypeStr == "b":
            return [np.bool_]
        elif dtypeStr == "C":
            return [np.string_, np.unicode_]
        elif dtypeStr == "r":
211
            return [np.int_, np.int64, np.int32, np.int16, np.int8]
212 213 214
        else:
            raise TypeError("Could not determine the numpy type for metainfo type '{}'".format(dtypeStr))

215 216
    def single_value_type_for_metainfo_type(self, dtypeStr):
        """Returns a list of numpy types corresponding to the dtypeStr of a
217 218 219
        metainfo.
        """
        if dtypeStr == "f":
220
            return [float, int, np.float_, np.float64, np.float32]
221
        elif dtypeStr == "i":
222
            return [int, np.int_, np.int64, np.int32, np.int16, np.int8]
223 224
        elif dtypeStr == "i64":
            return [int, np.int64]
225
        elif dtypeStr == "b":
226
            return [bool, np.bool_]
227
        elif dtypeStr == "C":
228
            return [type(b""), type(u""), str, np.string_, np.unicode_]
229
        elif dtypeStr == "r":
230
            return [int, np.int_, np.int64, np.int32, np.int16, np.int8]
231 232 233
        else:
            raise TypeError("Could not determine the type for metainfo type '{}'".format(dtypeStr))

234 235 236 237 238 239 240 241 242
    def setArrayValues(self, metaName, values, offset=None, gIndex=-1, unit=None):
        """Adds values to the last array added, array must be a numpy array
        """
        dataManager = self.dataManagers[metaName]
        dataManager.superSectionManager.setArrayValues(dataManager.metaInfo, values, offset, gIndex)

    def metaInfoEnv(self):
        return self.__metaInfoEnv

243
    def startedParsingSession(self, mainFileUri, parserInfo, parserStatus=None, parserErrors=None):
244 245
        pass

246
    def finishedParsingSession(self, parserStatus, parserErrors, mainFileUri=None, parserInfo=None,
247
                               parsingStats=None):
248 249
        """Called when the parsing finishes.
        """
250
        pass
251

252
    def addMatchTelemetry(self, match_telemetry, gIndex=-1):
253 254 255
        """ should be called for outputting match telemetry data:
        input data, together with capture info """
        pass
256

257 258 259 260 261 262
    def pwarn(self, msg):
        """Used to catch parser warnings. Currently disabled in the local
        backend.
        """
        pass

263

264
class Results(object):
265
    """A wrapper object for the collection of results gathered by a parser.
266
    """
267 268 269
    def __init__(self, metaInfoEnv, datamanagers, sectionmanagers):
        self._datamanagers = datamanagers
        self._sectionmanagers = sectionmanagers
270
        self._shortnames = defaultdict(list)
271 272 273
        self._metaInfoEnv = metaInfoEnv

    def __getitem__(self, metaname):
274 275 276 277
        """Return the data or section corrresponding the the given metainfo
        name. If given a section name, this function will return a list of
        Section objects. If given a name of a concrete value, this function
        will return all instances of that value as a list.
278 279 280 281 282

        Args:
            metaname: The unique name of the metainfo to get.

        Raises:
283 284 285
            LookupError: if the metaname is not defined in the metainfo
                environment or the parser has not output any value for it.
            ParserKeyError: if the parser did not output the queried metainfo.
286 287 288
        """
        self.test_validity(metaname)

289 290 291 292 293 294 295 296 297 298 299
        # See if in sections
        sectionmanager = self._sectionmanagers.get(metaname)
        if sectionmanager is not None:
            return sectionmanager.openSections

        # See if in data
        datamanager = self._datamanagers.get(metaname)
        if datamanager is not None:
            sectionmanager = datamanager.superSectionManager
            open_sections = sectionmanager.openSections
            result = []
300
            for section in open_sections:
301 302 303 304 305
                try:
                    data = section[metaname]
                except KeyError:
                    pass
                else:
306
                    result.append(data)
307 308
            if len(result) == 1:
                return result[0]
Lauri Himanen's avatar
Lauri Himanen committed
309
            elif len(result) == 0:
310
                raise KeyError("Could not find a parsing result for '{}'. The parser did not output this value.".format(metaname))
311 312
            else:
                return np.array(result)
313

314
        raise LookupError("The metainfo definition doesn't seem to contain '{}'. Check for typos of update you metainfo repository.".format(metaname))
315 316 317 318 319 320 321 322

    def test_validity(self, metaname):
        """Tests if the given metaname is present in the metainfo environment.
        """
        metainfo = self._metaInfoEnv.infoKinds.get(metaname)
        if metainfo is None:
            raise LookupError("The metainfo name '{}' does not exist in the metainfo environment. Check for typos or try updating the metainfo git package.".format(metaname))

323
    def traverse(self, root_section='section_run'):
324 325 326 327 328
        """A generator function for traversing the data in the parser results.

        This generator returns a tuple of three item: the metainfo name, the
        event type, and the event value.
        """
329 330
        root = self._sectionmanagers[root_section]
        for x in self.traverse_recursive(root_section, root.openSections):
331 332
            yield x

333
    def traverse_recursive(self, name, open_sections):
334 335
        """A generator function for traversing the data in the parser results.
        """
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
        for i_section, section in enumerate(open_sections):
            yield (name, ParserEvent.open_section, i_section)

            key_to_type_map = {}
            simple_keys = list(section.simple_values.keys())
            for key in simple_keys:
                key_to_type_map[key] = "simple"
            array_keys = list(section.array_values.keys())
            for key in array_keys:
                key_to_type_map[key] = "array"
            subsection_keys = list(section.subsections.keys())
            for key in subsection_keys:
                key_to_type_map[key] = "subsection"

            keys = []
            keys.extend(simple_keys)
            keys.extend(array_keys)
            keys.extend(subsection_keys)
            keys = sorted(keys)

            for key in keys:
                key_type = key_to_type_map[key]
                if key_type == "simple":
                    simple_value = section.simple_values[key]
                    yield (key, ParserEvent.add_value, simple_value)
                elif key_type == "array":
                    array_value = section.array_values[key]
                    yield (key, ParserEvent.add_array_value, array_value)
                elif key_type == "subsection":
                    subsection_value = section.subsections[key]
                    for x in self.traverse_recursive(key, subsection_value):
                        yield x
                else:
                    raise KeyError("Trying to access unknown data type.")

            yield (name, ParserEvent.close_section, i_section)

            # for value_name, value_value in section.simple_values.items():
                # yield (value_name, ParserEvent.add_value, value_value)
            # for array_name, array_value in section.array_values.items():
                # yield (array_name, ParserEvent.add_array_value, array_value)
            # for x in self.traverse_recursive(section.subsections):
                # yield x
379

380 381 382 383
    def print_summary(self):
        """Return a string representing the data contained in the results. This
        is a summary that can be used for debugging.
        """
384 385 386 387 388 389 390 391 392
        metas = {}
        roots = {}

        for meta in self._metaInfoEnv.infoKinds.values():
            metaobj = {}
            metaobj["name"] = meta.name
            metaobj["children"] = []
            metaobj["parents"] = meta.superNames
            metaobj["kindStr"] = meta.kindStr
393 394 395 396 397 398 399 400
            mapping = {
                "type_section": 0,
                "type_abstract_document_content": 1,
                "type_document_content": 2,
                "type_dimension": 3,
                "type_meta": 4,
            }
            metaobj["kind_number"] = mapping.get(meta.kindStr)
401 402 403 404 405 406 407 408 409 410 411
            metas[meta.name] = metaobj

        for meta in metas.values():
            parentNames = meta["parents"]
            if len(parentNames) == 0:
                roots[meta["name"]] = meta
            else:
                for parentName in parentNames:
                    parent = metas[parentName]
                    parent["children"].append(meta)

412 413 414
        # Sort the children according to type
        for meta in metas.values():
            meta["children"].sort(key=itemgetter('kind_number', 'name'))
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434

        section_run = roots["section_run"]
        self.print_metainfo(section_run)

    def print_metainfo(self, meta, level=0):
        """Recursive printing function for the metainfos. To print the whole
        tree, call this function on the root section.
        """
        name = meta["name"]
        metatype = meta["kindStr"]
        if metatype != "type_abstract_document_content":
            try:
                result = self[name]
            except LookupError:
                return
            if isinstance(result, dict):
                if len(result.keys()) == 0:
                    return

            if metatype == "type_section":
435
                print(level *"  " + name + ":")
436
            elif metatype == "type_document_content":
437
                print(level *"  " + name)
438
            elif metatype == "type_dimension":
439
                print(level *"  " + name)
440 441 442 443
            level += 1

        for child in meta["children"]:
            self.print_metainfo(child, level)
444

445 446 447 448

class Section(object):
    """Represents an open section.
    """
449
    def __init__(self, gIndex, references, parents, name, backend, debug=True):
450 451
        self.gIndex = gIndex
        self.references = references
452 453 454
        self.simple_values = {}
        self.array_values = {}
        self.subsections = {}
455 456 457
        self.parents = parents
        self.name = name
        self.backend = backend
458
        self.debug = debug
459 460 461
        self.has_results = False

    def __getitem__(self, metaName):
462 463
        """Returns the cached values corresponding to metaName. You can search
        values and subsections.
464
        """
465
        res = self.simple_values.get(metaName, None)
466
        if res is not None:
467
            return res
468
        res = self.array_values.get(metaName, None)
469
        if res is not None:
470
            return res
471
        res = self.subsections.get(metaName, None)
472 473 474
        if res is not None:
            return res

475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
        raise KeyError(
            "The metainfo '{}' could not be found in the section '{}' with gIndex '{}'"
            .format(metaName, self.name, self.gIndex))

    def get(self, key, default=None):
        try:
            value = self[key]
        except KeyError:
            return default
        else:
            return value

    def __len__(self):
        n_simple_values = len(self.simple_values)
        n_array_values = len(self.array_values)
        n_subsections = len(self.subsections)
        return n_simple_values + n_array_values + n_subsections

    def keys(self):
        keys_simple_values = self.simple_values.keys()
        keys_array_values = self.array_values.keys()
        keys_subsections = self.subsections.keys()

        for key in keys_simple_values:
            yield key
        for key in keys_array_values:
            yield key
        for key in keys_subsections:
            yield key

    def values(self):
        values_simple_values = self.simple_values.values()
        values_array_values = self.array_values.values()
        values_subsections = self.subsections.values()

        for value in values_simple_values:
            yield value
        for value in values_array_values:
            yield value
        for value in values_subsections:
            yield value

    def items(self):
        items_simple_values = self.simple_values.items()
        items_array_values = self.array_values.items()
        items_subsections = self.subsections.items()

        for item in items_simple_values:
            yield item
        for item in items_array_values:
            yield item
        for item in items_subsections:
            yield item

    def __contains__(self, key):
        keys = self.keys()
        return key in keys
532 533

    def addValue(self, metaInfo, value):
534
        if self.backend.store:  # Check if backend set to store values.
535
            if self.debug:
536
                vals = self.simple_values.get(metaInfo.name, None)
537
                if vals is None:
538
                    self.simple_values[metaInfo.name] = value
539 540
                else:
                    raise Exception("Trying to add values multiple times for metaname {} in section {}. ".format(metaInfo.name, self.name))
541
            else:
542
                self.simple_values[metaInfo.name] = value
543 544

    def setArrayValues(self, metaInfo, values, offset=None):
545
        if self.backend.store:
546
            vals = self.array_values.get(metaInfo.name, None)
547 548
            if vals is None:
                raise Exception("setArrayValues(%s,...) called before adding a value" % metaInfo.name)
549
            else:
550
                if offset:
551
                    idxs = [slice(offset[i], offset[i] + values.shape[i]) for i in range(len(offset))]
552
                else:
553
                    idxs = [slice(0, x) for x in values.shape]
554
                vals[len(vals) - 1][idxs] = values
555

556
    def addArrayValues(self, metaInfo, values, override: bool = False):
557
        if self.backend.store:
558
            if self.debug and not override:
559
                vals = self.array_values.get(metaInfo.name, None)
560
                if vals is None:
561
                    self.array_values[metaInfo.name] = values
562 563
                else:
                    raise Exception("Trying to add values multiple times for metaname {} in section {}. ".format(metaInfo.name, self.name))
564
            else:
565
                self.array_values[metaInfo.name] = values
566 567

    def addSubsection(self, metaInfo, section):
568
        vals = self.subsections.get(metaInfo.name, None)
569
        if vals is None:
570
            self.subsections[metaInfo.name] = [section]
571 572 573 574 575
        else:
            vals.append(section)


class SectionManager(object):
576 577
    """Manages the sections for the given metainfo.
    """
578
    def __init__(self, metaInfo, parentSectionNames, lastSectionGIndex=-1, debug=True):
579 580 581
        self.metaInfo = metaInfo
        self.parentSectionNames = parentSectionNames
        self.lastSectionGIndex = lastSectionGIndex
582
        self.debug = debug
583
        self.openSections = []
584 585 586 587 588 589 590 591 592 593

    def openSection(self, backend):
        newGIndex = self.lastSectionGIndex + 1
        self.openSectionWithGIndex(backend, newGIndex)
        return newGIndex

    def openSectionWithGIndex(self, backend, gIndex):
        self.lastSectionGIndex = gIndex
        references = []
        parents = []
594
        parent_found = False
595
        for parentName in self.parentSectionNames:
596
            pSect = backend.sectionManagers.get(parentName)
597 598 599
            try:
                parentSection = pSect.openSections[pSect.lastSectionGIndex]
            except KeyError:
600 601 602 603
                pass
            else:
                parent_found = True
                parents.append(parentSection)
604 605 606 607
            if pSect:
                references.append(pSect.lastSectionGIndex)
            else:
                references.append(-1)
608 609 610

        # If the section is supposed to have parents, and none were actually
        # open, raise an error
611 612 613
        if not parent_found and len(self.parentSectionNames) != 0:
            raise LookupError("Could not open section '{}' because none of it's parent sections '{}' could not be found".format(self.metaInfo.name, self.parentSectionNames))

614
        new_section = Section(gIndex, references, parents, self.metaInfo.name, backend, debug=self.debug)
615 616 617
        self.openSections.append(new_section)
        if parent_found:
            parents[0].addSubsection(self.metaInfo, new_section)
618 619

    def closeSection(self, backend, gIndex):
620
        pass
621 622 623 624 625 626 627 628

    def addValue(self, valueMetaInfo, value, gIndex):
        if (gIndex == -1):
            gI = self.lastSectionGIndex
        else:
            gI = gIndex
        try:
            self.openSections[gI].addValue(valueMetaInfo, value)
629
        except (KeyError, IndexError):
630 631 632 633 634 635 636 637 638
            raise Exception("Cannot add value for metadata %s to section %d (%d) of %s, as it is not open" % (valueMetaInfo.name, gI, gIndex, self.metaInfo.name))

    def setArrayValues(self, valueMetaInfo, value, offset=None, gIndex=-1):
        if gIndex == -1:
            gI = self.lastSectionGIndex
        else:
            gI = gIndex
        try:
            self.openSections[gI].setArrayValues(valueMetaInfo, value, offset)
639
        except (KeyError, IndexError):
640 641
            raise Exception("Cannot set array values for metadata %s to section %d (%d) of %s, as it is not open" % (valueMetaInfo.name, gI, gIndex, self.metaInfo.name))

642
    def addArrayValues(self, valueMetaInfo, value, gIndex=-1, **kwargs):
643 644 645 646 647
        if gIndex == -1:
            gI = self.lastSectionGIndex
        else:
            gI = gIndex
        try:
648
            self.openSections[gI].addArrayValues(valueMetaInfo, value, **kwargs)
649
        except (KeyError, IndexError):
650 651 652 653
            raise Exception("Cannot add array values for metadata %s to section %d (%d) of %s, as it is not open" % (valueMetaInfo.name, gI, gIndex, self.metaInfo.name))


class DataManager(object):
654 655
    """Stores the parent (SectionManager) for the given metainfo.
    """
656 657 658
    def __init__(self, metaInfo, superSectionManager):
        self.metaInfo = metaInfo
        self.superSectionManager = superSectionManager