Skip to content

Commit b4329ef

Browse files
committed
feat: data register supports dynamically loaded classes
1 parent 9158737 commit b4329ef

File tree

4 files changed

+62
-35
lines changed

4 files changed

+62
-35
lines changed

src/py_app_dev/core/data_registry.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,22 @@ def __init__(self, data: Any, provider_name: str) -> None:
1414

1515
class DataRegistry:
1616
def __init__(self) -> None:
17-
# Registry to store data entries by type
18-
self._registry: Dict[Type[Any], List[DataEntry]] = defaultdict(list)
17+
# Registry to store data entries by type (fully qualified name as the key)
18+
self._registry: Dict[str, List[DataEntry]] = defaultdict(list)
19+
20+
@staticmethod
21+
def _get_type_name(obj_type: Type[Any]) -> str:
22+
"""Get the fully qualified name of the type."""
23+
return f"{obj_type.__module__}.{obj_type.__name__}"
1924

2025
def insert(self, data: Any, provider: str) -> None:
2126
"""Registers a piece of information with the provider."""
22-
self._registry[type(data)].append(DataEntry(data, provider))
27+
self._registry[self._get_type_name(type(data))].append(DataEntry(data, provider))
2328

2429
def find_data(self, data_type: Type[T]) -> List[T]:
2530
"""Find all data of a given type."""
2631
return [entry.data for entry in self.find_entries(data_type)]
2732

2833
def find_entries(self, data_type: Type[T]) -> List[DataEntry]:
2934
"""Find all data entries of a given type. Each entry contains the data and the provider."""
30-
return self._registry.get(data_type, [])
35+
return self._registry.get(self._get_type_name(data_type), [])

tests/conftest.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import textwrap
2+
from pathlib import Path
3+
4+
import pytest
5+
6+
7+
@pytest.fixture
8+
def my_python_file(tmp_path: Path) -> Path:
9+
"""Fixture to create a temporary Python file with a sample step."""
10+
python_file = tmp_path / "my_python_file.py"
11+
python_file.write_text(
12+
textwrap.dedent(
13+
"""\
14+
from typing import List
15+
from pathlib import Path
16+
from py_app_dev.core.pipeline import PipelineStep
17+
class MyStep(PipelineStep):
18+
def run(self) -> None:
19+
pass
20+
def get_dependencies(self) -> List[Path]:
21+
pass
22+
def get_outputs(self) -> List[Path]:
23+
pass
24+
"""
25+
)
26+
)
27+
return python_file

tests/test_data_registry.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
from dataclasses import dataclass
1+
from pathlib import Path
22

33
from py_app_dev.core.data_registry import DataRegistry
4+
from py_app_dev.core.pipeline import PipelineLoader, PipelineStep, PipelineStepConfig
45

56

6-
class Provider1:
7-
pass
7+
class SomeData:
8+
def __init__(self, data: str):
9+
self.data = data
810

911

10-
@dataclass
11-
class SomeData:
12-
name: str
12+
class Provider1:
13+
pass
1314

1415

1516
def test_insert_and_find_data():
@@ -19,7 +20,7 @@ def test_insert_and_find_data():
1920

2021
string_data = registry.find_data(SomeData)
2122
assert len(string_data) == 1
22-
assert string_data[0].name == "new data"
23+
assert string_data[0].data == "new data"
2324

2425
int_data = registry.find_data(int)
2526
assert int_data == [123]
@@ -44,3 +45,21 @@ def test_find_data_empty():
4445
registry = DataRegistry()
4546
data = registry.find_data(str)
4647
assert data == []
48+
49+
50+
def test_support_dynamically_loaded_classes(my_python_file: Path) -> None:
51+
def load_step():
52+
return PipelineLoader[PipelineStep]._load_steps(
53+
"install",
54+
[PipelineStepConfig(step="MyStep", file=str(my_python_file), config={"data": "value"})],
55+
my_python_file.parent,
56+
)[0]
57+
58+
step_class_1st_load = load_step()._class
59+
step_class_2nd_load = load_step()._class
60+
assert step_class_1st_load != step_class_2nd_load # Different instances
61+
registry = DataRegistry()
62+
registry.insert(step_class_1st_load(), "provider1")
63+
registry.insert(step_class_2nd_load(), "provider1")
64+
data = registry.find_data(step_class_2nd_load)
65+
assert len(data) == 2

tests/test_pipeline.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import textwrap
21
from pathlib import Path
32
from typing import Any, List, OrderedDict
43

@@ -8,29 +7,6 @@
87
from py_app_dev.core.pipeline import PipelineLoader, PipelineStep, PipelineStepConfig
98

109

11-
@pytest.fixture
12-
def my_python_file(tmp_path: Path) -> Path:
13-
"""Fixture to create a temporary Python file with a sample step."""
14-
python_file = tmp_path / "my_python_file.py"
15-
python_file.write_text(
16-
textwrap.dedent(
17-
"""\
18-
from typing import List
19-
from pathlib import Path
20-
from py_app_dev.core.pipeline import PipelineStep
21-
class MyStep(PipelineStep):
22-
def run(self) -> None:
23-
pass
24-
def get_dependencies(self) -> List[Path]:
25-
pass
26-
def get_outputs(self) -> List[Path]:
27-
pass
28-
"""
29-
)
30-
)
31-
return python_file
32-
33-
3410
def test_load_unknown_step():
3511
with pytest.raises(UserNotificationException):
3612
PipelineLoader[PipelineStep]._load_steps("install", [PipelineStepConfig(step="StepIDontExist")], Path("."))

0 commit comments

Comments
 (0)