Skip to content

Commit 9838d2a

Browse files
committed
Add ruff and pyright for linting and type-checks
1 parent ab93270 commit 9838d2a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2584
-1166
lines changed

.github/workflows/test.yml

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,21 @@
1-
name: Test
2-
3-
on:
4-
pull_request:
5-
workflow_dispatch:
6-
push:
7-
branches:
8-
- main
9-
10-
permissions:
11-
contents: read
12-
checks: write
13-
pull-requests: write
14-
151
jobs:
162
lint-and-test:
173
name: "Lint and Test (Python ${{ matrix.python }})"
184
runs-on: ubuntu-latest
195
strategy:
206
matrix:
217
python: [ "3.10", "3.11", "3.12", "3.13" ]
8+
env:
9+
UV_PYTHON: ${{ matrix.python }}
2210
steps:
2311
- uses: actions/checkout@v4
2412
- uses: extractions/setup-just@v2
25-
- uses: actions/setup-python@v5
13+
- uses: astral-sh/setup-uv@v5
2614
with:
27-
python-version: ${{ matrix.python }}
28-
- name: Build Rust module
29-
uses: PyO3/maturin-action@v1
30-
with:
31-
args: --out dist --interpreter python${{ matrix.python }}
32-
sccache: 'true'
33-
container: off
34-
- run: pip install -r requirements.txt
35-
- run: pip install dist/*
15+
enable-cache: true
16+
- name: Install dependencies
17+
run: just sync
18+
- name: Build
19+
run: just build
3620
- name: Verify
3721
run: just verify

Justfile

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,47 @@
1-
# Justfile
1+
# Use UV_PYTHON env variable to select either a python version or
2+
# the complete python to your python interpreter
23

3-
python := "python3"
4+
default := "all"
45

5-
default:
6-
@echo "Available recipes:"
7-
@echo " mypy - Run mypy for type checking"
8-
@echo " pylint - Run pylint for linting"
9-
@echo " test - Run pytest for testing"
10-
@echo " verify - Run mypy, pylint, test"
6+
set shell := ["bash", "-c"]
117

12-
# Recipe to run mypy for type checking
13-
mypy:
14-
@echo "Running mypy..."
15-
{{python}} -m mypy --check-untyped-defs --ignore-missing-imports python/restate/
16-
{{python}} -m mypy --check-untyped-defs --ignore-missing-imports examples/
8+
sync:
9+
uv sync --all-extras --all-packages
1710

18-
# Recipe to run pylint for linting
19-
pylint:
20-
@echo "Running pylint..."
21-
{{python}} -m pylint python/restate --ignore-paths='^.*.?venv.*$'
22-
{{python}} -m pylint examples/ --ignore-paths='^.*\.?venv.*$'
11+
format:
12+
uv run ruff format
13+
uv run ruff check --fix --fix-only
14+
15+
lint:
16+
uv run ruff format --check
17+
uv run ruff check
18+
19+
typecheck-pyright:
20+
PYRIGHT_PYTHON_IGNORE_WARNINGS=1 uv run pyright python/
21+
PYRIGHT_PYTHON_IGNORE_WARNINGS=1 uv run pyright examples/
22+
PYRIGHT_PYTHON_IGNORE_WARNINGS=1 uv run pyright tests
23+
PYRIGHT_PYTHON_IGNORE_WARNINGS=1 uv run pyright test-services/
24+
25+
typecheck-mypy:
26+
uv run -m mypy --check-untyped-defs --ignore-missing-imports python/
27+
uv run -m mypy --check-untyped-defs --ignore-missing-imports examples/
28+
uv run -m mypy --check-untyped-defs --ignore-missing-imports tests/
29+
30+
typecheck: typecheck-pyright typecheck-mypy
2331

2432
test:
25-
@echo "Running Python tests..."
26-
{{python}} -m pytest tests/*
33+
uv run -m pytest tests/*
34+
2735

2836
# Recipe to run both mypy and pylint
29-
verify: mypy pylint test
37+
verify: format lint typecheck test
3038
@echo "Type checking and linting completed successfully."
3139

3240
# Recipe to build the project
3341
build:
3442
@echo "Building the project..."
35-
maturin build --release
43+
#maturin build --release
44+
uv build --all-packages
3645

3746
clean:
3847
@echo "Cleaning the project"

examples/concurrent_greeter.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,24 @@
2020
from pydantic import BaseModel
2121
from restate import wait_completed, Service, Context
2222

23+
2324
# models
2425
class GreetingRequest(BaseModel):
2526
name: str
2627

28+
2729
class Greeting(BaseModel):
2830
messages: typing.List[str]
2931

32+
3033
class Message(BaseModel):
3134
role: str
3235
content: str
3336

37+
3438
concurrent_greeter = Service("concurrent_greeter")
3539

40+
3641
@concurrent_greeter.handler()
3742
async def greet(ctx: Context, req: GreetingRequest) -> Greeting:
3843
claude = ctx.service_call(claude_sonnet, arg=Message(role="user", content=f"please greet {req.name}"))
@@ -45,17 +50,19 @@ async def greet(ctx: Context, req: GreetingRequest) -> Greeting:
4550

4651
# cancel the pending calls
4752
for f in pending:
48-
await f.cancel_invocation() # type: ignore
53+
await f.cancel_invocation() # type: ignore
4954

5055
return Greeting(messages=greetings)
5156

5257

5358
# not really interesting, just for this demo:
5459

60+
5561
@concurrent_greeter.handler()
5662
async def claude_sonnet(ctx: Context, req: Message) -> str:
5763
return f"Bonjour {req.content[13:]}!"
5864

65+
5966
@concurrent_greeter.handler()
6067
async def open_ai(ctx: Context, req: Message) -> str:
6168
return f"Hello {req.content[13:]}!"

examples/example.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,13 @@
2424

2525
logging.basicConfig(level=logging.INFO)
2626

27-
app = restate.app(services=[greeter,
28-
random_greeter,
29-
counter,
30-
payment,
31-
pydantic_greeter,
32-
concurrent_greeter])
27+
app = restate.app(services=[greeter, random_greeter, counter, payment, pydantic_greeter, concurrent_greeter])
3328

3429
if __name__ == "__main__":
3530
import hypercorn
3631
import hypercorn.asyncio
3732
import asyncio
33+
3834
conf = hypercorn.Config()
3935
conf.bind = ["0.0.0.0:9080"]
4036
asyncio.run(hypercorn.asyncio.serve(app, conf))

examples/greeter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
greeter = Service("greeter")
2323

24+
2425
@greeter.handler()
2526
async def greet(ctx: Context, name: str) -> str:
2627
logger.info("Received greeting request: %s", name)

examples/pydantic_greeter.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,21 @@
1717
from pydantic import BaseModel
1818
from restate import Service, Context
1919

20+
2021
# models
2122
class GreetingRequest(BaseModel):
2223
name: str
2324

25+
2426
class Greeting(BaseModel):
2527
message: str
2628

29+
2730
# service
2831

2932
pydantic_greeter = Service("pydantic_greeter")
3033

34+
3135
@pydantic_greeter.handler()
3236
async def greet(ctx: Context, req: GreetingRequest) -> Greeting:
3337
return Greeting(message=f"Hello {req.name}!")

examples/random_greeter.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
1010
#
1111
"""example.py"""
12+
1213
from datetime import datetime
1314

1415
# pylint: disable=C0116
@@ -18,9 +19,9 @@
1819

1920
random_greeter = Service("random_greeter")
2021

22+
2123
@random_greeter.handler()
2224
async def greet(ctx: Context, name: str) -> str:
23-
2425
# ctx.random() returns a Python Random instance seeded deterministically.
2526
# By using ctx.random() you don't write entries in the journal,
2627
# but you still get the same generated values on retries.
@@ -48,8 +49,10 @@ async def greet(ctx: Context, name: str) -> str:
4849
# end = await ctx.time()
4950
# delta = datetime.timedelta(seconds=(end-start))
5051

51-
return (f"Hello {name} with "
52-
f"random number {random_number}, "
53-
f"random bytes {random_bytes!r} "
54-
f"random uuid {random_uuid},"
55-
f"now datetime {now_datetime}!")
52+
return (
53+
f"Hello {name} with "
54+
f"random number {random_number}, "
55+
f"random bytes {random_bytes!r} "
56+
f"random uuid {random_uuid},"
57+
f"now datetime {now_datetime}!"
58+
)

examples/virtual_object.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616

1717
counter = VirtualObject("counter")
1818

19+
1920
@counter.handler()
2021
async def increment(ctx: ObjectContext, value: int) -> int:
2122
n = await ctx.get("counter", type_hint=int) or 0
2223
n += value
2324
ctx.set("counter", n)
2425
return n
2526

27+
2628
@counter.handler(kind="shared")
2729
async def count(ctx: ObjectSharedContext) -> int:
2830
return await ctx.get("counter") or 0

examples/workflow.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
payment = Workflow("payment")
2525

26+
2627
@payment.main()
2728
async def pay(ctx: WorkflowContext, amount: int):
2829
workflow_key = ctx.key()
@@ -44,16 +45,17 @@ def payment_gateway():
4445
# Wait for the payment to be verified
4546

4647
match await select(result=ctx.promise("verify.payment").value(), timeout=ctx.sleep(TIMEOUT)):
47-
case ['result', "approved"]:
48+
case ["result", "approved"]:
4849
ctx.set("status", "payment approved")
49-
return { "success" : True }
50-
case ['result', "declined"]:
50+
return {"success": True}
51+
case ["result", "declined"]:
5152
ctx.set("status", "payment declined")
5253
raise TerminalError(message="Payment declined", status_code=401)
53-
case ['timeout', _]:
54+
case ["timeout", _]:
5455
ctx.set("status", "payment verification timed out")
5556
raise TerminalError(message="Payment verification timed out", status_code=410)
5657

58+
5759
@payment.handler()
5860
async def payment_verified(ctx: WorkflowSharedContext, result: str):
5961
promise = ctx.promise("verify.payment", type_hint=str)

pyproject.toml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Source = "https://github.com/restatedev/sdk-python"
2222

2323
[project.optional-dependencies]
2424
test = ["pytest", "hypercorn"]
25-
lint = ["mypy", "pylint"]
25+
lint = ["mypy>=1.11.2", "pyright>=1.1.390", "ruff>=0.6.9"]
2626
harness = ["testcontainers", "hypercorn", "httpx"]
2727
serde = ["dacite", "pydantic"]
2828

@@ -34,3 +34,21 @@ build-backend = "maturin"
3434
features = ["pyo3/extension-module"]
3535
module-name = "restate._internal"
3636
python-source = "python"
37+
38+
[tool.ruff]
39+
line-length = 120
40+
target-version = "py310"
41+
include = [
42+
"python/**/*.py",
43+
"examples/**/*.py",
44+
"tests/**/*.py",
45+
"test-services//**/*.py",
46+
]
47+
48+
[tool.ruff.lint]
49+
ignore = ["E741"]
50+
51+
[tool.pytest.ini_options]
52+
filterwarnings = [
53+
"ignore:The @wait_container_is_ready decorator is deprecated:DeprecationWarning",
54+
]

0 commit comments

Comments
 (0)