Skip to content

Commit 881fb1a

Browse files
committed
Add a section explaining how to initialize an actor
The initializer is not trivial, as it is usually the one taking input and output channels, and the `Actor.__init__()` needs to be called too, and the optional `name` should be explained too. The new section includes an example and a continuation of this example is added to the "The `_run()` Method" section, to make it more clear what the bare minimum needed to implement a complete actor. We also add the `name` argument to the complete examples. Signed-off-by: Leandro Lucarella <[email protected]>
1 parent b00ab7d commit 881fb1a

File tree

1 file changed

+85
-6
lines changed

1 file changed

+85
-6
lines changed

src/frequenz/sdk/actor/__init__.py

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,56 @@ async def _run(self) -> None:
155155
## Implementing an Actor
156156
157157
To implement an actor, you must inherit from the [`Actor`][frequenz.sdk.actor.Actor]
158-
class and implement the abstract [`_run()`][_run] method.
158+
class and implement an *initializer* and the abstract [`_run()`][_run] method.
159+
160+
### Initialization
161+
162+
The [initializer][object.__init__] is called when the actor is created. The
163+
[`Actor`][frequenz.sdk.actor.Actor] class initializer
164+
([`__init__`][frequenz.sdk.actor.Actor.__init__]) should be always called first in the
165+
class we are implementing to make sure actors are properly initialized.
166+
167+
The [`Actor.__init__()`][frequenz.sdk.actor.Actor.__init__] takes one optional argument,
168+
a [`name`][frequenz.sdk.actor.Actor.name] that will be used to identify the actor in
169+
logs. If no name is provided, a default name will be generated, but it is recommended
170+
that [`Actor`][frequenz.sdk.actor.Actor] subclasses can also receive a name as an
171+
argument to make it easier to identify individual instances in logs more easily.
172+
173+
The actor initializer normally also accepts as arguments the input channels receivers
174+
and output channels senders that will be used for communication. These channels should
175+
be created outside the actor and passed to it as arguments to ensure actors can be
176+
composed easily.
177+
178+
???+ example "Example echo actor"
179+
180+
```python
181+
from frequenz.channels import Receiver, Sender
182+
from frequenz.sdk.actor import Actor
183+
184+
class EchoActor(Actor): # (1)!
185+
def __init__(
186+
self,
187+
input: Receiver[int], # (2)!
188+
output: Sender[int], # (3)!
189+
name: str | None = None, # (4)!
190+
) -> None:
191+
super().__init__(name=name) # (5)!
192+
self._input: Receiver[int] = input # (6)!
193+
self._output: Sender[int] = output # (7)!
194+
```
195+
196+
1. We define a new actor class called `EchoActor` that inherits from
197+
[`Actor`][frequenz.sdk.actor.Actor].
198+
199+
2. We accept an `input` argument that will be used to receive messages from
200+
a channel.
201+
3. We accept an `output` argument that will be used to send messages to a channel.
202+
4. We accept an optional `name` argument that will be used to identify the actor in
203+
logs.
204+
5. We call [`Actor.__init__()`][frequenz.sdk.actor.Actor.__init__] to make sure the
205+
actor is properly initialized.
206+
6. We store the `input` argument in a *private* instance variable to use it later.
207+
7. We store the `output` argument in a *private* instance variable to use it later.
159208
160209
### The `_run()` Method
161210
@@ -168,6 +217,32 @@ class and implement the abstract [`_run()`][_run] method.
168217
([receivers][frequenz.channels.Receiver]), processing them and sending the results to
169218
other channels ([senders][frequenz.channels.Sender]).
170219
220+
???+ example "Example echo actor"
221+
222+
```python
223+
from frequenz.channels import Receiver, Sender
224+
from frequenz.sdk.actor import Actor
225+
226+
class EchoActor(Actor):
227+
def __init__(
228+
self,
229+
input: Receiver[int],
230+
output: Sender[int],
231+
name: str | None = None,
232+
) -> None:
233+
super().__init__(name=name)
234+
self._input: Receiver[int] = input
235+
self._output: Sender[int] = output
236+
237+
async def _run(self) -> None: # (1)!
238+
async for msg in self._input: # (2)!
239+
await self._output.send(msg) # (3)!
240+
```
241+
242+
1. We implement the abstract [`_run()`][_run] method.
243+
2. We receive messages from the `input` channel one by one.
244+
3. We send the received message to the `output` channel.
245+
171246
### Stopping
172247
173248
By default, the [`stop()`][frequenz.sdk.actor.Actor.stop] method will call the
@@ -270,8 +345,9 @@ def __init__(
270345
self,
271346
receiver: Receiver[str],
272347
output: Sender[str],
348+
name: str | None = None,
273349
) -> None:
274-
super().__init__()
350+
super().__init__(name=name)
275351
self._receiver = receiver
276352
self._output = output
277353
@@ -285,8 +361,9 @@ def __init__(
285361
self,
286362
receiver: Receiver[str],
287363
output: Sender[str],
364+
name: str | None = None,
288365
) -> None:
289-
super().__init__()
366+
super().__init__(name=name)
290367
self._receiver = receiver
291368
self._output = output
292369
@@ -305,8 +382,8 @@ async def main() -> None: # (2)!
305382
output_receiver = output_channel.new_receiver()
306383
307384
async with ( # (5)!
308-
Actor1(input_channel.new_receiver(), middle_channel.new_sender()),
309-
Actor2(middle_channel.new_receiver(), output_channel.new_sender()),
385+
Actor1(input_channel.new_receiver(), middle_channel.new_sender(), "actor1"),
386+
Actor2(middle_channel.new_receiver(), output_channel.new_sender(), "actor1"),
310387
):
311388
await input_sender.send("Hello") # (6)!
312389
msg = await output_receiver.receive() # (7)!
@@ -388,8 +465,9 @@ def __init__(
388465
receiver_1: Receiver[bool],
389466
receiver_2: Receiver[bool],
390467
output: Sender[bool],
468+
name: str | None = None,
391469
) -> None:
392-
super().__init__()
470+
super().__init__(name=name)
393471
self._receiver_1 = receiver_1
394472
self._receiver_2 = receiver_2
395473
self._output = output
@@ -421,6 +499,7 @@ async def _run(self) -> None: # (2)!
421499
input_channel_1.new_receiver(),
422500
input_channel_2.new_receiver(),
423501
echo_channel.new_sender(),
502+
"echo-actor",
424503
)
425504
426505
echo_receiver = echo_channel.new_receiver() # (5)!

0 commit comments

Comments
 (0)