0
# Build System
1
2
HDMF's build system converts container objects to storage representations and manages type mappings between specifications and Python classes. It provides the bridge between HDMF's object-oriented container system and the storage backend formats, enabling serialization and deserialization with validation.
3
4
## Capabilities
5
6
### Builder Classes
7
8
Core builder classes for creating storage representations of different data elements.
9
10
```python { .api }
11
class Builder:
12
"""
13
Abstract base class for all builders.
14
15
Builders create storage representations that can be written to
16
different backends (HDF5, Zarr, etc.) while maintaining metadata
17
and structural information.
18
"""
19
20
def __init__(self, name: str, **kwargs):
21
"""
22
Initialize builder.
23
24
Args:
25
name: Name of the data element
26
**kwargs: Additional builder properties:
27
- attributes: Dictionary of attributes
28
- object_id: Unique identifier
29
- parent: Parent builder
30
"""
31
32
@property
33
def name(self) -> str:
34
"""Name of the data element."""
35
36
@property
37
def attributes(self) -> dict:
38
"""Dictionary of attributes."""
39
40
def set_attribute(self, name: str, value):
41
"""
42
Set attribute value.
43
44
Args:
45
name: Attribute name
46
value: Attribute value
47
"""
48
49
class GroupBuilder(Builder):
50
"""
51
Builder for group (container) structures.
52
53
Creates hierarchical storage representations that can contain
54
datasets, nested groups, and links with associated metadata.
55
"""
56
57
def __init__(self, name: str, **kwargs):
58
"""
59
Initialize group builder.
60
61
Args:
62
name: Name of the group
63
**kwargs: Group builder properties:
64
- groups: Dictionary of nested group builders
65
- datasets: Dictionary of dataset builders
66
- links: Dictionary of link builders
67
"""
68
69
def set_group(self, builder: 'GroupBuilder'):
70
"""
71
Add nested group builder.
72
73
Args:
74
builder: Group builder to add
75
"""
76
77
def set_dataset(self, builder: 'DatasetBuilder'):
78
"""
79
Add dataset builder.
80
81
Args:
82
builder: Dataset builder to add
83
"""
84
85
def set_link(self, builder: 'LinkBuilder'):
86
"""
87
Add link builder.
88
89
Args:
90
builder: Link builder to add
91
"""
92
93
@property
94
def groups(self) -> dict:
95
"""Dictionary of nested group builders."""
96
97
@property
98
def datasets(self) -> dict:
99
"""Dictionary of dataset builders."""
100
101
### Build Error Classes
102
103
Exception classes for build system errors and warnings with detailed error reporting.
104
105
```python { .api }
106
class BuildError(Exception):
107
"""
108
Base exception for build system errors.
109
110
Raised when build operations fail due to invalid specifications,
111
missing dependencies, or incompatible data structures.
112
"""
113
pass
114
115
class OrphanContainerBuildError(BuildError):
116
"""
117
Error for orphaned containers during build process.
118
119
Raised when containers lack proper parent relationships
120
required for building storage representations.
121
"""
122
pass
123
124
class ReferenceTargetNotBuiltError(BuildError):
125
"""
126
Error for unresolved reference targets during build.
127
128
Raised when referenced objects haven't been built yet
129
or are missing from the build context.
130
"""
131
pass
132
133
class ContainerConfigurationError(BuildError):
134
"""
135
Error for invalid container configurations.
136
137
Raised when container configurations violate specification
138
requirements or have incompatible settings.
139
"""
140
pass
141
142
class ConstructError(BuildError):
143
"""
144
Error during object construction from builders.
145
146
Raised when builders cannot be converted to container objects
147
due to specification mismatches or invalid data.
148
"""
149
pass
150
151
# Build warnings
152
class BuildWarning(UserWarning):
153
"""Base warning for build system issues."""
154
pass
155
156
class MissingRequiredBuildWarning(BuildWarning):
157
"""Warning for missing required build components."""
158
pass
159
160
class DtypeConversionWarning(BuildWarning):
161
"""Warning for data type conversion during build."""
162
pass
163
164
class IncorrectQuantityBuildWarning(BuildWarning):
165
"""Warning for incorrect quantities during build."""
166
pass
167
168
class OrphanContainerWarning(BuildWarning):
169
"""Warning for orphaned containers."""
170
pass
171
```
172
173
@property
174
def links(self) -> dict:
175
"""Dictionary of link builders."""
176
177
class DatasetBuilder(Builder):
178
"""
179
Builder for dataset structures.
180
181
Creates storage representations for data arrays with metadata,
182
chunking, compression, and other storage-specific properties.
183
"""
184
185
def __init__(self, name: str, data, **kwargs):
186
"""
187
Initialize dataset builder.
188
189
Args:
190
name: Name of the dataset
191
data: Dataset content
192
**kwargs: Dataset builder properties:
193
- dtype: Data type
194
- shape: Data shape
195
- maxshape: Maximum shape for resizable datasets
196
- chunks: Chunk configuration
197
- compression: Compression settings
198
"""
199
200
@property
201
def data(self):
202
"""Dataset content."""
203
204
@property
205
def shape(self) -> tuple:
206
"""Shape of the dataset."""
207
208
@property
209
def dtype(self):
210
"""Data type of the dataset."""
211
212
class LinkBuilder(Builder):
213
"""
214
Builder for link structures.
215
216
Creates storage representations for links between data elements,
217
enabling references and relationships within the data hierarchy.
218
"""
219
220
def __init__(self, builder: Builder, name: str, **kwargs):
221
"""
222
Initialize link builder.
223
224
Args:
225
builder: Target builder for the link
226
name: Name of the link
227
"""
228
229
@property
230
def builder(self) -> Builder:
231
"""Target builder for the link."""
232
233
class ReferenceBuilder(Builder):
234
"""
235
Builder for object references.
236
237
Creates storage representations for references to other objects
238
within the data hierarchy, enabling complex relationships and cross-references.
239
"""
240
241
def __init__(self, builder: Builder, **kwargs):
242
"""
243
Initialize reference builder.
244
245
Args:
246
builder: Target builder for the reference
247
"""
248
249
@property
250
def builder(self) -> Builder:
251
"""Target builder for the reference."""
252
```
253
254
### Build Management
255
256
Core management classes for coordinating the build process and type mappings.
257
258
```python { .api }
259
class BuildManager:
260
"""
261
Manager for building containers into storage representations.
262
263
Coordinates the conversion process from container objects to
264
builder objects, handling validation, type mapping, and error reporting.
265
"""
266
267
def __init__(self, type_map: 'TypeMap'):
268
"""
269
Initialize build manager.
270
271
Args:
272
type_map: Type mapping for container-to-builder conversion
273
"""
274
275
def build(self, container, source: str = None, **kwargs) -> Builder:
276
"""
277
Build storage representation from container.
278
279
Args:
280
container: Container object to build
281
source: Source identifier for tracking
282
**kwargs: Build options:
283
- root: Whether this is the root container
284
- exhaust_dci: Whether to exhaust data chunk iterators
285
286
Returns:
287
Builder object representing the container
288
"""
289
290
def construct(self, builder: Builder, **kwargs):
291
"""
292
Construct container from storage representation.
293
294
Args:
295
builder: Builder object to construct from
296
**kwargs: Construction options
297
298
Returns:
299
Container object constructed from builder
300
"""
301
302
def get_builder(self, container) -> Builder:
303
"""
304
Get existing builder for container if available.
305
306
Args:
307
container: Container to find builder for
308
309
Returns:
310
Builder object or None
311
"""
312
313
class TypeMap:
314
"""
315
Mapping between specifications, container classes, and object mappers.
316
317
Manages the relationships between data type specifications,
318
Python container classes, and the mappers that convert between them.
319
"""
320
321
def __init__(self, namespaces: 'NamespaceCatalog', type_map=None):
322
"""
323
Initialize type map.
324
325
Args:
326
namespaces: Catalog of available namespaces
327
type_map: Parent type map for inheritance
328
"""
329
330
def register_container_type(self, namespace: str, data_type: str, container_cls):
331
"""
332
Register container class for a data type.
333
334
Args:
335
namespace: Namespace containing the data type
336
data_type: Name of the data type
337
container_cls: Python class for the container
338
"""
339
340
def register_map(self, container_cls, mapper_cls):
341
"""
342
Register object mapper for a container class.
343
344
Args:
345
container_cls: Container class
346
mapper_cls: Mapper class for serialization
347
"""
348
349
def get_container_cls(self, namespace: str, data_type: str):
350
"""
351
Get container class for a data type.
352
353
Args:
354
namespace: Namespace containing the data type
355
data_type: Name of the data type
356
357
Returns:
358
Container class
359
"""
360
361
def get_map(self, container):
362
"""
363
Get object mapper for a container.
364
365
Args:
366
container: Container object
367
368
Returns:
369
ObjectMapper instance
370
"""
371
372
def copy(self) -> 'TypeMap':
373
"""
374
Create a copy of this type map.
375
376
Returns:
377
New TypeMap instance with same mappings
378
"""
379
```
380
381
### Object Mapping
382
383
Classes for mapping between container objects and builder representations.
384
385
```python { .api }
386
class ObjectMapper:
387
"""
388
Base class for mapping container objects to/from builders.
389
390
Provides the interface for converting between container objects
391
and their storage representations with validation and error handling.
392
"""
393
394
def __init__(self, spec):
395
"""
396
Initialize object mapper.
397
398
Args:
399
spec: Specification for the data type
400
"""
401
402
def build(self, container, manager: BuildManager, **kwargs) -> Builder:
403
"""
404
Build storage representation from container.
405
406
Args:
407
container: Container object to build
408
manager: Build manager coordinating the process
409
410
Returns:
411
Builder object
412
"""
413
414
def construct(self, builder: Builder, manager: BuildManager, **kwargs):
415
"""
416
Construct container from storage representation.
417
418
Args:
419
builder: Builder object to construct from
420
manager: Build manager coordinating the process
421
422
Returns:
423
Container object
424
"""
425
426
@property
427
def spec(self):
428
"""Specification for this mapper."""
429
430
class CustomClassGenerator:
431
"""
432
Generator for creating custom container classes from specifications.
433
434
Dynamically creates Python classes that match specification requirements
435
with proper validation, attributes, and methods.
436
"""
437
438
def __init__(self, **kwargs):
439
"""Initialize custom class generator."""
440
441
def generate_class(self, namespace: str, data_type: str, spec, parent_cls=None):
442
"""
443
Generate custom container class from specification.
444
445
Args:
446
namespace: Namespace containing the specification
447
data_type: Name of the data type
448
spec: Specification object
449
parent_cls: Parent class for inheritance
450
451
Returns:
452
Generated container class
453
"""
454
455
class MCIClassGenerator(CustomClassGenerator):
456
"""
457
Generator for Multi-Container Interface classes.
458
459
Creates classes that can hold multiple containers of the same type
460
with auto-generated methods for access and manipulation.
461
"""
462
463
def generate_class(self, namespace: str, data_type: str, spec, parent_cls=None):
464
"""
465
Generate MCI container class from specification.
466
467
Args:
468
namespace: Namespace containing the specification
469
data_type: Name of the data type
470
spec: Specification object
471
parent_cls: Parent class for inheritance
472
473
Returns:
474
Generated MCI container class
475
"""
476
```
477
478
### Build Errors and Warnings
479
480
Exception and warning classes for build process error handling.
481
482
```python { .api }
483
class BuildError(Exception):
484
"""Base exception for build process errors."""
485
pass
486
487
class OrphanContainerBuildError(BuildError):
488
"""Exception raised when trying to build orphaned containers."""
489
490
def __init__(self, container):
491
"""
492
Initialize with orphaned container.
493
494
Args:
495
container: Container that lacks proper parent relationships
496
"""
497
498
class ReferenceTargetNotBuiltError(BuildError):
499
"""Exception raised when reference target hasn't been built yet."""
500
501
def __init__(self, reference, target):
502
"""
503
Initialize with reference details.
504
505
Args:
506
reference: Reference that failed
507
target: Target that hasn't been built
508
"""
509
510
class ContainerConfigurationError(BuildError):
511
"""Exception raised for container configuration problems."""
512
pass
513
514
class ConstructError(Exception):
515
"""Exception raised during container construction from builders."""
516
pass
517
518
# Warning classes
519
class BuildWarning(UserWarning):
520
"""Base warning for build process issues."""
521
pass
522
523
class MissingRequiredBuildWarning(BuildWarning):
524
"""Warning for missing required fields during build."""
525
pass
526
527
class DtypeConversionWarning(BuildWarning):
528
"""Warning for data type conversions during build."""
529
pass
530
```
531
532
## Usage Examples
533
534
### Basic Building and Construction
535
536
```python
537
from hdmf.build import BuildManager, TypeMap
538
from hdmf.common import get_type_map
539
from hdmf import Container, Data
540
import numpy as np
541
542
# Get type map with HDMF common types
543
type_map = get_type_map()
544
build_manager = BuildManager(type_map)
545
546
# Create container structure
547
data_array = np.random.randn(100, 50)
548
data_container = Data(name='neural_data', data=data_array)
549
550
container = Container(name='experiment')
551
container.add_child(data_container)
552
553
# Build storage representation
554
builder = build_manager.build(container)
555
print(f"Built {builder.name} with {len(builder.datasets)} datasets")
556
557
# Construct container back from builder
558
constructed = build_manager.construct(builder)
559
print(f"Constructed: {constructed.name}")
560
```
561
562
### Custom Type Registration
563
564
```python
565
from hdmf.build import TypeMap, ObjectMapper
566
from hdmf.spec import GroupSpec, AttributeSpec
567
from hdmf import Container
568
from hdmf.common import get_manager
569
570
# Define custom container class
571
class ExperimentSession(Container):
572
573
@docval({'name': 'name', 'type': str, 'doc': 'Name of the session'},
574
{'name': 'session_id', 'type': str, 'doc': 'Unique session identifier'},
575
{'name': 'start_time', 'type': str, 'doc': 'Session start time'})
576
def __init__(self, **kwargs):
577
name, session_id, start_time = getargs('name', 'session_id', 'start_time', kwargs)
578
super().__init__(name=name)
579
self.session_id = session_id
580
self.start_time = start_time
581
582
# Create specification for the custom type
583
session_spec = GroupSpec(
584
doc='Container for experimental session data',
585
neurodata_type_def='ExperimentSession',
586
attributes=[
587
AttributeSpec('session_id', 'Unique session identifier', dtype='text'),
588
AttributeSpec('start_time', 'Session start time', dtype='text')
589
]
590
)
591
592
# Create custom mapper
593
class ExperimentSessionMapper(ObjectMapper):
594
595
def __init__(self, spec):
596
super().__init__(spec)
597
598
def construct(self, builder, manager, **kwargs):
599
return ExperimentSession(
600
name=builder.name,
601
session_id=builder.attributes['session_id'],
602
start_time=builder.attributes['start_time']
603
)
604
605
# Register the custom type
606
type_map = get_type_map()
607
type_map.register_container_type('custom', 'ExperimentSession', ExperimentSession)
608
type_map.register_map(ExperimentSession, ExperimentSessionMapper)
609
```
610
611
### Advanced Builder Manipulation
612
613
```python
614
from hdmf.build import GroupBuilder, DatasetBuilder, LinkBuilder
615
import numpy as np
616
617
# Create dataset builder with specific storage properties
618
data_builder = DatasetBuilder(
619
name='high_res_data',
620
data=np.random.randn(10000, 1000),
621
dtype='float64',
622
chunks=(1000, 100), # Optimized chunk size
623
compression='gzip',
624
compression_opts=9
625
)
626
627
# Create group builder with nested structure
628
analysis_group = GroupBuilder(name='analysis')
629
analysis_group.set_dataset(data_builder)
630
631
# Add metadata attributes
632
analysis_group.set_attribute('analysis_type', 'spike_detection')
633
analysis_group.set_attribute('algorithm_version', '2.1.0')
634
analysis_group.set_attribute('parameters', {
635
'threshold': -50.0,
636
'min_interval': 0.001,
637
'detection_method': 'threshold_crossing'
638
})
639
640
# Create main experiment group
641
experiment_group = GroupBuilder(name='experiment_001')
642
experiment_group.set_group(analysis_group)
643
644
# Create link to reference data from multiple locations
645
data_link = LinkBuilder(data_builder, name='reference_data')
646
experiment_group.set_link(data_link)
647
648
print(f"Built experiment with {len(experiment_group.groups)} groups")
649
print(f"Dataset shape: {data_builder.shape}")
650
```
651
652
### Error Handling and Validation
653
654
```python
655
from hdmf.build import (BuildManager, BuildError, OrphanContainerBuildError,
656
ReferenceTargetNotBuiltError)
657
from hdmf import Container
658
659
def safe_build_container(container, build_manager):
660
"""Safely build container with comprehensive error handling."""
661
662
try:
663
builder = build_manager.build(container)
664
return builder
665
666
except OrphanContainerBuildError as e:
667
print(f"Container {e.args[0].name} is orphaned - no parent relationship")
668
# Fix by adding to a parent or marking as root
669
return build_manager.build(container, root=True)
670
671
except ReferenceTargetNotBuiltError as e:
672
print(f"Reference target not built: {e.args[1]}")
673
# Build target first, then retry
674
target_builder = build_manager.build(e.args[1])
675
return build_manager.build(container)
676
677
except BuildError as e:
678
print(f"Build error: {e}")
679
return None
680
681
# Usage
682
container = Container(name='test_container')
683
type_map = get_type_map()
684
build_manager = BuildManager(type_map)
685
686
builder = safe_build_container(container, build_manager)
687
if builder:
688
print("Successfully built container")
689
```
690
691
### Dynamic Class Generation
692
693
```python
694
from hdmf.build import CustomClassGenerator
695
from hdmf.spec import GroupSpec, DatasetSpec, AttributeSpec
696
from hdmf.utils import docval, getargs
697
698
# Create specification for dynamic class
699
recording_spec = GroupSpec(
700
doc='Neural recording container',
701
neurodata_type_def='NeuralRecording',
702
datasets=[
703
DatasetSpec('data', 'Raw recording data', dtype='int16',
704
shape=(None, None), dims=['time', 'channels']),
705
DatasetSpec('timestamps', 'Sample timestamps', dtype='float64',
706
shape=(None,), dims=['time'])
707
],
708
attributes=[
709
AttributeSpec('sampling_rate', 'Sampling rate in Hz', dtype='float64'),
710
AttributeSpec('num_channels', 'Number of recording channels', dtype='int')
711
]
712
)
713
714
# Generate custom class from specification
715
generator = CustomClassGenerator()
716
NeuralRecording = generator.generate_class(
717
namespace='custom',
718
data_type='NeuralRecording',
719
spec=recording_spec,
720
parent_cls=Container
721
)
722
723
# Use the dynamically generated class
724
recording = NeuralRecording(
725
name='session_001',
726
data=np.random.randint(-1000, 1000, (30000, 64)),
727
timestamps=np.arange(30000) / 30000.0,
728
sampling_rate=30000.0,
729
num_channels=64
730
)
731
732
print(f"Created {recording.__class__.__name__} with {recording.num_channels} channels")
733
```
734
735
### Build Process Monitoring
736
737
```python
738
import warnings
739
from hdmf.build import BuildWarning, MissingRequiredBuildWarning, DtypeConversionWarning
740
741
def build_with_monitoring(container, build_manager):
742
"""Build container with detailed monitoring and warning handling."""
743
744
with warnings.catch_warnings(record=True) as warning_list:
745
warnings.simplefilter("always")
746
747
builder = build_manager.build(container)
748
749
# Process warnings
750
for warning in warning_list:
751
if issubclass(warning.category, MissingRequiredBuildWarning):
752
print(f"Missing required field: {warning.message}")
753
elif issubclass(warning.category, DtypeConversionWarning):
754
print(f"Data type conversion: {warning.message}")
755
elif issubclass(warning.category, BuildWarning):
756
print(f"Build warning: {warning.message}")
757
758
return builder
759
760
# Usage
761
container = Container(name='monitored_build')
762
type_map = get_type_map()
763
build_manager = BuildManager(type_map)
764
765
builder = build_with_monitoring(container, build_manager)
766
print(f"Build completed for: {builder.name}")
767
```