dotflow 0.8.2.dev2__py3-none-any.whl → 0.9.0.dev2__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Dotflow __init__ module."""
2
2
 
3
- __version__ = "0.8.2.dev2"
3
+ __version__ = "0.9.0.dev2"
4
4
  __description__ = "🎲 Dotflow turns an idea into flow!"
5
5
 
6
6
  from .core.config import Config
dotflow/cli/command.py CHANGED
@@ -1,10 +1,14 @@
1
1
  """Command module"""
2
2
 
3
+ from abc import ABC, abstractmethod
3
4
 
4
- class Command:
5
+
6
+ class Command(ABC):
5
7
 
6
8
  def __init__(self, **kwargs):
7
9
  self.params = kwargs.get("arguments")
10
+ self.setup()
8
11
 
9
- if hasattr(self.params, "option"):
10
- getattr(self, self.params.option)()
12
+ @abstractmethod
13
+ def setup(self):
14
+ pass
@@ -1,12 +1,12 @@
1
1
  """Commands __init__ module."""
2
2
 
3
3
  from dotflow.cli.commands.init import InitCommand
4
- from dotflow.cli.commands.server import ServerCommand
4
+ from dotflow.cli.commands.log import LogCommand
5
5
  from dotflow.cli.commands.start import StartCommand
6
6
 
7
7
 
8
8
  __all__ = [
9
9
  "InitCommand",
10
- "ServerCommand",
10
+ "LogCommand",
11
11
  "StartCommand"
12
12
  ]
@@ -1,16 +1,19 @@
1
1
  """Command init module"""
2
2
 
3
- from os import system, path
3
+ from os import system
4
4
 
5
+ from rich import print # type: ignore
6
+
7
+ from dotflow.settings import Settings as settings
5
8
  from dotflow.cli.command import Command
6
9
 
7
10
 
8
11
  class InitCommand(Command):
9
12
 
10
- def __init__(self, **kwargs):
11
- self.params = kwargs.get("arguments")
12
- self.init()
13
-
14
- def init(self):
15
- if path.isfile(".gitignore"):
13
+ def setup(self):
14
+ if settings.GITIGNORE.exists():
16
15
  system("echo '\n\n# Dotflow\n.output' >> .gitignore")
16
+ print(
17
+ settings.INFO_ALERT,
18
+ f"Installation complete! The ({settings.GITIGNORE.resolve()}) file has been modified."
19
+ )
@@ -0,0 +1,21 @@
1
+ """Command log module"""
2
+
3
+ from rich import print # type: ignore
4
+
5
+ from dotflow.utils import read_file
6
+ from dotflow.settings import Settings as settings
7
+ from dotflow.cli.command import Command
8
+
9
+
10
+ class LogCommand(Command):
11
+
12
+ def setup(self):
13
+ if settings.LOG_PATH.exists():
14
+ print(read_file(path=settings.LOG_PATH))
15
+
16
+ print(
17
+ settings.INFO_ALERT,
18
+ f"To access all logs, open the file ({settings.LOG_PATH.resolve()})."
19
+ )
20
+ else:
21
+ print(settings.WARNING_ALERT, "Log file not found.")
@@ -9,11 +9,7 @@ from dotflow.cli.command import Command
9
9
 
10
10
  class StartCommand(Command):
11
11
 
12
- def __init__(self, **kwargs):
13
- self.params = kwargs.get("arguments")
14
- self.start()
15
-
16
- def start(self):
12
+ def setup(self):
17
13
  workflow = DotFlow(
18
14
  config=Config(
19
15
  path=self.params.path,
dotflow/cli/setup.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from rich import print # type: ignore
4
4
 
5
5
  from dotflow import __version__, __description__
6
- from dotflow.log import logger
6
+ from dotflow.logging import logger
7
7
  from dotflow.settings import Settings as settings
8
8
  from dotflow.utils.basic_functions import basic_callback
9
9
  from dotflow.core.types.execution import TypeExecution
@@ -13,7 +13,11 @@ from dotflow.core.exception import (
13
13
  ModuleNotFound,
14
14
  MESSAGE_UNKNOWN_ERROR,
15
15
  )
16
- from dotflow.cli.commands import InitCommand, ServerCommand, StartCommand
16
+ from dotflow.cli.commands import (
17
+ InitCommand,
18
+ LogCommand,
19
+ StartCommand
20
+ )
17
21
 
18
22
 
19
23
  class Command:
@@ -32,16 +36,10 @@ class Command:
32
36
  )
33
37
 
34
38
  self.setup_init()
39
+ self.setup_logs()
35
40
  self.setup_start()
36
41
  self.command()
37
42
 
38
- def setup_server(self):
39
- self.cmd_server = self.subparsers.add_parser("server", help="Server")
40
- self.cmd_server = self.cmd_server.add_argument_group(
41
- "Usage: dotflow server [OPTIONS]"
42
- )
43
- self.cmd_server.set_defaults(exec=ServerCommand)
44
-
45
43
  def setup_init(self):
46
44
  self.cmd_init = self.subparsers.add_parser("init", help="Init")
47
45
  self.cmd_init = self.cmd_init.add_argument_group(
@@ -61,7 +59,7 @@ class Command:
61
59
  self.cmd_start.add_argument(
62
60
  "-o", "--output-context", default=False, action="store_true"
63
61
  )
64
- self.cmd_start.add_argument("-p", "--path", default=settings.INITIAL_PATH)
62
+ self.cmd_start.add_argument("-p", "--path", default=settings.START_PATH)
65
63
  self.cmd_start.add_argument(
66
64
  "-m",
67
65
  "--mode",
@@ -71,10 +69,14 @@ class Command:
71
69
 
72
70
  self.cmd_start.set_defaults(exec=StartCommand)
73
71
 
72
+ def setup_logs(self):
73
+ self.cmd_logs = self.subparsers.add_parser("logs", help="Logs")
74
+ self.cmd_logs = self.cmd_logs.add_argument_group(
75
+ "Usage: dotflow log [OPTIONS]"
76
+ )
77
+ self.cmd_logs.set_defaults(exec=LogCommand)
78
+
74
79
  def command(self):
75
- message_icon = ":game_die:"
76
- message_error = "[bold red]Error:[/bold red]"
77
-
78
80
  try:
79
81
  arguments = self.parser.parse_args()
80
82
  if hasattr(arguments, "exec"):
@@ -82,14 +84,14 @@ class Command:
82
84
  else:
83
85
  print(__description__)
84
86
  except MissingActionDecorator as err:
85
- print(message_icon, message_error, err)
87
+ print(settings.WARNING_ALERT, err)
86
88
 
87
89
  except ExecutionModeNotExist as err:
88
- print(message_icon, message_error, err)
90
+ print(settings.WARNING_ALERT, err)
89
91
 
90
92
  except ModuleNotFound as err:
91
- print(message_icon, message_error, err)
93
+ print(settings.WARNING_ALERT, err)
92
94
 
93
95
  except Exception as err:
94
- print(message_icon, message_error, MESSAGE_UNKNOWN_ERROR)
95
- logger.error(err)
96
+ logger.error(f"Internal problem: {str(err)}")
97
+ print(settings.ERROR_ALERT, MESSAGE_UNKNOWN_ERROR)
@@ -13,4 +13,4 @@ class StartValidator(BaseModel):
13
13
  callable: Optional[str] = Field(default=None)
14
14
  initial_context: Optional[str] = Field(default=None)
15
15
  output: Optional[bool] = Field(default=True)
16
- path: Optional[str] = Field(default=settings.INITIAL_PATH)
16
+ path: Optional[str] = Field(default=settings.START_PATH)
dotflow/core/config.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """Config module"""
2
2
 
3
- from dotflow.utils import make_dir
3
+ from pathlib import Path
4
+
4
5
  from dotflow.settings import Settings as settings
5
6
 
6
7
 
@@ -8,11 +9,13 @@ class Config:
8
9
 
9
10
  def __init__(
10
11
  self,
11
- path: str = settings.INITIAL_PATH,
12
+ path: str = settings.START_PATH,
12
13
  output: bool = False
13
14
  ) -> None:
14
- self.path = path
15
- self.log_path = f"{path}/{settings.LOG_FILE}"
15
+ self.path = Path(path)
16
+ self.task_path = Path(path, "tasks")
17
+ self.log_path = Path(path, settings.LOG_FILE_NAME)
16
18
  self.output = output
17
19
 
18
- make_dir(path=self.path, show_log=True)
20
+ self.path.mkdir(parents=True, exist_ok=True)
21
+ self.task_path.mkdir(parents=True, exist_ok=True)
@@ -7,6 +7,6 @@ def time(func):
7
7
  def inside(*args, **kwargs):
8
8
  start = datetime.now()
9
9
  task = func(*args, **kwargs)
10
- task._set_duration((datetime.now() - start).total_seconds())
10
+ task.duration = (datetime.now() - start).total_seconds()
11
11
  return task
12
12
  return inside
dotflow/core/exception.py CHANGED
@@ -1,10 +1,11 @@
1
1
  """Exception module"""
2
2
 
3
3
  MESSAGE_UNKNOWN_ERROR = "Unknown error, please check logs for more information."
4
- MESSAGE_MISSING_STEP_DECORATOR = "A step function necessarily needs an 'action' decorator to circulate in the workflow. For more implementation details, access the documentation: https://dotflow-io.github.io/dotflow/nav/getting-started/#3-task-function."
4
+ MESSAGE_MISSING_STEP_DECORATOR = "A step function necessarily needs an '@action' decorator to circulate in the workflow. For more implementation details, access the documentation: https://dotflow-io.github.io/dotflow/nav/getting-started/#3-task-function."
5
+ MESSAGE_NOT_CALLABLE_OBJECT = "Problem validating the '{name}' object type; this is not a callable object"
5
6
  MESSAGE_EXECUTION_NOT_EXIST = "The execution mode does not exist. Allowed parameter is 'sequential' and 'background'."
6
- MESSAGE_MODULE_NOT_FOUND = "Problem importing the python module, it probably doesn't exist or is wrong."
7
-
7
+ MESSAGE_MODULE_NOT_FOUND = "Problem importing the python module '{module}'."
8
+ MESSAGE_PROBLEM_ORDERING = "Problem with correctly ordering functions of the '{name}' class."
8
9
 
9
10
  class MissingActionDecorator(Exception):
10
11
 
@@ -24,7 +25,29 @@ class ExecutionModeNotExist(Exception):
24
25
 
25
26
  class ModuleNotFound(Exception):
26
27
 
27
- def __init__(self):
28
+ def __init__(self, module: str):
28
29
  super(ModuleNotFound, self).__init__(
29
- MESSAGE_MODULE_NOT_FOUND
30
+ MESSAGE_MODULE_NOT_FOUND.format(
31
+ module=module
32
+ )
30
33
  )
34
+
35
+
36
+ class NotCallableObject(Exception):
37
+
38
+ def __init__(self, name: str):
39
+ super(NotCallableObject, self).__init__(
40
+ MESSAGE_NOT_CALLABLE_OBJECT.format(
41
+ name=name
42
+ )
43
+ )
44
+
45
+
46
+ class ProblemOrdering(Exception):
47
+
48
+ def __init__(self, name: str):
49
+ super(ProblemOrdering, self).__init__(
50
+ MESSAGE_PROBLEM_ORDERING.format(
51
+ name=name
52
+ )
53
+ )
dotflow/core/execution.py CHANGED
@@ -1,8 +1,11 @@
1
1
  """Execution module"""
2
2
 
3
3
  from uuid import UUID
4
- from typing import Callable
4
+ from typing import Callable, List, Tuple
5
+ from inspect import getsourcelines
6
+ from types import FunctionType, NoneType
5
7
 
8
+ from dotflow.logging import logger
6
9
  from dotflow.core.action import Action
7
10
  from dotflow.core.context import Context
8
11
  from dotflow.core.task import Task
@@ -13,6 +16,19 @@ from dotflow.core.decorators import time
13
16
 
14
17
  class Execution:
15
18
 
19
+ VALID_OBJECTS = [
20
+ str,
21
+ int,
22
+ float,
23
+ dict,
24
+ list,
25
+ tuple,
26
+ set,
27
+ bool,
28
+ FunctionType,
29
+ NoneType
30
+ ]
31
+
16
32
  def __init__(
17
33
  self,
18
34
  task: Task,
@@ -21,39 +37,80 @@ class Execution:
21
37
  ) -> None:
22
38
  self.task = task
23
39
  self.task.status = TaskStatus.IN_PROGRESS
24
- self.task._set_workflow_id(workflow_id)
25
40
  self.task.previous_context = previous_context
41
+ self.task.workflow_id = workflow_id
26
42
 
27
43
  self._excution()
28
44
 
45
+ def _is_action(self, class_instance: Callable, func: Callable):
46
+ return (
47
+ callable(getattr(class_instance, func))
48
+ and not func.startswith("__")
49
+ and getattr(class_instance, func).__module__ is Action.__module__
50
+ )
51
+
52
+ def _execution_orderer(
53
+ self,
54
+ callable_list: List[str],
55
+ class_instance: Callable
56
+ ) -> Tuple[int, Callable]:
57
+ ordered_list = []
58
+
59
+ try:
60
+ inside_code = getsourcelines(class_instance.__class__)[0]
61
+
62
+ for callable_name in callable_list:
63
+ for index, code in enumerate(inside_code):
64
+ if code.find(f"def {callable_name}") != -1:
65
+ ordered_list.append((index, callable_name))
66
+
67
+ ordered_list.sort()
68
+ return ordered_list
69
+
70
+ except TypeError as err:
71
+ logger.error(f"Internal problem: {str(err)}")
72
+
73
+ for index, callable_name in enumerate(callable_list):
74
+ ordered_list.append((index, callable_name))
75
+
76
+ return ordered_list
77
+
29
78
  def _execution_with_class(self, class_instance: Callable):
30
- context = Context(storage=[])
79
+ new_context = Context(storage=[])
31
80
  previous_context = self.task.previous_context
81
+ callable_list = [
82
+ func
83
+ for func in dir(class_instance)
84
+ if self._is_action(class_instance, func)
85
+ ]
86
+
87
+ ordered_list = self._execution_orderer(
88
+ callable_list=callable_list, class_instance=class_instance
89
+ )
90
+
91
+ for _, new in ordered_list:
92
+ new_object = getattr(class_instance, new)
93
+ try:
94
+ subcontext = new_object(
95
+ initial_context=self.task.initial_context,
96
+ previous_context=previous_context,
97
+ )
98
+ new_context.storage.append(subcontext)
99
+ previous_context = subcontext
100
+
101
+ except Exception:
102
+ subcontext = new_object(
103
+ class_instance,
104
+ initial_context=self.task.initial_context,
105
+ previous_context=previous_context,
106
+ )
107
+ new_context.storage.append(subcontext)
108
+ previous_context = subcontext
32
109
 
33
- for func_name in dir(class_instance):
34
- additional_function = getattr(class_instance, func_name)
35
-
36
- if isinstance(additional_function, Action):
37
- try:
38
- current_context = additional_function(
39
- initial_context=self.task.initial_context,
40
- previous_context=previous_context,
41
- )
42
- context.storage.append(current_context)
43
- previous_context = current_context
44
- except TypeError:
45
- current_context = additional_function(
46
- class_instance,
47
- initial_context=self.task.initial_context,
48
- previous_context=previous_context,
49
- )
50
- context.storage.append(current_context)
51
- previous_context = current_context
52
-
53
- if not context.storage:
110
+ if not new_context.storage:
54
111
  return Context(storage=class_instance)
55
112
 
56
- return context
113
+ return new_context
57
114
 
58
115
  @time
59
116
  def _excution(self):
@@ -63,12 +120,7 @@ class Execution:
63
120
  previous_context=self.task.previous_context,
64
121
  )
65
122
 
66
- object_attributes = [
67
- type(getattr(current_context.storage, param)) is Action
68
- for param in dir(current_context.storage)
69
- ]
70
-
71
- if True in object_attributes:
123
+ if type(current_context.storage) not in self.VALID_OBJECTS:
72
124
  current_context = self._execution_with_class(
73
125
  class_instance=current_context.storage
74
126
  )
dotflow/core/module.py CHANGED
@@ -30,7 +30,9 @@ class Module:
30
30
  if hasattr(module, cls._get_name(value)):
31
31
  return getattr(module, cls._get_name(value))
32
32
 
33
- raise ModuleNotFound()
33
+ raise ModuleNotFound(
34
+ module=value
35
+ )
34
36
 
35
37
  @classmethod
36
38
  def _get_name(cls, value: str):
dotflow/core/task.py CHANGED
@@ -2,16 +2,25 @@
2
2
 
3
3
  from uuid import UUID
4
4
  from typing import Any, Callable, List
5
+ from time import strftime, localtime
5
6
 
6
- from dotflow.log import logger
7
+ from rich.console import Console # type: ignore
8
+
9
+ from dotflow.logging import logger
7
10
  from dotflow.core.config import Config
8
11
  from dotflow.core.action import Action
9
12
  from dotflow.core.context import Context
10
13
  from dotflow.core.module import Module
11
- from dotflow.core.exception import MissingActionDecorator
14
+ from dotflow.core.exception import MissingActionDecorator, NotCallableObject
12
15
  from dotflow.core.types.status import TaskStatus
13
16
  from dotflow.settings import Settings as settings
14
- from dotflow.utils import basic_callback, traceback_error, message_error, copy_file
17
+ from dotflow.utils import (
18
+ basic_callback,
19
+ traceback_error,
20
+ message_error,
21
+ write_file,
22
+ copy_file
23
+ )
15
24
 
16
25
 
17
26
  class TaskInstance:
@@ -19,11 +28,12 @@ class TaskInstance:
19
28
  def __init__(self, *args, **kwargs) -> None:
20
29
  self.task_id = None
21
30
  self.workflow_id = None
22
- self.step = None
23
- self.callback = None
31
+ self._step = None
32
+ self._callback = None
33
+ self._previous_context = None
24
34
  self._initial_context = None
25
35
  self._current_context = None
26
- self._previous_context = None
36
+ self._duration = None
27
37
  self._error = None
28
38
  self._status = None
29
39
  self._config = None
@@ -40,45 +50,52 @@ class Task(TaskInstance):
40
50
  workflow_id: UUID = None,
41
51
  config: Config = None,
42
52
  ) -> None:
43
- super().__init__(task_id, step, callback, initial_context, workflow_id)
44
- self.config = config
53
+ super().__init__(
54
+ task_id,
55
+ step,
56
+ callback,
57
+ initial_context,
58
+ workflow_id
59
+ )
45
60
  self.task_id = task_id
46
61
  self.workflow_id = workflow_id
47
62
  self.step = step
48
63
  self.callback = callback
49
64
  self.initial_context = initial_context
50
65
  self.status = TaskStatus.NOT_STARTED
66
+ self.config = config
51
67
 
52
68
  @property
53
- def status(self):
54
- if not self._status:
55
- return TaskStatus.NOT_STARTED
56
- return self._status
69
+ def step(self):
70
+ return self._step
57
71
 
58
- @status.setter
59
- def status(self, value: TaskStatus) -> None:
60
- self._status = value
72
+ @step.setter
73
+ def step(self, value: Callable):
74
+ new_step = value
61
75
 
62
- logger.info("ID %s - %s - %s", self.workflow_id, self.task_id, value)
76
+ if isinstance(value, str):
77
+ new_step = Module(value=value)
78
+
79
+ if new_step.__module__ != Action.__module__:
80
+ raise MissingActionDecorator()
81
+
82
+ self._step = new_step
63
83
 
64
84
  @property
65
- def error(self):
66
- if not self._error:
67
- return TaskError()
68
- return self._error
85
+ def callback(self):
86
+ return self._callback
69
87
 
70
- @error.setter
71
- def error(self, value: Exception) -> None:
72
- task_error = TaskError(value)
73
- self._error = task_error
88
+ @callback.setter
89
+ def callback(self, value: Callable):
90
+ new_callback = value
74
91
 
75
- logger.error(
76
- "ID %s - %s - %s \n %s",
77
- self.workflow_id,
78
- self.task_id,
79
- self.status,
80
- task_error.traceback,
81
- )
92
+ if isinstance(value, str):
93
+ new_callback = Module(value=value)
94
+
95
+ if not isinstance(new_callback, Callable):
96
+ raise NotCallableObject(name=str(new_callback))
97
+
98
+ self._callback = new_callback
82
99
 
83
100
  @property
84
101
  def previous_context(self):
@@ -90,6 +107,26 @@ class Task(TaskInstance):
90
107
  def previous_context(self, value: Context):
91
108
  self._previous_context = Context(value)
92
109
 
110
+ TaskController(task=self).controller_output_context(
111
+ content=self.previous_context.storage,
112
+ context_name="previous_context"
113
+ )
114
+
115
+ @property
116
+ def initial_context(self):
117
+ if not self._initial_context:
118
+ return Context()
119
+ return self._initial_context
120
+
121
+ @initial_context.setter
122
+ def initial_context(self, value: Context):
123
+ self._initial_context = Context(value)
124
+
125
+ TaskController(task=self).controller_output_context(
126
+ content=self.initial_context.storage,
127
+ context_name="initial_context"
128
+ )
129
+
93
130
  @property
94
131
  def current_context(self):
95
132
  if not self._current_context:
@@ -100,35 +137,52 @@ class Task(TaskInstance):
100
137
  def current_context(self, value: Context):
101
138
  self._current_context = Context(value)
102
139
 
103
- if self.config.output:
104
- logger.info(
105
- "ID %s - %s - Current Context -> %s",
106
- self.workflow_id,
107
- self.task_id,
108
- str(value.storage),
109
- )
140
+ TaskController(task=self).controller_output_context(
141
+ content=self.current_context.storage,
142
+ context_name="current_context"
143
+ )
110
144
 
111
- copy_file(source=settings.LOG_PATH, destination=self.config.log_path)
145
+ @property
146
+ def duration(self):
147
+ return self._duration
148
+
149
+ @duration.setter
150
+ def duration(self, value: float):
151
+ self._duration = value
112
152
 
113
153
  @property
114
- def initial_context(self):
115
- if not self._initial_context:
116
- return Context()
117
- return self._initial_context
154
+ def error(self):
155
+ if not self._error:
156
+ return TaskError()
157
+ return self._error
118
158
 
119
- @initial_context.setter
120
- def initial_context(self, value: Context):
121
- self._initial_context = Context(value)
159
+ @error.setter
160
+ def error(self, value: Exception) -> None:
161
+ task_error = TaskError(value)
162
+ self._error = task_error
122
163
 
123
- if self.config.output:
124
- logger.info(
125
- "ID %s - %s - Initial Context -> %s",
126
- self.workflow_id,
127
- self.task_id,
128
- str(value),
129
- )
164
+ logger.error(
165
+ "ID %s - %s - %s \n %s",
166
+ self.workflow_id,
167
+ self.task_id,
168
+ self.status,
169
+ task_error.traceback,
170
+ )
130
171
 
131
- copy_file(source=settings.LOG_PATH, destination=self.config.log_path)
172
+ console = Console()
173
+ console.print_exception(show_locals=True)
174
+
175
+ @property
176
+ def status(self):
177
+ if not self._status:
178
+ return TaskStatus.NOT_STARTED
179
+ return self._status
180
+
181
+ @status.setter
182
+ def status(self, value: TaskStatus) -> None:
183
+ self._status = value
184
+
185
+ TaskController(task=self).controller_logger()
132
186
 
133
187
  @property
134
188
  def config(self):
@@ -140,12 +194,6 @@ class Task(TaskInstance):
140
194
  def config(self, value: Config):
141
195
  self._config = value
142
196
 
143
- def _set_duration(self, value: float) -> None:
144
- self.duration = value
145
-
146
- def _set_workflow_id(self, value: UUID) -> None:
147
- self.workflow_id = value
148
-
149
197
 
150
198
  class TaskError:
151
199
 
@@ -157,8 +205,12 @@ class TaskError:
157
205
 
158
206
  class TaskBuilder:
159
207
 
160
- def __init__(self, config: Config, workflow_id: UUID = None) -> None:
161
- self.queu: List[Task] = []
208
+ def __init__(
209
+ self,
210
+ config: Config,
211
+ workflow_id: UUID = None
212
+ ) -> None:
213
+ self.queu: List[Callable] = []
162
214
  self.workflow_id = workflow_id
163
215
  self.config = config
164
216
 
@@ -168,10 +220,14 @@ class TaskBuilder:
168
220
  callback: Callable = basic_callback,
169
221
  initial_context: Any = None,
170
222
  ) -> None:
171
- step = Module(value=step)
172
-
173
- if step.__module__ != Action.__module__:
174
- raise MissingActionDecorator()
223
+ if isinstance(step, list):
224
+ for inside_step in step:
225
+ self.add(
226
+ step=inside_step,
227
+ callback=callback,
228
+ initial_context=initial_context
229
+ )
230
+ return self
175
231
 
176
232
  self.queu.append(
177
233
  Task(
@@ -191,3 +247,45 @@ class TaskBuilder:
191
247
 
192
248
  def clear(self) -> None:
193
249
  self.queu.clear()
250
+
251
+ def reverse(self) -> None:
252
+ self.queu.reverse()
253
+
254
+
255
+ class TaskController:
256
+
257
+ def __init__(self, task: Task):
258
+ self.task = task
259
+ self.default_time_format = "%Y-%m-%d %H:%M:%S"
260
+
261
+ def _get_body(self):
262
+ return "{time} ID: {workflow_id} - {task_id} - {task_status}".format(
263
+ time=strftime(self.default_time_format, localtime()),
264
+ workflow_id=self.task.workflow_id,
265
+ task_id=self.task.task_id,
266
+ task_status=self.task.status
267
+ )
268
+
269
+ def controller_logger(self):
270
+ logger.info(
271
+ "ID %s - %s - %s",
272
+ self.task.workflow_id,
273
+ self.task.task_id,
274
+ self.task.status,
275
+ )
276
+ copy_file(
277
+ source=settings.LOG_PATH,
278
+ destination=self.task.config.log_path
279
+ )
280
+
281
+ def controller_output_context(self, content: Any, context_name: str) -> None:
282
+ if self.task.config.output:
283
+ file_name = "{workflow_id}-{task_id}-{context_name}".format(
284
+ workflow_id=self.task.workflow_id.hex,
285
+ task_id=self.task.task_id,
286
+ context_name=context_name
287
+ )
288
+ write_file(
289
+ path=self.task.config.task_path.joinpath(file_name),
290
+ content=str(content)
291
+ )
@@ -3,25 +3,25 @@
3
3
  import logging
4
4
  import logging.config
5
5
 
6
- from dotflow.utils import make_dir
7
6
  from dotflow.settings import Settings as settings
8
7
 
9
- make_dir(path=settings.INITIAL_PATH, show_log=True)
8
+ settings.START_PATH.mkdir(parents=True, exist_ok=True)
10
9
 
11
10
  logging.basicConfig(
12
11
  filename=settings.LOG_PATH,
12
+ format=settings.LOG_FORMAT,
13
13
  level=logging.INFO,
14
- filemode="a"
14
+ filemode="a",
15
15
  )
16
16
 
17
17
  logger = logging.getLogger(settings.LOG_PROFILE)
18
- logger.setLevel(logging.DEBUG)
18
+ logger.setLevel(logging.INFO)
19
19
 
20
20
  ch = logging.StreamHandler()
21
- ch.setLevel(logging.DEBUG)
21
+ ch.setLevel(logging.INFO)
22
22
 
23
23
  formatter = logging.Formatter(
24
- '%(asctime)s - %(levelname)s [%(name)s]: %(message)s'
24
+ settings.LOG_FORMAT
25
25
  )
26
26
 
27
27
  ch.setFormatter(formatter)
dotflow/settings.py CHANGED
@@ -1,9 +1,19 @@
1
1
  """Settings"""
2
2
 
3
+ from pathlib import Path
4
+
3
5
 
4
6
  class Settings:
5
7
 
6
- INITIAL_PATH = ".output"
8
+ START_PATH = Path(".output")
9
+ GITIGNORE = Path(".gitignore")
10
+
7
11
  LOG_PROFILE = "dotflow"
8
- LOG_FILE = "flow.log"
9
- LOG_PATH = ".output/flow.log"
12
+ LOG_FILE_NAME = Path("flow.log")
13
+ LOG_PATH = Path(".output/flow.log")
14
+ LOG_FORMAT = "%(asctime)s - %(levelname)s [%(name)s]: %(message)s"
15
+
16
+ ICON = ":game_die:"
17
+ ERROR_ALERT = f"{ICON} [bold red]Error:[/bold red]"
18
+ INFO_ALERT = f"{ICON} [bold blue]Info:[/bold blue]"
19
+ WARNING_ALERT = f"{ICON} [bold yellow]Warning:[/bold yellow]"
dotflow/utils/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  from dotflow.utils.error_handler import traceback_error, message_error
4
4
  from dotflow.utils.basic_functions import basic_function, basic_callback
5
- from dotflow.utils.tools import make_dir, copy_file
5
+ from dotflow.utils.tools import make_dir, copy_file, write_file, read_file
6
6
 
7
7
 
8
8
  __all__ = [
@@ -11,5 +11,7 @@ __all__ = [
11
11
  "basic_function",
12
12
  "basic_callback",
13
13
  "make_dir",
14
- "copy_file"
14
+ "copy_file",
15
+ "write_file",
16
+ "read_file"
15
17
  ]
dotflow/utils/tools.py CHANGED
@@ -1,8 +1,9 @@
1
1
  """Tools"""
2
2
 
3
+ import json
3
4
  import logging
4
5
 
5
- from os import makedirs
6
+ from os import makedirs, system
6
7
  from shutil import copy
7
8
 
8
9
 
@@ -24,3 +25,23 @@ def copy_file(
24
25
  except Exception as err:
25
26
  if show_log:
26
27
  logging.error(err)
28
+
29
+
30
+ def write_file(
31
+ path: str,
32
+ content: str,
33
+ mode: str = "w"
34
+ ) -> None:
35
+ if isinstance(content, dict) or isinstance(content, list):
36
+ content = json.dumps(content)
37
+
38
+ try:
39
+ with open(file=path, mode=mode, encoding="utf-8") as file:
40
+ file.write(content)
41
+ except Exception:
42
+ system(f"echo '{content}' > {path}")
43
+
44
+
45
+ def read_file(path: str) -> str:
46
+ with open(file=path) as file:
47
+ return file.read()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dotflow
3
- Version: 0.8.2.dev2
3
+ Version: 0.9.0.dev2
4
4
  Summary: 🎲 Dotflow turns an idea into flow!
5
5
  License: MIT License
6
6
 
@@ -1,50 +1,50 @@
1
- dotflow/__init__.py,sha256=NdhtJ9DSS7rAMVJDffrx4rNSjS90H6aFMx7926tooOg,435
1
+ dotflow/__init__.py,sha256=B0YZtVSNAgNCAyDyhpN0V3xwjVJt-If9PKoW7J6FiRc,435
2
2
  dotflow/abc/__init__.py,sha256=UIk84jCJYoHAk4In_eVXAaFAnNE63lI3dVP5qunyBJs,32
3
3
  dotflow/abc/file.py,sha256=JNQa7Cy-kX3rX-3IJ8CXLWGajgYb0cFXIvAaPYv97Cw,346
4
4
  dotflow/abc/http.py,sha256=Py9DmkHw37tNUP_t7Z4O1NIMr5kohNgl0LUcgQIRFr4,383
5
5
  dotflow/abc/tcp.py,sha256=OTcwUDPA9YYHDp-B-L2Y-8F4A7rT6r-YFPQfwN3TnO4,570
6
6
  dotflow/cli/__init__.py,sha256=1NhA5RRAaAfhj3LdBgtC2eZ7WPZlmN0f-Kz-biUkUXE,27
7
- dotflow/cli/command.py,sha256=YG_3Za5VqD0AoxWcTY-iY1FdrZOvI71NQ_f_yc_ZaPQ,211
8
- dotflow/cli/commands/__init__.py,sha256=lAv_X7b-LJsjE-cjIR_IxVFmdGDSlEaloNPzs6cJ8jM,264
9
- dotflow/cli/commands/init.py,sha256=F7sbQCCpMPFWIgJKDInFUAIGRwSbZ2maMMBaINqn7K8,352
10
- dotflow/cli/commands/server.py,sha256=zk22EOzuGZPjraPPDs0_8wHt45iVCSFiAvpJ2s3EIwo,156
11
- dotflow/cli/commands/start.py,sha256=msxsRelAHZvb8sXCIDSqRfNimJPR1F8u2TsRlNm7a1I,836
12
- dotflow/cli/setup.py,sha256=o1w9o-ir6bQRloWSXRlOXT40FF1mddv91tfdHmpzmwY,3276
7
+ dotflow/cli/command.py,sha256=RRly3DJCLc1NU9zuaZuNiTxLiB40afHD6lp4NPrkbpw,237
8
+ dotflow/cli/commands/__init__.py,sha256=4EpWlJEMJwaQbLHQNeTugMKz3G16sDGN54pl3V-DKcg,255
9
+ dotflow/cli/commands/init.py,sha256=ZQaq4lE5Gx6QQE4f2AtBtRq_6zyZ1_C4IgLIlbqo2gQ,511
10
+ dotflow/cli/commands/log.py,sha256=brZJZva8fbK8UekG6_5AGelpQLRbTJOw7Uy122W96e0,571
11
+ dotflow/cli/commands/start.py,sha256=8sf_6ZXgnDCquScqwkaVvUWM51hOdJZD2PjCMVmU7Bk,734
12
+ dotflow/cli/setup.py,sha256=uBcQrij0Cx-sLqiVnpfnmnTKedIj7MnOXKkWZ1KRArU,3208
13
13
  dotflow/cli/validators/__init__.py,sha256=sf7g8WHowEt3N4Pe5Ec-Wmec__v3_Bfyf6uKOZwXcyk,128
14
- dotflow/cli/validators/start.py,sha256=gA54aY2d7wn2Y5sJS55UEunGnuYSftVbGSxW6iwQt_Q,433
14
+ dotflow/cli/validators/start.py,sha256=Gzr69n7WGr5ytpDN9lk4kocWLvMVvLptam7msovXNJs,431
15
15
  dotflow/core/__init__.py,sha256=baoenc4kd9OQjCrnmOKRm9JRinEjd5xb7luCeyQ-npY,28
16
16
  dotflow/core/action.py,sha256=srSFBBJvgpTGcrmJHwRuVcElAkxta7kQjyGKA1voWfI,2051
17
- dotflow/core/config.py,sha256=yDdI2MBIwwNHCz29IKD2sjlQE-t1_y0bHFsFtOJq1HI,411
17
+ dotflow/core/config.py,sha256=LaZ6LzInqAc91iyr7RRkEsCSSpvS3SsZtfJlMaPHliQ,519
18
18
  dotflow/core/context.py,sha256=7-6aTdeSp-XOpVFT028E_GdLyDKR6xZXNl-ws0n3xoI,315
19
19
  dotflow/core/decorators/__init__.py,sha256=g7U7swkYHDu67uHChcz-5IeoJ4sC6dA4ci_-zey8VJE,233
20
20
  dotflow/core/decorators/action.py,sha256=UfbshA95vWJqZPJaNiFN-zkVNKBc7-sp8Xii4I6w_r4,421
21
21
  dotflow/core/decorators/retry.py,sha256=Bki5MjnRlZKVbTjr7gE8zNNZoHJNsudPVUFeC07_Ciw,621
22
- dotflow/core/decorators/time.py,sha256=rcfzoQ3mL3zThrFioQQhU--Mc8c5JkHJOXQnwVWyUeo,275
22
+ dotflow/core/decorators/time.py,sha256=Nfpt0RQ10gzLa9vVzZxYnhy6jjrY9_ADQqcr0krEWi0,271
23
23
  dotflow/core/dotflow.py,sha256=yU_gG5rNdj5N8_tdeMpq-TzS0z_pNRUrZVyMd15RuLM,834
24
- dotflow/core/exception.py,sha256=_fAcJmThczS5MBCq7XjpxypRXybSlOpqLOn3HK_v_Yg,1082
25
- dotflow/core/execution.py,sha256=ZB27hpoYLp-i-5ZXWoQo6vG8_sjOYDb7RNmdh_E9XFg,2711
26
- dotflow/core/module.py,sha256=cZ1gHDTo8NEtKMoPKDsJSSOJ8aqNuXe8eFZx7J5pD7Q,933
24
+ dotflow/core/exception.py,sha256=hZvuAzE7BdoT56QQs8uUn5JJa2SK1mwmFWcNVnztqWI,1754
25
+ dotflow/core/execution.py,sha256=mb2KdvXLuVAIcFUJPPPMj4de0W0l3RMJOg1U8h1b_Ew,4048
26
+ dotflow/core/module.py,sha256=eq3FSllvMkmrjI-2SXQSZngoZWyFj8BfpSOtXGUlc8A,967
27
27
  dotflow/core/serializers/__init__.py,sha256=2b2vyCuXmvS8N_K-ePuCrWC9yvMqLPBMeORjAWS5U70,35
28
28
  dotflow/core/serializers/task.py,sha256=MC9AVKlHneGKO2vT14VE8s03X_FsTQzyuly4EICa0QM,687
29
29
  dotflow/core/serializers/transport.py,sha256=GbTTgS27OHkj15uwaAZMJ7oNXaOXcqUGGdO66zOORX4,535
30
30
  dotflow/core/serializers/workflow.py,sha256=zb5VWtcpfX-2IpI2_E11kPhffWiGvnrt7rmrGFYs6QI,255
31
- dotflow/core/task.py,sha256=WCvT8vtKPLB56TtwXLDXqjJtdp1z2vsSpGspJOzo6v4,5188
31
+ dotflow/core/task.py,sha256=z9HWZFAbWCXOwc8kbgMS6D9pAmwSihPBs25ziFUNQ4U,7617
32
32
  dotflow/core/types/__init__.py,sha256=pn8t6hLMkDT4PeSyj0rZplptv7PQV_AYRqb2hdR7HCQ,188
33
33
  dotflow/core/types/execution.py,sha256=kYc5hEWB9V_vITMw9DyclchMRy6F4YKTZU5yW89dmVs,173
34
34
  dotflow/core/types/status.py,sha256=PNaTNRPlb0kYOIgks4Bxac7saQOpMhNtHLibVHVvFKQ,207
35
35
  dotflow/core/types/worflow.py,sha256=vd-12mX7hZwQrGEMwHsY1EfrqZDvFGFygi1TvUHpHSI,135
36
36
  dotflow/core/workflow.py,sha256=xV0QWZbm8MubzbUXms7t2PH2oM2VBBbXK8rjDZKXWWY,2025
37
- dotflow/log.py,sha256=M4yY6hwiw9KMktTp9hQyr2Qi71xHrdCqxORsAwHwkmE,565
37
+ dotflow/logging.py,sha256=e4HEIxZ-A7RKJK2pTV-DkNUF2ATbfezjjJiQaRWwQSE,530
38
38
  dotflow/main.py,sha256=EBmtrOl3HA_VLWS76RMKKwxn30RMQdq7FeSn5aMb6CU,266
39
39
  dotflow/providers/__init__.py,sha256=LMWJOrHg08ZTRi2kjk6Fsi2hXUuiC7OSrYTqO7k_2ic,33
40
40
  dotflow/providers/zeromq.py,sha256=zKKyXR2QfKqOmL3WyoaeMy7wYMa6Uknx82S8-VkcgeA,1310
41
- dotflow/settings.py,sha256=mjIf8g4a6vJvZO4sR8birElcdzcdzHCkj5LDSSUGQJ8,151
42
- dotflow/utils/__init__.py,sha256=Q2XC4ijgk0BQ239gsiNlTWri7IdFJ5rPeSSY3BkdVN4,362
41
+ dotflow/settings.py,sha256=hDoXWlIYWGRvaDqjJP4EW3Qhve51XNkDp6CQXJEXvJs,508
42
+ dotflow/utils/__init__.py,sha256=b31Cfy2FN6TdR7U3ZTV9ca6J_ckJC_M-SgcefHmXJJo,420
43
43
  dotflow/utils/basic_functions.py,sha256=S-maghYauG5QkMtHdm7qNcxBkKkSoE4akHSiI178Bwg,131
44
44
  dotflow/utils/error_handler.py,sha256=z07U_Chd0FKcQH8CmBiQjhSXmDfQhzTv9c4q0sPj97A,508
45
- dotflow/utils/tools.py,sha256=jjSNzyuJD9mjBIS6k30k-Cwgh3OKE-Fo0jgY7cF1ONQ,499
46
- dotflow-0.8.2.dev2.dist-info/LICENSE,sha256=UnmVHQt00UH2v7RBXqtIo_0D3vGBh0ujTdBKPKopAFk,1072
47
- dotflow-0.8.2.dev2.dist-info/METADATA,sha256=Ar2A3x_iZtqXLFgOKUxkx5PGgnf0czGsa8ZV9a3LTk4,10280
48
- dotflow-0.8.2.dev2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
49
- dotflow-0.8.2.dev2.dist-info/entry_points.txt,sha256=3AW0pYKZpaMW6WZmN3t71X0FuwxQdivmQwe47_6yBp4,45
50
- dotflow-0.8.2.dev2.dist-info/RECORD,,
45
+ dotflow/utils/tools.py,sha256=0a_J7dug4m4JQhOEKQtQSQjaqhZaP9Q3r4UHKu5_ZsM,986
46
+ dotflow-0.9.0.dev2.dist-info/LICENSE,sha256=UnmVHQt00UH2v7RBXqtIo_0D3vGBh0ujTdBKPKopAFk,1072
47
+ dotflow-0.9.0.dev2.dist-info/METADATA,sha256=5iInmYx4CogzHQYOuwMoY5d3gZUkM-ldpWpgs0ZLqCs,10280
48
+ dotflow-0.9.0.dev2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
49
+ dotflow-0.9.0.dev2.dist-info/entry_points.txt,sha256=3AW0pYKZpaMW6WZmN3t71X0FuwxQdivmQwe47_6yBp4,45
50
+ dotflow-0.9.0.dev2.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- """Command server module"""
2
-
3
- from dotflow.cli.command import Command
4
-
5
-
6
- class ServerCommand(Command):
7
-
8
- def __init__(self, *args, **kwargs):
9
- pass