From a4ed3c8dceb7c63eb9f41eea10b9b9fb054aff6f Mon Sep 17 00:00:00 2001
From: Martin Lange <martin.lange@ufz.de>
Date: Mon, 24 Oct 2022 17:31:19 +0200
Subject: [PATCH] test code of components chapter

---
 .../finam-book/development/adapters.rst       | 22 +++++++++-
 .../finam-book/development/components.rst     | 40 +++++++++++--------
 .../finam-book/usage/coupling_scripts.rst     |  7 ++--
 3 files changed, 47 insertions(+), 22 deletions(-)

diff --git a/docs/source/finam-book/development/adapters.rst b/docs/source/finam-book/development/adapters.rst
index 62fd7e94..3686077b 100644
--- a/docs/source/finam-book/development/adapters.rst
+++ b/docs/source/finam-book/development/adapters.rst
@@ -31,7 +31,7 @@ which is called from downstream to request data.
 
 File ``src/scale.py``:
 
-.. code-block:: Python
+.. testcode:: scale-adapter
 
     import finam as fm
 
@@ -45,6 +45,11 @@ File ``src/scale.py``:
             d = self.pull_data(time)
             return d * self.scale
 
+.. testcode:: scale-adapter
+    :hide:
+
+    adapter = Scale(0.5)
+
 In :meth:`.Adapter._get_data`, we:
 
 1. Pull the input for the requested ``time``
@@ -129,6 +134,10 @@ In :meth:`.Adapter._source_updated`, we need to store incoming data:
             self.old_data = None
             self.new_data = None
 
+        @property
+        def needs_push(self):
+            return True
+
         def _source_updated(self, time):
             self.old_data = self.new_data
             self.new_data = (time, fm.data.strip_data(self.pull_data(time)))
@@ -141,7 +150,7 @@ As the output time will differ from the input time, we need to strip the time of
 
 In :meth:`.Adapter._get_data`, we can now do the interpolation whenever data is requested from upstream.
 
-.. code-block:: Python
+.. testcode:: time-adapter
 
     import finam as fm
 
@@ -152,6 +161,10 @@ In :meth:`.Adapter._get_data`, we can now do the interpolation whenever data is
             self.old_data = None
             self.new_data = None
 
+        @property
+        def needs_push(self):
+            return True
+
         def _source_updated(self, time):
             self.old_data = self.new_data
             self.new_data = (time, fm.data.strip_data(self.pull_data(time)))
@@ -167,6 +180,11 @@ In :meth:`.Adapter._get_data`, we can now do the interpolation whenever data is
 
             return o + dt * (n - o)
 
+.. testcode:: time-adapter
+    :hide:
+
+    adapter = TimeInterpolation()
+
 In :meth:`.Adapter._get_data`, the following happens:
 
 1. If only one data entry was received so far, we can't interpolate and simply return the available data. Otherwise...
diff --git a/docs/source/finam-book/development/components.rst b/docs/source/finam-book/development/components.rst
index 6f770bbb..712bf75c 100644
--- a/docs/source/finam-book/development/components.rst
+++ b/docs/source/finam-book/development/components.rst
@@ -145,9 +145,9 @@ It is called internally by the :meth:`.TimeComponent.initialize` method.
             # ...
 
         def _initialize(self):                                             # <--
-            self.inputs.add(name="A", grid=fm.NoGrid())                    # <--
-            self.inputs.add(name="B", grid=fm.NoGrid())                    # <--
-            self.outputs.add(name="Sum", grid=fm.NoGrid())                 # <--
+            self.inputs.add(name="A", time=self.time, grid=fm.NoGrid())    # <--
+            self.inputs.add(name="B", time=self.time, grid=fm.NoGrid())    # <--
+            self.outputs.add(name="Sum", time=self.time, grid=fm.NoGrid()) # <--
 
             self.create_connector()                                        # <--
 
@@ -196,7 +196,7 @@ After this connection phase, models can validate their state in :meth:`.TimeComp
             # ...
 
         def _connect(self):                                                      # <--
-            self.try_connect(time=self.time, push_data={"Sum": 0})               # <--
+            self.try_connect(push_data={"Sum": 0})                               # <--
 
         def _validate(self):                                                     # <--
             pass                                                                 # <--
@@ -227,7 +227,7 @@ For the tests, we need to set up a real coupling from here on, as the component'
 
             # a component to consume output, details not important
             consumer = fm.modules.debug.DebugConsumer(
-                inputs={"Sum": fm.Info(grid=fm.NoGrid())},
+                inputs={"Sum": fm.Info(time=None, grid=fm.NoGrid())},
                 start=datetime(2000, 1, 1),
                 step=timedelta(days=7)
             )
@@ -338,7 +338,7 @@ Final code
 
 Here is the final code of the completed component.
 
-.. code-block:: Python
+.. testcode::
 
     import unittest
     from datetime import datetime, timedelta
@@ -347,22 +347,22 @@ Here is the final code of the completed component.
 
 
     class DummyModel(fm.TimeComponent):
-        def __init__(self, start, step):  # <--
+        def __init__(self, start, step):
             super().__init__()
-            self._step = step  # <--
+            self._step = step
             self.time = start
 
-        def _initialize(self):  # <--
-            self.inputs.add(name="A", grid=fm.NoGrid())  # <--
-            self.inputs.add(name="B", grid=fm.NoGrid())  # <--
-            self.outputs.add(name="Sum", grid=fm.NoGrid())  # <--
+        def _initialize(self):
+            self.inputs.add(name="A", time=self.time, grid=fm.NoGrid())
+            self.inputs.add(name="B", time=self.time, grid=fm.NoGrid())
+            self.outputs.add(name="Sum", time=self.time, grid=fm.NoGrid())
 
-            self.create_connector()  # <--
+            self.create_connector()
 
-        def _connect(self):  # <--
-            self.try_connect(time=self.time, push_data={"Sum": 0})  # <--
+        def _connect(self):
+            self.try_connect(push_data={"Sum": 0})
 
-        def _validate(self):  # <--
+        def _validate(self):
             pass
 
         def _update(self):
@@ -395,7 +395,7 @@ Here is the final code of the completed component.
                 step=timedelta(days=7),
             )
             consumer = fm.modules.debug.DebugConsumer(
-                inputs={"Sum": fm.Info(grid=fm.NoGrid())},
+                inputs={"Sum": fm.Info(time=None, grid=fm.NoGrid())},
                 start=datetime(2000, 1, 1),
                 step=timedelta(days=7),
             )
@@ -412,3 +412,9 @@ Here is the final code of the completed component.
             self.assertEqual(consumer.data, {"Sum": 0})
 
             composition.run(t_max=datetime(2000, 12, 31))
+
+    TestDummy().test_dummy_model() #doctest: +ELLIPSIS
+
+.. testoutput::
+
+    ...
diff --git a/docs/source/finam-book/usage/coupling_scripts.rst b/docs/source/finam-book/usage/coupling_scripts.rst
index 7713beef..ae66720c 100644
--- a/docs/source/finam-book/usage/coupling_scripts.rst
+++ b/docs/source/finam-book/usage/coupling_scripts.rst
@@ -153,12 +153,13 @@ FINAM provides a comprehensive logging framework built on Pythons standard :mod:
 
 You can configure the base logger when creating the :class:`.Composition` as shown above:
 
-.. code-block:: Python
+.. testcode:: composition
 
+    import finam as fm
     import logging
 
-    comp = Composition(
-        modules,
+    comp = fm.Composition(
+        [],
         logger_name="FINAM",
         print_log=True,
         log_file=True,
-- 
GitLab