Skip to content

Commit e47f743

Browse files
Cleanup and enhance template registry (TGSAI#717)
* Update registry design pattern to return mutable instances of templates * pre-commit * Add warning comment for unpickling in test_template_registry.py to emphasize security concerns * Update docs * Update default template registry docs * Consistency in variables and remove redundancy * Ensure deep copies for safety and consistency * Fix documentation * Update return typehint * Update docstring for template * update `template_registry.md` documentation * revise `template_registry.md` for clarity and updated examples * enhance `template_registry.py` documentation for clarity and detail * fix rst rendering issues --------- Co-authored-by: Altay Sansal <[email protected]>
1 parent d6b8fd2 commit e47f743

File tree

5 files changed

+281
-181
lines changed

5 files changed

+281
-181
lines changed

docs/template_registry.md

Lines changed: 73 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,181 +1,112 @@
11
# Template Registry
22

3-
A thread-safe singleton registry for managing dataset templates in MDIO applications.
3+
A simple, thread-safe place to discover and fetch dataset templates for MDIO.
44

5-
## Overview
5+
## Why use it
66

7-
The `TemplateRegistry` implements the singleton pattern to ensure there's only one instance managing all dataset templates throughout the application lifecycle. This provides a centralized registry for template management with thread-safe operations.
7+
- One place to find all available templates
8+
- Safe to use across threads and the whole app (singleton)
9+
- Every fetch gives you your own editable copy (no side effects)
10+
- Comes preloaded with common seismic templates
811

9-
## Features
10-
11-
- **Singleton Pattern**: Ensures only one registry instance exists
12-
- **Thread Safety**: All operations are thread-safe using locks
13-
- **Global Access**: Convenient global functions for common operations
14-
- **Advanced Support**: Reset functionality for environment re-usability.
15-
- **Default Templates**: The registry is instantiated with the default set of templates:
16-
- PostStack2DTime
17-
- PostStack3DTime
18-
- PreStackCdpGathers3DTime
19-
- PreStackShotGathers3DTime
20-
- PostStack2DDepth
21-
- PostStack3DDepth
22-
- PreStackCdpGathers3DDepth
23-
- PreStackShotGathers3DDepth
24-
25-
## Usage
12+
```{note}
13+
Fetching a template with `get_template()` returns a deep copy. Editing it will not change the
14+
registry or anyone else’s copy.
15+
```
2616

27-
### Basic Usage
17+
## Quick start
2818

2919
```python
30-
from mdio.builder.template_registry import TemplateRegistry
31-
32-
# Get the singleton instance
33-
registry = TemplateRegistry()
34-
35-
# Or use the class method
36-
registry = TemplateRegistry.get_instance()
37-
38-
# Register a template
39-
template = MyDatasetTemplate()
40-
template_name = registry.register(template)
41-
print(f"Registered template named {template_name}")
20+
from mdio.builder.template_registry import get_template, list_templates
4221

43-
# Retrieve a template using a well-known name
44-
template = registry.get("my_template")
45-
# Retrieve a template using the name returned when the template was registered
46-
template = registry.get(template_name)
22+
# See what's available
23+
print(list_templates())
24+
# e.g. ["Seismic2DPostStackTime", "Seismic3DPostStackDepth", ...]
4725

48-
# Check if template exists
49-
if registry.is_registered("my_template"):
50-
print("Template is registered")
26+
# Grab a template by name
27+
tpl = get_template("Seismic3DPostStackTime")
5128

52-
# List all templates
53-
template_names = registry.list_all_templates()
29+
# Customize your copy (safe)
30+
tpl.add_units({"amplitude": "unitless"})
5431
```
5532

56-
### Global Functions
33+
## Common tasks
5734

58-
For convenience, you can use global functions that operate on the singleton instance:
35+
### Fetch a template you can edit
5936

6037
```python
61-
from mdio.builder.template_registry import (
62-
register_template,
63-
get_template,
64-
is_template_registered,
65-
list_templates
66-
)
38+
from mdio.builder.template_registry import get_template
6739

68-
# Register a template globally
69-
register_template(Seismic3DTemplate())
70-
71-
# Get a template
72-
template = get_template("seismic_3d")
73-
74-
# Check registration
75-
if is_template_registered("seismic_3d"):
76-
print("Template available")
77-
78-
# List all registered templates
79-
templates = list_templates()
40+
tpl = get_template("Seismic2DPostStackDepth")
41+
# Use/modify tpl freely — it’s your copy
8042
```
8143

82-
### Multiple Instantiation
83-
84-
The singleton pattern ensures all instantiations return the same object:
44+
### List available templates
8545

8646
```python
87-
registry1 = TemplateRegistry()
88-
registry2 = TemplateRegistry()
89-
registry3 = TemplateRegistry.get_instance()
47+
from mdio.builder.template_registry import list_templates
9048

91-
# All variables point to the same instance
92-
assert registry1 is registry2 is registry3
49+
names = list_templates()
50+
for name in names:
51+
print(name)
9352
```
9453

95-
## API Reference
54+
### Check if a template exists
9655

97-
```{eval-rst}
98-
.. automodule:: mdio.builder.template_registry
99-
:members:
56+
```python
57+
from mdio.builder.template_registry import is_template_registered
58+
59+
if is_template_registered("Seismic3DPostStackTime"):
60+
... # safe to fetch
10061
```
10162

102-
## Thread Safety
63+
### Register your own template (optional)
10364

104-
All operations on the registry are thread-safe:
65+
If you have a custom template class, register an instance so others can fetch it by name:
10566

10667
```python
107-
import threading
108-
109-
def register_templates():
110-
registry = TemplateRegistry()
111-
for i in range(10):
112-
template = MyTemplate(f"template_{i}")
113-
registry.register(template)
114-
115-
# Multiple threads can safely access the registry
116-
threads = [threading.Thread(target=register_templates) for _ in range(5)]
117-
for thread in threads:
118-
thread.start()
119-
for thread in threads:
120-
thread.join()
68+
from typing import Any
69+
from mdio.builder.template_registry import register_template
70+
from mdio.builder.templates.abstract_dataset_template import AbstractDatasetTemplate
71+
from mdio.builder.templates.types import SeismicDataDomain
72+
73+
class MyTemplate(AbstractDatasetTemplate):
74+
def __init__(self, domain: SeismicDataDomain = "time"):
75+
super().__init__(domain)
76+
77+
@property
78+
def _name(self) -> str:
79+
# The public name becomes something like "MyTemplateTime"
80+
return f"MyTemplate{self._data_domain.capitalize()}"
81+
82+
def _load_dataset_attributes(self) -> dict[str, Any]:
83+
return {"surveyType": "2D", "gatherType": "custom"}
84+
85+
# Make it available globally
86+
registered_name = register_template(MyTemplate("time"))
87+
print(registered_name) # "MyTemplateTime"
12188
```
12289

123-
## Best Practices
90+
```{tip}
91+
Use `list_templates()` to discover the exact names to pass to `get_template()`.
92+
```
12493

125-
1. **Use Global Functions**: For simple operations, prefer the global convenience functions
126-
2. **Register Early**: Register all templates during application startup
127-
3. **Thread Safety**: The registry is thread-safe, but individual templates may not be
128-
4. **Testing Isolation**: Always reset the singleton in test setup/teardown
94+
## Troubleshooting
12995

130-
## Example: Complete Template Management
96+
- KeyError: “Template 'XYZ' is not registered.”
97+
- The name is wrong or not registered yet.
98+
- Call `list_templates()` to see valid names, or `is_template_registered(name)` to check first.
13199

132-
```python
133-
from mdio.builder.template_registry import TemplateRegistry
134-
from mdio.builder.templates.seismic_3d_poststack import Seismic3DPostStackTemplate
135-
from mdio.builder.schemas.v1 import Seismic3DPostStackTimeTemplate
136-
from mdio.builder.schemas.v1 import Seismic3DPreStackTemplate
137-
138-
139-
def setup_templates():
140-
"""Register MDIO templates runtime.
141-
Custom templates can be created in external projects and added without modifying the MDIO library code
142-
"""
143-
# Use strongly-typed template
144-
template_name = TemplateRegistry.register(Seismic3DPostStackTimeTemplate())
145-
print(f"Registered template named {template_name}")
146-
# Use parametrized template
147-
template_name = TemplateRegistry.register(Seismic3DPostStackTemplate("Depth"))
148-
print(f"Registered template named {template_name}")
149-
template_name = TemplateRegistry.register(Seismic3DPreStackTemplate())
150-
print(f"Registered template named {template_name}")
151-
152-
print(f"Registered templates: {list_templates()}")
153-
154-
155-
# Application startup
156-
setup_standard_templates()
157-
158-
# Later in the application
159-
template = TemplateRegistry().get_template("PostStack3DDepth")
160-
dataset = template.create_dataset(name="Seismic 3d m/m/ft",
161-
sizes=[256, 512, 384])
162-
```
100+
## FAQ
163101

164-
## Error Handling
102+
- Do I need to create a TemplateRegistry instance?
103+
No. Use the global helpers: `get_template`, `list_templates`, `register_template`, and `is_template_registered`.
104+
- Are templates shared between callers or threads?
105+
No. Each `get_template()` call returns a deep-copied instance that is safe to modify independently.
165106

166-
The registry provides clear error messages:
107+
## API reference
167108

168-
```python
169-
# Template not registered
170-
try:
171-
template = get_template("nonexistent")
172-
except KeyError as e:
173-
print(f"Error: {e}") # "Template 'nonexistent' is not registered."
174-
175-
# Duplicate registration
176-
try:
177-
register_template("duplicate", template1)
178-
register_template("duplicate", template2)
179-
except ValueError as e:
180-
print(f"Error: {e}") # "Template 'duplicate' is already registered."
109+
```{eval-rst}
110+
.. automodule:: mdio.builder.template_registry
111+
:members:
181112
```

0 commit comments

Comments
 (0)