Skip to content

Commit

Permalink
Python docs (#454)
Browse files Browse the repository at this point in the history
  • Loading branch information
gvdongen authored Sep 9, 2024
1 parent b9b50ae commit 7b068cf
Show file tree
Hide file tree
Showing 49 changed files with 1,225 additions and 133 deletions.
2 changes: 1 addition & 1 deletion code_snippets/go/develop/serving.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func serving() {
// <start_identity>
if err := server.NewRestate().
Bind(restate.Reflect(MyService{})).
Bind(restate.Reflect(MyVirtualObject{})).
// withClass highlight-line
WithIdentityV1("publickeyv1_w7YHemBctH5Ck2nQRQ47iBBqhNHy4FV7t2Usbye2A6f").
Start(context.Background(), ":9080"); err != nil {
log.Fatal(err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ class MySecureApp {
public static void main(String[] args) {
RestateHttpEndpointBuilder.builder()
.bind(new MyService())
.bind(new MyVirtualObject())
.bind(new MyWorkflow())
// withClass(1:3) highlight-line
.withRequestIdentityVerifier(
RestateRequestIdentityVerifier.fromKeys(
"publickeyv1_w7YHemBctH5Ck2nQRQ47iBBqhNHy4FV7t2Usbye2A6f"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import dev.restate.sdk.http.vertx.RestateHttpEndpointBuilder
fun main() {
RestateHttpEndpointBuilder.builder()
.bind(MyService())
.bind(MyVirtualObject())
.bind(MyWorkflow())
// withClass(1:5) highlight-line
.withRequestIdentityVerifier(
RestateRequestIdentityVerifier.fromKeys(
"publickeyv1_w7YHemBctH5Ck2nQRQ47iBBqhNHy4FV7t2Usbye2A6f",
Expand Down
Empty file.
Empty file.
35 changes: 35 additions & 0 deletions code_snippets/python/src/develop/awakeables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from restate import Service, Context

my_service = Service("MyService")


@my_service.handler()
async def my_handler(ctx: Context, arg):

# <start_here>
# <mark_1>
name, promise = ctx.awakeable()
# </mark_1>

# <mark_2>
await ctx.run("trigger task", trigger_task_and_deliver_id(name))
# </mark_2>

# <mark_3>
payload = await promise
# </mark_3>
# <end_here>

# <start_resolve>
ctx.resolve_awakeable(name, payload)
# <end_resolve>

# <start_reject>
ctx.reject_awakeable(name, "My error reason")
# <end_reject>

return arg


def trigger_task_and_deliver_id(awakeable_id):
return "123"
12 changes: 12 additions & 0 deletions code_snippets/python/src/develop/durable_timers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from datetime import timedelta

from restate import Service, Context

my_service = Service("MyService")


@my_service.handler()
async def my_handler(ctx: Context, arg):
# <start_here>
await ctx.sleep(delta=timedelta(seconds=10))
# <end_here>
13 changes: 13 additions & 0 deletions code_snippets/python/src/develop/error_handling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from datetime import timedelta

from restate import Service, Context
from restate.exceptions import TerminalError

my_service = Service("MyService")


@my_service.handler()
async def my_handler(ctx: Context, arg):
# <start_here>
raise TerminalError("Something went wrong.")
# <end_here>
15 changes: 15 additions & 0 deletions code_snippets/python/src/develop/journaling_results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from restate import Service, Context

my_service = Service("MyService")


@my_service.handler()
async def my_handler(ctx: Context, arg):
# <start_side_effect>
async def do_db_request():
# ... implement ...
return "my_result"

# withClass highlight-line
result = await ctx.run("database request", do_db_request)
# <end_side_effect>
11 changes: 11 additions & 0 deletions code_snippets/python/src/develop/my_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import restate
from restate import Context, Service

my_service = Service("MyService")


@my_service.handler("myHandler")
async def my_service_handler(ctx: Context, greeting: str) -> str:
return f"${greeting}!"

app = restate.app(services=[my_service])
16 changes: 16 additions & 0 deletions code_snippets/python/src/develop/my_virtual_object.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import restate
from restate import VirtualObject, ObjectContext, ObjectSharedContext

my_virtual_object = VirtualObject("MyVirtualObject")


@my_virtual_object.handler("myHandler")
async def my_object_handler(ctx: ObjectContext, greeting: str) -> str:
return f"${greeting} ${ctx.key()}!"


@my_virtual_object.handler(kind="shared")
async def my_concurrent_handler(ctx: ObjectSharedContext, greeting: str) -> str:
return f"${greeting} ${ctx.key()}!"

app = restate.app(services=[my_virtual_object])
22 changes: 22 additions & 0 deletions code_snippets/python/src/develop/my_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import restate
from restate import Workflow, WorkflowSharedContext, WorkflowContext
from restate.serde import Serde

my_workflow = Workflow("MyWorkflow")


@my_workflow.main()
async def run(ctx: WorkflowContext, req: str) -> str:
# ... implement workflow logic here ---
return "success"


@my_workflow.handler()
async def interact_with_workflow(ctx: WorkflowSharedContext, req: str):
# ... implement interaction logic here ...
return

app = restate.app(services=[my_workflow])


Serde
58 changes: 58 additions & 0 deletions code_snippets/python/src/develop/serialization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import json
import typing

from restate.serde import Serde
from restate import ObjectContext, VirtualObject


# <start_custom>
class MyData(typing.TypedDict):
"""Represents a response from the GPT model."""
some_value: str
my_number: int


class MySerde(Serde[MyData]):
def deserialize(self, buf: bytes) -> typing.Optional[MyData]:
if not buf:
return None
data = json.loads(buf)
return MyData(some_value=data["some_value"], my_number=data["some_number"])

def serialize(self, obj: typing.Optional[MyData]) -> bytes:
if obj is None:
return bytes()
data = {
"some_value": obj["some_value"],
"some_number": obj["my_number"]
}
return bytes(json.dumps(data), "utf-8")
# <end_custom>


my_object = VirtualObject("MyService")


# <start_using_custom_serde>
# For the input/output serialization of your handlers
@my_object.handler(input_serde=MySerde(), output_serde=MySerde())
async def my_handler(ctx: ObjectContext, greeting: str) -> str:

# To serialize state
await ctx.get("my_state", serde=MySerde())
ctx.set("my_state", MyData(some_value="value", my_number=123), serde=MySerde())

# To serialize awakeable payloads
ctx.awakeable(serde=MySerde())

# To serialize the results of actions
await ctx.run("some-task", some_task, serde=MySerde())

# etc.

return "some-output"
# <end_using_custom_serde>


def some_task() -> MyData:
return MyData(some_value="value", my_number=123)
59 changes: 59 additions & 0 deletions code_snippets/python/src/develop/service_communication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from datetime import timedelta

from restate import Service, Context, VirtualObject, ObjectContext
# <start_import>
from src.develop.my_service import my_service_handler
# <end_import>
from src.develop.my_virtual_object import my_object_handler
from src.develop.my_workflow import run, interact_with_workflow

caller = Service("Caller")


@caller.handler()
async def calling_handler(ctx: Context, arg):
# <start_request_response_service>
response = await ctx.service_call(my_service_handler, arg="Hi")
# <end_request_response_service>

# <start_request_response_object>
response = await ctx.object_call(my_object_handler, key="Mary", arg="Hi")
# <end_request_response_object>

# <start_one_way_service>
ctx.service_send(my_service_handler, arg="Hi")
# <end_one_way_service>

# <start_one_way_object>
ctx.object_send(my_object_handler, key="Mary", arg="Hi")
# <end_one_way_object>

# <start_delayed_service>
ctx.service_send(my_service_handler, arg="Hi", send_delay=timedelta(seconds=5))
# <end_delayed_service>

# <start_delayed_object>
ctx.object_send(my_object_handler, key="Mary", arg="Hi", send_delay=timedelta(seconds=5))
# <end_delayed_object>

# <start_ordering>
ctx.object_send(my_object_handler, key="Mary", arg="I'm call A")
ctx.object_send(my_object_handler, key="Mary", arg="I'm call B")
# <end_ordering>


@caller.handler()
async def call_workflows(ctx: Context, arg):
# <start_request_response_workflow>
# Call the `run` handler of the workflow(only works once).
await ctx.workflow_call(run, key="my_workflow_id", arg="Hi")
# Call some other `interact_with_workflow` handler of the workflow.
await ctx.workflow_call(interact_with_workflow, key="my_workflow_id", arg=None)
# <end_request_response_workflow>

# <start_one_way_workflow>
# Call the `run` handler of the workflow (only works once).
ctx.workflow_send(run, key="my_workflow_id", arg="Hi")
# Call some other `interact_with_workflow` handler of the workflow.
ctx.workflow_send(interact_with_workflow, key="my_workflow_id", arg=None)
# <end_one_way_workflow>
16 changes: 16 additions & 0 deletions code_snippets/python/src/develop/serving.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from src.develop.my_service import my_service
from src.develop.my_virtual_object import my_virtual_object

# <start_endpoint>
import restate
app = restate.app(services=[my_service, my_virtual_object])
# <end_endpoint>


# <start_identity>
app = restate.app(
services=[my_service],
# withClass highlight-line
identity_keys=["publickeyv1_w7YHemBctH5Ck2nQRQ47iBBqhNHy4FV7t2Usbye2A6f"]
)
# <end_identity>
27 changes: 27 additions & 0 deletions code_snippets/python/src/develop/state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from restate import ObjectContext, VirtualObject

my_object = VirtualObject("Caller")


@my_object.handler()
async def caller(ctx: ObjectContext, arg):
# <start_statekeys>
state_keys = ctx.state_keys()
# <end_statekeys>

# <start_get>
my_string = await ctx.get("my-string-key") or "default-key"
my_number = await ctx.get("my-number-key") or 123
# <end_get>

# <start_set>
ctx.set("my-key", "my-new-value")
# <end_set>

# <start_clear>
ctx.clear("my-key")
# <end_clear>

# <start_clear_all>
ctx.clear_all()
# <end_clear_all>
14 changes: 14 additions & 0 deletions code_snippets/python/src/develop/workflows/email_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) 2024 - Restate Software, Inc., Restate GmbH
#
# This file is part of the Restate examples,
# which is released under the MIT license.
#
# You can find a copy of the license in the file LICENSE
# in the root directory of this repository or package or at
# https://github.com/restatedev/examples/

class EmailClient:

def send_email_with_link(self, email: str, secret: str):
# send the email
return
19 changes: 19 additions & 0 deletions code_snippets/python/src/develop/workflows/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from restate import VirtualObject, ObjectContext

from src.develop.workflows.signup import run, get_status

user_management_object = VirtualObject("UserManagementObject")


# <start_here>
@user_management_object.handler()
async def signup_user(ctx: ObjectContext, email: str):
# focus
result = await ctx.workflow_call(run, key="someone", arg=email)


@user_management_object.handler()
async def query_status(ctx: ObjectContext):
# focus
status = await ctx.workflow_call(get_status, key="someone", arg=None)
# <end_here>
Loading

0 comments on commit 7b068cf

Please sign in to comment.