simpleflow 0.34.0__tar.gz → 0.34.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. {simpleflow-0.34.0 → simpleflow-0.34.2}/PKG-INFO +2 -27
  2. {simpleflow-0.34.0 → simpleflow-0.34.2}/pyproject.toml +43 -32
  3. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/__init__.py +1 -1
  4. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/command.py +51 -18
  5. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/dispatch/dynamic_dispatcher.py +3 -3
  6. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/history.py +117 -0
  7. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/local/executor.py +3 -4
  8. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/metrology.py +23 -31
  9. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/process/_supervisor.py +1 -1
  10. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/executor.py +19 -16
  11. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/helpers.py +16 -2
  12. simpleflow-0.34.2/simpleflow/swf/mapper/__init__.py +0 -0
  13. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/base.py +8 -10
  14. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/event/base.py +1 -0
  15. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/event/workflow.py +34 -0
  16. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/workflow.py +1 -1
  17. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/querysets/activity.py +1 -1
  18. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/querysets/workflow.py +1 -1
  19. simpleflow-0.34.2/simpleflow/swf/process/decider/helpers.py +111 -0
  20. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/process/worker/base.py +1 -0
  21. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/stats/pretty.py +6 -2
  22. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/task.py +1 -2
  23. simpleflow-0.34.0/simpleflow/main.py +0 -17
  24. simpleflow-0.34.0/simpleflow/swf/process/decider/helpers.py +0 -154
  25. {simpleflow-0.34.0 → simpleflow-0.34.2}/.gitignore +0 -0
  26. {simpleflow-0.34.0 → simpleflow-0.34.2}/LICENSE +0 -0
  27. {simpleflow-0.34.0 → simpleflow-0.34.2}/README.md +0 -0
  28. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/__main__.py +0 -0
  29. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/_decorators.py +0 -0
  30. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/activity.py +0 -0
  31. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/base.py +0 -0
  32. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/boto3_utils.py +0 -0
  33. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/canvas.py +0 -0
  34. /simpleflow-0.34.0/simpleflow/job/__init__.py → /simpleflow-0.34.2/simpleflow/cli/workflow.py +0 -0
  35. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/constants.py +0 -0
  36. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/dispatch/__init__.py +0 -0
  37. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/dispatch/by_module.py +0 -0
  38. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/dispatch/dry_run.py +0 -0
  39. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/dispatch/exceptions.py +0 -0
  40. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/dispatch/from_task_registry.py +0 -0
  41. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/download.py +0 -0
  42. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/exceptions.py +0 -0
  43. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/execute.py +0 -0
  44. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/executor.py +0 -0
  45. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/format.py +0 -0
  46. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/futures.py +0 -0
  47. {simpleflow-0.34.0/simpleflow/step → simpleflow-0.34.2/simpleflow/job}/__init__.py +0 -0
  48. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/local/__init__.py +0 -0
  49. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/log.py +0 -0
  50. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/logging_context.py +0 -0
  51. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/marker.py +0 -0
  52. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/process/__init__.py +0 -0
  53. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/process/_named_mixin.py +0 -0
  54. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/registry.py +0 -0
  55. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/runtime/__init__.py +0 -0
  56. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/settings/__init__.py +0 -0
  57. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/settings/base.py +0 -0
  58. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/settings/default.py +0 -0
  59. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/signal.py +0 -0
  60. {simpleflow-0.34.0/simpleflow/swf → simpleflow-0.34.2/simpleflow/step}/__init__.py +0 -0
  61. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/step/constants.py +0 -0
  62. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/step/submittable.py +0 -0
  63. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/step/tasks.py +0 -0
  64. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/step/utils.py +0 -0
  65. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/step/workflow.py +0 -0
  66. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/storage.py +0 -0
  67. {simpleflow-0.34.0/simpleflow/swf/mapper → simpleflow-0.34.2/simpleflow/swf}/__init__.py +0 -0
  68. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/constants.py +0 -0
  69. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/actors/__init__.py +0 -0
  70. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/actors/core.py +0 -0
  71. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/actors/decider.py +0 -0
  72. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/actors/worker.py +0 -0
  73. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/constants.py +0 -0
  74. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/core.py +0 -0
  75. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/exceptions.py +0 -0
  76. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/__init__.py +0 -0
  77. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/activity.py +0 -0
  78. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/decision/__init__.py +0 -0
  79. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/decision/base.py +0 -0
  80. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/decision/marker.py +0 -0
  81. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/decision/task.py +0 -0
  82. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/decision/timer.py +0 -0
  83. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/decision/workflow.py +0 -0
  84. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/domain.py +0 -0
  85. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/event/__init__.py +0 -0
  86. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/event/compiler.py +0 -0
  87. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/event/factory.py +0 -0
  88. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/event/marker.py +0 -0
  89. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/event/task.py +0 -0
  90. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/event/timer.py +0 -0
  91. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/history/__init__.py +0 -0
  92. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/history/base.py +0 -0
  93. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/models/history/builder.py +0 -0
  94. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/querysets/__init__.py +0 -0
  95. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/querysets/base.py +0 -0
  96. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/querysets/domain.py +0 -0
  97. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/querysets/history.py +0 -0
  98. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/responses/__init__.py +0 -0
  99. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/responses/_base.py +0 -0
  100. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/settings.py +0 -0
  101. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/mapper/utils.py +0 -0
  102. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/process/__init__.py +0 -0
  103. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/process/decider/__init__.py +0 -0
  104. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/process/decider/base.py +0 -0
  105. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/process/decider/command.py +0 -0
  106. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/process/poller.py +0 -0
  107. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/process/worker/__init__.py +0 -0
  108. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/process/worker/command.py +0 -0
  109. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/stats/__init__.py +0 -0
  110. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/stats/base.py +0 -0
  111. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/task.py +0 -0
  112. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/swf/utils.py +0 -0
  113. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/utils/__init__.py +0 -0
  114. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/utils/_dict.py +0 -0
  115. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/utils/_json.py +0 -0
  116. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/utils/retry.py +0 -0
  117. {simpleflow-0.34.0 → simpleflow-0.34.2}/simpleflow/workflow.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simpleflow
