From f646f92c38022825e677b2ae058bbfc3db9804d2 Mon Sep 17 00:00:00 2001 From: Martin Lange <martin.lange@ufz.de> Date: Mon, 19 Jun 2023 14:29:32 +0200 Subject: [PATCH] implement collecting metadata from components and adapters --- src/finam/interfaces.py | 10 ++++++++++ src/finam/schedule.py | 28 ++++++++++++++++++++++++++++ src/finam/sdk/adapter.py | 5 +++++ src/finam/sdk/component.py | 5 +++++ tests/core/test_schedule.py | 25 +++++++++++++++++++++++++ 5 files changed, 73 insertions(+) diff --git a/src/finam/interfaces.py b/src/finam/interfaces.py index 2379977d..d720281b 100644 --- a/src/finam/interfaces.py +++ b/src/finam/interfaces.py @@ -138,6 +138,11 @@ class IComponent(ABC): def status(self): """The component's current status.""" + @property + @abstractmethod + def metadata(self): + """The component's meta data.""" + class ITimeComponent(IComponent, ABC): """Interface for components with a time step.""" @@ -453,6 +458,11 @@ class IOutput(ABC): class IAdapter(IInput, IOutput, ABC): """Interface for adapters.""" + @property + @abstractmethod + def metadata(self): + """The adapter's meta data.""" + class NoBranchAdapter: """Interface to mark adapters as allowing only a single end point.""" diff --git a/src/finam/schedule.py b/src/finam/schedule.py index e85a40a3..ecc2d36a 100644 --- a/src/finam/schedule.py +++ b/src/finam/schedule.py @@ -428,6 +428,34 @@ class Composition(Loggable): f"Expecting one of [{', '.join(map(str, desired_list))}]" ) + @property + def metadata(self): + """ + Meta data for all components and adapters. + Can only be used after ``connect``. + + Raises + ------ + FinamStatusError + Raises the error if ``connect`` was not called. + """ + if not self.is_connected: + with ErrorLogger(self.logger): + raise FinamStatusError( + "can't get meta data for a composition before connect was called" + ) + + md = {} + for mod in self.modules: + key = f"{mod.name}@{id(mod)}" + md[key] = mod.metadata + + for ada in self.adapters: + key = f"{ada.name}@{id(ada)}" + md[key] = ada.metadata + + return md + def _collect_adapters_input(inp: IInput, out_adapters: set): src = inp.get_source() diff --git a/src/finam/sdk/adapter.py b/src/finam/sdk/adapter.py index 1f5c3d39..bdc8a728 100644 --- a/src/finam/sdk/adapter.py +++ b/src/finam/sdk/adapter.py @@ -67,6 +67,11 @@ class Adapter(IAdapter, Input, Output, ABC): """bool: if the adapter needs push.""" return False + @property + def metadata(self): + """The adapter's meta data.""" + return {} + @final def push_data(self, data, time): """Push data into the output. diff --git a/src/finam/sdk/component.py b/src/finam/sdk/component.py index baf3610d..ef38a0dc 100644 --- a/src/finam/sdk/component.py +++ b/src/finam/sdk/component.py @@ -218,6 +218,11 @@ class Component(IComponent, Loggable, ABC): """Component name.""" return self._name + @property + def metadata(self): + """The component's meta data.""" + return {} + @property def logger_name(self): """Logger name derived from base logger name and class name.""" diff --git a/tests/core/test_schedule.py b/tests/core/test_schedule.py index e0a7e6b6..d9f36822 100644 --- a/tests/core/test_schedule.py +++ b/tests/core/test_schedule.py @@ -965,6 +965,31 @@ class TestComposition(unittest.TestCase): self.assertEqual([1, 8, 13], updates["A"]) self.assertEqual([1, 2, 5, 8, 11], updates["B"]) + def test_metadata(self): + module1 = MockupComponent( + callbacks={"Output": lambda t: t.day}, step=timedelta(1.0) + ) + module2 = MockupDependentComponent(step=timedelta(1.0)) + + composition = Composition([module2, module1]) + composition.initialize() + + ada1 = fm.adapters.Scale(1.0) + ada2 = fm.adapters.Scale(1.0) + module1.outputs["Output"] >> ada1 >> ada2 >> module2.inputs["Input"] + + with self.assertRaises(FinamStatusError) as context: + _ = composition.metadata + + composition.connect() + + md = composition.metadata + + self.assertIn(f"{module1.name}@{id(module1)}", md) + self.assertIn(f"{module2.name}@{id(module2)}", md) + self.assertIn(f"{ada1.name}@{id(ada1)}", md) + self.assertIn(f"{ada2.name}@{id(ada2)}", md) + if __name__ == "__main__": unittest.main() -- GitLab