Skip to content

Commit f1aa1c9

Browse files
authored
Workflow implementation (#21)
1 parent 23aa7ae commit f1aa1c9

30 files changed

+8095
-368
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ __pycache__
44
/dist
55
/docs/_autosummary
66
/docs/_build
7+
/docs/_build_pydoctor
78
temporalio/api/*
89
!temporalio/api/__init__.py
910
temporalio/bridge/proto/*

README.md

Lines changed: 323 additions & 44 deletions
Large diffs are not rendered by default.

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Modules
1212

1313
temporalio.client
1414
temporalio.worker
15+
temporalio.workflow
1516
temporalio.activity
1617
temporalio.converter
1718
temporalio.exceptions

poetry.lock

Lines changed: 400 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ black = "^21.12b0"
3737
furo = "^2022.3.4"
3838
grpcio-tools = "^1.43.0"
3939
isort = "^5.10.1"
40-
mypy = "^0.931"
40+
mypy = "^0.950"
4141
mypy-protobuf = "^3.2.0"
4242
pydocstyle = "^6.1.1"
43+
pydoctor = "^22.5.1"
4344
pytest = "^7.1.1"
4445
pytest-asyncio = "^0.18.0"
4546
pytest-timeout = "^2.1.0"
@@ -56,14 +57,17 @@ build-bridge-develop = "python scripts/setup_bridge.py develop"
5657
fix-wheel = "python scripts/fix_wheel.py"
5758
format = [{cmd = "black ."}, {cmd = "isort ."}]
5859
gen-docs = "sphinx-build docs docs/_build"
60+
gen-docs-pydoctor = "pydoctor"
5961
gen-protos = "python scripts/gen_protos.py"
6062
lint = [
6163
{cmd = "black --check ."},
6264
{cmd = "isort --check-only ."},
6365
{ref = "lint-types"},
6466
{ref = "lint-docs"},
6567
]
66-
lint-docs = "pydocstyle"
68+
# TODO(cretz): Why does pydocstyle complain about @overload missing docs after
69+
# https://github.com/PyCQA/pydocstyle/pull/511?
70+
lint-docs = "pydocstyle --ignore-decorators=overload"
6771
lint-types = "mypy ."
6872
test = "pytest"
6973

@@ -86,7 +90,7 @@ asyncio_mode = "auto"
8690
log_cli = true
8791
log_cli_level = "INFO"
8892
log_cli_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)"
89-
timeout = 60
93+
timeout = 600
9094
timeout_func_only = true
9195

9296
[tool.isort]
@@ -111,6 +115,15 @@ add_ignore = [
111115
"D205", "D415"
112116
]
113117

118+
[tool.pydoctor]
119+
add-package = ["temporalio"]
120+
docformat = "google"
121+
html-output = "docs/_build_pydoctor"
122+
intersphinx = ["https://docs.python.org/3/objects.inv", "https://googleapis.dev/python/protobuf/latest/objects.inv"]
123+
privacy = ["PRIVATE:temporalio.bridge", "HIDDEN:**.*_pb2*"]
124+
project-name = "Temporal"
125+
sidebar-expand-depth = 40
126+
114127
[build-system]
115128
build-backend = "poetry.core.masonry.api"
116129
requires = ["poetry-core>=1.0.0", "setuptools", "wheel", "setuptools-rust"]

scripts/gen_protos.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,17 @@ def fix_generated_output(base_path: Path):
6868
f.write(content)
6969
# Write init
7070
with (base_path / "__init__.py").open("w") as f:
71+
# Imports
72+
message_names = []
7173
for stem, messages in imports.items():
7274
for message in messages:
7375
f.write(f"from .{stem} import {message}\n")
76+
message_names.append(message)
77+
# __all__
78+
if message_names:
79+
f.write(
80+
f'\n__all__ = [\n "' + '",\n "'.join(message_names) + '",\n]\n'
81+
)
7482

7583

7684
if __name__ == "__main__":

temporalio/activity.py

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import asyncio
1313
import contextvars
14+
import inspect
1415
import logging
1516
import threading
1617
from dataclasses import dataclass
@@ -57,15 +58,8 @@ def defn(fn: Optional[ActivityFunc] = None, *, name: Optional[str] = None):
5758
"""
5859

5960
def with_name(name: str, fn: ActivityFunc) -> ActivityFunc:
60-
# Validate the activity
61-
if not callable(fn):
62-
raise TypeError("Activity is not callable")
63-
elif not fn.__code__:
64-
raise TypeError("Activity callable missing __code__")
65-
elif fn.__code__.co_kwonlyargcount:
66-
raise TypeError("Activity cannot have keyword-only arguments")
67-
# Set the name
68-
setattr(fn, "__temporal_activity_name", name)
61+
# This performs validation
62+
_Definition._apply_to_callable(fn, name)
6963
return fn
7064

7165
# If name option is available, return decorator function
@@ -320,7 +314,7 @@ def process(
320314
) -> Tuple[Any, MutableMapping[str, Any]]:
321315
"""Override to add activity details."""
322316
msg, kwargs = super().process(msg, kwargs)
323-
if self.activity_info_on_extra or self.activity_info_on_extra:
317+
if self.activity_info_on_message or self.activity_info_on_extra:
324318
context = _current_context.get(None)
325319
if context:
326320
if self.activity_info_on_message:
@@ -342,3 +336,45 @@ def base_logger(self) -> logging.Logger:
342336

343337
#: Logger that will have contextual activity details embedded.
344338
logger = LoggerAdapter(logging.getLogger(__name__), None)
339+
340+
341+
@dataclass
342+
class _Definition:
343+
name: str
344+
fn: Callable
345+
is_async: bool
346+
347+
@staticmethod
348+
def from_callable(fn: Callable) -> Optional[_Definition]:
349+
return getattr(fn, "__temporal_activity_definition", None)
350+
351+
@staticmethod
352+
def must_from_callable(fn: Callable) -> _Definition:
353+
ret = _Definition.from_callable(fn)
354+
if ret:
355+
return ret
356+
fn_name = getattr(fn, "__name__", "<unknown>")
357+
raise TypeError(
358+
f"Activity {fn_name} missing attributes, was it decorated with @activity.defn?"
359+
)
360+
361+
@staticmethod
362+
def _apply_to_callable(fn: Callable, activity_name: str) -> None:
363+
# Validate the activity
364+
if hasattr(fn, "__temporal_activity_definition"):
365+
raise ValueError("Function already contains activity definition")
366+
elif not callable(fn):
367+
raise TypeError("Activity is not callable")
368+
elif not fn.__code__:
369+
raise TypeError("Activity callable missing __code__")
370+
elif fn.__code__.co_kwonlyargcount:
371+
raise TypeError("Activity cannot have keyword-only arguments")
372+
setattr(
373+
fn,
374+
"__temporal_activity_definition",
375+
_Definition(
376+
name=activity_name,
377+
fn=fn,
378+
is_async=inspect.iscoroutinefunction(fn),
379+
),
380+
)

temporalio/bridge/Cargo.lock

Lines changed: 19 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

temporalio/bridge/proto/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
11
from .core_interface_pb2 import ActivityHeartbeat, ActivityTaskCompletion
2+
3+
__all__ = [
4+
"ActivityHeartbeat",
5+
"ActivityTaskCompletion",
6+
]

temporalio/bridge/sdk-core

Submodule sdk-core updated 69 files

0 commit comments

Comments
 (0)