3
- Version: 0.34.0
3
+ Version: 0.34.2
4
4
  Summary: Python library for dataflow programming with Amazon SWF
5
5
  Project-URL: documentation, https://botify-labs.github.io/simpleflow
6
6
  Project-URL: repository, https://github.com/botify-labs/simpleflow
7
7
  Project-URL: changelog, https://github.com/botify-labs/simpleflow/blob/main/CHANGELOG.md
8
8
  Author: Greg Leclercq
9
9
  Maintainer-email: tech@botify.com
10
- License: MIT License
10
+ License-Expression: MIT
11
11
  License-File: LICENSE
12
12
  Keywords: amazon swf,simple workflow,simpleflow
13
13
  Classifier: Development Status :: 4 - Beta
@@ -37,31 +37,6 @@ Requires-Dist: psutil
37
37
  Requires-Dist: pytz
38
38
  Requires-Dist: setproctitle
39
39
  Requires-Dist: tabulate<1.0.0,>=0.8.2
40
- Provides-Extra: dev
41
- Requires-Dist: boto3-stubs[s3,swf]; extra == 'dev'
42
- Requires-Dist: cffi==v1.17.0rc1; (python_full_version == '3.13.0b4') and extra == 'dev'
43
- Requires-Dist: flaky; extra == 'dev'
44
- Requires-Dist: hatch; extra == 'dev'
45
- Requires-Dist: invoke; extra == 'dev'
46
- Requires-Dist: moto<5.0.0,>=4.2.8; extra == 'dev'
47
- Requires-Dist: packaging; extra == 'dev'
48
- Requires-Dist: pre-commit; extra == 'dev'
49
- Requires-Dist: pytest; extra == 'dev'
50
- Requires-Dist: pytest-xdist>=3.2.0; extra == 'dev'
51
- Requires-Dist: ruff; extra == 'dev'
52
- Requires-Dist: setuptools<72; extra == 'dev'
53
- Requires-Dist: sphinx; extra == 'dev'
54
- Requires-Dist: sphinx-rtd-theme; extra == 'dev'
55
- Requires-Dist: sure; extra == 'dev'
56
- Requires-Dist: twine; extra == 'dev'
57
- Requires-Dist: typing-extensions; extra == 'dev'
58
- Requires-Dist: vcrpy; extra == 'dev'
59
- Provides-Extra: doc
60
- Requires-Dist: markdown-include; extra == 'doc'
61
- Requires-Dist: mkdocs; extra == 'doc'
62
- Requires-Dist: mkdocs-material; extra == 'doc'
63
- Requires-Dist: pygments; extra == 'doc'
64
- Requires-Dist: pymdown-extensions; extra == 'doc'
65
40
  Description-Content-Type: text/markdown
