Skip to content

Commit 5aa1286

Browse files
committed
Fix. Invoke and update readme
1 parent 5dec9fe commit 5aa1286

4 files changed

Lines changed: 141 additions & 42 deletions

File tree

README.md

Lines changed: 122 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,134 @@
11
# Spawn Python SDK
2-
32
Python User Language Support for [Spawn](https://github.com/eigr/spawn).
43

5-
## Installation via source
4+
# Table of Contents
5+
1. [Overview](#overview)
6+
2. [Getting Started](#getting-started)
7+
8+
9+
## Overview
10+
TODO
11+
12+
## Getting Started
13+
14+
First we must create a new Python project. In this example we will use [Poetry](https://python-poetry.org) as our package manager.
615

16+
```shell
17+
poetry new spawn-py-demo
718
```
8-
> git clone https://github.com/eigr-labs/spawn-python-sdk.git
9-
Cloning into 'spawn-python-sdk'...
1019

11-
> cd spawn-python-sdk
12-
> python3 -m venv env
13-
> source ./env/bin/activate
14-
> python --version
15-
Python 3.7.3
16-
> pip --version
17-
> pip install wheel
18-
> pip install .
20+
The second thing we have to do is add the spawn dependency to the project.
21+
22+
```toml
23+
[tool.poetry.dependencies]
24+
python = ">=3.9.7,<4.0"
25+
spawn = {git = "https://github.com/eigr/spawn-python-sdk.git"}
1926
```
2027

21-
### Generate installer
28+
Now it is necessary to download the dependencies via Poetry:
29+
30+
```shell
31+
poetry env use python3 # use your own python here
32+
poetry lock
33+
poetry install
34+
```
35+
36+
So far it's all pretty boring and not really Spawn related so it's time to start playing for real.
37+
The first thing we're going to do is define a place to put our protobuf files. In the root of the project we will create a folder called protobuf and some subfolders
38+
39+
```shell
40+
mkdir -p spawn_py_demo/protobuf/domain
2241
```
23-
python setup.py bdist_wheel
42+
43+
That done, let's create our protobuf file inside the example folder.
44+
45+
```shell
46+
touch spawn_py_demo/protobuf/domain/domain.proto
47+
```
48+
49+
And let's populate this file with the following content:
50+
51+
```proto
52+
syntax = "proto3";
53+
54+
package domain;
55+
56+
message JoeState {
57+
repeated string languages = 1;
58+
}
59+
60+
message Request {
61+
string language = 1;
62+
}
63+
64+
message Reply {
65+
string response = 1;
66+
}
2467
```
2568

26-
### Local install
69+
We must compile this file using the protoc utility. In the root of the project type the following command:
70+
71+
```shell
72+
protoc -I spawn_py_demo/protobuf/ --python_out=spawn_py_demo spawn_py_demo/protobuf/domain/domain.proto
73+
```
74+
75+
Now in the spawn_py_demo folder we will create our first python file containing the code of our Actor.
76+
77+
```shell
78+
touch spawn_py_demo/joe.py
79+
```
80+
81+
Populate this file with the following content:
82+
83+
```python
84+
from domain.domain_pb2 import JoeState, Request, Reply
85+
from spawn.eigr.functions.actors.api.actor import Actor
86+
from spawn.eigr.functions.actors.api.settings import ActorSettings
87+
from spawn.eigr.functions.actors.api.context import Context
88+
from spawn.eigr.functions.actors.api.value import Value
89+
from spawn.eigr.functions.actors.api.workflows.broadcast import Broadcast
90+
91+
actor = Actor(settings=ActorSettings(
92+
name="joe", stateful=True, channel="test"))
93+
94+
95+
@actor.timer_action(every=1000)
96+
def hi(ctx: Context) -> Value:
97+
new_state = None
98+
request = Request()
99+
request.language = "python"
100+
broadcast = Broadcast()
101+
broadcast.channel = "test"
102+
broadcast.action_name = "setLanguage"
103+
broadcast.value = request
104+
105+
if not ctx.state:
106+
new_state = JoeState()
107+
new_state.languages.append("python")
108+
else:
109+
new_state = ctx.state
110+
111+
return Value()\
112+
.broadcast(broadcast)\
113+
.state(new_state)\
114+
.noreply()
115+
116+
117+
@actor.action("setLanguage")
118+
def set_language(request: Request, ctx: Context) -> Value:
119+
reply = Reply()
120+
reply.response = "erlang"
121+
return Value().of(reply, ctx.state).reply()
122+
```
123+
124+
Now with our Actor properly defined, we just need to start the SDK correctly. Create another file to serve as your application's entrypoint and fill it with the following content:
125+
126+
```python
127+
from spawn.eigr.functions.actors.api.sdk import Spawn
128+
from joe import actor as joe_actor
129+
130+
if __name__ == "__main__":
131+
spawn = Spawn()
132+
spawn.port(8091).proxy_port(9003).actor_system(
133+
"spawn-system").add_actor(joe_actor).start()
27134
```
28-
python -m pip install dist/spawn-python-sdk-0.1.0-py3-none-any.whl
29-
```

example/joe.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,37 @@
66
from spawn.eigr.functions.actors.api.actor import Actor
77
from spawn.eigr.functions.actors.api.settings import ActorSettings
88
from spawn.eigr.functions.actors.api.context import Context
9-
from spawn.eigr.functions.actors.api.metadata import Metadata
109
from spawn.eigr.functions.actors.api.value import Value
1110
from spawn.eigr.functions.actors.api.workflows.broadcast import Broadcast
12-
from spawn.eigr.functions.actors.api.workflows.effect import Effect
1311

14-
actor = Actor(settings=ActorSettings(name="joe", stateful=True))
12+
actor = Actor(settings=ActorSettings(
13+
name="joe", stateful=True, channel="test"))
1514

1615

1716
@actor.timer_action(every=1000)
1817
def hi(ctx: Context) -> Value:
1918
new_state = None
19+
request = Request()
20+
request.language = "python"
2021
broadcast = Broadcast()
2122
broadcast.channel = "test"
2223
broadcast.action_name = "setLanguage"
23-
broadcast.value = Request()
24+
broadcast.value = request
2425

2526
if not ctx.state:
2627
new_state = JoeState()
27-
new_state.languages.append("portuguese")
28+
new_state.languages.append("python")
2829
else:
2930
new_state = ctx.state
3031

3132
return Value()\
32-
.of("test")\
3333
.broadcast(broadcast)\
3434
.state(new_state)\
35-
.reply()
35+
.noreply()
3636

3737

3838
@actor.action("setLanguage")
3939
def set_language(request: Request, ctx: Context) -> Value:
40-
return Value()\
41-
.of("test")\
42-
.broadcast()\
43-
.effect(Effect())\
44-
.metada(Metadata())\
45-
.state({})\
46-
.reply()
40+
reply = Reply()
41+
reply.response = "erlang"
42+
return Value().of(reply, ctx.state).reply()

spawn/eigr/functions/actors/api/actor.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,18 @@ def register_timer_action_handler(function):
6363

6464

6565
def invoke(function, parameters):
66-
ordered_parameters = []
6766
for parameter_definition in inspect.signature(function).parameters.values():
6867
annotation = parameter_definition.annotation
68+
6969
if annotation == inspect._empty:
7070
raise Exception("Cannot inject parameter {} of function {}: Missing type annotation".format(
7171
parameter_definition.name, function))
72-
match_found = False
73-
for param in parameters:
74-
if isinstance(param, annotation):
75-
match_found = True
76-
ordered_parameters.append(param)
77-
if not match_found:
78-
raise Exception("Cannot inject parameter {} of function {}: No matching value".format(
79-
parameter_definition.name, function))
72+
73+
ordered_parameters = []
74+
for param in parameters:
75+
if param != None:
76+
ordered_parameters.append(param)
77+
8078
return function(*ordered_parameters)
8179

8280

spawn/eigr/functions/actors/internal/controller.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ def handle_response(system, actor_name, result):
9797
actor_invocation_response.updated_context.CopyFrom(updated_context)
9898

9999
if result.get_reply_kind() == ReplyKind.NO_REPLY:
100-
actor_invocation_response.noop = Noop()
100+
actor_invocation_response.noop.CopyFrom(Noop())
101101
elif result.get_reply_kind == ReplyKind.REPLY:
102-
actor_invocation_response.value = pack(result.get_response())
102+
actor_invocation_response.value.CopyFrom(pack(result.get_response()))
103103

104104
if result.get_broadcast() != None:
105105
value_broadcast = result.get_broadcast()

0 commit comments

Comments
 (0)