dotflow 0.16.0.dev2__tar.gz → 0.16.0.dev4__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.
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/PKG-INFO +1 -1
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/__init__.py +1 -1
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/commands/start.py +32 -27
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/setup.py +17 -5
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/engine.py +3 -2
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/exception.py +18 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/__init__.py +0 -2
- dotflow-0.16.0.dev4/dotflow/providers/server_default.py +116 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/pyproject.toml +2 -2
- dotflow-0.16.0.dev2/dotflow/providers/server_api.py +0 -88
- dotflow-0.16.0.dev2/dotflow/providers/server_default.py +0 -23
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/LICENSE +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/README.md +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/file.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/flow.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/http.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/log.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/metrics.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/notify.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/scheduler.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/server.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/storage.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/tcp.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/abc/tracer.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/command.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/commands/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/commands/cloud.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/commands/deploy.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/commands/init.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/commands/log.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/commands/schedule.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/commands/storage.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/validators/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cli/validators/start.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/alibaba/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/alibaba/constants.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/alibaba/deployers/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/alibaba/deployers/fc.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/alibaba/deployers/fc_scheduled.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/alibaba/services/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/alibaba/services/cr.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/constants.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/deployers/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/deployers/base_lambda.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/deployers/ecs.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/deployers/ecs_scheduled.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/deployers/lambda_.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/deployers/lambda_api.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/deployers/lambda_s3.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/deployers/lambda_sqs.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/schedule.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/services/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/services/cloudwatch.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/services/ecr.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/services/iam.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/aws/services/s3.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/core.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/gcp/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/gcp/constants.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/gcp/deployers/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/gcp/deployers/cloudrun.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/gcp/services/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/gcp/services/apis.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/gcp/services/artifact_registry.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/gcp/services/gcs.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/github/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/github/constants.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/github/deployers/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/cloud/github/deployers/actions.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/action.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/config.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/context.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/decorators/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/decorators/time.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/dotflow.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/module.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/serializers/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/serializers/task.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/serializers/transport.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/serializers/workflow.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/task.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/types/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/types/execution.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/types/overlap.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/types/status.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/types/storage.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/types/workflow.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/core/workflow.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/logging.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/main.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/log_default.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/log_opentelemetry.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/log_sentry.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/metrics_default.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/metrics_opentelemetry.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/notify_default.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/notify_discord.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/notify_telegram.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/scheduler_cron.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/scheduler_default.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/storage_default.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/storage_file.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/storage_gcs.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/storage_s3.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/tracer_default.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/tracer_opentelemetry.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/providers/tracer_sentry.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/settings.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/types.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/utils/__init__.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/utils/basic_functions.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/utils/error_handler.py +0 -0
- {dotflow-0.16.0.dev2 → dotflow-0.16.0.dev4}/dotflow/utils/tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dotflow
|
|
3
|
-
Version: 0.16.0.
|
|
3
|
+
Version: 0.16.0.dev4
|
|
4
4
|
Summary: 🎲 Dotflow turns an idea into flow! Lightweight Python library for execution pipelines with retry, parallel, cron and async support.
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
"""Command start module"""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
from os import system
|
|
5
4
|
|
|
6
5
|
from dotflow import Config, DotFlow
|
|
7
6
|
from dotflow.cli.command import Command
|
|
7
|
+
from dotflow.core.exception import InvalidWorkflowFactory, WorkflowFlagConflict
|
|
8
|
+
from dotflow.core.module import Module
|
|
8
9
|
from dotflow.core.types.execution import TypeExecution
|
|
9
10
|
from dotflow.providers import (
|
|
10
|
-
ServerAPI,
|
|
11
|
-
ServerDefault,
|
|
12
11
|
StorageDefault,
|
|
13
12
|
StorageFile,
|
|
14
13
|
StorageGCS,
|
|
15
14
|
StorageS3,
|
|
16
15
|
)
|
|
16
|
+
from dotflow.utils.basic_functions import basic_callback
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class StartCommand(Command):
|
|
20
20
|
def setup(self):
|
|
21
|
+
if getattr(self.params, "workflow", None):
|
|
22
|
+
self._start_from_factory()
|
|
23
|
+
else:
|
|
24
|
+
self._start_from_step()
|
|
25
|
+
|
|
26
|
+
if self.params.mode == TypeExecution.BACKGROUND:
|
|
27
|
+
system("/bin/bash")
|
|
28
|
+
|
|
29
|
+
def _start_from_step(self):
|
|
21
30
|
workflow = self._new_workflow()
|
|
22
31
|
|
|
23
32
|
workflow.task.add(
|
|
@@ -28,36 +37,32 @@ class StartCommand(Command):
|
|
|
28
37
|
|
|
29
38
|
workflow.start(mode=self.params.mode)
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
def _start_from_factory(self):
|
|
41
|
+
step_only_flags = {
|
|
42
|
+
"--callback": self.params.callback is not basic_callback,
|
|
43
|
+
"--initial-context": self.params.initial_context is not None,
|
|
44
|
+
}
|
|
45
|
+
for flag, provided in step_only_flags.items():
|
|
46
|
+
if provided:
|
|
47
|
+
raise WorkflowFlagConflict(flag=flag)
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
return DotFlow(workflow_id=workflow_id)
|
|
40
|
-
return DotFlow(config=config, workflow_id=workflow_id)
|
|
49
|
+
factory = Module(self.params.workflow)
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
server = self._build_server()
|
|
51
|
+
if not callable(factory):
|
|
52
|
+
raise InvalidWorkflowFactory(factory=self.params.workflow)
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
return None
|
|
54
|
+
result = factory()
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
server=server or ServerDefault(),
|
|
52
|
-
)
|
|
56
|
+
if not isinstance(result, DotFlow):
|
|
57
|
+
raise InvalidWorkflowFactory(factory=self.params.workflow)
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
base_url = os.getenv("SERVER_BASE_URL")
|
|
56
|
-
user_token = os.getenv("SERVER_USER_TOKEN")
|
|
59
|
+
result.start(mode=self.params.mode)
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
def _new_workflow(self):
|
|
62
|
+
storage = self._build_storage()
|
|
63
|
+
if storage is None:
|
|
64
|
+
return DotFlow()
|
|
65
|
+
return DotFlow(config=Config(storage=storage))
|
|
61
66
|
|
|
62
67
|
def _build_storage(self):
|
|
63
68
|
if not self.params.storage:
|
|
@@ -16,7 +16,9 @@ from dotflow.core.exception import (
|
|
|
16
16
|
MESSAGE_UNKNOWN_ERROR,
|
|
17
17
|
ExecutionModeNotExist,
|
|
18
18
|
ImportModuleError,
|
|
19
|
+
InvalidWorkflowFactory,
|
|
19
20
|
MissingActionDecorator,
|
|
21
|
+
WorkflowFlagConflict,
|
|
20
22
|
)
|
|
21
23
|
from dotflow.core.types import TypeExecution, TypeOverlap, TypeStorage
|
|
22
24
|
from dotflow.logging import logger
|
|
@@ -56,12 +58,16 @@ class Command:
|
|
|
56
58
|
self.cmd_init.set_defaults(exec=InitCommand)
|
|
57
59
|
|
|
58
60
|
def setup_start(self):
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
cmd_start_parser = self.subparsers.add_parser("start", help="Start")
|
|
62
|
+
entry_group = cmd_start_parser.add_mutually_exclusive_group(
|
|
63
|
+
required=True
|
|
62
64
|
)
|
|
65
|
+
entry_group.add_argument("-s", "--step")
|
|
66
|
+
entry_group.add_argument("-w", "--workflow")
|
|
63
67
|
|
|
64
|
-
self.cmd_start.
|
|
68
|
+
self.cmd_start = cmd_start_parser.add_argument_group(
|
|
69
|
+
"Usage: dotflow start [OPTIONS]"
|
|
70
|
+
)
|
|
65
71
|
self.cmd_start.add_argument("-c", "--callback", default=basic_callback)
|
|
66
72
|
self.cmd_start.add_argument("-i", "--initial-context")
|
|
67
73
|
self.cmd_start.add_argument(
|
|
@@ -88,7 +94,7 @@ class Command:
|
|
|
88
94
|
],
|
|
89
95
|
)
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
cmd_start_parser.set_defaults(exec=StartCommand)
|
|
92
98
|
|
|
93
99
|
def setup_schedule(self):
|
|
94
100
|
self.cmd_schedule = self.subparsers.add_parser(
|
|
@@ -233,6 +239,12 @@ class Command:
|
|
|
233
239
|
except ImportModuleError as err:
|
|
234
240
|
print(settings.WARNING_ALERT, err)
|
|
235
241
|
|
|
242
|
+
except InvalidWorkflowFactory as err:
|
|
243
|
+
print(settings.WARNING_ALERT, err)
|
|
244
|
+
|
|
245
|
+
except WorkflowFlagConflict as err:
|
|
246
|
+
print(settings.WARNING_ALERT, err)
|
|
247
|
+
|
|
236
248
|
except KeyboardInterrupt:
|
|
237
249
|
print("\n", settings.INFO_ALERT, "Aborted.")
|
|
238
250
|
|
|
@@ -257,13 +257,14 @@ class TaskEngine:
|
|
|
257
257
|
|
|
258
258
|
for new in ordered_list:
|
|
259
259
|
new_object = getattr(class_instance, new[1])
|
|
260
|
+
task_ulid = str(ULID())
|
|
260
261
|
try:
|
|
261
262
|
subcontext = new_object(
|
|
262
263
|
initial_context=self.task.initial_context,
|
|
263
264
|
previous_context=previous_context,
|
|
264
265
|
task=self.task,
|
|
265
266
|
)
|
|
266
|
-
subcontext.task_id =
|
|
267
|
+
subcontext.task_id = task_ulid
|
|
267
268
|
new_context.storage.append(subcontext)
|
|
268
269
|
previous_context = subcontext
|
|
269
270
|
|
|
@@ -277,7 +278,7 @@ class TaskEngine:
|
|
|
277
278
|
previous_context=previous_context,
|
|
278
279
|
task=self.task,
|
|
279
280
|
)
|
|
280
|
-
subcontext.task_id =
|
|
281
|
+
subcontext.task_id = task_ulid
|
|
281
282
|
new_context.storage.append(subcontext)
|
|
282
283
|
previous_context = subcontext
|
|
283
284
|
|
|
@@ -15,6 +15,12 @@ MESSAGE_PROBLEM_ORDERING = (
|
|
|
15
15
|
MESSAGE_MODULE_NOT_FOUND = (
|
|
16
16
|
"Module '{module}' not found. Please install with 'pip install {library}'"
|
|
17
17
|
)
|
|
18
|
+
MESSAGE_INVALID_WORKFLOW_FACTORY = (
|
|
19
|
+
"'{factory}' must be a callable that returns a DotFlow instance."
|
|
20
|
+
)
|
|
21
|
+
MESSAGE_WORKFLOW_FLAG_CONFLICT = (
|
|
22
|
+
"{flag} is only valid with --step and cannot be used with --workflow."
|
|
23
|
+
)
|
|
18
24
|
|
|
19
25
|
|
|
20
26
|
class MissingActionDecorator(Exception):
|
|
@@ -49,6 +55,18 @@ class ModuleNotFound(Exception):
|
|
|
49
55
|
)
|
|
50
56
|
|
|
51
57
|
|
|
58
|
+
class InvalidWorkflowFactory(Exception):
|
|
59
|
+
def __init__(self, factory: str):
|
|
60
|
+
super().__init__(
|
|
61
|
+
MESSAGE_INVALID_WORKFLOW_FACTORY.format(factory=factory)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class WorkflowFlagConflict(Exception):
|
|
66
|
+
def __init__(self, flag: str):
|
|
67
|
+
super().__init__(MESSAGE_WORKFLOW_FLAG_CONFLICT.format(flag=flag))
|
|
68
|
+
|
|
69
|
+
|
|
52
70
|
class ExecutionWithClassError(Exception):
|
|
53
71
|
def __init__(self):
|
|
54
72
|
super().__init__("Unknown")
|
|
@@ -6,7 +6,6 @@ from dotflow.providers.notify_default import NotifyDefault
|
|
|
6
6
|
from dotflow.providers.notify_discord import NotifyDiscord
|
|
7
7
|
from dotflow.providers.notify_telegram import NotifyTelegram
|
|
8
8
|
from dotflow.providers.scheduler_default import SchedulerDefault
|
|
9
|
-
from dotflow.providers.server_api import ServerAPI
|
|
10
9
|
from dotflow.providers.server_default import ServerDefault
|
|
11
10
|
from dotflow.providers.storage_default import StorageDefault
|
|
12
11
|
from dotflow.providers.storage_file import StorageFile
|
|
@@ -21,7 +20,6 @@ __all__ = [
|
|
|
21
20
|
"NotifyTelegram",
|
|
22
21
|
"SchedulerCron",
|
|
23
22
|
"SchedulerDefault",
|
|
24
|
-
"ServerAPI",
|
|
25
23
|
"ServerDefault",
|
|
26
24
|
"StorageDefault",
|
|
27
25
|
"StorageFile",
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""ServerDefault — no-op or managed HTTP server provider."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from functools import wraps
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from requests import patch, post
|
|
10
|
+
from requests.exceptions import RequestException
|
|
11
|
+
|
|
12
|
+
from dotflow.abc.server import Server
|
|
13
|
+
from dotflow.logging import logger
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def managed(method):
|
|
17
|
+
@wraps(method)
|
|
18
|
+
def wrapper(self, *args, **kwargs):
|
|
19
|
+
if not self._managed:
|
|
20
|
+
return
|
|
21
|
+
return method(self, *args, **kwargs)
|
|
22
|
+
|
|
23
|
+
return wrapper
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ServerDefault(Server):
|
|
27
|
+
"""Default Server provider with auto-detected managed mode.
|
|
28
|
+
|
|
29
|
+
When ``SERVER_BASE_URL`` and ``SERVER_USER_TOKEN`` env vars are set,
|
|
30
|
+
sends workflow and task events to the remote API. Otherwise, all
|
|
31
|
+
methods are no-ops.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
MAX_RESULT_SIZE = 5_000_000
|
|
35
|
+
TIMEOUT = 15.0
|
|
36
|
+
|
|
37
|
+
ENDPOINT_WORKFLOWS = "/workflows"
|
|
38
|
+
ENDPOINT_WORKFLOW = "/workflows/{workflow_id}"
|
|
39
|
+
ENDPOINT_TASKS = "/workflows/{workflow_id}/tasks"
|
|
40
|
+
ENDPOINT_TASK = "/workflows/{workflow_id}/tasks/{task_id}"
|
|
41
|
+
|
|
42
|
+
def __init__(self) -> None:
|
|
43
|
+
base_url = os.getenv("SERVER_BASE_URL")
|
|
44
|
+
user_token = os.getenv("SERVER_USER_TOKEN")
|
|
45
|
+
self._managed = bool(base_url and user_token)
|
|
46
|
+
|
|
47
|
+
self._base_url = base_url.rstrip("/") if base_url else None
|
|
48
|
+
self._user_token = user_token
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def _headers(self) -> dict:
|
|
52
|
+
return {
|
|
53
|
+
"Authorization": f"Bearer {self._user_token}",
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def _post(self, url: str, json: dict) -> None:
|
|
58
|
+
try:
|
|
59
|
+
response = post(
|
|
60
|
+
url,
|
|
61
|
+
json=json,
|
|
62
|
+
headers=self._headers,
|
|
63
|
+
timeout=self.TIMEOUT,
|
|
64
|
+
)
|
|
65
|
+
logger.info("POST %s [%s]", url, response.status_code)
|
|
66
|
+
except RequestException as error:
|
|
67
|
+
logger.error("POST %s failed: %s", url, error)
|
|
68
|
+
|
|
69
|
+
def _patch(self, url: str, json: dict) -> None:
|
|
70
|
+
try:
|
|
71
|
+
response = patch(
|
|
72
|
+
url,
|
|
73
|
+
json=json,
|
|
74
|
+
headers=self._headers,
|
|
75
|
+
timeout=self.TIMEOUT,
|
|
76
|
+
)
|
|
77
|
+
logger.info("PATCH %s [%s]", url, response.status_code)
|
|
78
|
+
except RequestException as error:
|
|
79
|
+
logger.error("PATCH %s failed: %s", url, error)
|
|
80
|
+
|
|
81
|
+
@managed
|
|
82
|
+
def create_workflow(self, workflow: Any) -> None:
|
|
83
|
+
self._post(
|
|
84
|
+
self._base_url + self.ENDPOINT_WORKFLOWS,
|
|
85
|
+
json={"id": str(workflow)},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@managed
|
|
89
|
+
def update_workflow(self, workflow: Any, status: str = "") -> None:
|
|
90
|
+
self._patch(
|
|
91
|
+
self._base_url
|
|
92
|
+
+ self.ENDPOINT_WORKFLOW.format(workflow_id=workflow),
|
|
93
|
+
json={"status": status},
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
@managed
|
|
97
|
+
def create_task(self, task: Any) -> None:
|
|
98
|
+
data = task.result(max=self.MAX_RESULT_SIZE)
|
|
99
|
+
data["id"] = data.pop("task_id", task.task_id)
|
|
100
|
+
self._post(
|
|
101
|
+
self._base_url
|
|
102
|
+
+ self.ENDPOINT_TASKS.format(workflow_id=task.workflow_id),
|
|
103
|
+
json=data,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
@managed
|
|
107
|
+
def update_task(self, task: Any) -> None:
|
|
108
|
+
data = task.result(max=self.MAX_RESULT_SIZE)
|
|
109
|
+
self._patch(
|
|
110
|
+
self._base_url
|
|
111
|
+
+ self.ENDPOINT_TASK.format(
|
|
112
|
+
workflow_id=task.workflow_id,
|
|
113
|
+
task_id=task.task_id,
|
|
114
|
+
),
|
|
115
|
+
json=data,
|
|
116
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dotflow"
|
|
3
|
-
version = "0.16.0.
|
|
3
|
+
version = "0.16.0.dev4"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="Fernando Celmer", email="email@fernandocelmer.com" },
|
|
6
6
|
]
|
|
@@ -67,7 +67,7 @@ sentry = ["sentry-sdk"]
|
|
|
67
67
|
|
|
68
68
|
[tool.poetry]
|
|
69
69
|
name = "dotflow"
|
|
70
|
-
version = "0.16.0.
|
|
70
|
+
version = "0.16.0.dev4"
|
|
71
71
|
description = "🎲 Dotflow turns an idea into flow! Lightweight Python library for execution pipelines with retry, parallel, cron and async support."
|
|
72
72
|
authors = ["Fernando Celmer <email@fernandocelmer.com>"]
|
|
73
73
|
readme = "README.md"
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"""ServerAPI — sends workflow/task events to a remote HTTP endpoint."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
from requests import patch, post
|
|
9
|
-
from requests.exceptions import RequestException
|
|
10
|
-
|
|
11
|
-
from dotflow.abc.server import Server
|
|
12
|
-
|
|
13
|
-
logger = logging.getLogger(__name__)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ServerAPI(Server):
|
|
17
|
-
"""HTTP implementation of the Server ABC."""
|
|
18
|
-
|
|
19
|
-
def __init__(
|
|
20
|
-
self,
|
|
21
|
-
base_url: str,
|
|
22
|
-
user_token: str,
|
|
23
|
-
timeout: float = 15.0,
|
|
24
|
-
):
|
|
25
|
-
self._base_url = base_url.rstrip("/")
|
|
26
|
-
self._user_token = user_token
|
|
27
|
-
self._timeout = timeout
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def _headers(self) -> dict:
|
|
31
|
-
return {
|
|
32
|
-
"Authorization": f"Bearer {self._user_token}",
|
|
33
|
-
"Content-Type": "application/json",
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
def _post(self, url: str, json: dict) -> None:
|
|
37
|
-
try:
|
|
38
|
-
response = post(
|
|
39
|
-
url,
|
|
40
|
-
json=json,
|
|
41
|
-
headers=self._headers,
|
|
42
|
-
timeout=self._timeout,
|
|
43
|
-
)
|
|
44
|
-
logger.info("POST %s [%s]", url, response.status_code)
|
|
45
|
-
except RequestException as error:
|
|
46
|
-
logger.error("POST %s failed: %s", url, error)
|
|
47
|
-
|
|
48
|
-
def _patch(self, url: str, json: dict) -> None:
|
|
49
|
-
try:
|
|
50
|
-
response = patch(
|
|
51
|
-
url,
|
|
52
|
-
json=json,
|
|
53
|
-
headers=self._headers,
|
|
54
|
-
timeout=self._timeout,
|
|
55
|
-
)
|
|
56
|
-
logger.info("PATCH %s [%s]", url, response.status_code)
|
|
57
|
-
except RequestException as error:
|
|
58
|
-
logger.error("PATCH %s failed: %s", url, error)
|
|
59
|
-
|
|
60
|
-
def create_workflow(self, workflow: Any) -> None:
|
|
61
|
-
self._post(
|
|
62
|
-
f"{self._base_url}/workflows",
|
|
63
|
-
json={"id": str(workflow)},
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
def update_workflow(self, workflow: Any, status: str = "") -> None:
|
|
67
|
-
self._patch(
|
|
68
|
-
f"{self._base_url}/workflows/{workflow}",
|
|
69
|
-
json={"status": status},
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
def create_task(self, task: Any) -> None:
|
|
73
|
-
data = task.result(max=5_000_000)
|
|
74
|
-
data["id"] = data.pop("task_id", task.task_id)
|
|
75
|
-
self._post(
|
|
76
|
-
f"{self._base_url}/workflows/{task.workflow_id}/tasks",
|
|
77
|
-
json=data,
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
def update_task(self, task: Any) -> None:
|
|
81
|
-
data = task.result(max=5_000_000)
|
|
82
|
-
self._patch(
|
|
83
|
-
(
|
|
84
|
-
f"{self._base_url}/workflows/{task.workflow_id}"
|
|
85
|
-
f"/tasks/{task.task_id}"
|
|
86
|
-
),
|
|
87
|
-
json=data,
|
|
88
|
-
)
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"""ServerDefault — no-op server provider."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from typing import Any
|
|
6
|
-
|
|
7
|
-
from dotflow.abc.server import Server
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class ServerDefault(Server):
|
|
11
|
-
"""Default Server implementation (no-op)."""
|
|
12
|
-
|
|
13
|
-
def create_workflow(self, workflow: Any) -> None:
|
|
14
|
-
pass
|
|
15
|
-
|
|
16
|
-
def update_workflow(self, workflow: Any, status: str = "") -> None:
|
|
17
|
-
pass
|
|
18
|
-
|
|
19
|
-
def create_task(self, task: Any) -> None:
|
|
20
|
-
pass
|
|
21
|
-
|
|
22
|
-
def update_task(self, task: Any) -> None:
|
|
23
|
-
pass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|