66
41
 
67
42
  Simpleflow
@@ -7,7 +7,7 @@ name = "simpleflow"
7
7
  description = "Python library for dataflow programming with Amazon SWF"
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.7"
10
- license = { text = "MIT License" }
10
+ license = "MIT"
11
11
  authors = [
12
12
  { name = "Greg Leclercq" },
13
13
  ]
@@ -48,36 +48,6 @@ dependencies = [
48
48
  "pytz",
49
49
  ]
50
50
 
51
- [project.optional-dependencies]
52
- dev = [
53
- "boto3-stubs[s3,swf]",
54
- "cffi==v1.17.0rc1; python_full_version=='3.13.0b4'", # via cryptography via moto, secretstorage
55
- "flaky",
56
- "hatch",
57
- "invoke",
58
- "moto>=4.2.8,<5.0.0",
59
- "packaging",
60
- "pre-commit",
61
- "pytest",
62
- "pytest-xdist>=3.2.0",
63
- "ruff",
64
- "setuptools<72", # See https://github.com/pypa/setuptools/issues/4519
65
- "sphinx",
66
- "sphinx_rtd_theme",
67
- "sure",
68
- "typing-extensions",
69
- "vcrpy",
70
- "twine",
71
- ]
72
-
73
- doc = [
74
- "mkdocs",
75
- "mkdocs-material",
76
- "pygments",
77
- "pymdown-extensions",
78
- "markdown-include",
79
- ]
80
-
81
51
  [project.urls]
82
52
  documentation = "https://botify-labs.github.io/simpleflow"
83
53
  repository = "https://github.com/botify-labs/simpleflow"
@@ -97,10 +67,11 @@ extend-select = [
97
67
  "W", # pycodestyle warnings
98
68
  "RUF", # ruff
99
69
  ]
100
- fixable = ["ALL"]
101
70
  allowed-confusables = ["‘", "’"]
102
71
  extend-ignore = [
103
72
  ]
73
+ ignore = ["E501"]
74
+
104
75
  [tool.ruff.lint.isort]
105
76
  required-imports = ["from __future__ import annotations"]
106
77
 
@@ -117,3 +88,43 @@ path = "simpleflow/__init__.py"
117
88
  [tool.hatch.build]
118
89
  include = ["/simpleflow"]
119
90
  exclude = ["*~"]
91
+
92
+ [dependency-groups]
93
+ dev = [
94
+ "boto3-stubs[s3,swf]",
95
+ "cffi==v1.17.0rc1; python_full_version=='3.13.0b4'", # via cryptography via moto, secretstorage
96
+ "flaky",
97
+ "hatch",
98
+ "invoke",
99
+ "moto>=4.2.8,<5.0.0",
100
+ "packaging",
101
+ "pre-commit",
102
+ "pytest",
103
+ "pytest-xdist>=3.2.0",
104
+ "ruff",
105
+ "setuptools<72", # See https://github.com/pypa/setuptools/issues/4519
106
+ "sphinx",
107
+ "sphinx_rtd_theme",
108
+ "sure",
109
+ "typing-extensions",
110
+ "vcrpy",
111
+ "twine",
112
+ ]
113
+
114
+ doc = [
115
+ "mkdocs",
116
+ "mkdocs-material",
117
+ "pygments",
118
+ "pymdown-extensions",
119
+ "markdown-include",
120
+ ]
121
+
122
+ [[tool.uv.index]]
123
+ name = "pypi"
124
+ url = "https://pypi.org/simple/"
125
+ publish-url = "https://pypi.org/legacy/"
126
+
127
+ [[tool.uv.index]]
128
+ name = "testpypi"
129
+ url = "https://test.pypi.org/simple/"
130
+ publish-url = "https://test.pypi.org/legacy/"
@@ -5,6 +5,6 @@ from .runtime import logger # NOQA
5
5
  from .signal import WaitForSignal # NOQA
6
6
  from .workflow import Workflow # NOQA
7
7
 
8
- __version__ = "0.34.0"
8
+ __version__ = "0.34.2"
9
9
  __author__ = "Greg Leclercq"
10
10
  __license__ = "MIT"
@@ -9,6 +9,7 @@ import time
9
9
  from contextlib import contextmanager
10
10
  from datetime import datetime
11
11
  from typing import TYPE_CHECKING
12
+ from urllib.parse import unquote
12
13
  from uuid import uuid4
13
14
 
14
15
  import click
@@ -50,6 +51,18 @@ def comma_separated_list(value: str) -> list[str]:
50
51
  return value.split(",")
51
52
 
52
53
 
54
+ class RunIdParamType(click.ParamType):
55
+ name = "str"
56
+
57
+ def convert(self, value: Any, param: click.Parameter | None, ctx: click.Context | None) -> str:
58
+ if "%" not in value:
59
+ return value
60
+ return unquote(value)
61
+
62
+
63
+ RUN_ID = RunIdParamType()
64
+
65
+
53
66
  CONTEXT_SETTINGS = dict(help_option_names=["--help", "-h"])
54
67
 
55
68
 
@@ -207,7 +220,7 @@ def start_workflow(
207
220
  return execution
208
221
 
209
222
 
210
- @click.argument("run_id", required=False)
223
+ @click.argument("run_id", type=RUN_ID, required=False)
211
224
  @click.argument("workflow_id")
212
225
  @click.argument(
213
226
  "domain",
@@ -223,10 +236,13 @@ def terminate_workflow(
223
236
  run_id: str | None,
224
237
  ):
225
238
  ex = helpers.get_workflow_execution(domain, workflow_id, run_id)
239
+ if not ex:
240
+ print(f"Execution {workflow_id} {run_id} not found" if run_id else f"Workflow {workflow_id} not found")
241
+ sys.exit(1)
226
242
  ex.terminate()
227
243
 
228
244
 
229
- @click.argument("run_id", required=False)
245
+ @click.argument("run_id", type=RUN_ID, required=False)
230
246
  @click.argument("workflow_id")
231
247
  @click.argument(
232
248
  "domain",
@@ -238,6 +254,9 @@ def terminate_workflow(
238
254
  )
239
255
  def restart_workflow(domain: str, workflow_id: str, run_id: str | None):
240
256
  ex = helpers.get_workflow_execution(domain, workflow_id, run_id)
257
+ if not ex:
258
+ print(f"Execution {workflow_id} {run_id} not found" if run_id else f"Workflow {workflow_id} not found")
259
+ sys.exit(1)
241
260
  history = ex.history()
242
261
  ex.terminate(reason="workflow.restart")
243
262
  new_ex = ex.workflow_type.start_execution(
@@ -258,7 +277,7 @@ def with_format(ctx):
258
277
  )
259
278
 
260
279
 
261
- @click.argument("run_id", required=False)
280
+ @click.argument("run_id", type=RUN_ID, required=False)
262
281
  @click.argument("workflow_id")
263
282
  @click.argument(
264
283
  "domain",
@@ -283,7 +302,7 @@ def workflow_info(ctx, domain: str, workflow_id: str, run_id: str | None):
283
302
  type=int,
284
303
  help="Maximum number of tasks to display.",
285
304
  )
286
- @click.argument("run_id", required=False)
305
+ @click.argument("run_id", type=RUN_ID, required=False)
287
306
  @click.argument("workflow_id")
288
307
  @click.argument(
289
308
  "domain",
@@ -302,6 +321,7 @@ def profile(ctx, domain, workflow_id, run_id, nb_tasks):
302
321
  )
303
322
 
304
323
 
324
+ # FIXME superseded by history
305
325
  @click.option(
306
326
  "--nb-tasks",
307
327
  "-n",
@@ -309,7 +329,7 @@ def profile(ctx, domain, workflow_id, run_id, nb_tasks):
309
329
  type=int,
310
330
  help="Maximum number of tasks to display.",
311
331
  )
312
- @click.argument("run_id", required=False)
332
+ @click.argument("run_id", type=RUN_ID, required=False)
313
333
  @click.argument("workflow_id")
314
334
  @click.argument(
315
335
  "domain",
@@ -334,6 +354,7 @@ def workflow_tasks(
334
354
  )
335
355
 
336
356
 
357
+ # FIXME superseded by filter
337
358
  @click.argument(
338
359
  "domain",
339
360
  envvar="SWF_DOMAIN",
@@ -360,18 +381,19 @@ def list_workflows(ctx, domain: str, status: str, started_since: int):
360
381
  _NOTSET = object()
361
382
 
362
383
 
363
- @click.argument(
364
- "domain",
365
- envvar="SWF_DOMAIN",
366
- )
367
384
  @cli.command(
368
385
  "workflow.history",
369
386
  help="Workflow history from workflow WORKFLOW_ID [RUN_ID].",
370
387
  )
371
388
  @click.argument("workflow_id")
372
- @click.argument("run_id", required=False)
389
+ @click.argument("run_id", type=RUN_ID, required=False)
390
+ @click.option(
391
+ "--domain",
392
+ envvar="SWF_DOMAIN",
393
+ )
373
394
  @click.option(
374
395
  "--output-format",
396
+ "--of",
375
397
  "-o",
376
398
  required=False,
377
399
  type=click.Choice(["events", "raw", "cooked"]),
@@ -388,12 +410,15 @@ def workflow_history(
388
410
  output_format: str,
389
411
  reverse_order: bool = False,
390
412
  ) -> None:
391
- from simpleflow.swf.mapper.models.history.base import History as BaseHistory
392
-
393
413
  if ctx.parent.params["format"] != "json" or not ctx.parent.params["header"]:
394
414
  raise NotImplementedError("Only pretty JSON mode is implemented")
395
415
 
416
+ from simpleflow.swf.mapper.models.history.base import History as BaseHistory
417
+
396
418
  ex = helpers.get_workflow_execution(domain, workflow_id, run_id)
419
+ if not ex:
420
+ print(f"Execution {workflow_id} {run_id} not found" if run_id else f"Workflow {workflow_id} not found")
421
+ sys.exit(1)
397
422
  events = ex.history_events(
398
423
  callback=get_progression_callback("events"),
399
424
  reverse_order=reverse_order,
@@ -418,11 +443,15 @@ def workflow_history(
418
443
  elif output_format == "cooked":
419
444
  history.parse()
420
445
  events = {
446
+ "workflow": history.workflow,
421
447
  "activities": history.activities,
422
448
  "child_workflows": history.child_workflows,
423
449
  "markers": history.markers,
424
- "signals": history.signals,
425
450
  "timers": history.timers,
451
+ "signals": history.signals,
452
+ "signal_lists": history.signal_lists,
453
+ "external_workflows_signaling": history.external_workflows_signaling,
454
+ "signaled_workflows": history.signaled_workflows,
426
455
  }
427
456
  else:
428
457
  raise NotImplementedError
@@ -740,7 +769,7 @@ def standalone(
740
769
  # get the previous execution history, it will serve as "default history"
741
770
  # for activities that succeeded in the previous execution
742
771
  logger.info(
743
- f"retrieving history of previous execution: domain={domain} " f"workflow_id={repair} run_id={repair_run_id}"
772
+ f"retrieving history of previous execution: domain={domain} workflow_id={repair} run_id={repair_run_id}"
744
773
  )
745
774
  workflow_execution = get_workflow_execution(domain, repair, run_id=repair_run_id)
746
775
  previous_history = History(workflow_execution.history())
@@ -826,6 +855,11 @@ def standalone(
826
855
  ex.workflow_id,
827
856
  ex.run_id,
828
857
  )
858
+ if not ex:
859
+ print(
860
+ f"Execution {workflow_id} {ex.run_id} not found" if ex.run_id else f"Workflow {workflow_id} not found"
861
+ )
862
+ sys.exit(1)
829
863
  if display_status:
830
864
  print(f"status: {ex.status}", file=sys.stderr)
831
865
  if ex.status == ex.STATUS_CLOSED:
@@ -841,7 +875,7 @@ def standalone(
841
875
  @click.option("--domain", envvar="SWF_DOMAIN", required=False, help="Amazon SWF Domain.")
842
876
  @click.option("--workflow-id", required=True, help="ID of the workflow execution.")
843
877
  @click.option("--input", "-i", required=False, help="JSON input of the workflow.")
844
- @click.option("--run-id", required=False, help="Run ID of the workflow execution.")
878
+ @click.option("--run-id", type=RUN_ID, required=False, help="Run ID of the workflow execution.")
845
879
  @click.option(
846
880
  "--scheduled-id",
847
881
  required=False,
@@ -891,7 +925,7 @@ def activity_rerun(domain, workflow_id, run_id, input, scheduled_id, activity_id
891
925
  for line in json_dumps(params, pretty=True).split("\n"):
892
926
  logger.debug(line)
893
927
  if input_override:
894
- logger.info("NB: input will be overriden with the passed one!")
928
+ logger.info("NB: input will be overridden with the passed one!")
895
929
  logger.info(f"Will re-run: {task}(*{args}, **{kwargs}) [+meta={meta}]")
896
930
 
897
931
  # download binaries if needed
@@ -913,8 +947,7 @@ def activity_rerun(domain, workflow_id, run_id, input, scheduled_id, activity_id
913
947
  )
914
948
  @cli.command(
915
949
  "info",
916
- help="Display versions, settings, and environment variables. "
917
- "Available sections: versions, settings, environment.",
950
+ help="Display versions, settings, and environment variables. Available sections: versions, settings, environment.",
918
951
  )
919
952
  def info(sections):
920
953
  @contextmanager
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from simpleflow.activity import Activity
4
- from simpleflow.utils import import_object_from_module
4
+ from simpleflow.utils import format_exc, import_object_from_module
5
5
 
6
6
  from .exceptions import DispatchError
7
7
 
@@ -25,9 +25,9 @@ class Dispatcher:
25
25
  module_name, activity_name = name.rsplit(".", 1)
26
26
  try:
27
27
  activity = import_object_from_module(module_name, activity_name)
28
- except ImportError:
28
+ except ImportError as e:
29
29
  # We were not able to import a function at all.
30
- raise DispatchError(f"unable to import '{name}'")
30
+ raise DispatchError(f"unable to import '{name}': {format_exc(e)}")
31
31
  if not isinstance(activity, Activity):
32
32
  # We managed to import a function (or callable) but it's not an
33
33
  # "Activity". We will transform it into an Activity now. That way
@@ -43,11 +43,16 @@ class History:
43
43
  self.started_decision_id: int | None = None
44
44
  self.completed_decision_id: int | None = None
45
45
  self.last_event_id: int | None = None
46
+ self._workflow: dict[str, Any] = {}
46
47
 
47
48
  @property
48
49
  def swf_history(self) -> simpleflow.swf.mapper.models.history.History:
49
50
  return self._history
50
51
 
52
+ @property
53
+ def workflow(self):
54
+ return self._workflow
55
+
51
56
  @property
52
57
  def activities(self) -> dict[str, ActivityTaskEventDict]:
53
58
  """
@@ -432,6 +437,118 @@ class History:
432
437
  """
433
438
  Parse a workflow event.
434
439
  """
440
+ if event.state == "started":
441
+ self._workflow.update(
442
+ {
443
+ "state": event.state,
444
+ f"{event.state}_id": event.id,
445
+ f"{event.state}_timestamp": event.timestamp,
446
+ "child_policy": getattr(event, "child_policy", None),
447
+ "task_list": event.task_list["name"],
448
+ "workflow_type": event.workflow_type,
449
+ "continued_execution_run_id": getattr(event, "continued_execution_run_id", None),
450
+ "execution_start_to_close_timeout": getattr(event, "execution_start_to_close_timeout", None),
451
+ "input": getattr(event, "input", None),
452
+ "lambda_role": getattr(event, "lambda_role", None),
453
+ "parent_initiated_event_id": getattr(event, "parent_initiated_event_id", None),
454
+ "parent_workflow_execution": getattr(event, "parent_workflow_execution", None),
455
+ "tag_list": getattr(event, "tag_list", None),
456
+ "task_priority": getattr(event, "task_priority", None),
457
+ "task_start_to_close_timeout": getattr(event, "task_start_to_close_timeout", None),
458
+ }
459
+ )
460
+ elif event.state == "continued_as_new":
461
+ self._workflow.update(
462
+ {
463
+ "state": event.state,
464
+ f"{event.state}_id": event.id,
465
+ f"{event.state}_timestamp": event.timestamp,
466
+ f"{event.state}_decision_task_completed_event_id": event.decision_task_completed_event_id,
467
+ "new_execution_run_id": event.new_execution_run_id,
468
+ "task_list": event.task_list["name"],
469
+ "workflow_type": event.workflow_type,
470
+ "execution_start_to_close_timeout": getattr(event, "execution_start_to_close_timeout", None),
471
+ "input": getattr(event, "input", None),
472
+ "lambda_role": getattr(event, "lambda_role", None),
473
+ "tag_list": getattr(event, "tag_list", None),
474
+ "task_priority": getattr(event, "task_priority", None),
475
+ "task_start_to_close_timeout": getattr(event, "task_start_to_close_timeout", None),
476
+ }
477
+ )
478
+ elif event.state == "completed":
479
+ self._workflow.update(
480
+ {
481
+ "state": event.state,
482
+ f"{event.state}_id": event.id,
483
+ f"{event.state}_timestamp": event.timestamp,
484
+ "initiated_event_id": getattr(event, "initiated_event_id", None),
485
+ "result": getattr(event, "result", None),
486
+ }
487
+ )
488
+ elif event.state == "cancelled":
489
+ self._workflow.update(
490
+ {
491
+ "state": event.state,
492
+ f"{event.state}_id": event.id,
493
+ f"{event.state}_timestamp": event.timestamp,
494
+ "initiated_event_id": getattr(event, "initiated_event_id", None),
495
+ "decision_task_completed_event_id": event.decision_task_completed_event_id,
496
+ "details": getattr(event, "details", None),
497
+ }
498
+ )
499
+ elif event.state == "failed":
500
+ self._workflow.update(
501
+ {
502
+ "state": event.state,
503
+ f"{event.state}_id": event.id,
504
+ f"{event.state}_timestamp": event.timestamp,
505
+ "initiated_event_id": getattr(event, "initiated_event_id", None),
506
+ "decision_task_completed_event_id": event.decision_task_completed_event_id,
507
+ "reason": getattr(event, "reason", None),
508
+ "details": getattr(event, "details", None),
509
+ }
510
+ )
511
+ elif event.state == "terminated":
512
+ self._workflow.update(
513
+ {
514
+ "state": event.state,
515
+ f"{event.state}_id": event.id,
516
+ f"{event.state}_timestamp": event.timestamp,
517
+ "initiated_event_id": getattr(event, "initiated_event_id", None),
518
+ "cause": getattr(event, "cause", None),
519
+ "details": getattr(event, "details", None),
520
+ }
521
+ )
522
+ elif event.state == "timed_out":
523
+ self._workflow.update(
524
+ {
525
+ "state": event.state,
526
+ f"{event.state}_id": event.id,
527
+ f"{event.state}_timestamp": event.timestamp,
528
+ "initiated_event_id": getattr(event, "initiated_event_id", None),
529
+ "timeout_type": event.timeout_type,
530
+ }
531
+ )
532
+ # elif event.state in (
533
+ # "cancel_failed",
534
+ # "complete_failed",
535
+ # "continue_as_new",
536
+ # "fail_failed",
537
+ # "start_child_failed",
538
+ # "start_failed",
539
+ # "terminate_failed",
540
+ # ):
541
+ # self._workflow.update(
542
+ # {
543
+ # "state": event.state,
544
+ # f"{event.state}_id": event.id,
545
+ # f"{event.state}_cause": getattr(event, "cause", None),
546
+ # f"{event.state}_decision_task_completed_event_id": event.decision_task_completed_event_id,
547
+ # }
548
+ # )
549
+
550
+ if event.state == "cancel_requested":
551
+ self._workflow.update()
435
552
  if event.state == "signaled":
436
553
  signal = {
437
554
  "type": "signal",
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import collections
4
3
  import sys
5
4
  import traceback
6
5
  import uuid
@@ -27,8 +26,8 @@ class Executor(executor.Executor):
27
26
  super().__init__(workflow_class)
28
27
  self.update_workflow_class()
29
28
  self.nb_activities = 0
30
- self.signals_sent = set()
31
- self._markers = collections.OrderedDict()
29
+ self.signals_sent: set[str] = set()
30
+ self._markers: dict[str, list[Marker]] = {}
32
31
 
33
32
  self.wf_run_id = []
34
33
  self.wf_id = []
@@ -206,7 +205,7 @@ class Executor(executor.Executor):
206
205
  self.update_workflow_class()
207
206
  self.nb_activities = 0
208
207
  self.signals_sent = set()
209
- self._markers = collections.OrderedDict()
208
+ self._markers = {}
210
209
 
211
210
  self.wf_run_id = []
212
211
  self.wf_id = []
@@ -6,7 +6,6 @@ import os
6
6
  import re
7
7
  import time
8
8
  from abc import ABC
9
- from collections import OrderedDict
10
9
  from typing import Any
11
10
  from urllib.parse import quote_plus
12
11
 
@@ -18,31 +17,29 @@ ACTIVITY_KEY_RE = re.compile(r"activity\.(.+)\.json")
18
17
 
19
18
 
20
19
  class StepIO:
21
- def __init__(self):
20
+ def __init__(self) -> None:
22
21
  self.bytes = 0
23
22
  self.records = 0
24
23
  self.sampled = False
25
24
 
26
- def get_stats(self, time_total):
25
+ def get_stats(self, time_total: float) -> dict[str, Any]:
27
26
  mb_s = None
28
27
  rec_s = None
29
28
  if self.bytes:
30
29
  mb_s = round(float(self.bytes) / (1024 * 1024) / time_total, 2)
31
30
  if self.records:
32
31
  rec_s = int(self.records / time_total)
33
- return OrderedDict(
34
- [
35
- ("bytes", self.bytes),
36
- ("records", self.records),
37
- ("mb_s", mb_s),
38
- ("rec_s", rec_s),
39
- ("sampled", self.sampled),
40
- ]
41
- )
32
+ return {
33
+ "bytes": self.bytes,
34
+ "records": self.records,
35
+ "mb_s": mb_s,
36
+ "rec_s": rec_s,
37
+ "sampled": self.sampled,
38
+ }
42
39
 
43
40
 
44
41
  class Step:
45
- def __init__(self, name, task):
42
+ def __init__(self, name: str, task: MetrologyTask) -> None:
46
43
  self.name = name
47
44
  self.task = task
48
45
  self.read = StepIO()
@@ -57,22 +54,17 @@ class Step:
57
54
  self.time_total = self.time_finished - self.time_started
58
55
 
59
56
  def get_stats(self):
60
- stats = OrderedDict(
61
- [
62
- ("name", self.name),
63
- ("metadata", self.metadata),
64
- ("time_started", self.time_started),
65
- ("time_finished", self.time_finished),
66
- ("time_total", self.time_total),
67
- ("read", self.read.get_stats(self.time_total)),
68
- ("write", self.write.get_stats(self.time_total)),
69
- ]
70
- )
71
- return stats
57
+ stats = {
58
+ "name": self.name,
59
+ "metadata": self.metadata,
60
+ "time_started": self.time_started,
61
+ "time_finished": self.time_finished,
62
+ "time_total": self.time_total,
63
+ "read": self.read.get_stats(self.time_total),
64
+ "write": self.write.get_stats(self.time_total),
65
+ }
72
66
 
73
- def mset_metadata(self, kvs):
74
- for k, v in kvs:
75
- self.metadata[k] = v
67
+ return stats
76
68
 
77
69
 
78
70
  class StepExecution:
@@ -90,14 +82,14 @@ class MetrologyTask:
90
82
  context: dict[str, Any]
91
83
  steps: list[Step]
92
84
 
93
- def can_upload(self):
85
+ def can_upload(self) -> bool:
94
86
  if not hasattr(self, "context"):
95
87
  return False
96
88
  return all(c in self.context for c in ("workflow_id", "run_id", "activity_id"))
97
89
 
98
90
  @property
99
91
  def metrology_path(self):
100
- path = []
92
+ path: list[str] = []
101
93
  if settings.METROLOGY_PATH_PREFIX is not None:
102
94
  path.append(settings.METROLOGY_PATH_PREFIX)
103
95
  path.append(self.context["workflow_id"])
@@ -105,7 +97,7 @@ class MetrologyTask:
105
97
  path.append(f"activity.{self.context['activity_id']}.json")
106
98
  return str(os.path.join(*path))
107
99
 
108
- def step(self, name):
100
+ def step(self, name: str) -> StepExecution:
109
101
  """
110
102
  To be called in a `with` execution
111
103
  Ex :
@@ -208,7 +208,7 @@ class Supervisor(NamedMixin):
208
208
  Terminate all worker processes managed by this Supervisor.
209
209
  """
210
210
  self._terminating = True
211
- logger.info("process: will stop workers, this might take up several minutes. " "Please, be patient.")
211
+ logger.info("process: will stop workers, this might take up several minutes. Please, be patient.")
212
212
  self._killall()
213
213
 
214
214
  def _killall(self):