From 8f740c5d0087b77b364b86aa1460fdacc6993f46 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Wed, 13 Aug 2025 16:33:50 -0300 Subject: [PATCH 01/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Queue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- dotflow/abc/flow.py | 8 +- dotflow/core/dotflow.py | 11 +-- dotflow/core/task.py | 67 ++++++++++++++-- dotflow/core/workflow.py | 82 ++++++++++---------- tests/core/test_workflow_background.py | 8 +- tests/core/test_workflow_parallel.py | 8 +- tests/core/test_workflow_sequential.py | 2 +- tests/core/test_workflow_sequential_group.py | 8 +- 9 files changed, 124 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index b3d7bf3..f943d86 100644 --- a/.gitignore +++ b/.gitignore @@ -175,7 +175,7 @@ cython_debug/ # Dotflow -.output +*.output .storage .dotflow-io *.json diff --git a/dotflow/abc/flow.py b/dotflow/abc/flow.py index 3840dd1..829cf0a 100644 --- a/dotflow/abc/flow.py +++ b/dotflow/abc/flow.py @@ -11,16 +11,14 @@ class Flow(ABC): def __init__( self, - tasks: List[Task], workflow_id: UUID, ignore: bool, - groups: Dict[str, List[Task]] + group: Dict[str, List[Task]] ) -> None: self.queue = None - self.tasks = tasks self.workflow_id = workflow_id self.ignore = ignore - self.groups = groups + self.group = group self.setup_queue() self.run() @@ -30,7 +28,7 @@ def setup_queue(self) -> None: self.queue = [] @abstractmethod - def get_tasks(self) -> List[Task]: + def get_groups(self) -> List[Task]: return self.queue @abstractmethod diff --git a/dotflow/core/dotflow.py b/dotflow/core/dotflow.py index c5b271a..59cd487 100644 --- a/dotflow/core/dotflow.py +++ b/dotflow/core/dotflow.py @@ -48,33 +48,34 @@ def __init__( config=config, workflow_id=self.workflow_id ) + self.add = self.task.add self.start = partial( Manager, - tasks=self.task.queue, + group=self.task.group, workflow_id=self.workflow_id ) - + def result_task(self): """ Returns: list (List[Task]): Returns a list of Task class. """ - return self.task.queue + return self.task.group.tasks() def result_context(self): """ Returns: list (List[Context]): Returns a list of Context class. """ - return [task.current_context for task in self.task.queue] + return [task.current_context for task in self.task.group.tasks()] def result_storage(self): """ Returns: list (List[Any]): Returns a list of assorted objects. """ - return [task.current_context.storage for task in self.task.queue] + return [task.current_context.storage for task in self.task.group.tasks()] def result(self): return self.task.result() diff --git a/dotflow/core/task.py b/dotflow/core/task.py index e96d20e..d687cd4 100644 --- a/dotflow/core/task.py +++ b/dotflow/core/task.py @@ -3,7 +3,7 @@ import json from uuid import UUID -from typing import Any, Callable, List +from typing import Any, Callable, List, Dict from dotflow.core.config import Config from dotflow.core.action import Action @@ -271,7 +271,7 @@ def __init__( config: Config, workflow_id: UUID = None ) -> None: - self.queue: List[Callable] = [] + self.group: QueueGroup = QueueGroup() self.workflow_id = workflow_id self.config = config @@ -313,9 +313,9 @@ def add( ) return self - self.queue.append( - Task( - task_id=len(self.queue), + self.group.add( + item=Task( + task_id=self.group.size(), step=step, callback=Module(value=callback), initial_context=initial_context, @@ -339,9 +339,64 @@ def reverse(self) -> None: def schema(self) -> SerializerWorkflow: return SerializerWorkflow( workflow_id=self.workflow_id, - tasks=[item.schema() for item in self.queue] + tasks=[item.schema() for item in self.group.tasks()] ) def result(self) -> SerializerWorkflow: item = self.schema().model_dump_json() return json.loads(item) + + +class QueueGroup: + + def __init__(self): + self.queue: Dict[str, Queue] = {} + + def add(self, item: Task) -> None: + if not self.queue.get(item.group_name): + self.queue[item.group_name] = Queue() + + self.queue[item.group_name].add(item=item) + + def size(self) -> int: + current_size = 0 + for _, group in self.queue.items(): + current_size += group.size() + + return current_size + + def count(self) -> int: + return len(self.queue) + + def tasks(self) -> List[Task]: + tasks = [] + for _, queue in self.queue.items(): + tasks += queue.tasks + + return tasks + + +class Queue: + + INITIAL_INDEX = 0 + + def __init__(self): + self.tasks: List[Task] = [] + + def add(self, item: Task) -> None: + self.tasks.append(item) + + def remove(self) -> Task: + return self.tasks.pop(self.INITIAL_INDEX) + + def size(self) -> int: + return len(self.tasks) + + def reverse(self) -> None: + self.tasks.reverse() + + def clear(self) -> None: + self.tasks.clear() + + def get(self) -> List[Task]: + return self.tasks diff --git a/dotflow/core/workflow.py b/dotflow/core/workflow.py index accf05d..db326f3 100644 --- a/dotflow/core/workflow.py +++ b/dotflow/core/workflow.py @@ -8,31 +8,20 @@ from multiprocessing import Process, Queue from uuid import UUID, uuid4 -from typing import Callable, Dict, List +from typing import Callable, List from dotflow.abc.flow import Flow from dotflow.core.context import Context from dotflow.core.execution import Execution from dotflow.core.exception import ExecutionModeNotExist from dotflow.core.types import TypeExecution, TypeStatus -from dotflow.core.task import Task +from dotflow.core.task import Task, QueueGroup from dotflow.utils import basic_callback -def is_darwin() -> bool: +def is_darwin_arm() -> bool: """Is Darwin""" - return platform.system() == "Darwin" - - -def grouper(tasks: List[Task]) -> Dict[str, List[Task]]: - """Grouper""" - groups = {} - for task in tasks: - if not groups.get(task.group_name): - groups[task.group_name] = [] - groups[task.group_name].append(task) - - return groups + return platform.system() == "Darwin" and platform.processor() == "arm" class Manager: @@ -90,21 +79,19 @@ class Manager: def __init__( self, - tasks: List[Task], + group: QueueGroup, on_success: Callable = basic_callback, on_failure: Callable = basic_callback, mode: TypeExecution = TypeExecution.SEQUENTIAL, keep_going: bool = False, workflow_id: UUID = None, ) -> None: - self.tasks = tasks + self.group = group self.on_success = on_success self.on_failure = on_failure self.workflow_id = workflow_id or uuid4() self.started = datetime.now() - execution = None - groups = grouper(tasks=tasks) try: execution = getattr(self, mode) @@ -112,12 +99,15 @@ def __init__( raise ExecutionModeNotExist() from err self.tasks = execution( - tasks=tasks, workflow_id=workflow_id, ignore=keep_going, groups=groups + workflow_id=workflow_id, + ignore=keep_going, + group=group ) - self._callback_workflow(tasks=self.tasks) + self._callback_workflow(queue_group=self.group) - def _callback_workflow(self, tasks: List[Task]): + def _callback_workflow(self, queue_group: QueueGroup): + tasks = queue_group.tasks() final_status = [task.status for task in tasks] if TypeStatus.FAILED in final_status: @@ -126,33 +116,40 @@ def _callback_workflow(self, tasks: List[Task]): self.on_success(tasks=tasks) def sequential(self, **kwargs) -> List[Task]: - if len(kwargs.get("groups", {})) > 1 and not is_darwin(): + """Sequential execution""" + many_groups = 1 + group: QueueGroup = kwargs.get("group") + + if group.size() > many_groups: process = SequentialGroup(**kwargs) - return process.get_tasks() + return process.get_groups() process = Sequential(**kwargs) - return process.get_tasks() + return process.get_groups() def sequential_group(self, **kwargs): + """Sequential Group execution""" process = SequentialGroup(**kwargs) - return process.get_tasks() + return process.get_groups() def background(self, **kwargs) -> List[Task]: + """Background execution""" process = Background(**kwargs) - return process.get_tasks() + return process.get_groups() def parallel(self, **kwargs) -> List[Task]: - if is_darwin(): + """Sequential execution""" + if is_darwin_arm(): warnings.warn( "Parallel mode does not work with MacOS." " Running tasks in sequence.", Warning ) process = Sequential(**kwargs) - return process.get_tasks() + return process.get_groups() process = Parallel(**kwargs) - return process.get_tasks() + return process.get_groups() class Sequential(Flow): @@ -161,16 +158,18 @@ class Sequential(Flow): def setup_queue(self) -> None: self.queue = [] - def get_tasks(self) -> List[Task]: + def get_groups(self) -> List[Task]: return self.queue def _flow_callback(self, task: Task) -> None: self.queue.append(task) def run(self) -> None: - previous_context = Context(workflow_id=self.workflow_id) + previous_context = Context( + workflow_id=self.workflow_id + ) - for task in self.tasks: + for task in self.group.tasks(): Execution( task=task, workflow_id=self.workflow_id, @@ -269,8 +268,8 @@ class Background(Flow): def setup_queue(self) -> None: self.queue = [] - def get_tasks(self) -> List[Task]: - return self.tasks + def get_groups(self) -> List[Task]: + return self.group def _flow_callback(self, task: Task) -> None: pass @@ -279,10 +278,9 @@ def run(self) -> None: thread = threading.Thread( target=Sequential, args=( - self.tasks, self.workflow_id, self.ignore, - self.groups, + self.group, ), ) thread.start() @@ -295,19 +293,19 @@ class Parallel(Flow): def setup_queue(self) -> None: self.queue = Queue() - def get_tasks(self) -> List[Task]: + def get_groups(self) -> List[Task]: contexts = {} - while len(contexts) < len(self.tasks): + while len(contexts) < len(self.group.size()): if not self.queue.empty(): contexts = {**contexts, **self.queue.get()} - for task in self.tasks: + for task in self.group.tasks(): task.current_context = contexts[task.task_id]["current_context"] task.duration = contexts[task.task_id]["duration"] task.error = contexts[task.task_id]["error"] task.status = contexts[task.task_id]["status"] - return self.tasks + return self.group.tasks() def _flow_callback(self, task: Task) -> None: current_task = { @@ -324,7 +322,7 @@ def run(self) -> None: processes = [] previous_context = Context(workflow_id=self.workflow_id) - for task in self.tasks: + for task in self.group.tasks(): process = Process( target=Execution, args=(task, self.workflow_id, previous_context, self._flow_callback), diff --git a/tests/core/test_workflow_background.py b/tests/core/test_workflow_background.py index 8b01d5b..b0aaa9f 100644 --- a/tests/core/test_workflow_background.py +++ b/tests/core/test_workflow_background.py @@ -32,7 +32,7 @@ def test_instantiating_background_class(self): groups=groups, ) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertListEqual(tasks, tasks) self.assertDictEqual(execution.groups, groups) @@ -49,7 +49,7 @@ def test_workflow_with_background_function_completed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) @@ -66,7 +66,7 @@ def test_workflow_with_background_function_failed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].status, TypeStatus.FAILED) self.assertIsNone(tasks[0].current_context.storage) @@ -102,5 +102,5 @@ def test_instantiating_background_flow_callback(self): execution.setup_queue() execution._flow_callback(task=task) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].task_id, 5) diff --git a/tests/core/test_workflow_parallel.py b/tests/core/test_workflow_parallel.py index 48c0341..ca318e8 100644 --- a/tests/core/test_workflow_parallel.py +++ b/tests/core/test_workflow_parallel.py @@ -33,7 +33,7 @@ def test_instantiating_parallel_class(self): groups=groups, ) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertListEqual(tasks, tasks) self.assertDictEqual(execution.groups, groups) @@ -50,7 +50,7 @@ def test_workflow_with_parallel_function_completed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) @@ -67,7 +67,7 @@ def test_workflow_with_parallel_function_failed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].status, TypeStatus.FAILED) self.assertIsNone(tasks[0].current_context.storage) @@ -103,5 +103,5 @@ def test_instantiating_parallel_flow_callback(self): execution.setup_queue() execution._flow_callback(task=task) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].task_id, 5) diff --git a/tests/core/test_workflow_sequential.py b/tests/core/test_workflow_sequential.py index beddac0..bd518b3 100644 --- a/tests/core/test_workflow_sequential.py +++ b/tests/core/test_workflow_sequential.py @@ -110,5 +110,5 @@ def test_instantiating_sequential_flow_callback(self): execution.setup_queue() execution._flow_callback(task=task) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].task_id, 5) diff --git a/tests/core/test_workflow_sequential_group.py b/tests/core/test_workflow_sequential_group.py index 6348d90..292836f 100644 --- a/tests/core/test_workflow_sequential_group.py +++ b/tests/core/test_workflow_sequential_group.py @@ -34,7 +34,7 @@ def test_instantiating_sequential_group_class(self): groups=groups, ) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertListEqual(tasks, tasks) self.assertDictEqual(execution.groups, groups) @@ -51,7 +51,7 @@ def test_workflow_with_sequential_group_function_completed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) @@ -68,7 +68,7 @@ def test_workflow_with_sequential_group_function_failed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].status, TypeStatus.FAILED) self.assertIsNone(tasks[0].current_context.storage) @@ -104,5 +104,5 @@ def test_instantiating_sequential_group_flow_callback(self): execution.setup_queue() execution._flow_callback(task=task) - tasks = execution.get_tasks() + tasks = execution.get_groups() self.assertEqual(tasks[0].task_id, 5) From fb63b3462b890a8c866d4accca93beb6bfc3aa4e Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Wed, 13 Aug 2025 17:54:55 -0300 Subject: [PATCH 02/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20New=20flo?= =?UTF-8?q?w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotflow/abc/flow.py | 2 +- dotflow/core/dotflow.py | 2 +- dotflow/core/workflow.py | 69 +++++++++++--------- tests/core/test_workflow_background.py | 8 +-- tests/core/test_workflow_parallel.py | 8 +-- tests/core/test_workflow_sequential.py | 2 +- tests/core/test_workflow_sequential_group.py | 8 +-- 7 files changed, 52 insertions(+), 47 deletions(-) diff --git a/dotflow/abc/flow.py b/dotflow/abc/flow.py index 829cf0a..4f86d9e 100644 --- a/dotflow/abc/flow.py +++ b/dotflow/abc/flow.py @@ -28,7 +28,7 @@ def setup_queue(self) -> None: self.queue = [] @abstractmethod - def get_groups(self) -> List[Task]: + def transport(self) -> List[Task]: return self.queue @abstractmethod diff --git a/dotflow/core/dotflow.py b/dotflow/core/dotflow.py index 59cd487..c69591d 100644 --- a/dotflow/core/dotflow.py +++ b/dotflow/core/dotflow.py @@ -55,7 +55,7 @@ def __init__( group=self.task.group, workflow_id=self.workflow_id ) - + def result_task(self): """ Returns: diff --git a/dotflow/core/workflow.py b/dotflow/core/workflow.py index db326f3..9696a64 100644 --- a/dotflow/core/workflow.py +++ b/dotflow/core/workflow.py @@ -122,23 +122,23 @@ def sequential(self, **kwargs) -> List[Task]: if group.size() > many_groups: process = SequentialGroup(**kwargs) - return process.get_groups() + return process.transport() process = Sequential(**kwargs) - return process.get_groups() + return process.transport() def sequential_group(self, **kwargs): """Sequential Group execution""" process = SequentialGroup(**kwargs) - return process.get_groups() + return process.transport() def background(self, **kwargs) -> List[Task]: """Background execution""" process = Background(**kwargs) - return process.get_groups() + return process.transport() def parallel(self, **kwargs) -> List[Task]: - """Sequential execution""" + """Parallel execution""" if is_darwin_arm(): warnings.warn( "Parallel mode does not work with MacOS." @@ -146,10 +146,10 @@ def parallel(self, **kwargs) -> List[Task]: Warning ) process = Sequential(**kwargs) - return process.get_groups() + return process.transport() process = Parallel(**kwargs) - return process.get_groups() + return process.transport() class Sequential(Flow): @@ -158,7 +158,7 @@ class Sequential(Flow): def setup_queue(self) -> None: self.queue = [] - def get_groups(self) -> List[Task]: + def transport(self) -> List[Task]: return self.queue def _flow_callback(self, task: Task) -> None: @@ -186,25 +186,25 @@ def run(self) -> None: class SequentialGroup(Flow): - """SequentialGroup""" + """Sequential Group""" def setup_queue(self) -> None: self.queue = Queue() - def get_tasks(self) -> List[Task]: + def transport(self) -> List[Task]: contexts = {} - while len(contexts) < len(self.tasks): + while len(contexts) < self.group.size(): if not self.queue.empty(): contexts = {**contexts, **self.queue.get()} if contexts: - for task in self.tasks: + for task in self.group.tasks(): task.current_context = contexts[task.task_id]["current_context"] task.duration = contexts[task.task_id]["duration"] task.error = contexts[task.task_id]["error"] task.status = contexts[task.task_id]["status"] - return self.tasks + return self.group.tasks() def _flow_callback(self, task: Task) -> None: current_task = { @@ -221,10 +221,10 @@ def run(self) -> None: threads = [] processes = [] - for _, group_tasks in self.groups.items(): + for _, queue in self.group.queue.items(): thread = threading.Thread( target=self._launch_group, - args=(processes, group_tasks,) + args=(processes, queue,) ) thread.start() threads.append(thread) @@ -235,18 +235,18 @@ def run(self) -> None: for thread in threads: thread.join() - def _launch_group(self, processes, group_tasks): + def _launch_group(self, processes, queue): process = Process( target=self._run_group, - args=(group_tasks,) + args=(queue,) ) process.start() processes.append(process) - def _run_group(self, groups: List[Task]) -> None: + def _run_group(self, queue: List[Task]) -> None: previous_context = Context(workflow_id=self.workflow_id) - for task in groups: + for task in queue.tasks: Execution( task=task, workflow_id=self.workflow_id, @@ -268,11 +268,8 @@ class Background(Flow): def setup_queue(self) -> None: self.queue = [] - def get_groups(self) -> List[Task]: - return self.group - def _flow_callback(self, task: Task) -> None: - pass + return self.queue def run(self) -> None: thread = threading.Thread( @@ -293,17 +290,18 @@ class Parallel(Flow): def setup_queue(self) -> None: self.queue = Queue() - def get_groups(self) -> List[Task]: + def transport(self) -> List[Task]: contexts = {} - while len(contexts) < len(self.group.size()): + while len(contexts) < self.group.size(): if not self.queue.empty(): contexts = {**contexts, **self.queue.get()} - for task in self.group.tasks(): - task.current_context = contexts[task.task_id]["current_context"] - task.duration = contexts[task.task_id]["duration"] - task.error = contexts[task.task_id]["error"] - task.status = contexts[task.task_id]["status"] + if contexts: + for task in self.group.tasks(): + task.current_context = contexts[task.task_id]["current_context"] + task.duration = contexts[task.task_id]["duration"] + task.error = contexts[task.task_id]["error"] + task.status = contexts[task.task_id]["status"] return self.group.tasks() @@ -320,12 +318,19 @@ def _flow_callback(self, task: Task) -> None: def run(self) -> None: processes = [] - previous_context = Context(workflow_id=self.workflow_id) + previous_context = Context( + workflow_id=self.workflow_id + ) for task in self.group.tasks(): process = Process( target=Execution, - args=(task, self.workflow_id, previous_context, self._flow_callback), + args=( + task, + self.workflow_id, + previous_context, + self._flow_callback + ), ) process.start() processes.append(process) diff --git a/tests/core/test_workflow_background.py b/tests/core/test_workflow_background.py index b0aaa9f..a3a999d 100644 --- a/tests/core/test_workflow_background.py +++ b/tests/core/test_workflow_background.py @@ -32,7 +32,7 @@ def test_instantiating_background_class(self): groups=groups, ) - tasks = execution.get_groups() + tasks = execution.transport() self.assertListEqual(tasks, tasks) self.assertDictEqual(execution.groups, groups) @@ -49,7 +49,7 @@ def test_workflow_with_background_function_completed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) @@ -66,7 +66,7 @@ def test_workflow_with_background_function_failed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].status, TypeStatus.FAILED) self.assertIsNone(tasks[0].current_context.storage) @@ -102,5 +102,5 @@ def test_instantiating_background_flow_callback(self): execution.setup_queue() execution._flow_callback(task=task) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].task_id, 5) diff --git a/tests/core/test_workflow_parallel.py b/tests/core/test_workflow_parallel.py index ca318e8..9c72a21 100644 --- a/tests/core/test_workflow_parallel.py +++ b/tests/core/test_workflow_parallel.py @@ -33,7 +33,7 @@ def test_instantiating_parallel_class(self): groups=groups, ) - tasks = execution.get_groups() + tasks = execution.transport() self.assertListEqual(tasks, tasks) self.assertDictEqual(execution.groups, groups) @@ -50,7 +50,7 @@ def test_workflow_with_parallel_function_completed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) @@ -67,7 +67,7 @@ def test_workflow_with_parallel_function_failed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].status, TypeStatus.FAILED) self.assertIsNone(tasks[0].current_context.storage) @@ -103,5 +103,5 @@ def test_instantiating_parallel_flow_callback(self): execution.setup_queue() execution._flow_callback(task=task) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].task_id, 5) diff --git a/tests/core/test_workflow_sequential.py b/tests/core/test_workflow_sequential.py index bd518b3..d3095ed 100644 --- a/tests/core/test_workflow_sequential.py +++ b/tests/core/test_workflow_sequential.py @@ -110,5 +110,5 @@ def test_instantiating_sequential_flow_callback(self): execution.setup_queue() execution._flow_callback(task=task) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].task_id, 5) diff --git a/tests/core/test_workflow_sequential_group.py b/tests/core/test_workflow_sequential_group.py index 292836f..eb70810 100644 --- a/tests/core/test_workflow_sequential_group.py +++ b/tests/core/test_workflow_sequential_group.py @@ -34,7 +34,7 @@ def test_instantiating_sequential_group_class(self): groups=groups, ) - tasks = execution.get_groups() + tasks = execution.transport() self.assertListEqual(tasks, tasks) self.assertDictEqual(execution.groups, groups) @@ -51,7 +51,7 @@ def test_workflow_with_sequential_group_function_completed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) @@ -68,7 +68,7 @@ def test_workflow_with_sequential_group_function_failed(self): groups=grouper(tasks=tasks), ) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].status, TypeStatus.FAILED) self.assertIsNone(tasks[0].current_context.storage) @@ -104,5 +104,5 @@ def test_instantiating_sequential_group_flow_callback(self): execution.setup_queue() execution._flow_callback(task=task) - tasks = execution.get_groups() + tasks = execution.transport() self.assertEqual(tasks[0].task_id, 5) From 7c7bc6455b6105fab5fc6a5fa70203aada9ee278 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Wed, 13 Aug 2025 20:31:25 -0300 Subject: [PATCH 03/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Code=20AP?= =?UTF-8?q?I=20Improvement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 32 ++++++++++----------- docs/nav/learn/getting-started.md | 4 +-- docs/nav/learn/notify-with-telegram.md | 2 +- dotflow/cli/commands/start.py | 2 +- dotflow/core/execution.py | 40 +++++++++++++------------- tests/core/test_dotflow.py | 2 +- tests/test_flow.py | 22 +++++++------- 7 files changed, 52 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 31f9b8a..60fbadc 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,8 @@ def my_task_y(): workflow = DotFlow() -workflow.task.add(step=my_task_x, callback=my_callback) -workflow.task.add(step=my_task_y, callback=my_callback) +workflow.add(step=my_task_x, callback=my_callback) +workflow.add(step=my_task_y, callback=my_callback) workflow.start() ``` @@ -152,20 +152,20 @@ Now, simply add the `my_task_x` and `my_callback` functions you created earlier - Adding one step at a time: ```python -workflow.task.add(step=my_task_x, callback=my_callback) -workflow.task.add(step=my_task_y, callback=my_callback) +workflow.add(step=my_task_x, callback=my_callback) +workflow.add(step=my_task_y, callback=my_callback) ``` - Adding multiple steps at the same time: ```python -workflow.task.add(step=[my_task_x, my_task_y], callback=my_callback) +workflow.add(step=[my_task_x, my_task_y], callback=my_callback) ``` - Adding a step with the module path: ```python -workflow.task.add(step="module.task.my_task_x", callback=my_callback) +workflow.add(step="module.task.my_task_x", callback=my_callback) ``` #### 6. Start @@ -215,8 +215,8 @@ dotflow start --step examples.cli_with_mode.simple_step --mode parallel #### Sequential ```python -workflow.task.add(step=task_foo) -workflow.task.add(step=task_bar) +workflow.add(step=task_foo) +workflow.add(step=task_bar) workflow.start() ``` @@ -236,8 +236,8 @@ D[Finish] #### Sequential with Groups ```python -workflow.task.add(step=task_foo, group_name="foo") -workflow.task.add(step=task_bar, group_name="bar") +workflow.add(step=task_foo, group_name="foo") +workflow.add(step=task_bar, group_name="bar") workflow.start() ``` @@ -261,8 +261,8 @@ flowchart TD #### Background ```python -workflow.task.add(step=task_foo) -workflow.task.add(step=task_bar) +workflow.add(step=task_foo) +workflow.add(step=task_bar) workflow.start(mode="background") ``` @@ -283,10 +283,10 @@ D[Finish] #### Parallel ```python -workflow.task.add(step=task_a) -workflow.task.add(step=task_b) -workflow.task.add(step=task_c) -workflow.task.add(step=task_d) +workflow.add(step=task_a) +workflow.add(step=task_b) +workflow.add(step=task_c) +workflow.add(step=task_d) workflow.start(mode="parallel") ``` diff --git a/docs/nav/learn/getting-started.md b/docs/nav/learn/getting-started.md index ef9fbeb..db161dd 100644 --- a/docs/nav/learn/getting-started.md +++ b/docs/nav/learn/getting-started.md @@ -35,7 +35,7 @@ def my_task(): print("task") workflow = DotFlow() -workflow.task.add(step=my_task, callback=my_callback) +workflow.add(step=my_task, callback=my_callback) workflow.start() ``` @@ -78,7 +78,7 @@ workflow = DotFlow() Now, simply add the `my_task` and `my_callback` functions you created earlier to the workflow using the code below. This process is necessary to define which tasks will be executed and the order in which they will run. The execution order follows the sequence in which they were added to the workflow. [More details](https://dotflow-io.github.io/dotflow/nav/reference/task-builder/#dotflow.core.task.TaskBuilder.add) ```python -workflow.task.add(step=my_task, callback=my_callback) +workflow.add(step=my_task, callback=my_callback) ``` ### 6. Start diff --git a/docs/nav/learn/notify-with-telegram.md b/docs/nav/learn/notify-with-telegram.md index 2d4c4cf..a91bf42 100644 --- a/docs/nav/learn/notify-with-telegram.md +++ b/docs/nav/learn/notify-with-telegram.md @@ -92,7 +92,7 @@ workflow = DotFlow( Add your defined task as a step in the workflow: ```python -workflow.task.add(step=simple_task) +workflow.add(step=simple_task) ``` ### Start diff --git a/dotflow/cli/commands/start.py b/dotflow/cli/commands/start.py index 64aa8b9..401b4e4 100644 --- a/dotflow/cli/commands/start.py +++ b/dotflow/cli/commands/start.py @@ -13,7 +13,7 @@ class StartCommand(Command): def setup(self): workflow = self._new_workflow() - workflow.task.add( + workflow.add( step=self.params.step, callback=self.params.callback, initial_context=self.params.initial_context, diff --git a/dotflow/core/execution.py b/dotflow/core/execution.py index 8d33149..fd436df 100644 --- a/dotflow/core/execution.py +++ b/dotflow/core/execution.py @@ -20,27 +20,27 @@ from dotflow.utils import basic_callback +VALID_OBJECTS = [ + str, + int, + float, + complex, + dict, + list, + tuple, + set, + frozenset, + range, + bool, + FunctionType, + NoneType, + bytes, + bytearray, + memoryview, +] -class Execution: - VALID_OBJECTS = [ - str, - int, - float, - complex, - dict, - list, - tuple, - set, - frozenset, - range, - bool, - FunctionType, - NoneType, - bytes, - bytearray, - memoryview, - ] +class Execution: def __init__( self, @@ -146,7 +146,7 @@ def _excution(self, _flow_callback): task=self.task, ) - if type(current_context.storage) not in self.VALID_OBJECTS: + if type(current_context.storage) not in VALID_OBJECTS: current_context = self._execution_with_class( class_instance=current_context.storage ) diff --git a/tests/core/test_dotflow.py b/tests/core/test_dotflow.py index 19fb404..926058a 100644 --- a/tests/core/test_dotflow.py +++ b/tests/core/test_dotflow.py @@ -15,7 +15,7 @@ class TestDotFlow(unittest.TestCase): def setUp(self): self.workflow = DotFlow() - self.workflow.task.add(step=action_step) + self.workflow.add(step=action_step) def test_instantiating_dotflow_class(self): self.assertIsInstance(self.workflow.task, TaskBuilder) diff --git a/tests/test_flow.py b/tests/test_flow.py index a60a094..a0528e9 100644 --- a/tests/test_flow.py +++ b/tests/test_flow.py @@ -95,19 +95,19 @@ def load_task_y(previous_context): def main(): workflow = DotFlow() - workflow.task.add(step=StepX) - workflow.task.add(step=StepY, initial_context={"foo": "bar"}) - workflow.task.add(step=simple_step) - workflow.task.add(step=extract_task_x) - workflow.task.add(step=transform_task_x, initial_context={"foo": True}) - workflow.task.add(step=load_task_x) - workflow.task.add(step=extract_task_y) - workflow.task.add(step=transform_task_y) - workflow.task.add(step=load_task_y) + workflow.add(step=StepX) + workflow.add(step=StepY, initial_context={"foo": "bar"}) + workflow.add(step=simple_step) + workflow.add(step=extract_task_x) + workflow.add(step=transform_task_x, initial_context={"foo": True}) + workflow.add(step=load_task_x) + workflow.add(step=extract_task_y) + workflow.add(step=transform_task_y) + workflow.add(step=load_task_y) workflow.start() workflow = DotFlow() - workflow.task.add( + workflow.add( [ StepX, StepY, @@ -120,7 +120,7 @@ def main(): workflow.start() workflow = DotFlow() - workflow.task.add( + workflow.add( [ "tests.test_flow.StepX", "tests.test_flow.StepY", From 4a5f50036c2760c2265edbb5670bcbfe4ad3d0fe Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Wed, 13 Aug 2025 20:41:29 -0300 Subject: [PATCH 04/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Code=20AP?= =?UTF-8?q?I=20Improvement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotflow/core/workflow.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dotflow/core/workflow.py b/dotflow/core/workflow.py index 9696a64..806caed 100644 --- a/dotflow/core/workflow.py +++ b/dotflow/core/workflow.py @@ -120,7 +120,7 @@ def sequential(self, **kwargs) -> List[Task]: many_groups = 1 group: QueueGroup = kwargs.get("group") - if group.size() > many_groups: + if group.size() < many_groups: process = SequentialGroup(**kwargs) return process.transport() @@ -268,9 +268,12 @@ class Background(Flow): def setup_queue(self) -> None: self.queue = [] - def _flow_callback(self, task: Task) -> None: + def transport(self) -> List[Task]: return self.queue + def _flow_callback(self, task: Task) -> None: + self.queue.append(task) + def run(self) -> None: thread = threading.Thread( target=Sequential, From fffafbb2c20a47346d1cb2b0970d9974d46de955 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Fri, 15 Aug 2025 21:10:44 -0300 Subject: [PATCH 05/20] =?UTF-8?q?=E2=9D=A4=EF=B8=8F=EF=B8=8F=20TEST:=20Upd?= =?UTF-8?q?ated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotflow/core/workflow.py | 60 +++++++++--- main.py | 39 ++++++++ tests/core/test_task_build.py | 34 ++++--- tests/core/test_workflow.py | 97 +++++++++++-------- tests/core/test_workflow_background.py | 46 +++++---- tests/core/test_workflow_parallel.py | 48 +++++---- tests/core/test_workflow_sequential.py | 73 ++++++++------ tests/core/test_workflow_sequential_group.py | 46 +++++---- tests/{test_integration.py => integration.py} | 0 9 files changed, 282 insertions(+), 161 deletions(-) create mode 100644 main.py rename tests/{test_integration.py => integration.py} (100%) diff --git a/dotflow/core/workflow.py b/dotflow/core/workflow.py index 806caed..a1374fa 100644 --- a/dotflow/core/workflow.py +++ b/dotflow/core/workflow.py @@ -1,9 +1,12 @@ """Workflow module""" +import sys import threading import warnings import platform +import multiprocessing as mp +from queue import Empty from datetime import datetime from multiprocessing import Process, Queue @@ -18,6 +21,8 @@ from dotflow.core.task import Task, QueueGroup from dotflow.utils import basic_callback +# mp.set_start_method("spawn", force=True) + def is_darwin_arm() -> bool: """Is Darwin""" @@ -42,8 +47,8 @@ class Manager: ) Args: - tasks (List[Task]): - A list containing objects of type Task. + group (QueueGroup): + A group containing objects of type Task. on_success (Callable): Success function to be executed after the completion of the entire @@ -159,7 +164,7 @@ def setup_queue(self) -> None: self.queue = [] def transport(self) -> List[Task]: - return self.queue + return self.group.tasks() def _flow_callback(self, task: Task) -> None: self.queue.append(task) @@ -189,13 +194,21 @@ class SequentialGroup(Flow): """Sequential Group""" def setup_queue(self) -> None: - self.queue = Queue() + self.queue = None + + if sys.version_info >= (3, 12): + self.queue = mp.Queue() + else: + self.queue = Queue() def transport(self) -> List[Task]: contexts = {} while len(contexts) < self.group.size(): - if not self.queue.empty(): - contexts = {**contexts, **self.queue.get()} + try: + data = self.queue.get(timeout=1) + contexts.update(data) + except Empty: + pass if contexts: for task in self.group.tasks(): @@ -229,14 +242,21 @@ def run(self) -> None: thread.start() threads.append(thread) - for process in processes: - process.join() - for thread in threads: thread.join() + for process in processes: + process.join() + def _launch_group(self, processes, queue): - process = Process( + new_process = None + + if sys.version_info >= (3, 12): + new_process = mp.Process + else: + new_process = Process + + process = new_process( target=self._run_group, args=(queue,) ) @@ -269,10 +289,10 @@ def setup_queue(self) -> None: self.queue = [] def transport(self) -> List[Task]: - return self.queue + return self.group.tasks() def _flow_callback(self, task: Task) -> None: - self.queue.append(task) + pass def run(self) -> None: thread = threading.Thread( @@ -291,7 +311,12 @@ class Parallel(Flow): """Parallel""" def setup_queue(self) -> None: - self.queue = Queue() + self.queue = None + + if sys.version_info >= (3, 12): + self.queue = mp.Queue() + else: + self.queue = Queue() def transport(self) -> List[Task]: contexts = {} @@ -320,13 +345,20 @@ def _flow_callback(self, task: Task) -> None: self.queue.put(current_task) def run(self) -> None: + new_process = None + + if sys.version_info >= (3, 12): + new_process = mp.Process + else: + new_process = Process + processes = [] previous_context = Context( workflow_id=self.workflow_id ) for task in self.group.tasks(): - process = Process( + process = new_process( target=Execution, args=( task, diff --git a/main.py b/main.py new file mode 100644 index 0000000..e92a8db --- /dev/null +++ b/main.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +from time import sleep + +from dotflow import DotFlow, action + + +@action +def task_foo(initial_context): + sleep(2) + value = initial_context.storage + return value * value * value, "foo" + + +@action +def task_bar(initial_context): + sleep(1) + value = initial_context.storage + return value * value * value, "bar" + + +def main(): + workflow = DotFlow() + + workflow.task.add(step=task_foo, initial_context=10, group_name="foo") + workflow.task.add(step=task_bar, initial_context=10, group_name="bar") + + # workflow.start(mode="sequential") + workflow.start(mode="background") + # workflow.start(mode="parallel") + + for context in workflow.result_context(): + print(context.storage) + + return workflow + + +if __name__ == "__main__": + main() diff --git a/tests/core/test_task_build.py b/tests/core/test_task_build.py index d616072..a584003 100644 --- a/tests/core/test_task_build.py +++ b/tests/core/test_task_build.py @@ -6,7 +6,7 @@ from dotflow.core.config import Config from dotflow.core.context import Context from dotflow.core.exception import MissingActionDecorator -from dotflow.core.task import Task, TaskBuilder +from dotflow.core.task import Task, TaskBuilder, QueueGroup from dotflow.core.serializers.workflow import SerializerWorkflow from dotflow.core.serializers.task import SerializerTask from dotflow.utils import basic_callback @@ -23,32 +23,36 @@ def setUp(self): def test_instantiating_task_build_class(self): task = TaskBuilder(config=self.config) - self.assertListEqual(task.queue, []) + self.assertIsInstance(task.group, QueueGroup) def test_add_method(self): task = TaskBuilder(config=self.config) task.add(step=action_step) + tasks = task.group.tasks() - self.assertEqual(task.queue[0].task_id, 0) - self.assertIsInstance(task.queue[0], Task) - self.assertEqual(task.queue[0].callback, basic_callback) - self.assertEqual(len(task.queue), 1) + self.assertTrue(task.group.size()) + self.assertEqual(tasks[0].task_id, 0) + self.assertIsInstance(tasks[0], Task) + self.assertEqual(tasks[0].callback, basic_callback) + self.assertEqual(len(tasks), 1) def test_add_method_with_class_context(self): task = TaskBuilder(config=self.config) task.add(step=action_step, initial_context=Context(storage=self.content)) + tasks = task.group.tasks() - self.assertEqual(task.queue[0].initial_context.storage, self.content) + self.assertEqual(tasks[0].initial_context.storage, self.content) - self.assertIsInstance(task.queue[0].initial_context, Context) + self.assertIsInstance(tasks[0].initial_context, Context) def test_add_method_without_class_context(self): task = TaskBuilder(config=self.config) task.add(step=action_step, initial_context=self.content) + tasks = task.group.tasks() - self.assertEqual(task.queue[0].initial_context.storage, self.content) + self.assertEqual(tasks[0].initial_context.storage, self.content) - self.assertIsInstance(task.queue[0].initial_context, Context) + self.assertIsInstance(tasks[0].initial_context, Context) def test_count_method(self): task = TaskBuilder(config=self.config) @@ -56,11 +60,11 @@ def test_count_method(self): initial_count = 0 final_count = 1 - self.assertEqual(task.count(), initial_count) + self.assertEqual(task.group.size(), initial_count) task.add(step=action_step) - self.assertEqual(task.count(), final_count) + self.assertEqual(task.group.size(), final_count) def test_clear_method(self): task = TaskBuilder(config=self.config) @@ -69,11 +73,11 @@ def test_clear_method(self): expected_count_after = 0 task.add(step=action_step) - self.assertEqual(task.count(), expected_count_before) + self.assertEqual(task.group.size(), expected_count_before) - task.clear() + task.group.queue["default"].clear() - self.assertEqual(task.count(), expected_count_after) + self.assertEqual(task.group.size(), expected_count_after) def test_with_method_step_without_decorator(self): task = TaskBuilder(config=self.config) diff --git a/tests/core/test_workflow.py b/tests/core/test_workflow.py index b2b56df..66cc6e9 100644 --- a/tests/core/test_workflow.py +++ b/tests/core/test_workflow.py @@ -9,7 +9,7 @@ from dotflow.core.workflow import Manager from dotflow.core.types import TypeExecution, TypeStatus from dotflow.core.exception import ExecutionModeNotExist -from dotflow.core.task import Task +from dotflow.core.task import Task, QueueGroup from tests.mocks import ( ActionStep, @@ -22,82 +22,99 @@ class TestWorkflow(unittest.TestCase): def setUp(self): - task = Task( - task_id=0, - step=action_step, - callback=simple_callback + self.group = QueueGroup() + self.group.add( + item=Task( + task_id=0, + step=action_step, + callback=simple_callback + ) ) - self.tasks = [task] def test_instantiating_workflow_class(self): - controller = Manager(tasks=self.tasks) + controller = Manager(group=self.group) - self.assertListEqual(controller.tasks, self.tasks) + self.assertEqual(controller.group, self.group) self.assertIsInstance(controller.workflow_id, UUID) self.assertIsInstance(controller.on_success, FunctionType) self.assertIsInstance(controller.on_failure, FunctionType) def test_workflow_with_function_completed(self): - task = Task( - task_id=0, - step=action_step, - callback=simple_callback + group = QueueGroup() + group.add( + item=Task( + task_id=0, + step=action_step, + callback=simple_callback + ) ) - controller = Manager(tasks=[task]) - self.assertEqual(controller.tasks[0].status, TypeStatus.COMPLETED) + controller = Manager(group=group) + self.assertEqual(controller.group.tasks()[0].status, TypeStatus.COMPLETED) def test_workflow_with_function_failed(self): - task = Task( - task_id=0, - step=action_step_with_error, - callback=simple_callback + group = QueueGroup() + group.add( + item=Task( + task_id=0, + step=action_step_with_error, + callback=simple_callback + ) ) - controller = Manager(tasks=[task]) - self.assertEqual(controller.tasks[0].status, TypeStatus.FAILED) + controller = Manager(group=group) + self.assertEqual(controller.group.tasks()[0].status, TypeStatus.FAILED) def test_with_execution_mode_that_does_not_exist(self): with self.assertRaises(ExecutionModeNotExist): - Manager(tasks=self.tasks, mode="unknown") + Manager(group=self.group, mode="unknown") def test_with_execution_mode_sequential(self): - Manager(tasks=self.tasks, mode=TypeExecution.SEQUENTIAL) + Manager(group=self.group, mode=TypeExecution.SEQUENTIAL) def test_with_execution_mode_background(self): - Manager(tasks=self.tasks, mode=TypeExecution.BACKGROUND) + Manager(group=self.group, mode=TypeExecution.BACKGROUND) def test_with_execution_mode_parallel(self): - Manager(tasks=self.tasks, mode=TypeExecution.PARALLEL) + Manager(group=self.group, mode=TypeExecution.PARALLEL) def test_callback_success_called(self): - task = Task( - task_id=0, - step=action_step, - callback=simple_callback + group = QueueGroup() + group.add( + item=Task( + task_id=0, + step=action_step, + callback=simple_callback + ) ) mock_success = Mock() - Manager(tasks=[task], on_success=mock_success) + Manager(group=group, on_success=mock_success) mock_success.assert_called() def test_callback_failure_called(self): - task = Task( - task_id=0, - step=action_step_with_error, - callback=simple_callback + group = QueueGroup() + group.add( + item=Task( + task_id=0, + step=action_step_with_error, + callback=simple_callback + ) ) mock_failure = Mock() - Manager(tasks=[task], on_failure=mock_failure) + Manager(group=group, on_failure=mock_failure) mock_failure.assert_called() def test_workflow_with_class_completed(self): - task = Task( - task_id=0, - step=ActionStep, - callback=simple_callback + group = QueueGroup() + group.add( + item=Task( + task_id=0, + step=ActionStep, + callback=simple_callback + ) ) - controller = Manager(tasks=[task]) - self.assertEqual(controller.tasks[0].status, TypeStatus.COMPLETED) + controller = Manager(group=group) + self.assertEqual(controller.group.tasks()[0].status, TypeStatus.COMPLETED) diff --git a/tests/core/test_workflow_background.py b/tests/core/test_workflow_background.py index a3a999d..0b2c7a1 100644 --- a/tests/core/test_workflow_background.py +++ b/tests/core/test_workflow_background.py @@ -4,9 +4,9 @@ from uuid import uuid4 -from dotflow.core.workflow import Background, grouper +from dotflow.core.workflow import Background from dotflow.core.types import TypeStatus -from dotflow.core.task import Task, TaskError +from dotflow.core.task import Task, TaskError, QueueGroup from tests.mocks import ( action_step, @@ -22,31 +22,34 @@ def setUp(self): self.ignore = False def test_instantiating_background_class(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] - groups = grouper(tasks=tasks) + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = Background( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) tasks = execution.transport() self.assertListEqual(tasks, tasks) - self.assertDictEqual(execution.groups, groups) + self.assertIsInstance(execution.group, QueueGroup) self.assertEqual(execution.workflow_id, self.workflow_id) self.assertEqual(execution.ignore, self.ignore) def test_workflow_with_background_function_completed(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = Background( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=grouper(tasks=tasks), + group=group, ) tasks = execution.transport() @@ -57,13 +60,15 @@ def test_workflow_with_background_function_completed(self): self.assertEqual(tasks[0].error.message, "") def test_workflow_with_background_function_failed(self): - tasks = [Task(task_id=0, step=action_step_with_error, callback=simple_callback)] + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step_with_error, callback=simple_callback) + ) execution = Background( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=grouper(tasks=tasks), + group=group, ) tasks = execution.transport() @@ -74,14 +79,15 @@ def test_workflow_with_background_function_failed(self): self.assertEqual(tasks[0].error.message, "Fail!") def test_instantiating_background_setup_queue(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] - groups = grouper(tasks=tasks) + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = Background( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) execution.setup_queue() @@ -90,13 +96,13 @@ def test_instantiating_background_setup_queue(self): def test_instantiating_background_flow_callback(self): task = Task(task_id=5, step=action_step, callback=simple_callback) - groups = grouper(tasks=[task]) + group = QueueGroup() + group.add(item=task) execution = Background( - tasks=[task], workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) execution.setup_queue() diff --git a/tests/core/test_workflow_parallel.py b/tests/core/test_workflow_parallel.py index 9c72a21..6a46a91 100644 --- a/tests/core/test_workflow_parallel.py +++ b/tests/core/test_workflow_parallel.py @@ -5,9 +5,9 @@ from uuid import uuid4 from multiprocessing.queues import Queue -from dotflow.core.workflow import Parallel, grouper +from dotflow.core.workflow import Parallel from dotflow.core.types import TypeStatus -from dotflow.core.task import Task, TaskError +from dotflow.core.task import Task, TaskError, QueueGroup from tests.mocks import ( action_step, @@ -23,31 +23,34 @@ def setUp(self): self.ignore = False def test_instantiating_parallel_class(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] - groups = grouper(tasks=tasks) + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = Parallel( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) tasks = execution.transport() self.assertListEqual(tasks, tasks) - self.assertDictEqual(execution.groups, groups) + self.assertIsInstance(execution.group, QueueGroup) self.assertEqual(execution.workflow_id, self.workflow_id) self.assertEqual(execution.ignore, self.ignore) def test_workflow_with_parallel_function_completed(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = Parallel( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=grouper(tasks=tasks), + group=group, ) tasks = execution.transport() @@ -58,13 +61,15 @@ def test_workflow_with_parallel_function_completed(self): self.assertEqual(tasks[0].error.message, "") def test_workflow_with_parallel_function_failed(self): - tasks = [Task(task_id=0, step=action_step_with_error, callback=simple_callback)] + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step_with_error, callback=simple_callback) + ) execution = Parallel( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=grouper(tasks=tasks), + group=group, ) tasks = execution.transport() @@ -75,14 +80,15 @@ def test_workflow_with_parallel_function_failed(self): self.assertEqual(tasks[0].error.message, "Fail!") def test_instantiating_parallel_setup_queue(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] - groups = grouper(tasks=tasks) + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = Parallel( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) execution.setup_queue() @@ -91,13 +97,15 @@ def test_instantiating_parallel_setup_queue(self): def test_instantiating_parallel_flow_callback(self): task = Task(task_id=5, step=action_step, callback=simple_callback) - groups = grouper(tasks=[task]) + group = QueueGroup() + group.add( + item=Task(task_id=5, step=action_step, callback=simple_callback) + ) execution = Parallel( - tasks=[task], workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) execution.setup_queue() diff --git a/tests/core/test_workflow_sequential.py b/tests/core/test_workflow_sequential.py index d3095ed..88a1f6b 100644 --- a/tests/core/test_workflow_sequential.py +++ b/tests/core/test_workflow_sequential.py @@ -5,9 +5,9 @@ from uuid import uuid4 from unittest.mock import Mock -from dotflow.core.workflow import Sequential, grouper +from dotflow.core.workflow import Sequential from dotflow.core.types import TypeStatus -from dotflow.core.task import Task, TaskError +from dotflow.core.task import Task, TaskError, QueueGroup from tests.mocks import ( action_step, @@ -23,73 +23,82 @@ def setUp(self): self.ignore = False def test_instantiating_sequential_class(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] - groups = grouper(tasks=tasks) + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = Sequential( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) - self.assertListEqual(execution.tasks, tasks) - self.assertDictEqual(execution.groups, groups) + self.assertIsInstance(execution.group, QueueGroup) self.assertEqual(execution.workflow_id, self.workflow_id) self.assertEqual(execution.ignore, self.ignore) def test_workflow_with_callback(self): mock_callback = Mock() - tasks = [Task(task_id=0, step=action_step, callback=mock_callback)] + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=mock_callback) + ) Sequential( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=grouper(tasks=tasks), + group=group, ) mock_callback.assert_called() def test_workflow_with_function_completed(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = Sequential( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=grouper(tasks=tasks), + group=group, ) + tasks = execution.group.tasks() - self.assertEqual(execution.tasks[0].status, TypeStatus.COMPLETED) - self.assertEqual(execution.tasks[0].current_context.storage, {"foo": "bar"}) - self.assertIsInstance(execution.tasks[0].error, TaskError) - self.assertEqual(execution.tasks[0].error.message, "") + self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) + self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) + self.assertIsInstance(tasks[0].error, TaskError) + self.assertEqual(tasks[0].error.message, "") def test_workflow_with_function_failed(self): - tasks = [Task(task_id=0, step=action_step_with_error, callback=simple_callback)] + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step_with_error, callback=simple_callback) + ) execution = Sequential( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=grouper(tasks=tasks), + group=group, ) + tasks = execution.group.tasks() - self.assertEqual(execution.tasks[0].status, TypeStatus.FAILED) - self.assertIsNone(execution.tasks[0].current_context.storage) - self.assertIsInstance(execution.tasks[0].error, TaskError) - self.assertEqual(execution.tasks[0].error.message, "Fail!") + self.assertEqual(tasks[0].status, TypeStatus.FAILED) + self.assertIsNone(tasks[0].current_context.storage) + self.assertIsInstance(tasks[0].error, TaskError) + self.assertEqual(tasks[0].error.message, "Fail!") def test_instantiating_sequential_setup_queue(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] - groups = grouper(tasks=tasks) + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = Sequential( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) execution.setup_queue() @@ -98,13 +107,13 @@ def test_instantiating_sequential_setup_queue(self): def test_instantiating_sequential_flow_callback(self): task = Task(task_id=5, step=action_step, callback=simple_callback) - groups = grouper(tasks=[task]) + group = QueueGroup() + group.add(item=task) execution = Sequential( - tasks=[task], workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) execution.setup_queue() diff --git a/tests/core/test_workflow_sequential_group.py b/tests/core/test_workflow_sequential_group.py index eb70810..d8725e1 100644 --- a/tests/core/test_workflow_sequential_group.py +++ b/tests/core/test_workflow_sequential_group.py @@ -6,9 +6,9 @@ from multiprocessing.queues import Queue -from dotflow.core.workflow import SequentialGroup, grouper +from dotflow.core.workflow import SequentialGroup from dotflow.core.types import TypeStatus -from dotflow.core.task import Task, TaskError +from dotflow.core.task import Task, TaskError, QueueGroup from tests.mocks import ( action_step, @@ -24,31 +24,34 @@ def setUp(self): self.ignore = False def test_instantiating_sequential_group_class(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] - groups = grouper(tasks=tasks) + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = SequentialGroup( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) tasks = execution.transport() self.assertListEqual(tasks, tasks) - self.assertDictEqual(execution.groups, groups) + self.assertIsInstance(execution.group, QueueGroup) self.assertEqual(execution.workflow_id, self.workflow_id) self.assertEqual(execution.ignore, self.ignore) def test_workflow_with_sequential_group_function_completed(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = SequentialGroup( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=grouper(tasks=tasks), + group=group, ) tasks = execution.transport() @@ -59,13 +62,15 @@ def test_workflow_with_sequential_group_function_completed(self): self.assertEqual(tasks[0].error.message, "") def test_workflow_with_sequential_group_function_failed(self): - tasks = [Task(task_id=0, step=action_step_with_error, callback=simple_callback)] + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step_with_error, callback=simple_callback) + ) execution = SequentialGroup( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=grouper(tasks=tasks), + group=group, ) tasks = execution.transport() @@ -76,14 +81,15 @@ def test_workflow_with_sequential_group_function_failed(self): self.assertEqual(tasks[0].error.message, "Fail!") def test_instantiating_sequential_group_setup_queue(self): - tasks = [Task(task_id=0, step=action_step, callback=simple_callback)] - groups = grouper(tasks=tasks) + group = QueueGroup() + group.add( + item=Task(task_id=0, step=action_step, callback=simple_callback) + ) execution = SequentialGroup( - tasks=tasks, workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) execution.setup_queue() @@ -92,13 +98,13 @@ def test_instantiating_sequential_group_setup_queue(self): def test_instantiating_sequential_group_flow_callback(self): task = Task(task_id=5, step=action_step, callback=simple_callback) - groups = grouper(tasks=[task]) + group = QueueGroup() + group.add(item=task) execution = SequentialGroup( - tasks=[task], workflow_id=self.workflow_id, ignore=self.ignore, - groups=groups, + group=group, ) execution.setup_queue() diff --git a/tests/test_integration.py b/tests/integration.py similarity index 100% rename from tests/test_integration.py rename to tests/integration.py From 04e8273bcb8481d42855ed5df1cac456c6d294c5 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Fri, 15 Aug 2025 23:56:34 -0300 Subject: [PATCH 06/20] =?UTF-8?q?=E2=9D=A4=EF=B8=8F=EF=B8=8F=20TEST:=20Upd?= =?UTF-8?q?ated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotflow/core/task.py | 6 +- tests/core/test_queue.py | 135 ++++++++++++++++++++++++++++++++++ tests/core/test_task_build.py | 4 +- 3 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 tests/core/test_queue.py diff --git a/dotflow/core/task.py b/dotflow/core/task.py index 5f03c5b..b683abf 100644 --- a/dotflow/core/task.py +++ b/dotflow/core/task.py @@ -19,6 +19,8 @@ message_error ) +TASK_GROUP_NAME = "default" + class TaskInstance: """ @@ -89,7 +91,7 @@ def __init__( initial_context: Any = None, workflow_id: UUID = None, config: Config = None, - group_name: str = "default" + group_name: str = TASK_GROUP_NAME ) -> None: super().__init__( task_id, @@ -280,7 +282,7 @@ def add( step: Callable, callback: Callable = basic_callback, initial_context: Any = None, - group_name: str = "default" + group_name: str = TASK_GROUP_NAME ) -> None: """ Args: diff --git a/tests/core/test_queue.py b/tests/core/test_queue.py new file mode 100644 index 0000000..f0048cb --- /dev/null +++ b/tests/core/test_queue.py @@ -0,0 +1,135 @@ +"""Test queue of task""" + +import unittest + +from dotflow.core.task import Task, Queue, QueueGroup, TASK_GROUP_NAME + +from tests.mocks import action_step, simple_callback + + +class TestQueue(unittest.TestCase): + + def setUp(self): + self.task = Task( + task_id=0, + step=action_step, + callback=simple_callback + ) + + def test_instantiating_queue_class(self): + expected_tasks = [] + queue = Queue() + + self.assertIsInstance(queue, Queue) + self.assertListEqual(queue.tasks, expected_tasks) + + def test_queue_add(self): + queue = Queue() + + queue.add(item=self.task) + + self.assertEqual(queue.tasks[0], self.task) + + def test_queue_remove(self): + queue = Queue() + + queue.add(item=self.task) + queue.remove() + + self.assertFalse(queue.tasks) + + def test_queue_size(self): + expected_size = 1 + queue = Queue() + + queue.add(item=self.task) + + self.assertEqual(queue.size(), expected_size) + + def test_queue_reverse(self): + expected_task = Task( + task_id=1, + step=action_step, + callback=simple_callback + ) + queue = Queue() + + queue.add(item=self.task) + queue.add(item=expected_task) + + queue.reverse() + + self.assertEqual(queue.tasks[0], expected_task) + + def test_queue_clear(self): + queue = Queue() + + queue.add(item=self.task) + + queue.clear() + + self.assertFalse(queue.tasks) + + def test_queue_get(self): + queue = Queue() + + queue.add(item=self.task) + + self.assertListEqual(queue.get(), [self.task]) + + +class TestQueueGroup(unittest.TestCase): + + def setUp(self): + self.task = Task( + task_id=0, + step=action_step, + callback=simple_callback + ) + + def test_instantiating_queue_group_class(self): + expected_queue = {} + queue_group = QueueGroup() + + self.assertIsInstance(queue_group, QueueGroup) + self.assertDictEqual(queue_group.queue, expected_queue) + + def test_queue_group_add(self): + queue_group = QueueGroup() + + queue_group.add(item=self.task) + + self.assertIsNotNone(queue_group.queue.get(TASK_GROUP_NAME)) + self.assertIsInstance(queue_group.queue.get(TASK_GROUP_NAME), Queue) + self.assertEqual(queue_group.queue[TASK_GROUP_NAME].tasks[0], self.task) + + def test_queue_group_size(self): + expected_size = 1 + queue = QueueGroup() + + queue.add(item=self.task) + + self.assertEqual(queue.size(), expected_size) + + def test_queue_group_count(self): + expected_count = 2 + task = Task( + task_id=0, + step=action_step, + callback=simple_callback, + group_name="new_group" + ) + queue = QueueGroup() + + queue.add(item=self.task) + queue.add(item=task) + + self.assertEqual(queue.count(), expected_count) + + def test_queue_group_tasks(self): + queue = QueueGroup() + + queue.add(item=self.task) + + self.assertIsNotNone(queue.tasks()) + self.assertListEqual(queue.tasks(), [self.task]) diff --git a/tests/core/test_task_build.py b/tests/core/test_task_build.py index a584003..0a6446b 100644 --- a/tests/core/test_task_build.py +++ b/tests/core/test_task_build.py @@ -6,7 +6,7 @@ from dotflow.core.config import Config from dotflow.core.context import Context from dotflow.core.exception import MissingActionDecorator -from dotflow.core.task import Task, TaskBuilder, QueueGroup +from dotflow.core.task import Task, TaskBuilder, QueueGroup, TASK_GROUP_NAME from dotflow.core.serializers.workflow import SerializerWorkflow from dotflow.core.serializers.task import SerializerTask from dotflow.utils import basic_callback @@ -75,7 +75,7 @@ def test_clear_method(self): task.add(step=action_step) self.assertEqual(task.group.size(), expected_count_before) - task.group.queue["default"].clear() + task.group.queue[TASK_GROUP_NAME].clear() self.assertEqual(task.group.size(), expected_count_after) From 793b8ae2c7d604f71114b1f934409ffecc5c4ef8 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Fri, 15 Aug 2025 23:59:15 -0300 Subject: [PATCH 07/20] =?UTF-8?q?=E2=9D=A4=EF=B8=8F=EF=B8=8F=20TEST:=20Upd?= =?UTF-8?q?ated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 main.py diff --git a/main.py b/main.py deleted file mode 100644 index e92a8db..0000000 --- a/main.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python - -from time import sleep - -from dotflow import DotFlow, action - - -@action -def task_foo(initial_context): - sleep(2) - value = initial_context.storage - return value * value * value, "foo" - - -@action -def task_bar(initial_context): - sleep(1) - value = initial_context.storage - return value * value * value, "bar" - - -def main(): - workflow = DotFlow() - - workflow.task.add(step=task_foo, initial_context=10, group_name="foo") - workflow.task.add(step=task_bar, initial_context=10, group_name="bar") - - # workflow.start(mode="sequential") - workflow.start(mode="background") - # workflow.start(mode="parallel") - - for context in workflow.result_context(): - print(context.storage) - - return workflow - - -if __name__ == "__main__": - main() From 21dd54aa524498c9ac7bc323f6236be500548f33 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Sun, 17 Aug 2025 22:22:36 -0300 Subject: [PATCH 08/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20=20GIT:=20Submodule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples b/examples index ef1f9eb..ae1d54f 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit ef1f9ebf9a2f904a5027e2fe59210bfedfd0b04e +Subproject commit ae1d54fb2d653dd4b22c1ceb544808d84701577a From f400b339a589c9397e64ed8d642b1e087096f51b Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Sun, 17 Aug 2025 22:32:44 -0300 Subject: [PATCH 09/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F:=20FEATURE:=20Updated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 648 ++++++++++++++++------------------------------- requirements.txt | 18 +- 2 files changed, 232 insertions(+), 434 deletions(-) diff --git a/poetry.lock b/poetry.lock index 87b0daf..5224ecd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,7 +6,6 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -18,7 +17,6 @@ version = "3.3.11" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" -groups = ["dev"] files = [ {file = "astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec"}, {file = "astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce"}, @@ -33,14 +31,13 @@ version = "2.17.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, ] [package.extras] -dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] +dev = ["backports.zoneinfo", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata"] [[package]] name = "backrefs" @@ -48,7 +45,6 @@ version = "5.9" description = "A wrapper around re and regex that adds additional back references." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "backrefs-5.9-py310-none-any.whl", hash = "sha256:db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f"}, {file = "backrefs-5.9-py311-none-any.whl", hash = "sha256:6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf"}, @@ -68,7 +64,6 @@ version = "1.3.0" description = "A simple, correct Python build frontend" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "build-1.3.0-py3-none-any.whl", hash = "sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4"}, {file = "build-1.3.0.tar.gz", hash = "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397"}, @@ -83,7 +78,7 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [package.extras] uv = ["uv (>=0.1.18)"] -virtualenv = ["virtualenv (>=20.11) ; python_version < \"3.10\"", "virtualenv (>=20.17) ; python_version >= \"3.10\" and python_version < \"3.14\"", "virtualenv (>=20.31) ; python_version >= \"3.14\""] +virtualenv = ["virtualenv (>=20.11)", "virtualenv (>=20.17)", "virtualenv (>=20.31)"] [[package]] name = "cachetools" @@ -91,7 +86,6 @@ version = "6.1.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "cachetools-6.1.0-py3-none-any.whl", hash = "sha256:1c7bb3cf9193deaf3508b7c5f2a79986c13ea38965c5adcff1f84519cf39163e"}, {file = "cachetools-6.1.0.tar.gz", hash = "sha256:b4c4f404392848db3ce7aac34950d17be4d864da4b8b66911008e430bc544587"}, @@ -103,7 +97,6 @@ version = "2025.8.3" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" -groups = ["main", "docs"] files = [ {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, @@ -115,8 +108,6 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "implementation_name == \"pypy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -196,7 +187,6 @@ version = "5.2.0" description = "Universal encoding detector for Python 3" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, @@ -204,104 +194,90 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.4.2" +version = "3.4.3" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main", "docs"] -files = [ - {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, - {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, - {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, +files = [ + {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, + {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, + {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, ] [[package]] @@ -310,7 +286,6 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -325,7 +300,6 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev", "docs"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -333,107 +307,106 @@ files = [ [[package]] name = "coverage" -version = "7.10.2" +version = "7.10.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "coverage-7.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:79f0283ab5e6499fd5fe382ca3d62afa40fb50ff227676a3125d18af70eabf65"}, - {file = "coverage-7.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4545e906f595ee8ab8e03e21be20d899bfc06647925bc5b224ad7e8c40e08b8"}, - {file = "coverage-7.10.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ae385e1d58fbc6a9b1c315e5510ac52281e271478b45f92ca9b5ad42cf39643f"}, - {file = "coverage-7.10.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f0cbe5f7dd19f3a32bac2251b95d51c3b89621ac88a2648096ce40f9a5aa1e7"}, - {file = "coverage-7.10.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd17f427f041f6b116dc90b4049c6f3e1230524407d00daa2d8c7915037b5947"}, - {file = "coverage-7.10.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7f10ca4cde7b466405cce0a0e9971a13eb22e57a5ecc8b5f93a81090cc9c7eb9"}, - {file = "coverage-7.10.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3b990df23dd51dccce26d18fb09fd85a77ebe46368f387b0ffba7a74e470b31b"}, - {file = "coverage-7.10.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc3902584d25c7eef57fb38f440aa849a26a3a9f761a029a72b69acfca4e31f8"}, - {file = "coverage-7.10.2-cp310-cp310-win32.whl", hash = "sha256:9dd37e9ac00d5eb72f38ed93e3cdf2280b1dbda3bb9b48c6941805f265ad8d87"}, - {file = "coverage-7.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:99d16f15cb5baf0729354c5bd3080ae53847a4072b9ba1e10957522fb290417f"}, - {file = "coverage-7.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c3b210d79925a476dfc8d74c7d53224888421edebf3a611f3adae923e212b27"}, - {file = "coverage-7.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf67d1787cd317c3f8b2e4c6ed1ae93497be7e30605a0d32237ac37a37a8a322"}, - {file = "coverage-7.10.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:069b779d03d458602bc0e27189876e7d8bdf6b24ac0f12900de22dd2154e6ad7"}, - {file = "coverage-7.10.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4c2de4cb80b9990e71c62c2d3e9f3ec71b804b1f9ca4784ec7e74127e0f42468"}, - {file = "coverage-7.10.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75bf7ab2374a7eb107602f1e07310cda164016cd60968abf817b7a0b5703e288"}, - {file = "coverage-7.10.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3f37516458ec1550815134937f73d6d15b434059cd10f64678a2068f65c62406"}, - {file = "coverage-7.10.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:de3c6271c482c250d3303fb5c6bdb8ca025fff20a67245e1425df04dc990ece9"}, - {file = "coverage-7.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:98a838101321ac3089c9bb1d4bfa967e8afed58021fda72d7880dc1997f20ae1"}, - {file = "coverage-7.10.2-cp311-cp311-win32.whl", hash = "sha256:f2a79145a531a0e42df32d37be5af069b4a914845b6f686590739b786f2f7bce"}, - {file = "coverage-7.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:e4f5f1320f8ee0d7cfa421ceb257bef9d39fd614dd3ddcfcacd284d4824ed2c2"}, - {file = "coverage-7.10.2-cp311-cp311-win_arm64.whl", hash = "sha256:d8f2d83118f25328552c728b8e91babf93217db259ca5c2cd4dd4220b8926293"}, - {file = "coverage-7.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:890ad3a26da9ec7bf69255b9371800e2a8da9bc223ae5d86daeb940b42247c83"}, - {file = "coverage-7.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38fd1ccfca7838c031d7a7874d4353e2f1b98eb5d2a80a2fe5732d542ae25e9c"}, - {file = "coverage-7.10.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:76c1ffaaf4f6f0f6e8e9ca06f24bb6454a7a5d4ced97a1bc466f0d6baf4bd518"}, - {file = "coverage-7.10.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:86da8a3a84b79ead5c7d0e960c34f580bc3b231bb546627773a3f53c532c2f21"}, - {file = "coverage-7.10.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99cef9731c8a39801830a604cc53c93c9e57ea8b44953d26589499eded9576e0"}, - {file = "coverage-7.10.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea58b112f2966a8b91eb13f5d3b1f8bb43c180d624cd3283fb33b1cedcc2dd75"}, - {file = "coverage-7.10.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:20f405188d28da9522b7232e51154e1b884fc18d0b3a10f382d54784715bbe01"}, - {file = "coverage-7.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:64586ce42bbe0da4d9f76f97235c545d1abb9b25985a8791857690f96e23dc3b"}, - {file = "coverage-7.10.2-cp312-cp312-win32.whl", hash = "sha256:bc2e69b795d97ee6d126e7e22e78a509438b46be6ff44f4dccbb5230f550d340"}, - {file = "coverage-7.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:adda2268b8cf0d11f160fad3743b4dfe9813cd6ecf02c1d6397eceaa5b45b388"}, - {file = "coverage-7.10.2-cp312-cp312-win_arm64.whl", hash = "sha256:164429decd0d6b39a0582eaa30c67bf482612c0330572343042d0ed9e7f15c20"}, - {file = "coverage-7.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:aca7b5645afa688de6d4f8e89d30c577f62956fefb1bad021490d63173874186"}, - {file = "coverage-7.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:96e5921342574a14303dfdb73de0019e1ac041c863743c8fe1aa6c2b4a257226"}, - {file = "coverage-7.10.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:11333094c1bff621aa811b67ed794865cbcaa99984dedea4bd9cf780ad64ecba"}, - {file = "coverage-7.10.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6eb586fa7d2aee8d65d5ae1dd71414020b2f447435c57ee8de8abea0a77d5074"}, - {file = "coverage-7.10.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d358f259d8019d4ef25d8c5b78aca4c7af25e28bd4231312911c22a0e824a57"}, - {file = "coverage-7.10.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5250bda76e30382e0a2dcd68d961afcab92c3a7613606e6269855c6979a1b0bb"}, - {file = "coverage-7.10.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a91e027d66eff214d88d9afbe528e21c9ef1ecdf4956c46e366c50f3094696d0"}, - {file = "coverage-7.10.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:228946da741558904e2c03ce870ba5efd9cd6e48cbc004d9a27abee08100a15a"}, - {file = "coverage-7.10.2-cp313-cp313-win32.whl", hash = "sha256:95e23987b52d02e7c413bf2d6dc6288bd5721beb518052109a13bfdc62c8033b"}, - {file = "coverage-7.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:f35481d42c6d146d48ec92d4e239c23f97b53a3f1fbd2302e7c64336f28641fe"}, - {file = "coverage-7.10.2-cp313-cp313-win_arm64.whl", hash = "sha256:65b451949cb789c346f9f9002441fc934d8ccedcc9ec09daabc2139ad13853f7"}, - {file = "coverage-7.10.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e8415918856a3e7d57a4e0ad94651b761317de459eb74d34cc1bb51aad80f07e"}, - {file = "coverage-7.10.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f287a25a8ca53901c613498e4a40885b19361a2fe8fbfdbb7f8ef2cad2a23f03"}, - {file = "coverage-7.10.2-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:75cc1a3f8c88c69bf16a871dab1fe5a7303fdb1e9f285f204b60f1ee539b8fc0"}, - {file = "coverage-7.10.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ca07fa78cc9d26bc8c4740de1abd3489cf9c47cc06d9a8ab3d552ff5101af4c0"}, - {file = "coverage-7.10.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2e117e64c26300032755d4520cd769f2623cde1a1d1c3515b05a3b8add0ade1"}, - {file = "coverage-7.10.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:daaf98009977f577b71f8800208f4d40d4dcf5c2db53d4d822787cdc198d76e1"}, - {file = "coverage-7.10.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ea8d8fe546c528535c761ba424410bbeb36ba8a0f24be653e94b70c93fd8a8ca"}, - {file = "coverage-7.10.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fe024d40ac31eb8d5aae70215b41dafa264676caa4404ae155f77d2fa95c37bb"}, - {file = "coverage-7.10.2-cp313-cp313t-win32.whl", hash = "sha256:8f34b09f68bdadec122ffad312154eda965ade433559cc1eadd96cca3de5c824"}, - {file = "coverage-7.10.2-cp313-cp313t-win_amd64.whl", hash = "sha256:71d40b3ac0f26fa9ffa6ee16219a714fed5c6ec197cdcd2018904ab5e75bcfa3"}, - {file = "coverage-7.10.2-cp313-cp313t-win_arm64.whl", hash = "sha256:abb57fdd38bf6f7dcc66b38dafb7af7c5fdc31ac6029ce373a6f7f5331d6f60f"}, - {file = "coverage-7.10.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a3e853cc04987c85ec410905667eed4bf08b1d84d80dfab2684bb250ac8da4f6"}, - {file = "coverage-7.10.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0100b19f230df72c90fdb36db59d3f39232391e8d89616a7de30f677da4f532b"}, - {file = "coverage-7.10.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9c1cd71483ea78331bdfadb8dcec4f4edfb73c7002c1206d8e0af6797853f5be"}, - {file = "coverage-7.10.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9f75dbf4899e29a37d74f48342f29279391668ef625fdac6d2f67363518056a1"}, - {file = "coverage-7.10.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7df481e7508de1c38b9b8043da48d94931aefa3e32b47dd20277e4978ed5b95"}, - {file = "coverage-7.10.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:835f39e618099325e7612b3406f57af30ab0a0af350490eff6421e2e5f608e46"}, - {file = "coverage-7.10.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:12e52b5aa00aa720097d6947d2eb9e404e7c1101ad775f9661ba165ed0a28303"}, - {file = "coverage-7.10.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:718044729bf1fe3e9eb9f31b52e44ddae07e434ec050c8c628bf5adc56fe4bdd"}, - {file = "coverage-7.10.2-cp314-cp314-win32.whl", hash = "sha256:f256173b48cc68486299d510a3e729a96e62c889703807482dbf56946befb5c8"}, - {file = "coverage-7.10.2-cp314-cp314-win_amd64.whl", hash = "sha256:2e980e4179f33d9b65ac4acb86c9c0dde904098853f27f289766657ed16e07b3"}, - {file = "coverage-7.10.2-cp314-cp314-win_arm64.whl", hash = "sha256:14fb5b6641ab5b3c4161572579f0f2ea8834f9d3af2f7dd8fbaecd58ef9175cc"}, - {file = "coverage-7.10.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e96649ac34a3d0e6491e82a2af71098e43be2874b619547c3282fc11d3840a4b"}, - {file = "coverage-7.10.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1a2e934e9da26341d342d30bfe91422bbfdb3f1f069ec87f19b2909d10d8dcc4"}, - {file = "coverage-7.10.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:651015dcd5fd9b5a51ca79ece60d353cacc5beaf304db750407b29c89f72fe2b"}, - {file = "coverage-7.10.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81bf6a32212f9f66da03d63ecb9cd9bd48e662050a937db7199dbf47d19831de"}, - {file = "coverage-7.10.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d800705f6951f75a905ea6feb03fff8f3ea3468b81e7563373ddc29aa3e5d1ca"}, - {file = "coverage-7.10.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:248b5394718e10d067354448dc406d651709c6765669679311170da18e0e9af8"}, - {file = "coverage-7.10.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:5c61675a922b569137cf943770d7ad3edd0202d992ce53ac328c5ff68213ccf4"}, - {file = "coverage-7.10.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:52d708b5fd65589461381fa442d9905f5903d76c086c6a4108e8e9efdca7a7ed"}, - {file = "coverage-7.10.2-cp314-cp314t-win32.whl", hash = "sha256:916369b3b914186b2c5e5ad2f7264b02cff5df96cdd7cdad65dccd39aa5fd9f0"}, - {file = "coverage-7.10.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5b9d538e8e04916a5df63052d698b30c74eb0174f2ca9cd942c981f274a18eaf"}, - {file = "coverage-7.10.2-cp314-cp314t-win_arm64.whl", hash = "sha256:04c74f9ef1f925456a9fd23a7eef1103126186d0500ef9a0acb0bd2514bdc7cc"}, - {file = "coverage-7.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:765b13b164685a2f8b2abef867ad07aebedc0e090c757958a186f64e39d63dbd"}, - {file = "coverage-7.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a219b70100500d0c7fd3ebb824a3302efb6b1a122baa9d4eb3f43df8f0b3d899"}, - {file = "coverage-7.10.2-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e33e79a219105aa315439ee051bd50b6caa705dc4164a5aba6932c8ac3ce2d98"}, - {file = "coverage-7.10.2-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc3945b7bad33957a9eca16e9e5eae4b17cb03173ef594fdaad228f4fc7da53b"}, - {file = "coverage-7.10.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bdff88e858ee608a924acfad32a180d2bf6e13e059d6a7174abbae075f30436"}, - {file = "coverage-7.10.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44329cbed24966c0b49acb386352c9722219af1f0c80db7f218af7793d251902"}, - {file = "coverage-7.10.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:be127f292496d0fbe20d8025f73221b36117b3587f890346e80a13b310712982"}, - {file = "coverage-7.10.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6c031da749a05f7a01447dd7f47beedb498edd293e31e1878c0d52db18787df0"}, - {file = "coverage-7.10.2-cp39-cp39-win32.whl", hash = "sha256:22aca3e691c7709c5999ccf48b7a8ff5cf5a8bd6fe9b36efbd4993f5a36b2fcf"}, - {file = "coverage-7.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c7195444b932356055a8e287fa910bf9753a84a1bc33aeb3770e8fca521e032e"}, - {file = "coverage-7.10.2-py3-none-any.whl", hash = "sha256:95db3750dd2e6e93d99fa2498f3a1580581e49c494bddccc6f85c5c21604921f"}, - {file = "coverage-7.10.2.tar.gz", hash = "sha256:5d6e6d84e6dd31a8ded64759626627247d676a23c1b892e1326f7c55c8d61055"}, +files = [ + {file = "coverage-7.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d92d6edb0ccafd20c6fbf9891ca720b39c2a6a4b4a6f9cf323ca2c986f33e475"}, + {file = "coverage-7.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7202da14dc0236884fcc45665ffb2d79d4991a53fbdf152ab22f69f70923cc22"}, + {file = "coverage-7.10.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ada418633ae24ec8d0fcad5efe6fc7aa3c62497c6ed86589e57844ad04365674"}, + {file = "coverage-7.10.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b828e33eca6c3322adda3b5884456f98c435182a44917ded05005adfa1415500"}, + {file = "coverage-7.10.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:802793ba397afcfdbe9f91f89d65ae88b958d95edc8caf948e1f47d8b6b2b606"}, + {file = "coverage-7.10.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d0b23512338c54101d3bf7a1ab107d9d75abda1d5f69bc0887fd079253e4c27e"}, + {file = "coverage-7.10.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f36b7dcf72d06a8c5e2dd3aca02be2b1b5db5f86404627dff834396efce958f2"}, + {file = "coverage-7.10.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fce316c367a1dc2c411821365592eeb335ff1781956d87a0410eae248188ba51"}, + {file = "coverage-7.10.4-cp310-cp310-win32.whl", hash = "sha256:8c5dab29fc8070b3766b5fc85f8d89b19634584429a2da6d42da5edfadaf32ae"}, + {file = "coverage-7.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:4b0d114616f0fccb529a1817457d5fb52a10e106f86c5fb3b0bd0d45d0d69b93"}, + {file = "coverage-7.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:05d5f98ec893d4a2abc8bc5f046f2f4367404e7e5d5d18b83de8fde1093ebc4f"}, + {file = "coverage-7.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9267efd28f8994b750d171e58e481e3bbd69e44baed540e4c789f8e368b24b88"}, + {file = "coverage-7.10.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4456a039fdc1a89ea60823d0330f1ac6f97b0dbe9e2b6fb4873e889584b085fb"}, + {file = "coverage-7.10.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c2bfbd2a9f7e68a21c5bd191be94bfdb2691ac40d325bac9ef3ae45ff5c753d9"}, + {file = "coverage-7.10.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab7765f10ae1df7e7fe37de9e64b5a269b812ee22e2da3f84f97b1c7732a0d8"}, + {file = "coverage-7.10.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a09b13695166236e171ec1627ff8434b9a9bae47528d0ba9d944c912d33b3d2"}, + {file = "coverage-7.10.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5c9e75dfdc0167d5675e9804f04a56b2cf47fb83a524654297000b578b8adcb7"}, + {file = "coverage-7.10.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c751261bfe6481caba15ec005a194cb60aad06f29235a74c24f18546d8377df0"}, + {file = "coverage-7.10.4-cp311-cp311-win32.whl", hash = "sha256:051c7c9e765f003c2ff6e8c81ccea28a70fb5b0142671e4e3ede7cebd45c80af"}, + {file = "coverage-7.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:1a647b152f10be08fb771ae4a1421dbff66141e3d8ab27d543b5eb9ea5af8e52"}, + {file = "coverage-7.10.4-cp311-cp311-win_arm64.whl", hash = "sha256:b09b9e4e1de0d406ca9f19a371c2beefe3193b542f64a6dd40cfcf435b7d6aa0"}, + {file = "coverage-7.10.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a1f0264abcabd4853d4cb9b3d164adbf1565da7dab1da1669e93f3ea60162d79"}, + {file = "coverage-7.10.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:536cbe6b118a4df231b11af3e0f974a72a095182ff8ec5f4868c931e8043ef3e"}, + {file = "coverage-7.10.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9a4c0d84134797b7bf3f080599d0cd501471f6c98b715405166860d79cfaa97e"}, + {file = "coverage-7.10.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7c155fc0f9cee8c9803ea0ad153ab6a3b956baa5d4cd993405dc0b45b2a0b9e0"}, + {file = "coverage-7.10.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5f2ab6e451d4b07855d8bcf063adf11e199bff421a4ba57f5bb95b7444ca62"}, + {file = "coverage-7.10.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:685b67d99b945b0c221be0780c336b303a7753b3e0ec0d618c795aada25d5e7a"}, + {file = "coverage-7.10.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0c079027e50c2ae44da51c2e294596cbc9dbb58f7ca45b30651c7e411060fc23"}, + {file = "coverage-7.10.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3749aa72b93ce516f77cf5034d8e3c0dfd45c6e8a163a602ede2dc5f9a0bb927"}, + {file = "coverage-7.10.4-cp312-cp312-win32.whl", hash = "sha256:fecb97b3a52fa9bcd5a7375e72fae209088faf671d39fae67261f37772d5559a"}, + {file = "coverage-7.10.4-cp312-cp312-win_amd64.whl", hash = "sha256:26de58f355626628a21fe6a70e1e1fad95702dafebfb0685280962ae1449f17b"}, + {file = "coverage-7.10.4-cp312-cp312-win_arm64.whl", hash = "sha256:67e8885408f8325198862bc487038a4980c9277d753cb8812510927f2176437a"}, + {file = "coverage-7.10.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b8e1d2015d5dfdbf964ecef12944c0c8c55b885bb5c0467ae8ef55e0e151233"}, + {file = "coverage-7.10.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:25735c299439018d66eb2dccf54f625aceb78645687a05f9f848f6e6c751e169"}, + {file = "coverage-7.10.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:715c06cb5eceac4d9b7cdf783ce04aa495f6aff657543fea75c30215b28ddb74"}, + {file = "coverage-7.10.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e017ac69fac9aacd7df6dc464c05833e834dc5b00c914d7af9a5249fcccf07ef"}, + {file = "coverage-7.10.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bad180cc40b3fccb0f0e8c702d781492654ac2580d468e3ffc8065e38c6c2408"}, + {file = "coverage-7.10.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:becbdcd14f685fada010a5f792bf0895675ecf7481304fe159f0cd3f289550bd"}, + {file = "coverage-7.10.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0b485ca21e16a76f68060911f97ebbe3e0d891da1dbbce6af7ca1ab3f98b9097"}, + {file = "coverage-7.10.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6c1d098ccfe8e1e0a1ed9a0249138899948afd2978cbf48eb1cc3fcd38469690"}, + {file = "coverage-7.10.4-cp313-cp313-win32.whl", hash = "sha256:8630f8af2ca84b5c367c3df907b1706621abe06d6929f5045fd628968d421e6e"}, + {file = "coverage-7.10.4-cp313-cp313-win_amd64.whl", hash = "sha256:f68835d31c421736be367d32f179e14ca932978293fe1b4c7a6a49b555dff5b2"}, + {file = "coverage-7.10.4-cp313-cp313-win_arm64.whl", hash = "sha256:6eaa61ff6724ca7ebc5326d1fae062d85e19b38dd922d50903702e6078370ae7"}, + {file = "coverage-7.10.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:702978108876bfb3d997604930b05fe769462cc3000150b0e607b7b444f2fd84"}, + {file = "coverage-7.10.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e8f978e8c5521d9c8f2086ac60d931d583fab0a16f382f6eb89453fe998e2484"}, + {file = "coverage-7.10.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:df0ac2ccfd19351411c45e43ab60932b74472e4648b0a9edf6a3b58846e246a9"}, + {file = "coverage-7.10.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:73a0d1aaaa3796179f336448e1576a3de6fc95ff4f07c2d7251d4caf5d18cf8d"}, + {file = "coverage-7.10.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:873da6d0ed6b3ffc0bc01f2c7e3ad7e2023751c0d8d86c26fe7322c314b031dc"}, + {file = "coverage-7.10.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c6446c75b0e7dda5daa876a1c87b480b2b52affb972fedd6c22edf1aaf2e00ec"}, + {file = "coverage-7.10.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6e73933e296634e520390c44758d553d3b573b321608118363e52113790633b9"}, + {file = "coverage-7.10.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52073d4b08d2cb571234c8a71eb32af3c6923149cf644a51d5957ac128cf6aa4"}, + {file = "coverage-7.10.4-cp313-cp313t-win32.whl", hash = "sha256:e24afb178f21f9ceb1aefbc73eb524769aa9b504a42b26857243f881af56880c"}, + {file = "coverage-7.10.4-cp313-cp313t-win_amd64.whl", hash = "sha256:be04507ff1ad206f4be3d156a674e3fb84bbb751ea1b23b142979ac9eebaa15f"}, + {file = "coverage-7.10.4-cp313-cp313t-win_arm64.whl", hash = "sha256:f3e3ff3f69d02b5dad67a6eac68cc9c71ae343b6328aae96e914f9f2f23a22e2"}, + {file = "coverage-7.10.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a59fe0af7dd7211ba595cf7e2867458381f7e5d7b4cffe46274e0b2f5b9f4eb4"}, + {file = "coverage-7.10.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3a6c35c5b70f569ee38dc3350cd14fdd0347a8b389a18bb37538cc43e6f730e6"}, + {file = "coverage-7.10.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:acb7baf49f513554c4af6ef8e2bd6e8ac74e6ea0c7386df8b3eb586d82ccccc4"}, + {file = "coverage-7.10.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a89afecec1ed12ac13ed203238b560cbfad3522bae37d91c102e690b8b1dc46c"}, + {file = "coverage-7.10.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:480442727f464407d8ade6e677b7f21f3b96a9838ab541b9a28ce9e44123c14e"}, + {file = "coverage-7.10.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a89bf193707f4a17f1ed461504031074d87f035153239f16ce86dfb8f8c7ac76"}, + {file = "coverage-7.10.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:3ddd912c2fc440f0fb3229e764feec85669d5d80a988ff1b336a27d73f63c818"}, + {file = "coverage-7.10.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a538944ee3a42265e61c7298aeba9ea43f31c01271cf028f437a7b4075592cf"}, + {file = "coverage-7.10.4-cp314-cp314-win32.whl", hash = "sha256:fd2e6002be1c62476eb862b8514b1ba7e7684c50165f2a8d389e77da6c9a2ebd"}, + {file = "coverage-7.10.4-cp314-cp314-win_amd64.whl", hash = "sha256:ec113277f2b5cf188d95fb66a65c7431f2b9192ee7e6ec9b72b30bbfb53c244a"}, + {file = "coverage-7.10.4-cp314-cp314-win_arm64.whl", hash = "sha256:9744954bfd387796c6a091b50d55ca7cac3d08767795b5eec69ad0f7dbf12d38"}, + {file = "coverage-7.10.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5af4829904dda6aabb54a23879f0f4412094ba9ef153aaa464e3c1b1c9bc98e6"}, + {file = "coverage-7.10.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7bba5ed85e034831fac761ae506c0644d24fd5594727e174b5a73aff343a7508"}, + {file = "coverage-7.10.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d57d555b0719834b55ad35045de6cc80fc2b28e05adb6b03c98479f9553b387f"}, + {file = "coverage-7.10.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ba62c51a72048bb1ea72db265e6bd8beaabf9809cd2125bbb5306c6ce105f214"}, + {file = "coverage-7.10.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0acf0c62a6095f07e9db4ec365cc58c0ef5babb757e54745a1aa2ea2a2564af1"}, + {file = "coverage-7.10.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e1033bf0f763f5cf49ffe6594314b11027dcc1073ac590b415ea93463466deec"}, + {file = "coverage-7.10.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:92c29eff894832b6a40da1789b1f252305af921750b03ee4535919db9179453d"}, + {file = "coverage-7.10.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:822c4c830989c2093527e92acd97be4638a44eb042b1bdc0e7a278d84a070bd3"}, + {file = "coverage-7.10.4-cp314-cp314t-win32.whl", hash = "sha256:e694d855dac2e7cf194ba33653e4ba7aad7267a802a7b3fc4347d0517d5d65cd"}, + {file = "coverage-7.10.4-cp314-cp314t-win_amd64.whl", hash = "sha256:efcc54b38ef7d5bfa98050f220b415bc5bb3d432bd6350a861cf6da0ede2cdcd"}, + {file = "coverage-7.10.4-cp314-cp314t-win_arm64.whl", hash = "sha256:6f3a3496c0fa26bfac4ebc458747b778cff201c8ae94fa05e1391bab0dbc473c"}, + {file = "coverage-7.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:48fd4d52600c2a9d5622e52dfae674a7845c5e1dceaf68b88c99feb511fbcfd6"}, + {file = "coverage-7.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:56217b470d09d69e6b7dcae38200f95e389a77db801cb129101697a4553b18b6"}, + {file = "coverage-7.10.4-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:44ac3f21a6e28c5ff7f7a47bca5f87885f6a1e623e637899125ba47acd87334d"}, + {file = "coverage-7.10.4-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3387739d72c84d17b4d2f7348749cac2e6700e7152026912b60998ee9a40066b"}, + {file = "coverage-7.10.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f111ff20d9a6348e0125be892608e33408dd268f73b020940dfa8511ad05503"}, + {file = "coverage-7.10.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:01a852f0a9859734b018a3f483cc962d0b381d48d350b1a0c47d618c73a0c398"}, + {file = "coverage-7.10.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:225111dd06759ba4e37cee4c0b4f3df2b15c879e9e3c37bf986389300b9917c3"}, + {file = "coverage-7.10.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2178d4183bd1ba608f0bb12e71e55838ba1b7dbb730264f8b08de9f8ef0c27d0"}, + {file = "coverage-7.10.4-cp39-cp39-win32.whl", hash = "sha256:93d175fe81913aee7a6ea430abbdf2a79f1d9fd451610e12e334e4fe3264f563"}, + {file = "coverage-7.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:2221a823404bb941c7721cf0ef55ac6ee5c25d905beb60c0bba5e5e85415d353"}, + {file = "coverage-7.10.4-py3-none-any.whl", hash = "sha256:065d75447228d05121e5c938ca8f0e91eed60a1eb2d1258d42d5084fecfc3302"}, + {file = "coverage-7.10.4.tar.gz", hash = "sha256:25f5130af6c8e7297fd14634955ba9e1697f47143f289e2a23284177c0061d27"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] -toml = ["tomli ; python_full_version <= \"3.11.0a6\""] +toml = ["tomli"] [[package]] name = "cyclic" @@ -441,7 +414,6 @@ version = "1.0.0" description = "Handle cyclic relations" optional = false python-versions = "*" -groups = ["docs"] files = [ {file = "cyclic-1.0.0-py3-none-any.whl", hash = "sha256:32d8181d7698f426bce6f14f4c3921ef95b6a84af9f96192b59beb05bc00c3ed"}, {file = "cyclic-1.0.0.tar.gz", hash = "sha256:ecddd56cb831ee3e6b79f61ecb0ad71caee606c507136867782911aa01c3e5eb"}, @@ -453,7 +425,6 @@ version = "0.4.0" description = "serialize all of Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049"}, {file = "dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0"}, @@ -469,59 +440,17 @@ version = "0.4.0" description = "Distribution utilities" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, ] -[[package]] -name = "dnspython" -version = "2.7.0" -description = "DNS toolkit" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"mongodb\"" -files = [ - {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, - {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, -] - -[package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=43)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=1.0.0)"] -idna = ["idna (>=3.7)"] -trio = ["trio (>=0.23)"] -wmi = ["wmi (>=1.5.1)"] - -[[package]] -name = "dotflow-mongodb" -version = "0.1.2" -description = "Dotflow Storage MongoDB" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"mongodb\"" -files = [ - {file = "dotflow_mongodb-0.1.2-py3-none-any.whl", hash = "sha256:cc2d1de05939bf333e1aa88f8b8d3998beaa926241ecb6c85d86311e1d234a6d"}, - {file = "dotflow_mongodb-0.1.2.tar.gz", hash = "sha256:1a6451b649ba843da8ea5379199f18b4f2751e9b3d877a45ae8894cfa6171870"}, -] - -[package.dependencies] -dotflow = "*" -pymongo = "*" - [[package]] name = "exceptiongroup" version = "1.3.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, @@ -535,28 +464,21 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.18.0" +version = "3.19.1" description = "A platform independent file lock." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ - {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, - {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, + {file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"}, + {file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"}, ] -[package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] - [[package]] name = "flake8" version = "7.3.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, @@ -573,7 +495,6 @@ version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" -groups = ["docs"] files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, @@ -587,14 +508,13 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "griffe" -version = "1.9.0" +version = "1.12.1" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ - {file = "griffe-1.9.0-py3-none-any.whl", hash = "sha256:bcf90ee3ad42bbae70a2a490c782fc8e443de9b84aa089d857c278a4e23215fc"}, - {file = "griffe-1.9.0.tar.gz", hash = "sha256:b5531cf45e9b73f0842c2121cc4d4bcbb98a55475e191fc9830e7aef87a920a0"}, + {file = "griffe-1.12.1-py3-none-any.whl", hash = "sha256:2d7c12334de00089c31905424a00abcfd931b45b8b516967f224133903d302cc"}, + {file = "griffe-1.12.1.tar.gz", hash = "sha256:29f5a6114c0aeda7d9c86a570f736883f8a2c5b38b57323d56b3d1c000565567"}, ] [package.dependencies] @@ -606,7 +526,6 @@ version = "0.2.8" description = "Griffe extension for PEP 727 – Documentation Metadata in Typing." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "griffe_typingdoc-0.2.8-py3-none-any.whl", hash = "sha256:a4ed3dd73b9d48311b138d8b317916a0589325a73c525236bf5969a8fe2626b1"}, {file = "griffe_typingdoc-0.2.8.tar.gz", hash = "sha256:36f2c2f2568240a5d0ab462153d1f3cfec01a9cc56b2291f16ce7869f0f7af05"}, @@ -622,7 +541,6 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["main", "docs"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -637,23 +555,21 @@ version = "8.7.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" -groups = ["dev", "docs"] files = [ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, ] -markers = {dev = "python_full_version < \"3.10.2\""} [package.dependencies] zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -662,7 +578,6 @@ version = "6.5.2" description = "Read resources from Python packages" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"}, {file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"}, @@ -672,7 +587,7 @@ files = [ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -685,7 +600,6 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, @@ -697,7 +611,6 @@ version = "6.0.1" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.9.0" -groups = ["dev"] files = [ {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, @@ -713,7 +626,6 @@ version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -731,7 +643,6 @@ version = "3.8.2" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"}, {file = "markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45"}, @@ -750,7 +661,6 @@ version = "0.0.4" description = "Markdown extension to expand directives to include source example files to also include their variants. Only useful to tiangolo's projets. Don't use it. 😅" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "markdown_include_variants-0.0.4-py3-none-any.whl", hash = "sha256:f940b3978ca63d05118a261ff653d54d1d325120de41da9b901ccf4a85f1da1c"}, {file = "markdown_include_variants-0.0.4.tar.gz", hash = "sha256:8207c8a451eb79f10973d10ef5745b619bc51e245e4c4e5f0556a7a05243bea7"}, @@ -765,7 +675,6 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -790,7 +699,6 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -861,7 +769,6 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" -groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -873,7 +780,6 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -885,7 +791,6 @@ version = "1.4.2" description = "Python Markdown extension to include local or remote files" optional = false python-versions = "*" -groups = ["docs"] files = [ {file = "mdx_include-1.4.2-py3-none-any.whl", hash = "sha256:cfbeadd59985f27a9b70cb7ab0a3d209892fe1bb1aa342df055e0b135b3c9f34"}, {file = "mdx_include-1.4.2.tar.gz", hash = "sha256:992f9fbc492b5cf43f7d8cb4b90b52a4e4c5fdd7fd04570290a83eea5c84f297"}, @@ -902,7 +807,6 @@ version = "1.3.4" description = "A deep merge function for 🐍." optional = false python-versions = ">=3.6" -groups = ["docs"] files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, @@ -914,7 +818,6 @@ version = "2.1.3" description = "Manage multiple versions of your MkDocs-powered documentation" optional = false python-versions = "*" -groups = ["docs"] files = [ {file = "mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a"}, {file = "mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810"}, @@ -940,7 +843,6 @@ version = "1.6.1" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, @@ -964,7 +866,7 @@ watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-autorefs" @@ -972,7 +874,6 @@ version = "1.4.2" description = "Automatically link across pages in MkDocs." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "mkdocs_autorefs-1.4.2-py3-none-any.whl", hash = "sha256:83d6d777b66ec3c372a1aad4ae0cf77c243ba5bcda5bf0c6b8a2c5e7a3d89f13"}, {file = "mkdocs_autorefs-1.4.2.tar.gz", hash = "sha256:e2ebe1abd2b67d597ed19378c0fff84d73d1dbce411fce7a7cc6f161888b6749"}, @@ -989,7 +890,6 @@ version = "0.2.0" description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, @@ -1003,19 +903,19 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.6.16" +version = "9.6.17" description = "Documentation that simply works" optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ - {file = "mkdocs_material-9.6.16-py3-none-any.whl", hash = "sha256:8d1a1282b892fe1fdf77bfeb08c485ba3909dd743c9ba69a19a40f637c6ec18c"}, - {file = "mkdocs_material-9.6.16.tar.gz", hash = "sha256:d07011df4a5c02ee0877496d9f1bfc986cfb93d964799b032dd99fe34c0e9d19"}, + {file = "mkdocs_material-9.6.17-py3-none-any.whl", hash = "sha256:221dd8b37a63f52e580bcab4a7e0290e4a6f59bd66190be9c3d40767e05f9417"}, + {file = "mkdocs_material-9.6.17.tar.gz", hash = "sha256:48ae7aec72a3f9f501a70be3fbd329c96ff5f5a385b67a1563e5ed5ce064affe"}, ] [package.dependencies] babel = ">=2.10,<3.0" backrefs = ">=5.7.post1,<6.0" +click = "<8.2.2" colorama = ">=0.4,<1.0" jinja2 = ">=3.1,<4.0" markdown = ">=3.2,<4.0" @@ -1037,7 +937,6 @@ version = "1.3.1" description = "Extension pack for Python Markdown and MkDocs Material." optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, @@ -1049,7 +948,6 @@ version = "0.2.0" description = "Mkdocs Blog Theme" optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "mkdocs_simple_blog-0.2.0-py3-none-any.whl", hash = "sha256:ffa09798ca598c67c260e6f588f63d7dd5a1e50eeb632644e4952fee8116d447"}, {file = "mkdocs_simple_blog-0.2.0.tar.gz", hash = "sha256:62f7ad2594566020f97fa614002ec71bc97e7fb38060341b2c2ccd9ca2cb1fe0"}, @@ -1064,7 +962,6 @@ version = "1.3.0" description = "MkDocs i18n plugin using static translation markdown files" optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "mkdocs_static_i18n-1.3.0-py3-none-any.whl", hash = "sha256:7905d52fff71d2c108b6c344fd223e848ca7e39ddf319b70864dfa47dba85d6b"}, {file = "mkdocs_static_i18n-1.3.0.tar.gz", hash = "sha256:65731e1e4ec6d719693e24fee9340f5516460b2b7244d2a89bed4ce3cfa6a173"}, @@ -1082,7 +979,6 @@ version = "0.29.1" description = "Automatic documentation from sources, for MkDocs." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "mkdocstrings-0.29.1-py3-none-any.whl", hash = "sha256:37a9736134934eea89cbd055a513d40a020d87dfcae9e3052c2a6b8cd4af09b6"}, {file = "mkdocstrings-0.29.1.tar.gz", hash = "sha256:8722f8f8c5cd75da56671e0a0c1bbed1df9946c0cef74794d6141b34011abd42"}, @@ -1109,7 +1005,6 @@ version = "1.16.12" description = "A Python handler for mkdocstrings." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "mkdocstrings_python-1.16.12-py3-none-any.whl", hash = "sha256:22ded3a63b3d823d57457a70ff9860d5a4de9e8b1e482876fc9baabaf6f5f374"}, {file = "mkdocstrings_python-1.16.12.tar.gz", hash = "sha256:9b9eaa066e0024342d433e332a41095c4e429937024945fea511afe58f63175d"}, @@ -1127,7 +1022,6 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["dev", "docs"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -1139,7 +1033,6 @@ version = "0.5.7" description = "Divides large result sets into pages for easier browsing" optional = false python-versions = "*" -groups = ["docs"] files = [ {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, @@ -1155,7 +1048,6 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1167,7 +1059,6 @@ version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" -groups = ["dev", "docs"] files = [ {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, @@ -1184,7 +1075,6 @@ version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, @@ -1200,7 +1090,6 @@ version = "2.14.0" description = "Python style guide checker" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, @@ -1212,8 +1101,6 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "implementation_name == \"pypy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1225,7 +1112,6 @@ version = "2.11.7" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, @@ -1239,7 +1125,7 @@ typing-inspection = ">=0.4.0" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] +timezone = ["tzdata"] [[package]] name = "pydantic-core" @@ -1247,7 +1133,6 @@ version = "2.33.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, @@ -1359,7 +1244,6 @@ version = "3.4.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, @@ -1371,7 +1255,6 @@ version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["main", "dev", "docs"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, @@ -1382,14 +1265,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" -version = "3.3.7" +version = "3.3.8" description = "python code static checker" optional = false python-versions = ">=3.9.0" -groups = ["dev"] files = [ - {file = "pylint-3.3.7-py3-none-any.whl", hash = "sha256:43860aafefce92fca4cf6b61fe199cdc5ae54ea28f9bf4cd49de267b5195803d"}, - {file = "pylint-3.3.7.tar.gz", hash = "sha256:2b11de8bde49f9c5059452e0c310c079c746a0a8eeaa789e5aa966ecc23e4559"}, + {file = "pylint-3.3.8-py3-none-any.whl", hash = "sha256:7ef94aa692a600e82fabdd17102b73fc226758218c97473c7ad67bd4cb905d83"}, + {file = "pylint-3.3.8.tar.gz", hash = "sha256:26698de19941363037e2937d3db9ed94fb3303fdadf7d98847875345a8bb6b05"}, ] [package.dependencies] @@ -1398,7 +1280,7 @@ colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version == \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] isort = ">=4.2.5,<5.13 || >5.13,<7" mccabe = ">=0.6,<0.8" @@ -1417,7 +1299,6 @@ version = "10.16.1" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "pymdown_extensions-10.16.1-py3-none-any.whl", hash = "sha256:d6ba157a6c03146a7fb122b2b9a121300056384eafeec9c9f9e584adfdb2a32d"}, {file = "pymdown_extensions-10.16.1.tar.gz", hash = "sha256:aace82bcccba3efc03e25d584e6a22d27a8e17caa3f4dd9f207e49b787aa9a91"}, @@ -1430,94 +1311,12 @@ pyyaml = "*" [package.extras] extra = ["pygments (>=2.19.1)"] -[[package]] -name = "pymongo" -version = "4.14.0" -description = "PyMongo - the Official MongoDB Python driver" -optional = true -python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"mongodb\"" -files = [ - {file = "pymongo-4.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8f63035da92f923d966fb07017969b5c42d0450412ceb6c3f837a02b72a5a69d"}, - {file = "pymongo-4.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7e6b1f34db93856b083760fe2848562c0b39433fc1a0827155ea02fee5733c64"}, - {file = "pymongo-4.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5af18f9239e672c2b9713a8e5f0267e927c9dcc3960dce9599712f304fd377f"}, - {file = "pymongo-4.14.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ced2a58339aeb2e0f4e347778a5b5130acca0384c0146135e1afe7bf2b9a2701"}, - {file = "pymongo-4.14.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23d48bf539f581603a7425e4eeaea279bdb89caa0462aab29e163c11c5d02a9c"}, - {file = "pymongo-4.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4220f42630b260c2594589202efaa4916f3799e87c9fdc479fb6ed62457c74fe"}, - {file = "pymongo-4.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7e34bd3fe6e5034e22d47d685391ae0f2f2b22d150e9a36a6b77f0c0234e21d"}, - {file = "pymongo-4.14.0-cp310-cp310-win32.whl", hash = "sha256:f4712911b6dc07aaa00aaa87749b63370ca5900714a2b4b19ba1385163e90295"}, - {file = "pymongo-4.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:74fb430c98c5a7f871bfa40f77c8158af8bde256215296d7cfe1ec9c5a417718"}, - {file = "pymongo-4.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88f9c415c59450c0ac4133aa4745459101619ca7997dc468209bf395563667d2"}, - {file = "pymongo-4.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6513474970fdf3afd9dc9de9065a31a1efc8288ca9068510e5e973fa80200c8f"}, - {file = "pymongo-4.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d60c0e9b13603317062f316906fb5be4000f5b5fe288eb6e9df4ef8695863cd8"}, - {file = "pymongo-4.14.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a203cb757f75804c43aec48f23cb138e890a24219716ce9958041dace39ba470"}, - {file = "pymongo-4.14.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bdf65bf8167a92b70de44d28ed9df1c2dec83fe2a82e26c01fc89da8ca6bc34"}, - {file = "pymongo-4.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1f79fdd99db135dbc1678477793764c156c11623d0d9dbe4c57767d081b79b8"}, - {file = "pymongo-4.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05152a2ca55805c37f77ac473e51915f44bba9a6b32fed875fa9df61d81681ca"}, - {file = "pymongo-4.14.0-cp311-cp311-win32.whl", hash = "sha256:aa25505e36e32bef3fa135578461f24735e9d4b7b62e6aa21eb8f2d163cef86d"}, - {file = "pymongo-4.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57aef3b48e8c7818689604ff24e54524e164056ec56ee5ea48384264360bf59"}, - {file = "pymongo-4.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:864f005459ef9b19c60cb96ca755124e0291d211000859f33c62a166f55eba27"}, - {file = "pymongo-4.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:69956f971a6f8dafc62e5c83ac21ebf15d5757e13758284f12218ad7fbd3c0fe"}, - {file = "pymongo-4.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91200f47453a1cb97136629e28f4091a109756ec37067b622f90c4b626b4af8d"}, - {file = "pymongo-4.14.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c32f5e23e9dd31e20800d993f3695b6627664dc7da30ac1669f9716833b33175"}, - {file = "pymongo-4.14.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4407c1ab7514e08d4af0f58cf9d7eddc86e45e458fe46f99a72a5d18dbc71dc"}, - {file = "pymongo-4.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ec6666e599ee10cc5cde0cc6a8519373384a14af3a310ede1bf177105f38fb0"}, - {file = "pymongo-4.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a809a982a843bb561c7030059d54ea7f1dcc967cc72a45f1435695e2a2a515a5"}, - {file = "pymongo-4.14.0-cp312-cp312-win32.whl", hash = "sha256:3866d031fcbe81d7677c078026e650aeef8915560ba758a28051debce38f6b77"}, - {file = "pymongo-4.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:91b8de871a40225bbe4f92d6dc3f20c26bf838e49d3563592131401af0d665a6"}, - {file = "pymongo-4.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54576faf8e6fefe17886a201f9df61bdf728ccac9a7a0847095a0e8480cd6ec1"}, - {file = "pymongo-4.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c64ef5e58adedecb853a680eb5d1aea50770197f212e202d6eb50c801797b576"}, - {file = "pymongo-4.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f1c0cdddf9783065bf55d3fe025843c0974a828bafc9bb5514ae28dd2828a40"}, - {file = "pymongo-4.14.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22805d4fa587b526ac3129ee634a8761abbeb76883045718438f5b8e72f91ce6"}, - {file = "pymongo-4.14.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c3e6e669cf36b27694de2730f5d5b31ef492ffe99446563192c4e8ee84ca859"}, - {file = "pymongo-4.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9763a2477d5388df65ab6f591cf0cb7fd34f4a66f873c31e63288fd79887742c"}, - {file = "pymongo-4.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d6f5aceab3528b8760942a57635599925f7fd743691f9d759a02124207dfd0"}, - {file = "pymongo-4.14.0-cp313-cp313-win32.whl", hash = "sha256:e283feafde118cbbb03adc036b882be042b0a2eca121ec5d6bbec3e12980e8fa"}, - {file = "pymongo-4.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:a29b62d421a512833e99d4781b64e695cfe23b4c4a9159ea83e56fc2660f2480"}, - {file = "pymongo-4.14.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67225d5fb1be2a34c6f73cde9d81a8041a095a94ed2433e2cf9e2f1657443def"}, - {file = "pymongo-4.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bbf0dbe8554978c1271bdc82366852245a03ab124a1387b6f3531f64adac3c39"}, - {file = "pymongo-4.14.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7f61561cbc7426ffc2d37b46e65ab5323fc366644f70c8e2240ed5452e2c402"}, - {file = "pymongo-4.14.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:502cd551679753fb7838221c3bbb963da4b4aa0576848192afb1f78128ff729a"}, - {file = "pymongo-4.14.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba6b59afef2b47c4859bf36115fa577330601b93e39d04f39fcc6103e801286"}, - {file = "pymongo-4.14.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c0fa103f978c15f7c2f0d7b2e010c24c327432a0310503bc0ec93c5f9be9e81"}, - {file = "pymongo-4.14.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fe93676afe37794f01b8df5cf16528dce4d7d174cdf51ea1586c234eb5263c2"}, - {file = "pymongo-4.14.0-cp313-cp313t-win32.whl", hash = "sha256:1462fc2bb39527f01eea5378172b66c45d62e22fa4be957afe2ec747c4d2ff51"}, - {file = "pymongo-4.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e506af9b25aac77cc5c5ea4a72f81764e4f5ea90ca799aac43d665ab269f291d"}, - {file = "pymongo-4.14.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:66c70cea48a6d8eba16f7435033fe3f948335132f807a85e9d861f908ea43fe7"}, - {file = "pymongo-4.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f8874b1ac78b204a3df69351b3c594971be91dba80f7a9189b95097595601223"}, - {file = "pymongo-4.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34e1a670892b0435456bbc068e244c71ae3dc0994f7abdf5a0b06db73e32dd86"}, - {file = "pymongo-4.14.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e08f4dcd8f23f7ea2c9ae1bb5a043d9b77dfbf03fc489b7e1de91422b0c4772a"}, - {file = "pymongo-4.14.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1914e06a443db7e32ddb992050983910bd6861e690200005d7eb1418ad2dc4be"}, - {file = "pymongo-4.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71a6915825716edc65239b5658b7de6a09af5d30517706122df66579ddba9349"}, - {file = "pymongo-4.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fa071c00fd5556b79c0d94d95b2b778d70ff4ea701aeecb63dd5353d7e025f7"}, - {file = "pymongo-4.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f51b4885ed197a874272f1bff99d15d0c1f35c665d86b9a2a6572a10246af974"}, - {file = "pymongo-4.14.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7698c8cf749988b7fd259ea65a5fcd409ebde1c6bc1bf75ab7fadbfbd154a3a4"}, - {file = "pymongo-4.14.0-cp39-cp39-win32.whl", hash = "sha256:85d8d145e06916db46b1e25f39b5361fdf62d58e127b1b1652a0df4ea8081b2f"}, - {file = "pymongo-4.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:ee0e677b2139c5b560b63d745a5f26d2e985f624848132deb366c6d3322836bb"}, - {file = "pymongo-4.14.0.tar.gz", hash = "sha256:15674e3fddce78cf134fc4e55f90abf1608a48430130cd35efdf3802fd47a1d1"}, -] - -[package.dependencies] -dnspython = ">=1.16.0,<3.0.0" - -[package.extras] -aws = ["pymongo-auth-aws (>=1.1.0,<2.0.0)"] -docs = ["furo (==2025.7.19)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<9)", "sphinx-autobuild (>=2020.9.1)", "sphinx-rtd-theme (>=2,<4)", "sphinxcontrib-shellcheck (>=1,<2)"] -encryption = ["certifi ; os_name == \"nt\" or sys_platform == \"darwin\"", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.13.0,<2.0.0)"] -gssapi = ["pykerberos ; os_name != \"nt\"", "winkerberos (>=0.5.0) ; os_name == \"nt\""] -ocsp = ["certifi ; os_name == \"nt\" or sys_platform == \"darwin\"", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"] -snappy = ["python-snappy"] -test = ["pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"] -zstd = ["zstandard"] - [[package]] name = "pyparsing" version = "3.2.3" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"}, {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"}, @@ -1532,7 +1331,6 @@ version = "1.9.1" description = "API to interact with the python pyproject.toml based projects" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pyproject_api-1.9.1-py3-none-any.whl", hash = "sha256:7d6238d92f8962773dd75b5f0c4a6a27cce092a14b623b811dba656f3b628948"}, {file = "pyproject_api-1.9.1.tar.gz", hash = "sha256:43c9918f49daab37e302038fc1aed54a8c7a91a9fa935d00b9a485f37e0f5335"}, @@ -1552,7 +1350,6 @@ version = "1.2.0" description = "Wrappers to call pyproject.toml-based build backend hooks." optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"}, {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, @@ -1564,7 +1361,6 @@ version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, @@ -1588,7 +1384,6 @@ version = "6.2.1" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"}, {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"}, @@ -1608,7 +1403,6 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["docs"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -1617,13 +1411,26 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "1.1.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.9" +files = [ + {file = "python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc"}, + {file = "python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "pyyaml" version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1686,7 +1493,6 @@ version = "1.1" description = "A custom YAML tag for referencing environment variables in YAML files." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"}, {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"}, @@ -1701,7 +1507,6 @@ version = "26.4.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pyzmq-26.4.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:0329bdf83e170ac133f44a233fc651f6ed66ef8e66693b5af7d54f45d1ef5918"}, {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:398a825d2dea96227cf6460ce0a174cf7657d6f6827807d4d1ae9d0f9ae64315"}, @@ -1807,7 +1612,6 @@ version = "1.1.0" description = "Slice a list of sliceables (1 indexed, start and end index both are inclusive)" optional = false python-versions = "*" -groups = ["docs"] files = [ {file = "rcslice-1.1.0-py3-none-any.whl", hash = "sha256:1b12fc0c0ca452e8a9fd2b56ac008162f19e250906a4290a7e7a98be3200c2a6"}, {file = "rcslice-1.1.0.tar.gz", hash = "sha256:a2ce70a60690eb63e52b722e046b334c3aaec5e900b28578f529878782ee5c6e"}, @@ -1819,7 +1623,6 @@ version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["main", "docs"] files = [ {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, @@ -1841,7 +1644,6 @@ version = "14.1.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" -groups = ["main"] files = [ {file = "rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f"}, {file = "rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8"}, @@ -1860,7 +1662,6 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["docs"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -1872,8 +1673,6 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -1915,7 +1714,6 @@ version = "0.13.3" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, @@ -1927,7 +1725,6 @@ version = "4.28.4" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "tox-4.28.4-py3-none-any.whl", hash = "sha256:8d4ad9ee916ebbb59272bb045e154a10fa12e3bbdcf94cc5185cbdaf9b241f99"}, {file = "tox-4.28.4.tar.gz", hash = "sha256:b5b14c6307bd8994ff1eba5074275826620325ee1a4f61316959d562bfd70b9d"}, @@ -1952,12 +1749,10 @@ version = "4.14.1" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main", "dev", "docs"] files = [ {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, ] -markers = {dev = "python_version < \"3.11\""} [[package]] name = "typing-inspection" @@ -1965,7 +1760,6 @@ version = "0.4.1" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, @@ -1980,14 +1774,13 @@ version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main", "docs"] files = [ {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1998,7 +1791,6 @@ version = "0.1.0" description = "Flexible version handling" optional = false python-versions = "*" -groups = ["docs"] files = [ {file = "verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31"}, {file = "verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e"}, @@ -2009,24 +1801,24 @@ test = ["coverage", "flake8 (>=3.7)", "mypy", "pretend", "pytest"] [[package]] name = "virtualenv" -version = "20.33.0" +version = "20.34.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ - {file = "virtualenv-20.33.0-py3-none-any.whl", hash = "sha256:106b6baa8ab1b526d5a9b71165c85c456fbd49b16976c88e2bc9352ee3bc5d3f"}, - {file = "virtualenv-20.33.0.tar.gz", hash = "sha256:47e0c0d2ef1801fce721708ccdf2a28b9403fa2307c3268aebd03225976f61d2"}, + {file = "virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026"}, + {file = "virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" +typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""} [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "watchdog" @@ -2034,7 +1826,6 @@ version = "6.0.0" description = "Filesystem events monitoring" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, @@ -2077,25 +1868,20 @@ version = "3.23.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" -groups = ["dev", "docs"] files = [ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, ] -markers = {dev = "python_full_version < \"3.10.2\""} [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] -[extras] -mongodb = ["dotflow-mongodb"] - [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = ">=3.9.0" -content-hash = "aee92d8a8ed56c547f089ff0e338ab2591274a62d59e019b8d4485ebd949a853" +content-hash = "16bb43f1e4c9b2fbce58818fcde3d08869e63b78b70868313949bb785daa244b" diff --git a/requirements.txt b/requirements.txt index f557d2d..aa614b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,15 @@ -rich -pydantic -typing-extensions \ No newline at end of file +annotated-types==0.7.0 ; python_version >= "3.9" +certifi==2025.8.3 ; python_full_version >= "3.9.0" +charset-normalizer==3.4.3 ; python_full_version >= "3.9.0" +idna==3.10 ; python_full_version >= "3.9.0" +markdown-it-py==3.0.0 ; python_full_version >= "3.9.0" +mdurl==0.1.2 ; python_full_version >= "3.9.0" +pydantic-core==2.33.2 ; python_version >= "3.9" +pydantic==2.11.7 ; python_version >= "3.9" +pygments==2.19.2 ; python_full_version >= "3.9.0" +python-dotenv==1.1.1 ; python_version >= "3.9" +requests==2.32.4 ; python_full_version >= "3.9.0" +rich==14.1.0 ; python_full_version >= "3.9.0" +typing-extensions==4.14.1 ; python_version >= "3.9" +typing-inspection==0.4.1 ; python_version >= "3.9" +urllib3==2.5.0 ; python_version >= "3.9" From 4dcd1a6d229f1c89a5517ca263e6cb05af8a54fc Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 00:12:09 -0300 Subject: [PATCH 10/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Log=20str?= =?UTF-8?q?ucture=20improvement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/nav/reference/abc-log.md | 4 +-- docs/nav/reference/log-default.md | 4 +-- dotflow/abc/log.py | 17 ---------- dotflow/abc/logs.py | 52 +++++++++++++++++++++++++++++++ dotflow/cli/commands/log.py | 2 +- dotflow/core/config.py | 14 ++++----- dotflow/core/task.py | 15 +++++++-- dotflow/core/types/execution.py | 8 +++++ dotflow/logging.py | 2 +- dotflow/plugins/__init__.py | 1 + dotflow/plugins/logs.py | 42 +++++++++++++++++++++++++ dotflow/providers/__init__.py | 4 +-- dotflow/providers/log_default.py | 30 ------------------ 13 files changed, 131 insertions(+), 64 deletions(-) delete mode 100644 dotflow/abc/log.py create mode 100644 dotflow/abc/logs.py create mode 100644 dotflow/plugins/__init__.py create mode 100644 dotflow/plugins/logs.py delete mode 100644 dotflow/providers/log_default.py diff --git a/docs/nav/reference/abc-log.md b/docs/nav/reference/abc-log.md index 6a669c6..285b2da 100644 --- a/docs/nav/reference/abc-log.md +++ b/docs/nav/reference/abc-log.md @@ -1,3 +1,3 @@ -# Log +# Logs -::: dotflow.abc.log.Log +::: dotflow.abc.log.Logs diff --git a/docs/nav/reference/log-default.md b/docs/nav/reference/log-default.md index e5de777..1469a36 100644 --- a/docs/nav/reference/log-default.md +++ b/docs/nav/reference/log-default.md @@ -1,3 +1,3 @@ -# LogDefault +# LogsHandler -::: dotflow.providers.log_default.LogDefault +::: dotflow.providers.log_default.LogsHandler diff --git a/dotflow/abc/log.py b/dotflow/abc/log.py deleted file mode 100644 index 5f22904..0000000 --- a/dotflow/abc/log.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Notify ABC""" - -from typing import Any - -from abc import ABC, abstractmethod - - -class Log(ABC): - """Log""" - - @abstractmethod - def info(self, task: Any) -> None: - """Info""" - - @abstractmethod - def error(self, task: Any) -> None: - """Error""" diff --git a/dotflow/abc/logs.py b/dotflow/abc/logs.py new file mode 100644 index 0000000..edbb9e9 --- /dev/null +++ b/dotflow/abc/logs.py @@ -0,0 +1,52 @@ +"""Logs ABC""" + +from typing import Literal +from enum import StrEnum + +from abc import ABC, abstractmethod + + +class TypeLog(StrEnum): + + CRITICAL = "CRITICAL" + ERROR = "ERROR" + WARNING = "WARNING" + INFO = "INFO" + DEBUG = "DEBUG" + NOTSET = "NOTSET" + + +TYPE_LOG = Literal[ + TypeLog.CRITICAL, + TypeLog.ERROR, + TypeLog.WARNING, + TypeLog.INFO, + TypeLog.DEBUG, + TypeLog.NOTSET, +] + +TASK_LOG_FORMAT = "{task_id}: {workflow_id} - {status}" +WORKFLOW_LOG_FORMAT = "{workflow_id}: {status}" + + +class Logs(ABC): + """Logs""" + + @abstractmethod + def post_task( + self, + task_id: int, + wotkflow_id: str, + status: str, + type: TYPE_LOG + ) -> None: + pass + + @abstractmethod + def post_workflow( + self, + wotkflow_id: str, + status: str, + type: TYPE_LOG + ) -> None: + pass diff --git a/dotflow/cli/commands/log.py b/dotflow/cli/commands/log.py index 619f98d..e7d78aa 100644 --- a/dotflow/cli/commands/log.py +++ b/dotflow/cli/commands/log.py @@ -18,4 +18,4 @@ def setup(self): f"To access all logs, open the file ({settings.LOG_PATH.resolve()})." ) else: - print(settings.WARNING_ALERT, "Log file not found.") + print(settings.WARNING_ALERT, "Logs file not found.") diff --git a/dotflow/core/config.py b/dotflow/core/config.py index 480bfe2..cc279f7 100644 --- a/dotflow/core/config.py +++ b/dotflow/core/config.py @@ -2,11 +2,11 @@ from typing import Optional -from dotflow.abc.log import Log +from dotflow.abc.logs import Logs from dotflow.abc.storage import Storage from dotflow.abc.notify import Notify -from dotflow.providers.log_default import LogDefault +from dotflow.plugins.logs import LogsHandler from dotflow.providers.storage_default import StorageDefault from dotflow.providers.notify_default import NotifyDefault @@ -21,7 +21,7 @@ class Config: from dotflow.providers import ( StorageDefault, NotifyDefault, - LogDefault + LogsHandler ) Example: @@ -30,25 +30,25 @@ class Config: config = Config( storage=StorageFile(path=".output"), notify=NotifyDefault(), - log=LogDefault() + log=LogsHandler() ) Args: storage (Optional[Storage]): Type of the storage. notify (Optional[Notify]): Type of the notify. - log (Optional[Log]): Type of the notify. + log (Optional[Logs]): Type of the notify. Attributes: storage (Optional[Storage]): notify (Optional[Notify]): - log (Optional[Log]): + log (Optional[Logs]): """ def __init__( self, storage: Optional[Storage] = StorageDefault(), notify: Optional[Notify] = NotifyDefault(), - log: Optional[Log] = LogDefault(), + log: Optional[Logs] = LogsHandler(), ) -> None: self.storage = storage self.notify = notify diff --git a/dotflow/core/task.py b/dotflow/core/task.py index b683abf..8d0560f 100644 --- a/dotflow/core/task.py +++ b/dotflow/core/task.py @@ -5,6 +5,7 @@ from uuid import UUID from typing import Any, Callable, List, Dict +from dotflow.abc.logs import TypeLog from dotflow.core.config import Config from dotflow.core.action import Action from dotflow.core.context import Context @@ -205,7 +206,12 @@ def error(self, value: Exception) -> None: task_error = TaskError(value) self._error = task_error - self.config.log.error(task=self) + self.config.log.post_task( + task_id=self.task_id, + wotkflow_id=self.workflow_id, + status=self.status, + type=TypeLog.ERROR + ) @property def status(self): @@ -218,7 +224,12 @@ def status(self, value: TypeStatus) -> None: self._status = value self.config.notify.send(task=self) - self.config.log.info(task=self) + self.config.log.post_task( + task_id=self.task_id, + wotkflow_id=self.workflow_id, + status=self.status, + type=TypeLog.INFO + ) @property def config(self): diff --git a/dotflow/core/types/execution.py b/dotflow/core/types/execution.py index ed6a069..8f9d81d 100644 --- a/dotflow/core/types/execution.py +++ b/dotflow/core/types/execution.py @@ -1,5 +1,6 @@ """Type Execution mode module""" +from typing import Literal from typing_extensions import Annotated, Doc @@ -14,3 +15,10 @@ class TypeExecution: SEQUENTIAL: Annotated[str, Doc("Sequential execution.")] = "sequential" BACKGROUND: Annotated[str, Doc("Background execution.")] = "background" PARALLEL: Annotated[str, Doc("Parallel execution.")] = "parallel" + + +TYPE_EXECUTION = Literal[ + TypeExecution.SEQUENTIAL, + TypeExecution.BACKGROUND, + TypeExecution.PARALLEL +] diff --git a/dotflow/logging.py b/dotflow/logging.py index 4555948..ff19590 100644 --- a/dotflow/logging.py +++ b/dotflow/logging.py @@ -1,4 +1,4 @@ -"""Log""" +"""Logs""" import logging import logging.config diff --git a/dotflow/plugins/__init__.py b/dotflow/plugins/__init__.py new file mode 100644 index 0000000..0cb16a2 --- /dev/null +++ b/dotflow/plugins/__init__.py @@ -0,0 +1 @@ +"""Plugins __init__ module.""" diff --git a/dotflow/plugins/logs.py b/dotflow/plugins/logs.py new file mode 100644 index 0000000..53f0c9a --- /dev/null +++ b/dotflow/plugins/logs.py @@ -0,0 +1,42 @@ +"""Logs Default""" + +from dotflow.abc.logs import ( + Logs, + TYPE_LOG, + TASK_LOG_FORMAT, + WORKFLOW_LOG_FORMAT +) +from dotflow.logging import logger + + +class LogsHandler(Logs): + + def post_task( + self, + task_id: int, + wotkflow_id: str, + status: str, + type: TYPE_LOG + ) -> None: + new_log = getattr(logger, type.lower()) + new_log( + TASK_LOG_FORMAT.format( + task_id=task_id, + workflow_id=wotkflow_id, + status=status, + ) + ) + + def post_workflow( + self, + wotkflow_id: str, + status: str, + type: TYPE_LOG + ) -> None: + new_log = getattr(logger, type.lower()) + new_log( + WORKFLOW_LOG_FORMAT.format( + workflow_id=wotkflow_id, + status=status, + ) + ) diff --git a/dotflow/providers/__init__.py b/dotflow/providers/__init__.py index a8f4f3e..acce2a9 100644 --- a/dotflow/providers/__init__.py +++ b/dotflow/providers/__init__.py @@ -1,13 +1,13 @@ """Providers __init__ module.""" -from dotflow.providers.log_default import LogDefault +from dotflow.plugins.logs import LogsHandler from dotflow.providers.notify_default import NotifyDefault from dotflow.providers.notify_telegram import NotifyTelegram from dotflow.providers.storage_default import StorageDefault from dotflow.providers.storage_file import StorageFile __all__ = [ - "LogDefault", + "LogsHandler", "NotifyDefault", "NotifyTelegram", "StorageDefault", diff --git a/dotflow/providers/log_default.py b/dotflow/providers/log_default.py deleted file mode 100644 index 053406b..0000000 --- a/dotflow/providers/log_default.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Notify Default""" - -from typing import Any - -from rich.console import Console # type: ignore - -from dotflow.abc.log import Log -from dotflow.logging import logger - - -class LogDefault(Log): - - def info(self, task: Any) -> None: - logger.info( - "ID %s - %s - %s", - task.workflow_id, - task.task_id, - task.status, - ) - - def error(self, task: Any) -> None: - logger.error( - "ID %s - %s - %s \n %s", - task.workflow_id, - task.task_id, - task.status, - task.error.traceback, - ) - console = Console() - console.print_exception(show_locals=True) From 1f9bd5a8c846d396b55f1aaecfb0246f23e313cb Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 00:12:40 -0300 Subject: [PATCH 11/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Log=20str?= =?UTF-8?q?ucture=20improvement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mkdocs.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 44fc1c9..242c4ba 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -163,8 +163,9 @@ nav: - nav/reference/utils.md - nav/reference/decorators.md - nav/reference/exception.md + - Plugin: + - nav/reference/logs-handler.md - Providers: - - nav/reference/log-default.md - nav/reference/notify-default.md - nav/reference/notify-telegram.md - nav/reference/storage-init.md @@ -175,9 +176,9 @@ nav: - Types: - nav/reference/type-execution.md - nav/reference/type-status.md - - Abstract methods: + - Abstract Methods: - nav/reference/abc-tcp.md - - nav/reference/abc-log.md + - nav/reference/abc-logs.md - nav/reference/abc-flow.md - nav/reference/abc-notify.md - nav/reference/abc-storage.md From 3775e8f33d4d93e7412dbdc961fd4cea40f289e7 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 00:14:08 -0300 Subject: [PATCH 12/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Log=20str?= =?UTF-8?q?ucture=20improvement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/nav/reference/abc-log.md | 3 --- docs/nav/reference/abc-logs.md | 3 +++ docs/nav/reference/log-default.md | 3 --- docs/nav/reference/logs-handler.md | 3 +++ dotflow/abc/logs.py | 17 +--------------- dotflow/plugins/logs.py | 31 +++++------------------------- 6 files changed, 12 insertions(+), 48 deletions(-) delete mode 100644 docs/nav/reference/abc-log.md create mode 100644 docs/nav/reference/abc-logs.md delete mode 100644 docs/nav/reference/log-default.md create mode 100644 docs/nav/reference/logs-handler.md diff --git a/docs/nav/reference/abc-log.md b/docs/nav/reference/abc-log.md deleted file mode 100644 index 285b2da..0000000 --- a/docs/nav/reference/abc-log.md +++ /dev/null @@ -1,3 +0,0 @@ -# Logs - -::: dotflow.abc.log.Logs diff --git a/docs/nav/reference/abc-logs.md b/docs/nav/reference/abc-logs.md new file mode 100644 index 0000000..07f08b2 --- /dev/null +++ b/docs/nav/reference/abc-logs.md @@ -0,0 +1,3 @@ +# Logs + +::: dotflow.abc.logs.Logs diff --git a/docs/nav/reference/log-default.md b/docs/nav/reference/log-default.md deleted file mode 100644 index 1469a36..0000000 --- a/docs/nav/reference/log-default.md +++ /dev/null @@ -1,3 +0,0 @@ -# LogsHandler - -::: dotflow.providers.log_default.LogsHandler diff --git a/docs/nav/reference/logs-handler.md b/docs/nav/reference/logs-handler.md new file mode 100644 index 0000000..123a0aa --- /dev/null +++ b/docs/nav/reference/logs-handler.md @@ -0,0 +1,3 @@ +# LogsHandler + +::: dotflow.plugins.logs.LogsHandler diff --git a/dotflow/abc/logs.py b/dotflow/abc/logs.py index edbb9e9..505a22e 100644 --- a/dotflow/abc/logs.py +++ b/dotflow/abc/logs.py @@ -33,20 +33,5 @@ class Logs(ABC): """Logs""" @abstractmethod - def post_task( - self, - task_id: int, - wotkflow_id: str, - status: str, - type: TYPE_LOG - ) -> None: - pass - - @abstractmethod - def post_workflow( - self, - wotkflow_id: str, - status: str, - type: TYPE_LOG - ) -> None: + def post_task(self, task_object, type: TYPE_LOG) -> None: pass diff --git a/dotflow/plugins/logs.py b/dotflow/plugins/logs.py index 53f0c9a..d97c4f4 100644 --- a/dotflow/plugins/logs.py +++ b/dotflow/plugins/logs.py @@ -3,40 +3,19 @@ from dotflow.abc.logs import ( Logs, TYPE_LOG, - TASK_LOG_FORMAT, - WORKFLOW_LOG_FORMAT + TASK_LOG_FORMAT ) from dotflow.logging import logger class LogsHandler(Logs): - def post_task( - self, - task_id: int, - wotkflow_id: str, - status: str, - type: TYPE_LOG - ) -> None: + def post_task(self, task_object, type: TYPE_LOG) -> None: new_log = getattr(logger, type.lower()) new_log( TASK_LOG_FORMAT.format( - task_id=task_id, - workflow_id=wotkflow_id, - status=status, - ) - ) - - def post_workflow( - self, - wotkflow_id: str, - status: str, - type: TYPE_LOG - ) -> None: - new_log = getattr(logger, type.lower()) - new_log( - WORKFLOW_LOG_FORMAT.format( - workflow_id=wotkflow_id, - status=status, + task_id=task_object.task_id, + workflow_id=task_object.workflow_id, + status=task_object.status, ) ) From 2c0c15bb8dcdef6ef5e0328113d1265776ff5a3f Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 00:23:18 -0300 Subject: [PATCH 13/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Improveme?= =?UTF-8?q?nt=20of=20nomenclature=20and=20data=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/nav/advanced/notify-with-telegram.md | 4 +- docs/nav/reference/type-execution.md | 4 +- docs/nav/reference/type-status.md | 4 +- docs_src/callback/task_callback.py | 6 +-- .../callback/workflow_callback_failure.py | 4 +- .../callback/workflow_callback_success.py | 4 +- dotflow/cli/commands/start.py | 4 +- dotflow/cli/setup.py | 12 ++--- dotflow/core/execution.py | 8 ++-- dotflow/core/task.py | 22 +++------ dotflow/core/types/__init__.py | 14 +++--- dotflow/core/types/execution.py | 24 +++++----- dotflow/core/types/status.py | 45 ++++++++++++------- dotflow/core/types/storage.py | 21 ++++++--- dotflow/core/types/worflow.py | 18 ++++++-- dotflow/core/workflow.py | 12 ++--- dotflow/providers/notify_telegram.py | 6 +-- dotflow/types.py | 8 ++-- tests/core/test_dotflow.py | 6 +-- tests/core/test_execution.py | 24 +++++----- tests/core/test_task.py | 6 +-- tests/core/test_workflow.py | 14 +++--- tests/core/test_workflow_background.py | 6 +-- tests/core/test_workflow_parallel.py | 6 +-- tests/core/test_workflow_sequential.py | 6 +-- tests/core/test_workflow_sequential_group.py | 6 +-- 26 files changed, 157 insertions(+), 137 deletions(-) diff --git a/docs/nav/advanced/notify-with-telegram.md b/docs/nav/advanced/notify-with-telegram.md index f8b9521..146ca3c 100644 --- a/docs/nav/advanced/notify-with-telegram.md +++ b/docs/nav/advanced/notify-with-telegram.md @@ -47,7 +47,7 @@ import os from dotflow import Config, DotFlow, action from dotflow.notify import NotifyTelegram -from dotflow.types import TypeStatus +from dotflow.types import StatusTaskType ``` ### Task function @@ -69,7 +69,7 @@ Instantiate the `NotifyTelegram` class with your Telegram bot credentials. You c notify = NotifyTelegram( token=os.getenv("TOKEN"), chat_id=os.getenv("CHAT_ID"), - notification_type=TypeStatus.FAILED # Notify only on failure + notification_type=StatusTaskType.FAILED # Notify only on failure ) ``` diff --git a/docs/nav/reference/type-execution.md b/docs/nav/reference/type-execution.md index 75889ec..0e40a14 100644 --- a/docs/nav/reference/type-execution.md +++ b/docs/nav/reference/type-execution.md @@ -1,6 +1,6 @@ -# TypeExecution +# ExecutionModeType -::: dotflow.core.types.execution.TypeExecution +::: dotflow.core.types.execution.ExecutionModeType options: members: - SEQUENTIAL diff --git a/docs/nav/reference/type-status.md b/docs/nav/reference/type-status.md index a37f95b..5cce16d 100644 --- a/docs/nav/reference/type-status.md +++ b/docs/nav/reference/type-status.md @@ -1,6 +1,6 @@ -# TypeStatus +# StatusTaskType -::: dotflow.core.types.status.TypeStatus +::: dotflow.core.types.status.StatusTaskType options: members: - NOT_STARTED diff --git a/docs_src/callback/task_callback.py b/docs_src/callback/task_callback.py index 44c735e..c4fc319 100644 --- a/docs_src/callback/task_callback.py +++ b/docs_src/callback/task_callback.py @@ -1,14 +1,14 @@ from dotflow import DotFlow, action, Task -from dotflow.core.types.status import TypeStatus +from dotflow.core.types.status import StatusTaskType def callback_one(task: Task): - assert task.status is TypeStatus.COMPLETED + assert task.status is StatusTaskType.COMPLETED print(task.task_id, task.status, task.current_context.storage) def callback_two(task: Task): - assert task.status is TypeStatus.FAILED + assert task.status is StatusTaskType.FAILED print(task.task_id, task.status, task.current_context.storage) diff --git a/docs_src/callback/workflow_callback_failure.py b/docs_src/callback/workflow_callback_failure.py index 7c892c9..103aa2b 100644 --- a/docs_src/callback/workflow_callback_failure.py +++ b/docs_src/callback/workflow_callback_failure.py @@ -1,7 +1,7 @@ from typing import List from dotflow import DotFlow, action, Task -from dotflow.core.types.status import TypeStatus +from dotflow.core.types.status import StatusTaskType def callback(tasks: List[Task]): @@ -9,7 +9,7 @@ def callback(tasks: List[Task]): assert len(tasks) for task in tasks: - assert task.status is TypeStatus.FAILED + assert task.status is StatusTaskType.FAILED print(task.task_id, task.status, task.current_context.storage) diff --git a/docs_src/callback/workflow_callback_success.py b/docs_src/callback/workflow_callback_success.py index e4bde25..6a03009 100644 --- a/docs_src/callback/workflow_callback_success.py +++ b/docs_src/callback/workflow_callback_success.py @@ -1,7 +1,7 @@ from typing import List from dotflow import DotFlow, action, Task -from dotflow.core.types.status import TypeStatus +from dotflow.core.types.status import StatusTaskType def callback(tasks: List[Task]): @@ -9,7 +9,7 @@ def callback(tasks: List[Task]): assert len(tasks) for task in tasks: - assert task.status is TypeStatus.COMPLETED + assert task.status is StatusTaskType.COMPLETED print(task.task_id, task.status, task.current_context.storage) diff --git a/dotflow/cli/commands/start.py b/dotflow/cli/commands/start.py index 401b4e4..9119914 100644 --- a/dotflow/cli/commands/start.py +++ b/dotflow/cli/commands/start.py @@ -4,7 +4,7 @@ from dotflow import DotFlow, Config from dotflow.providers import StorageDefault, StorageFile -from dotflow.core.types.execution import TypeExecution +from dotflow.core.types.execution import ExecutionModeType from dotflow.cli.command import Command @@ -21,7 +21,7 @@ def setup(self): workflow.start(mode=self.params.mode) - if self.params.mode == TypeExecution.BACKGROUND: + if self.params.mode == ExecutionModeType.BACKGROUND: system("/bin/bash") def _new_workflow(self): diff --git a/dotflow/cli/setup.py b/dotflow/cli/setup.py index 8e90f33..3e4b523 100644 --- a/dotflow/cli/setup.py +++ b/dotflow/cli/setup.py @@ -6,7 +6,7 @@ from dotflow.logging import logger from dotflow.settings import Settings as settings from dotflow.utils.basic_functions import basic_callback -from dotflow.core.types import TypeExecution, TypeStorage +from dotflow.core.types import ExecutionModeType, StorageType from dotflow.core.exception import ( MissingActionDecorator, ExecutionModeNotExist, @@ -53,17 +53,17 @@ def setup_start(self): self.cmd_start.add_argument("-c", "--callback", default=basic_callback) self.cmd_start.add_argument("-i", "--initial-context") self.cmd_start.add_argument( - "-o", "--storage", choices=[TypeStorage.DEFAULT, TypeStorage.FILE] + "-o", "--storage", choices=[StorageType.DEFAULT, StorageType.FILE] ) self.cmd_start.add_argument("-p", "--path", default=settings.START_PATH) self.cmd_start.add_argument( "-m", "--mode", - default=TypeExecution.SEQUENTIAL, + default=ExecutionModeType.SEQUENTIAL, choices=[ - TypeExecution.SEQUENTIAL, - TypeExecution.BACKGROUND, - TypeExecution.PARALLEL, + ExecutionModeType.SEQUENTIAL, + ExecutionModeType.BACKGROUND, + ExecutionModeType.PARALLEL, ], ) diff --git a/dotflow/core/execution.py b/dotflow/core/execution.py index fd436df..dc75442 100644 --- a/dotflow/core/execution.py +++ b/dotflow/core/execution.py @@ -16,7 +16,7 @@ from dotflow.core.action import Action from dotflow.core.context import Context from dotflow.core.task import Task -from dotflow.core.types import TypeStatus +from dotflow.core.types import StatusTaskType from dotflow.utils import basic_callback @@ -50,7 +50,7 @@ def __init__( _flow_callback: Callable = basic_callback, ) -> None: self.task = task - self.task.status = TypeStatus.IN_PROGRESS + self.task.status = StatusTaskType.IN_PROGRESS self.task.previous_context = previous_context self.task.workflow_id = workflow_id @@ -153,7 +153,7 @@ def _excution(self, _flow_callback): self.task.current_context = current_context self.task.duration = (datetime.now() - start).total_seconds() - self.task.status = TypeStatus.COMPLETED + self.task.status = StatusTaskType.COMPLETED except AssertionError as err: raise err @@ -161,7 +161,7 @@ def _excution(self, _flow_callback): except Exception as err: self.task.error = err self.task.current_context = None - self.task.status = TypeStatus.FAILED + self.task.status = StatusTaskType.FAILED finally: self.task.callback(task=self.task) diff --git a/dotflow/core/task.py b/dotflow/core/task.py index 8d0560f..1dfdb18 100644 --- a/dotflow/core/task.py +++ b/dotflow/core/task.py @@ -13,7 +13,7 @@ from dotflow.core.serializers.task import SerializerTask from dotflow.core.serializers.workflow import SerializerWorkflow from dotflow.core.exception import MissingActionDecorator, NotCallableObject -from dotflow.core.types.status import TypeStatus +from dotflow.core.types.status import StatusTaskType, TYPE_STATUS_TASK from dotflow.utils import ( basic_callback, traceback_error, @@ -110,7 +110,7 @@ def __init__( self.step = step self.callback = callback self.initial_context = initial_context - self.status = TypeStatus.NOT_STARTED + self.status = StatusTaskType.NOT_STARTED @property def step(self): @@ -206,30 +206,20 @@ def error(self, value: Exception) -> None: task_error = TaskError(value) self._error = task_error - self.config.log.post_task( - task_id=self.task_id, - wotkflow_id=self.workflow_id, - status=self.status, - type=TypeLog.ERROR - ) + self.config.log.post_task(task_object=self, type=TypeLog.ERROR) @property def status(self): if not self._status: - return TypeStatus.NOT_STARTED + return StatusTaskType.NOT_STARTED return self._status @status.setter - def status(self, value: TypeStatus) -> None: + def status(self, value: TYPE_STATUS_TASK) -> None: self._status = value self.config.notify.send(task=self) - self.config.log.post_task( - task_id=self.task_id, - wotkflow_id=self.workflow_id, - status=self.status, - type=TypeLog.INFO - ) + self.config.log.post_task(task_object=self, type=TypeLog.INFO) @property def config(self): diff --git a/dotflow/core/types/__init__.py b/dotflow/core/types/__init__.py index d11f57f..d687de5 100644 --- a/dotflow/core/types/__init__.py +++ b/dotflow/core/types/__init__.py @@ -1,12 +1,14 @@ """Types __init__ module.""" -from dotflow.core.types.execution import TypeExecution -from dotflow.core.types.status import TypeStatus -from dotflow.core.types.storage import TypeStorage +from dotflow.core.types.execution import ExecutionModeType +from dotflow.core.types.status import StatusTaskType +from dotflow.core.types.storage import StorageType +from dotflow.core.types.worflow import WorkflowStatusType __all__ = [ - "TypeExecution", - "TypeStatus", - "TypeStorage" + "ExecutionModeType", + "StatusTaskType", + "StorageType", + "WorkflowStatusType" ] diff --git a/dotflow/core/types/execution.py b/dotflow/core/types/execution.py index 8f9d81d..339f0c1 100644 --- a/dotflow/core/types/execution.py +++ b/dotflow/core/types/execution.py @@ -1,24 +1,24 @@ -"""Type Execution mode module""" +"""Execution Mode Type module""" +from enum import StrEnum from typing import Literal -from typing_extensions import Annotated, Doc -class TypeExecution: +class ExecutionModeType(StrEnum): """ Import: - You can import the **TypeExecution** class with: + You can import the **ExecutionModeType** class with: - from dotflow.core.types import TypeExecution + from dotflow.core.types import ExecutionModeType """ - SEQUENTIAL: Annotated[str, Doc("Sequential execution.")] = "sequential" - BACKGROUND: Annotated[str, Doc("Background execution.")] = "background" - PARALLEL: Annotated[str, Doc("Parallel execution.")] = "parallel" + SEQUENTIAL = "sequential" + BACKGROUND = "background" + PARALLEL = "parallel" -TYPE_EXECUTION = Literal[ - TypeExecution.SEQUENTIAL, - TypeExecution.BACKGROUND, - TypeExecution.PARALLEL +TYPE_MODE_EXECUTION = Literal[ + ExecutionModeType.SEQUENTIAL, + ExecutionModeType.BACKGROUND, + ExecutionModeType.PARALLEL ] diff --git a/dotflow/core/types/status.py b/dotflow/core/types/status.py index 83e7758..6a38458 100644 --- a/dotflow/core/types/status.py +++ b/dotflow/core/types/status.py @@ -1,31 +1,42 @@ -"""Type TypeStatus mode module""" +"""Status Task Type module""" -from typing_extensions import Annotated, Doc +from enum import StrEnum +from typing_extensions import Literal -class TypeStatus: +class StatusTaskType(StrEnum): """ Import: - You can import the **TypeStatus** class with: + You can import the **StatusTaskType** class with: - from dotflow.core.types import TypeStatus + from dotflow.core.types import StatusTaskType """ - NOT_STARTED: Annotated[str, Doc("Status not started.")] = "Not started" - IN_PROGRESS: Annotated[str, Doc("Status in progress.")] = "In progress" - COMPLETED: Annotated[str, Doc("Status completed.")] = "Completed" - PAUSED: Annotated[str, Doc("Status paused.")] = "Paused" - RETRY: Annotated[str, Doc("Status retry.")] = "Retry" - FAILED: Annotated[str, Doc("Status failed.")] = "Failed" + NOT_STARTED = "Not started" + IN_PROGRESS = "In progress" + COMPLETED = "Completed" + PAUSED = "Paused" + RETRY = "Retry" + FAILED = "Failed" @classmethod def get_symbol(cls, value: str) -> str: status = { - TypeStatus.NOT_STARTED: "⚪", - TypeStatus.IN_PROGRESS: "🔵", - TypeStatus.COMPLETED: "✅", - TypeStatus.PAUSED: "◼️", - TypeStatus.RETRY: "❗", - TypeStatus.FAILED: "❌" + StatusTaskType.NOT_STARTED: "⚪", + StatusTaskType.IN_PROGRESS: "🔵", + StatusTaskType.COMPLETED: "✅", + StatusTaskType.PAUSED: "◼️", + StatusTaskType.RETRY: "❗", + StatusTaskType.FAILED: "❌" } return status.get(value) + + +TYPE_STATUS_TASK = Literal[ + StatusTaskType.NOT_STARTED, + StatusTaskType.IN_PROGRESS, + StatusTaskType.COMPLETED, + StatusTaskType.PAUSED, + StatusTaskType.RETRY, + StatusTaskType.FAILED +] diff --git a/dotflow/core/types/storage.py b/dotflow/core/types/storage.py index 9e8ac87..f2d51f1 100644 --- a/dotflow/core/types/storage.py +++ b/dotflow/core/types/storage.py @@ -1,15 +1,22 @@ -"""Type Storage mode""" +"""Storage Type module""" -from typing_extensions import Annotated, Doc +from enum import StrEnum +from typing import Literal -class TypeStorage: +class StorageType(StrEnum): """ Import: - You can import the **TypeStorage** class with: + You can import the **StorageType** class with: - from dotflow.core.types import TypeStorage + from dotflow.core.types import StorageType """ - DEFAULT: Annotated[str, Doc("Default storage.")] = "default" - FILE: Annotated[str, Doc("File storage.")] = "file" + DEFAULT = "default" + FILE = "file" + + +STORAGE_TYPE = Literal[ + StorageType.DEFAULT, + StorageType.FILE +] diff --git a/dotflow/core/types/worflow.py b/dotflow/core/types/worflow.py index 4835e39..2010cb8 100644 --- a/dotflow/core/types/worflow.py +++ b/dotflow/core/types/worflow.py @@ -1,14 +1,24 @@ -"""Step Workflow Status module""" +"""Workflow Status Type module""" +from enum import StrEnum +from typing import Literal -class WorkflowStatus: + +class WorkflowStatusType(StrEnum): """ Import: - You can import the **WorkflowStatus** class with: + You can import the **WorkflowStatusType** class with: - from dotflow.core.types import WorkflowStatus + from dotflow.core.types import WorkflowStatusType """ NEW = "New" IN_PROGRESS = "In progress" COMPLETED = "Completed" + + +WORKFLOW_STATUS_TYPE = Literal[ + WorkflowStatusType.NEW, + WorkflowStatusType.IN_PROGRESS, + WorkflowStatusType.COMPLETED +] \ No newline at end of file diff --git a/dotflow/core/workflow.py b/dotflow/core/workflow.py index a1374fa..cfdf051 100644 --- a/dotflow/core/workflow.py +++ b/dotflow/core/workflow.py @@ -17,7 +17,7 @@ from dotflow.core.context import Context from dotflow.core.execution import Execution from dotflow.core.exception import ExecutionModeNotExist -from dotflow.core.types import TypeExecution, TypeStatus +from dotflow.core.types import ExecutionModeType, StatusTaskType from dotflow.core.task import Task, QueueGroup from dotflow.utils import basic_callback @@ -58,7 +58,7 @@ class Manager: Failure function to be executed after the completion of the entire workflow. It's essentially a callback for error scenarios - mode (TypeExecution): + mode (ExecutionModeType): Parameter that defines the execution mode of the workflow. Currently, there are options to execute in **sequential**, **background**, or **parallel** mode. The sequential mode is used by default. @@ -87,7 +87,7 @@ def __init__( group: QueueGroup, on_success: Callable = basic_callback, on_failure: Callable = basic_callback, - mode: TypeExecution = TypeExecution.SEQUENTIAL, + mode: ExecutionModeType = ExecutionModeType.SEQUENTIAL, keep_going: bool = False, workflow_id: UUID = None, ) -> None: @@ -115,7 +115,7 @@ def _callback_workflow(self, queue_group: QueueGroup): tasks = queue_group.tasks() final_status = [task.status for task in tasks] - if TypeStatus.FAILED in final_status: + if StatusTaskType.FAILED in final_status: self.on_failure(tasks=tasks) else: self.on_success(tasks=tasks) @@ -186,7 +186,7 @@ def run(self) -> None: key=task.config.storage.key(task=task) ) - if not self.ignore and task.status == TypeStatus.FAILED: + if not self.ignore and task.status == StatusTaskType.FAILED: break @@ -278,7 +278,7 @@ def _run_group(self, queue: List[Task]) -> None: key=task.config.storage.key(task=task) ) - if not self.ignore and task.status == TypeStatus.FAILED: + if not self.ignore and task.status == StatusTaskType.FAILED: break diff --git a/dotflow/providers/notify_telegram.py b/dotflow/providers/notify_telegram.py index d401587..339d673 100644 --- a/dotflow/providers/notify_telegram.py +++ b/dotflow/providers/notify_telegram.py @@ -5,7 +5,7 @@ from requests import post -from dotflow.core.types.status import TypeStatus +from dotflow.core.types.status import StatusTaskType from dotflow.abc.notify import Notify from dotflow.logging import logger @@ -19,7 +19,7 @@ def __init__( self, token: str, chat_id: int, - notification_type: Optional[TypeStatus] = None, + notification_type: Optional[StatusTaskType] = None, timeout: int = 1.5 ): self.token = token @@ -50,7 +50,7 @@ def send(self, task: Any) -> None: def _get_text(self, task: Any) -> str: return self.MESSAGE.format( - symbol=TypeStatus.get_symbol(task.status), + symbol=StatusTaskType.get_symbol(task.status), status=task.status, workflow_id=task.workflow_id, task_id=task.task_id, diff --git a/dotflow/types.py b/dotflow/types.py index b5f320d..13bfa3f 100644 --- a/dotflow/types.py +++ b/dotflow/types.py @@ -1,9 +1,9 @@ """Types module""" -from dotflow.core.types.status import TypeStatus -from dotflow.core.types.storage import TypeStorage +from dotflow.core.types.status import StatusTaskType +from dotflow.core.types.storage import StorageType __all__ = [ - "TypeStatus", - "TypeStorage" + "StatusTaskType", + "StorageType" ] diff --git a/tests/core/test_dotflow.py b/tests/core/test_dotflow.py index 926058a..e5bb2e2 100644 --- a/tests/core/test_dotflow.py +++ b/tests/core/test_dotflow.py @@ -6,7 +6,7 @@ from dotflow.core.context import Context from dotflow.core.task import Task, TaskBuilder from dotflow.core.dotflow import DotFlow -from dotflow.core.types.status import TypeStatus +from dotflow.core.types.status import StatusTaskType from tests.mocks import action_step @@ -27,7 +27,7 @@ def test_result_task_with_start(self): self.assertEqual(len(result), 1) self.assertIsInstance(result[0], Task) - self.assertEqual(result[0].status, TypeStatus.COMPLETED) + self.assertEqual(result[0].status, StatusTaskType.COMPLETED) def test_result_context_with_start(self): self.workflow.start() @@ -48,7 +48,7 @@ def test_result_task_without_start(self): self.assertEqual(len(result), 1) self.assertIsInstance(result[0], Task) - self.assertEqual(result[0].status, TypeStatus.NOT_STARTED) + self.assertEqual(result[0].status, StatusTaskType.NOT_STARTED) def test_result_context_without_start(self): result = self.workflow.result_context() diff --git a/tests/core/test_execution.py b/tests/core/test_execution.py index 39da73e..68e550d 100644 --- a/tests/core/test_execution.py +++ b/tests/core/test_execution.py @@ -9,7 +9,7 @@ from dotflow.core.context import Context from dotflow.core.execution import Execution -from dotflow.core.types import TypeStatus +from dotflow.core.types import StatusTaskType from dotflow.core.task import Task from tests.mocks import ( @@ -49,7 +49,7 @@ def test_execution_with_function_completed(self): task=task, workflow_id=workflow_id, previous_context=Context() ) - self.assertEqual(controller.task.status, TypeStatus.COMPLETED) + self.assertEqual(controller.task.status, StatusTaskType.COMPLETED) self.assertEqual(controller.task.workflow_id, workflow_id) def test_execution_with_function_failed(self): @@ -59,7 +59,7 @@ def test_execution_with_function_failed(self): task=task, workflow_id=workflow_id, previous_context=Context() ) - self.assertEqual(controller.task.status, TypeStatus.FAILED) + self.assertEqual(controller.task.status, StatusTaskType.FAILED) self.assertEqual(controller.task.workflow_id, workflow_id) def test_execution_with_class_completed(self): @@ -78,7 +78,7 @@ def test_execution_with_class_completed(self): execution_log = log.message self.assertEqual(execution_log, "ActionStep: Run function executed") - self.assertEqual(controller.task.status, TypeStatus.COMPLETED) + self.assertEqual(controller.task.status, StatusTaskType.COMPLETED) self.assertEqual(controller.task.workflow_id, workflow_id) def test_execution_with_class_failed(self): @@ -97,7 +97,7 @@ def test_execution_with_class_failed(self): execution_log = log.message self.assertEqual(execution_log, "ActionStepWithError: Run function executed") - self.assertEqual(controller.task.status, TypeStatus.FAILED) + self.assertEqual(controller.task.status, StatusTaskType.FAILED) self.assertEqual(controller.task.workflow_id, workflow_id) def test_execution_function_with_initial_context(self): @@ -111,7 +111,7 @@ def test_execution_function_with_initial_context(self): task=task, workflow_id=self.workflow_id, previous_context=None ) - self.assertEqual(controller.task.status, TypeStatus.COMPLETED) + self.assertEqual(controller.task.status, StatusTaskType.COMPLETED) self.assertEqual(controller.task.initial_context.storage, self.context) def test_execution_function_with_previous_context(self): @@ -122,7 +122,7 @@ def test_execution_function_with_previous_context(self): task=task, workflow_id=self.workflow_id, previous_context=self.context ) - self.assertEqual(controller.task.status, TypeStatus.COMPLETED) + self.assertEqual(controller.task.status, StatusTaskType.COMPLETED) self.assertEqual(controller.task._previous_context.storage, self.context) def test_execution_function_with_contexts(self): @@ -136,7 +136,7 @@ def test_execution_function_with_contexts(self): task=task, workflow_id=self.workflow_id, previous_context=self.context ) - self.assertEqual(controller.task.status, TypeStatus.COMPLETED) + self.assertEqual(controller.task.status, StatusTaskType.COMPLETED) self.assertEqual(controller.task.initial_context.storage, self.context) self.assertEqual(controller.task.previous_context.storage, self.context) @@ -151,7 +151,7 @@ def test_execution_class_with_initial_context(self): task=task, workflow_id=self.workflow_id, previous_context=None ) - self.assertEqual(controller.task.status, TypeStatus.COMPLETED) + self.assertEqual(controller.task.status, StatusTaskType.COMPLETED) self.assertEqual(controller.task.initial_context.storage, self.context) def test_execution_class_with_previous_context(self): @@ -162,7 +162,7 @@ def test_execution_class_with_previous_context(self): task=task, workflow_id=self.workflow_id, previous_context=self.context ) - self.assertEqual(controller.task.status, TypeStatus.COMPLETED) + self.assertEqual(controller.task.status, StatusTaskType.COMPLETED) self.assertEqual(controller.task.previous_context.storage, self.context) self.assertEqual( @@ -183,7 +183,7 @@ def test_execution_class_with_contexts(self): task=task, workflow_id=self.workflow_id, previous_context=self.context ) - self.assertEqual(controller.task.status, TypeStatus.COMPLETED) + self.assertEqual(controller.task.status, StatusTaskType.COMPLETED) self.assertEqual(controller.task.initial_context.storage, self.context) self.assertEqual(controller.task.previous_context.storage, self.context) @@ -289,5 +289,5 @@ def test_valid_objects(self): previous_context=None ) - self.assertEqual(controller.task.status, TypeStatus.COMPLETED) + self.assertEqual(controller.task.status, StatusTaskType.COMPLETED) self.assertEqual(controller.task.current_context.storage, input_value) diff --git a/tests/core/test_task.py b/tests/core/test_task.py index b8ddaed..0c0865d 100644 --- a/tests/core/test_task.py +++ b/tests/core/test_task.py @@ -8,7 +8,7 @@ from dotflow.core.action import Action from dotflow.core.config import Config from dotflow.core.context import Context -from dotflow.core.types.status import TypeStatus +from dotflow.core.types.status import StatusTaskType from dotflow.core.serializers.task import SerializerTaskError, SerializerTask from dotflow.core.exception import ( MissingActionDecorator, @@ -92,7 +92,7 @@ def test_task_schema(self): self.assertEqual(schema.task_id, 0) self.assertEqual(schema.workflow_id, expected_workflow_id) - self.assertEqual(schema.status, TypeStatus.NOT_STARTED) + self.assertEqual(schema.status, StatusTaskType.NOT_STARTED) self.assertEqual(schema.error.message, expected_error_message) self.assertEqual(schema.duration, expected_duration) self.assertEqual(schema.initial_context, json.dumps(self.content)) @@ -221,7 +221,7 @@ def test_set_error(self): self.assertIsInstance(self.task.error.exception, Exception) def test_set_status(self): - expected_value = TypeStatus.COMPLETED + expected_value = StatusTaskType.COMPLETED self.task.status = expected_value self.assertEqual(self.task.status, expected_value) diff --git a/tests/core/test_workflow.py b/tests/core/test_workflow.py index 66cc6e9..80ba42b 100644 --- a/tests/core/test_workflow.py +++ b/tests/core/test_workflow.py @@ -7,7 +7,7 @@ from types import FunctionType from dotflow.core.workflow import Manager -from dotflow.core.types import TypeExecution, TypeStatus +from dotflow.core.types import ExecutionModeType, StatusTaskType from dotflow.core.exception import ExecutionModeNotExist from dotflow.core.task import Task, QueueGroup @@ -50,7 +50,7 @@ def test_workflow_with_function_completed(self): ) controller = Manager(group=group) - self.assertEqual(controller.group.tasks()[0].status, TypeStatus.COMPLETED) + self.assertEqual(controller.group.tasks()[0].status, StatusTaskType.COMPLETED) def test_workflow_with_function_failed(self): group = QueueGroup() @@ -63,20 +63,20 @@ def test_workflow_with_function_failed(self): ) controller = Manager(group=group) - self.assertEqual(controller.group.tasks()[0].status, TypeStatus.FAILED) + self.assertEqual(controller.group.tasks()[0].status, StatusTaskType.FAILED) def test_with_execution_mode_that_does_not_exist(self): with self.assertRaises(ExecutionModeNotExist): Manager(group=self.group, mode="unknown") def test_with_execution_mode_sequential(self): - Manager(group=self.group, mode=TypeExecution.SEQUENTIAL) + Manager(group=self.group, mode=ExecutionModeType.SEQUENTIAL) def test_with_execution_mode_background(self): - Manager(group=self.group, mode=TypeExecution.BACKGROUND) + Manager(group=self.group, mode=ExecutionModeType.BACKGROUND) def test_with_execution_mode_parallel(self): - Manager(group=self.group, mode=TypeExecution.PARALLEL) + Manager(group=self.group, mode=ExecutionModeType.PARALLEL) def test_callback_success_called(self): group = QueueGroup() @@ -117,4 +117,4 @@ def test_workflow_with_class_completed(self): ) controller = Manager(group=group) - self.assertEqual(controller.group.tasks()[0].status, TypeStatus.COMPLETED) + self.assertEqual(controller.group.tasks()[0].status, StatusTaskType.COMPLETED) diff --git a/tests/core/test_workflow_background.py b/tests/core/test_workflow_background.py index 0b2c7a1..46962fd 100644 --- a/tests/core/test_workflow_background.py +++ b/tests/core/test_workflow_background.py @@ -5,7 +5,7 @@ from uuid import uuid4 from dotflow.core.workflow import Background -from dotflow.core.types import TypeStatus +from dotflow.core.types import StatusTaskType from dotflow.core.task import Task, TaskError, QueueGroup from tests.mocks import ( @@ -54,7 +54,7 @@ def test_workflow_with_background_function_completed(self): tasks = execution.transport() - self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) + self.assertEqual(tasks[0].status, StatusTaskType.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) self.assertIsInstance(tasks[0].error, TaskError) self.assertEqual(tasks[0].error.message, "") @@ -73,7 +73,7 @@ def test_workflow_with_background_function_failed(self): tasks = execution.transport() - self.assertEqual(tasks[0].status, TypeStatus.FAILED) + self.assertEqual(tasks[0].status, StatusTaskType.FAILED) self.assertIsNone(tasks[0].current_context.storage) self.assertIsInstance(tasks[0].error, TaskError) self.assertEqual(tasks[0].error.message, "Fail!") diff --git a/tests/core/test_workflow_parallel.py b/tests/core/test_workflow_parallel.py index 6a46a91..d523ada 100644 --- a/tests/core/test_workflow_parallel.py +++ b/tests/core/test_workflow_parallel.py @@ -6,7 +6,7 @@ from multiprocessing.queues import Queue from dotflow.core.workflow import Parallel -from dotflow.core.types import TypeStatus +from dotflow.core.types import StatusTaskType from dotflow.core.task import Task, TaskError, QueueGroup from tests.mocks import ( @@ -55,7 +55,7 @@ def test_workflow_with_parallel_function_completed(self): tasks = execution.transport() - self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) + self.assertEqual(tasks[0].status, StatusTaskType.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) self.assertIsInstance(tasks[0].error, TaskError) self.assertEqual(tasks[0].error.message, "") @@ -74,7 +74,7 @@ def test_workflow_with_parallel_function_failed(self): tasks = execution.transport() - self.assertEqual(tasks[0].status, TypeStatus.FAILED) + self.assertEqual(tasks[0].status, StatusTaskType.FAILED) self.assertIsNone(tasks[0].current_context.storage) self.assertIsInstance(tasks[0].error, TaskError) self.assertEqual(tasks[0].error.message, "Fail!") diff --git a/tests/core/test_workflow_sequential.py b/tests/core/test_workflow_sequential.py index 88a1f6b..3021980 100644 --- a/tests/core/test_workflow_sequential.py +++ b/tests/core/test_workflow_sequential.py @@ -6,7 +6,7 @@ from unittest.mock import Mock from dotflow.core.workflow import Sequential -from dotflow.core.types import TypeStatus +from dotflow.core.types import StatusTaskType from dotflow.core.task import Task, TaskError, QueueGroup from tests.mocks import ( @@ -66,7 +66,7 @@ def test_workflow_with_function_completed(self): ) tasks = execution.group.tasks() - self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) + self.assertEqual(tasks[0].status, StatusTaskType.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) self.assertIsInstance(tasks[0].error, TaskError) self.assertEqual(tasks[0].error.message, "") @@ -84,7 +84,7 @@ def test_workflow_with_function_failed(self): ) tasks = execution.group.tasks() - self.assertEqual(tasks[0].status, TypeStatus.FAILED) + self.assertEqual(tasks[0].status, StatusTaskType.FAILED) self.assertIsNone(tasks[0].current_context.storage) self.assertIsInstance(tasks[0].error, TaskError) self.assertEqual(tasks[0].error.message, "Fail!") diff --git a/tests/core/test_workflow_sequential_group.py b/tests/core/test_workflow_sequential_group.py index d8725e1..d1987f3 100644 --- a/tests/core/test_workflow_sequential_group.py +++ b/tests/core/test_workflow_sequential_group.py @@ -7,7 +7,7 @@ from multiprocessing.queues import Queue from dotflow.core.workflow import SequentialGroup -from dotflow.core.types import TypeStatus +from dotflow.core.types import StatusTaskType from dotflow.core.task import Task, TaskError, QueueGroup from tests.mocks import ( @@ -56,7 +56,7 @@ def test_workflow_with_sequential_group_function_completed(self): tasks = execution.transport() - self.assertEqual(tasks[0].status, TypeStatus.COMPLETED) + self.assertEqual(tasks[0].status, StatusTaskType.COMPLETED) self.assertEqual(tasks[0].current_context.storage, {"foo": "bar"}) self.assertIsInstance(tasks[0].error, TaskError) self.assertEqual(tasks[0].error.message, "") @@ -75,7 +75,7 @@ def test_workflow_with_sequential_group_function_failed(self): tasks = execution.transport() - self.assertEqual(tasks[0].status, TypeStatus.FAILED) + self.assertEqual(tasks[0].status, StatusTaskType.FAILED) self.assertIsNone(tasks[0].current_context.storage) self.assertIsInstance(tasks[0].error, TaskError) self.assertEqual(tasks[0].error.message, "Fail!") From 46e2ca8f494b181a85b8d1a4a61195aec35b0eb5 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 02:17:50 -0300 Subject: [PATCH 14/20] =?UTF-8?q?=F0=9F=93=98=20DOCS:=20Update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_src/backoff/backoff.py | 6 +++--- docs_src/callback/task_callback.py | 4 ++-- docs_src/callback/workflow_callback_failure.py | 2 +- docs_src/callback/workflow_callback_success.py | 2 +- docs_src/first_steps/first_steps.py | 8 ++++---- docs_src/initial_context/initial_context.py | 12 ++++++------ docs_src/output/step_class_result_context.py | 2 +- docs_src/output/step_class_result_storage.py | 2 +- docs_src/output/step_class_result_task.py | 2 +- docs_src/output/step_function_result_context.py | 2 +- docs_src/output/step_function_result_storage.py | 2 +- docs_src/output/step_function_result_task.py | 2 +- docs_src/previous_context/previous_context.py | 6 +++--- docs_src/process_mode/background.py | 4 ++-- docs_src/process_mode/parallel.py | 4 ++-- docs_src/process_mode/parallel_group.py | 4 ++-- docs_src/process_mode/sequential.py | 4 ++-- docs_src/retry/retry.py | 6 +++--- docs_src/retry/retry_delay.py | 6 +++--- docs_src/timeout/timeout.py | 6 +++--- 20 files changed, 43 insertions(+), 43 deletions(-) diff --git a/docs_src/backoff/backoff.py b/docs_src/backoff/backoff.py index acb8f5e..9f2c4ef 100644 --- a/docs_src/backoff/backoff.py +++ b/docs_src/backoff/backoff.py @@ -25,15 +25,15 @@ def run(self): def main(): workflow = DotFlow() - workflow.task.add(step=simple_step) + workflow.add(step=simple_step) workflow.start() workflow.task.clear() - workflow.task.add(step=SimpleStepX) + workflow.add(step=SimpleStepX) workflow.start() workflow.task.clear() - workflow.task.add(step=SimpleStepY) + workflow.add(step=SimpleStepY) workflow.start() workflow.task.clear() diff --git a/docs_src/callback/task_callback.py b/docs_src/callback/task_callback.py index c4fc319..e0bde23 100644 --- a/docs_src/callback/task_callback.py +++ b/docs_src/callback/task_callback.py @@ -25,8 +25,8 @@ def simple_two(): def main(): workflow = DotFlow() - workflow.task.add(step=simple_one, callback=callback_one) - workflow.task.add(step=simple_two, callback=callback_two) + workflow.add(step=simple_one, callback=callback_one) + workflow.add(step=simple_two, callback=callback_two) workflow.start(keep_going=True) return workflow diff --git a/docs_src/callback/workflow_callback_failure.py b/docs_src/callback/workflow_callback_failure.py index 103aa2b..1d30404 100644 --- a/docs_src/callback/workflow_callback_failure.py +++ b/docs_src/callback/workflow_callback_failure.py @@ -21,7 +21,7 @@ def simple_step(): def main(): workflow = DotFlow() - workflow.task.add(step=simple_step) + workflow.add(step=simple_step) workflow.start(on_failure=callback) return workflow diff --git a/docs_src/callback/workflow_callback_success.py b/docs_src/callback/workflow_callback_success.py index 6a03009..b713215 100644 --- a/docs_src/callback/workflow_callback_success.py +++ b/docs_src/callback/workflow_callback_success.py @@ -21,7 +21,7 @@ def simple_step(): def main(): workflow = DotFlow() - workflow.task.add(step=simple_step) + workflow.add(step=simple_step) workflow.start(on_success=callback) return workflow diff --git a/docs_src/first_steps/first_steps.py b/docs_src/first_steps/first_steps.py index 82fe724..1f371ef 100644 --- a/docs_src/first_steps/first_steps.py +++ b/docs_src/first_steps/first_steps.py @@ -1,8 +1,8 @@ -from dotflow import DotFlow, action +from dotflow import DotFlow, action, Task -def my_callback(*args, **kwargs): - print(args, kwargs) +def my_callback(task: Task): + print(task.status) @action @@ -11,5 +11,5 @@ def my_task(): workflow = DotFlow() -workflow.task.add(step=my_task, callback=my_callback) +workflow.add(step=my_task, callback=my_callback) workflow.start() diff --git a/docs_src/initial_context/initial_context.py b/docs_src/initial_context/initial_context.py index 5344dd7..b8fbea2 100644 --- a/docs_src/initial_context/initial_context.py +++ b/docs_src/initial_context/initial_context.py @@ -1,8 +1,8 @@ -from dotflow import DotFlow, action +from dotflow import DotFlow, action, Context @action -def extract_task(initial_context): +def extract_task(initial_context: Context): print(initial_context.storage, "extract") assert initial_context.storage == {"foo": True} @@ -10,7 +10,7 @@ def extract_task(initial_context): @action -def transform_task(initial_context): +def transform_task(initial_context: Context): print(initial_context.storage, "transform") assert initial_context.storage == {"bar": True} @@ -25,9 +25,9 @@ def load_task(): def main(): workflow = DotFlow() - workflow.task.add(step=extract_task, initial_context={"foo": True}) - workflow.task.add(step=transform_task, initial_context={"bar": True}) - workflow.task.add(step=load_task) + workflow.add(step=extract_task, initial_context={"foo": True}) + workflow.add(step=transform_task, initial_context={"bar": True}) + workflow.add(step=load_task) workflow.start() diff --git a/docs_src/output/step_class_result_context.py b/docs_src/output/step_class_result_context.py index 449e417..36910db 100644 --- a/docs_src/output/step_class_result_context.py +++ b/docs_src/output/step_class_result_context.py @@ -21,7 +21,7 @@ def second_function(self): def main(): workflow = DotFlow() - workflow.task.add(step=Step) + workflow.add(step=Step) workflow.start() for contexts in workflow.result_context(): diff --git a/docs_src/output/step_class_result_storage.py b/docs_src/output/step_class_result_storage.py index 45880a3..d89ab50 100644 --- a/docs_src/output/step_class_result_storage.py +++ b/docs_src/output/step_class_result_storage.py @@ -21,7 +21,7 @@ def second_function(self): def main(): workflow = DotFlow() - workflow.task.add(step=Step) + workflow.add(step=Step) workflow.start() for storages in workflow.result_storage(): diff --git a/docs_src/output/step_class_result_task.py b/docs_src/output/step_class_result_task.py index f1d9423..68b4ed1 100644 --- a/docs_src/output/step_class_result_task.py +++ b/docs_src/output/step_class_result_task.py @@ -21,7 +21,7 @@ def second_function(self): def main(): workflow = DotFlow() - workflow.task.add(step=Step) + workflow.add(step=Step) workflow.start() for task in workflow.result_task(): diff --git a/docs_src/output/step_function_result_context.py b/docs_src/output/step_function_result_context.py index 93e634b..c5d3f3b 100644 --- a/docs_src/output/step_function_result_context.py +++ b/docs_src/output/step_function_result_context.py @@ -9,7 +9,7 @@ def simple_step(): def main(): workflow = DotFlow() - workflow.task.add(step=simple_step) + workflow.add(step=simple_step) workflow.start() for context in workflow.result_context(): diff --git a/docs_src/output/step_function_result_storage.py b/docs_src/output/step_function_result_storage.py index b79ea40..08352d7 100644 --- a/docs_src/output/step_function_result_storage.py +++ b/docs_src/output/step_function_result_storage.py @@ -9,7 +9,7 @@ def simple_step(): def main(): workflow = DotFlow() - workflow.task.add(step=simple_step) + workflow.add(step=simple_step) workflow.start() for storage in workflow.result_storage(): diff --git a/docs_src/output/step_function_result_task.py b/docs_src/output/step_function_result_task.py index 9799ea5..24d76db 100644 --- a/docs_src/output/step_function_result_task.py +++ b/docs_src/output/step_function_result_task.py @@ -9,7 +9,7 @@ def simple_step(): def main(): workflow = DotFlow() - workflow.task.add(step=simple_step) + workflow.add(step=simple_step) workflow.start() for task in workflow.result_task(): diff --git a/docs_src/previous_context/previous_context.py b/docs_src/previous_context/previous_context.py index be2d9d6..4c07681 100644 --- a/docs_src/previous_context/previous_context.py +++ b/docs_src/previous_context/previous_context.py @@ -26,9 +26,9 @@ def load_task(previous_context: Context): def main(): workflow = DotFlow() - workflow.task.add(step=extract_task) - workflow.task.add(step=transform_task) - workflow.task.add(step=load_task) + workflow.add(step=extract_task) + workflow.add(step=transform_task) + workflow.add(step=load_task) workflow.start() diff --git a/docs_src/process_mode/background.py b/docs_src/process_mode/background.py index 723991d..afe4d35 100644 --- a/docs_src/process_mode/background.py +++ b/docs_src/process_mode/background.py @@ -20,8 +20,8 @@ def task_bar(initial_context: Context): def main(): workflow = DotFlow() - workflow.task.add(step=task_foo, initial_context=10) - workflow.task.add(step=task_bar, initial_context=10) + workflow.add(step=task_foo, initial_context=10) + workflow.add(step=task_bar, initial_context=10) workflow.start(mode="background") diff --git a/docs_src/process_mode/parallel.py b/docs_src/process_mode/parallel.py index 15d8714..e62b539 100644 --- a/docs_src/process_mode/parallel.py +++ b/docs_src/process_mode/parallel.py @@ -20,8 +20,8 @@ def task_bar(initial_context: Context): def main(): workflow = DotFlow() - workflow.task.add(step=task_foo, initial_context=10) - workflow.task.add(step=task_bar, initial_context=10) + workflow.add(step=task_foo, initial_context=10) + workflow.add(step=task_bar, initial_context=10) workflow.start(mode="parallel") diff --git a/docs_src/process_mode/parallel_group.py b/docs_src/process_mode/parallel_group.py index 9108be5..b307740 100644 --- a/docs_src/process_mode/parallel_group.py +++ b/docs_src/process_mode/parallel_group.py @@ -20,8 +20,8 @@ def task_bar(initial_context: Context): def main(): workflow = DotFlow() - workflow.task.add(step=task_foo, initial_context=10, group_name="foo") - workflow.task.add(step=task_bar, initial_context=10, group_name="bar") + workflow.add(step=task_foo, initial_context=10, group_name="foo") + workflow.add(step=task_bar, initial_context=10, group_name="bar") workflow.start() diff --git a/docs_src/process_mode/sequential.py b/docs_src/process_mode/sequential.py index e33a1f9..5272a94 100644 --- a/docs_src/process_mode/sequential.py +++ b/docs_src/process_mode/sequential.py @@ -20,8 +20,8 @@ def task_bar(initial_context: Context): def main(): workflow = DotFlow() - workflow.task.add(step=task_foo, initial_context=10) - workflow.task.add(step=task_bar, initial_context=10) + workflow.add(step=task_foo, initial_context=10) + workflow.add(step=task_bar, initial_context=10) workflow.start(mode="sequential") diff --git a/docs_src/retry/retry.py b/docs_src/retry/retry.py index 7089197..7d2eeee 100644 --- a/docs_src/retry/retry.py +++ b/docs_src/retry/retry.py @@ -25,15 +25,15 @@ def run(self): def main(): workflow = DotFlow() - workflow.task.add(step=simple_step) + workflow.add(step=simple_step) workflow.start() workflow.task.clear() - workflow.task.add(step=SimpleStepX) + workflow.add(step=SimpleStepX) workflow.start() workflow.task.clear() - workflow.task.add(step=SimpleStepY) + workflow.add(step=SimpleStepY) workflow.start() workflow.task.clear() diff --git a/docs_src/retry/retry_delay.py b/docs_src/retry/retry_delay.py index 3c72208..2c6e380 100644 --- a/docs_src/retry/retry_delay.py +++ b/docs_src/retry/retry_delay.py @@ -25,15 +25,15 @@ def run(self): def main(): workflow = DotFlow() - workflow.task.add(step=simple_step) + workflow.add(step=simple_step) workflow.start() workflow.task.clear() - workflow.task.add(step=SimpleStepX) + workflow.add(step=SimpleStepX) workflow.start() workflow.task.clear() - workflow.task.add(step=SimpleStepY) + workflow.add(step=SimpleStepY) workflow.start() workflow.task.clear() diff --git a/docs_src/timeout/timeout.py b/docs_src/timeout/timeout.py index 910586e..dc3a886 100644 --- a/docs_src/timeout/timeout.py +++ b/docs_src/timeout/timeout.py @@ -26,15 +26,15 @@ def __init__(self): def main(): workflow = DotFlow() - workflow.task.add(step=simple_step) + workflow.add(step=simple_step) workflow.start() workflow.task.clear() - workflow.task.add(step=SimpleStepX) + workflow.add(step=SimpleStepX) workflow.start() workflow.task.clear() - workflow.task.add(step=SimpleStepY) + workflow.add(step=SimpleStepY) workflow.start() workflow.task.clear() From 68bdc64dfcc0dc6565f7547b32d67f3514e93bcf Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 23:28:18 -0300 Subject: [PATCH 15/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Plugin=20?= =?UTF-8?q?for=20dynamic=20logs=20included?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotflow/abc/logs.py | 2 +- dotflow/core/task.py | 9 ++++++--- dotflow/plugins/logs.py | 2 +- dotflow/utils/tools.py | 25 ++++++++++++++++++++++--- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/dotflow/abc/logs.py b/dotflow/abc/logs.py index 505a22e..b0a98ec 100644 --- a/dotflow/abc/logs.py +++ b/dotflow/abc/logs.py @@ -33,5 +33,5 @@ class Logs(ABC): """Logs""" @abstractmethod - def post_task(self, task_object, type: TYPE_LOG) -> None: + def on_task_status_change(self, task_object, type: TYPE_LOG) -> None: pass diff --git a/dotflow/core/task.py b/dotflow/core/task.py index 1dfdb18..dab2dbd 100644 --- a/dotflow/core/task.py +++ b/dotflow/core/task.py @@ -31,7 +31,7 @@ class TaskInstance: from dotflow.core.task import TaskInstance """ - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *_args, **_kwargs) -> None: self.task_id = None self.workflow_id = None self._step = None @@ -206,7 +206,8 @@ def error(self, value: Exception) -> None: task_error = TaskError(value) self._error = task_error - self.config.log.post_task(task_object=self, type=TypeLog.ERROR) + for log in self.config.logs: + log.on_task_status_change(task_object=self, type=TypeLog.ERROR) @property def status(self): @@ -219,7 +220,9 @@ def status(self, value: TYPE_STATUS_TASK) -> None: self._status = value self.config.notify.send(task=self) - self.config.log.post_task(task_object=self, type=TypeLog.INFO) + + for log in self.config.logs: + log.on_task_status_change(task_object=self, type=TypeLog.INFO) @property def config(self): diff --git a/dotflow/plugins/logs.py b/dotflow/plugins/logs.py index d97c4f4..f1a4535 100644 --- a/dotflow/plugins/logs.py +++ b/dotflow/plugins/logs.py @@ -10,7 +10,7 @@ class LogsHandler(Logs): - def post_task(self, task_object, type: TYPE_LOG) -> None: + def on_task_status_change(self, task_object, type: TYPE_LOG) -> None: new_log = getattr(logger, type.lower()) new_log( TASK_LOG_FORMAT.format( diff --git a/dotflow/utils/tools.py b/dotflow/utils/tools.py index 49d6011..76050e1 100644 --- a/dotflow/utils/tools.py +++ b/dotflow/utils/tools.py @@ -11,7 +11,8 @@ def write_file_system( content: str, mode: str = "w" ) -> None: - """Write file system""" + """Write file system + """ if mode == "a": system(f"echo '{content}' >> {path}") @@ -25,7 +26,8 @@ def write_file( mode: str = "w", encoding: str = "utf-8" ) -> None: - """Write file""" + """Write file + """ try: with open(file=path, mode=mode, encoding=encoding) as file: file.write(dumps(content)) @@ -38,7 +40,8 @@ def read_file( path: Path, encoding: str = "utf-8" ) -> Any: - """Read file""" + """Read file + """ if path.exists(): with open(file=path, mode="r", encoding=encoding) as file: try: @@ -46,3 +49,19 @@ def read_file( except JSONDecodeError: return file.read() return None + + +def start_and_validate_instance(current_class, instance): + """ + This function starts a class if it is not instantiated and also + performs validation that the class instance matches what is expected. + """ + if callable(current_class): + current_class = current_class() + + if not isinstance(current_class, instance): + raise ValueError( + f"Not a valid {current_class.__name__} instance class." + ) + + return current_class From b5af4ca95010f28813c75e7e46a13f7ca9a5d587 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 23:29:00 -0300 Subject: [PATCH 16/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Config=20?= =?UTF-8?q?Class=20Improvement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_src/config/config.py | 20 +++++++++++ dotflow/core/config.py | 76 ++++++++++++++++++++++++++++++++------- 2 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 docs_src/config/config.py diff --git a/docs_src/config/config.py b/docs_src/config/config.py new file mode 100644 index 0000000..2d39cf4 --- /dev/null +++ b/docs_src/config/config.py @@ -0,0 +1,20 @@ +from dotflow import Config, DotFlow, action + + +@action +def my_task(): + print("task") + + +def main(): + my_config = Config() + + workflow = DotFlow(config=my_config) + workflow.add(step=my_task) + workflow.start() + + return workflow + + +if __name__ == "__main__": + main() diff --git a/dotflow/core/config.py b/dotflow/core/config.py index cc279f7..daa728a 100644 --- a/dotflow/core/config.py +++ b/dotflow/core/config.py @@ -1,17 +1,26 @@ """Config module""" -from typing import Optional +from typing import List, Optional -from dotflow.abc.logs import Logs from dotflow.abc.storage import Storage from dotflow.abc.notify import Notify +from dotflow.abc.logs import Logs from dotflow.plugins.logs import LogsHandler from dotflow.providers.storage_default import StorageDefault from dotflow.providers.notify_default import NotifyDefault +from dotflow.utils.tools import start_and_validate_instance + + +class ConfigInstance: + def __init__(self, *_args, **_kwargs): + self._storage: Storage = None + self._notify: Notify = None + self._logs: List[Logs] = None -class Config: + +class Config(ConfigInstance): """ Import: You can import the **Config** class with: @@ -30,26 +39,67 @@ class Config: config = Config( storage=StorageFile(path=".output"), notify=NotifyDefault(), - log=LogsHandler() + logs=LogsHandler ) Args: - storage (Optional[Storage]): Type of the storage. - notify (Optional[Notify]): Type of the notify. - log (Optional[Logs]): Type of the notify. + storage (Optional[Storage]): Type of the Storage. + notify (Optional[Notify]): Type of the Notify. + logs (Optional[List[Logs]]): Type of the Logs. Attributes: storage (Optional[Storage]): notify (Optional[Notify]): - log (Optional[Logs]): + logs (Optional[List[Logs]]): """ def __init__( self, - storage: Optional[Storage] = StorageDefault(), - notify: Optional[Notify] = NotifyDefault(), - log: Optional[Logs] = LogsHandler(), - ) -> None: + storage: Optional[Storage] = StorageDefault, + notify: Optional[Notify] = NotifyDefault, + logs: Optional[List[Logs]] = LogsHandler + ): + super().__init__() self.storage = storage self.notify = notify - self.log = log + self.logs = logs + + @property + def storage(self): + return self._storage + + @storage.setter + def storage(self, value): + if not value: + value = StorageDefault() + + self._storage = start_and_validate_instance(value, Storage) + + @property + def notify(self): + return self._notify + + @notify.setter + def notify(self, value): + if not value: + value = NotifyDefault() + + self._notify = start_and_validate_instance(value, Notify) + + @property + def logs(self): + return self._logs + + @logs.setter + def logs(self, value): + if not value: + value = [LogsHandler] + + if isinstance(value, list): + if isinstance(value, list): + for index, current_log in enumerate(value): + value[index] = start_and_validate_instance(current_log, Logs) + else: + value = [start_and_validate_instance(value, Logs)] + + self._logs = value From 3c6c7f0677cf1fb1943ec58c7376b430a1ab1df9 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 23:29:15 -0300 Subject: [PATCH 17/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Dotflow?= =?UTF-8?q?=20Class=20Improvement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotflow/core/dotflow.py | 109 +++++++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 24 deletions(-) diff --git a/dotflow/core/dotflow.py b/dotflow/core/dotflow.py index c69591d..3c8e6bb 100644 --- a/dotflow/core/dotflow.py +++ b/dotflow/core/dotflow.py @@ -1,15 +1,27 @@ -"""DotFlow""" +"""DotFlow module""" -from uuid import uuid4 +from uuid import uuid4, UUID from functools import partial -from typing import Optional + +from typing import Callable, Optional from dotflow.core.config import Config from dotflow.core.workflow import Manager from dotflow.core.task import TaskBuilder +from dotflow.utils.tools import start_and_validate_instance + + +class DotflowInstance: + + def __init__(self, *_args, **_kwargs): + self._config: Config = None + self._workflow_id: UUID = None + self._task: TaskBuilder = None + self._add: Callable = None + self._start: Manager = None -class DotFlow: +class DotFlow(DotflowInstance): """ Import: You can import the **Dotflow** class directly from dotflow: @@ -27,34 +39,83 @@ class DotFlow: workflow = DotFlow(config=config) Args: - config (Optional[Config]): Configuration class. + config (Optional[Config]): Config class. Attributes: workflow_id (UUID): - task (List[Task]): + task (TaskBuilder): + + add (Callable): start (Manager): """ - def __init__( - self, - config: Optional[Config] = None - ) -> None: - self.workflow_id = uuid4() - config = config if config else Config() - - self.task = TaskBuilder( - config=config, - workflow_id=self.workflow_id - ) - self.add = self.task.add - - self.start = partial( - Manager, - group=self.task.group, - workflow_id=self.workflow_id - ) + def __init__(self, config: Optional[Config] = Config) -> None: + super().__init__() + self.config: Config = config + self.workflow_id: UUID = None + self.task: TaskBuilder = None + self.add: Callable = None + self.start: Manager = None + + @property + def config(self): + return self._config + + @config.setter + def config(self, value): + if not value: + value = Config() + + self._config = start_and_validate_instance(value, Config) + + @property + def workflow_id(self): + return self._workflow_id + + @workflow_id.setter + def workflow_id(self, value): + if not value: + value = uuid4() + self._workflow_id = value + + @property + def task(self): + return self._task + + @task.setter + def task(self, value): + if not value: + value = TaskBuilder( + config=self.config, + workflow_id=self.workflow_id + ) + self._task = value + + @property + def add(self): + return self._add + + @add.setter + def add(self, value): + if not value: + value = self.task.add + self._add = value + + @property + def start(self): + return self._start + + @start.setter + def start(self, value): + if not value: + value = partial( + Manager, + group=self.task.group, + workflow_id=self.workflow_id + ) + self._start = value def result_task(self): """ From 5eb746874bcf0f2ab9bce8978f54896752aa38c1 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 23:29:53 -0300 Subject: [PATCH 18/20] =?UTF-8?q?=E2=9D=A4=EF=B8=8F=20TEST:=20Updated=20un?= =?UTF-8?q?it=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/core/test_config.py | 44 +++++++++++++++++ tests/core/test_dotflow.py | 98 +++++++++++++++++++++++++++----------- tests/core/test_queue.py | 19 ++------ 3 files changed, 118 insertions(+), 43 deletions(-) create mode 100644 tests/core/test_config.py diff --git a/tests/core/test_config.py b/tests/core/test_config.py new file mode 100644 index 0000000..9b430f0 --- /dev/null +++ b/tests/core/test_config.py @@ -0,0 +1,44 @@ +"""Test of config""" + +import unittest + +from dotflow.core.config import Config, ConfigInstance +from dotflow.abc.storage import Storage +from dotflow.abc.notify import Notify +from dotflow.abc.logs import Logs + + +class TestConfigInstance(unittest.TestCase): + + def setUp(self): + self.instance = ConfigInstance() + + def test_instantiating_config_instance_class(self): + self.assertIsNone(self.instance._storage) + self.assertIsNone(self.instance._notify) + self.assertIsNone(self.instance._logs) + + +class TestConfig(unittest.TestCase): + + def setUp(self): + self.config = Config() + self.index = 0 + + def test_instantiating_config_class(self): + self.assertIsInstance(self.config.storage, Storage) + self.assertIsInstance(self.config.notify, Notify) + self.assertIsInstance(self.config.logs, list) + self.assertIsInstance(self.config.logs[self.index], Logs) + + def test_config_storage_object(self): + self.assertIsNotNone(self.config.storage) + self.assertFalse(callable(self.config.storage)) + + def test_config_notify_object(self): + self.assertIsNotNone(self.config.notify) + self.assertFalse(callable(self.config.notify)) + + def test_config_logs_object(self): + self.assertIsNotNone(self.config.logs) + self.assertFalse(callable(self.config.logs[self.index])) diff --git a/tests/core/test_dotflow.py b/tests/core/test_dotflow.py index e5bb2e2..4e1fe6c 100644 --- a/tests/core/test_dotflow.py +++ b/tests/core/test_dotflow.py @@ -1,63 +1,103 @@ -"""Test context of workflow""" +"""Test of dotflow""" import unittest +from uuid import UUID + +from dotflow.core.config import Config from dotflow.core.workflow import Manager from dotflow.core.context import Context from dotflow.core.task import Task, TaskBuilder -from dotflow.core.dotflow import DotFlow +from dotflow.core.dotflow import DotFlow, DotflowInstance from dotflow.core.types.status import StatusTaskType from tests.mocks import action_step +class TestDotflowInstance(unittest.TestCase): + + def setUp(self): + self.instance = DotflowInstance() + + def test_instantiating_dotflow_instance_class(self): + self.assertIsNone(self.instance._config) + self.assertIsNone(self.instance._workflow_id) + self.assertIsNone(self.instance._task) + self.assertIsNone(self.instance._add) + self.assertIsNone(self.instance._start) + + class TestDotFlow(unittest.TestCase): def setUp(self): - self.workflow = DotFlow() - self.workflow.add(step=action_step) + self.dotflow = DotFlow() + self.dotflow.add(step=action_step) + self.index = 0 + self.size = 1 def test_instantiating_dotflow_class(self): - self.assertIsInstance(self.workflow.task, TaskBuilder) - self.assertIsInstance(self.workflow.start(), Manager) + self.assertIsInstance(self.dotflow.config, Config) + self.assertIsInstance(self.dotflow.workflow_id, UUID) + self.assertIsInstance(self.dotflow.task, TaskBuilder) + self.assertIsInstance(self.dotflow.start(), Manager) def test_result_task_with_start(self): - self.workflow.start() - result = self.workflow.result_task() + self.dotflow.start() + result = self.dotflow.result_task() - self.assertEqual(len(result), 1) - self.assertIsInstance(result[0], Task) - self.assertEqual(result[0].status, StatusTaskType.COMPLETED) + self.assertEqual(len(result), self.size) + self.assertIsInstance(result[self.index], Task) + self.assertEqual(result[self.index].status, StatusTaskType.COMPLETED) def test_result_context_with_start(self): - self.workflow.start() - result = self.workflow.result_context() + self.dotflow.start() + result = self.dotflow.result_context() - self.assertEqual(len(result), 1) - self.assertIsInstance(result[0], Context) + self.assertEqual(len(result), self.size) + self.assertIsInstance(result[self.index], Context) def test_result_storage_with_start(self): - self.workflow.start() - result = self.workflow.result_storage() + self.dotflow.start() + result = self.dotflow.result_storage() - self.assertEqual(len(result), 1) - self.assertEqual(result[0], {"foo": "bar"}) + self.assertEqual(len(result), self.size) + self.assertEqual(result[self.index], {"foo": "bar"}) def test_result_task_without_start(self): - result = self.workflow.result_task() + result = self.dotflow.result_task() - self.assertEqual(len(result), 1) - self.assertIsInstance(result[0], Task) - self.assertEqual(result[0].status, StatusTaskType.NOT_STARTED) + self.assertEqual(len(result), self.size) + self.assertIsInstance(result[self.index], Task) + self.assertEqual(result[self.index].status, StatusTaskType.NOT_STARTED) def test_result_context_without_start(self): - result = self.workflow.result_context() + result = self.dotflow.result_context() - self.assertEqual(len(result), 1) - self.assertIsInstance(result[0], Context) + self.assertEqual(len(result), self.size) + self.assertIsInstance(result[self.index], Context) def test_result_storage_without_start(self): - result = self.workflow.result_storage() + result = self.dotflow.result_storage() + + self.assertEqual(len(result), self.size) + self.assertEqual(result[self.index], None) + + def test_dotflow_config_object(self): + self.assertIsNotNone(self.dotflow.config) + self.assertFalse(callable(self.dotflow.config)) + + def test_dotflow_workflow_id_object(self): + self.assertIsNotNone(self.dotflow.workflow_id) + self.assertFalse(callable(self.dotflow.workflow_id)) + + def test_dotflow_task_object(self): + self.assertIsNotNone(self.dotflow.task) + self.assertFalse(callable(self.dotflow.task)) + + def test_dotflow_add_object(self): + self.assertIsNotNone(self.dotflow.add) + self.assertTrue(callable(self.dotflow.add)) - self.assertEqual(len(result), 1) - self.assertEqual(result[0], None) + def test_dotflow_start_object(self): + self.assertIsNotNone(self.dotflow.start) + self.assertTrue(callable(self.dotflow.start)) diff --git a/tests/core/test_queue.py b/tests/core/test_queue.py index f0048cb..68f6da5 100644 --- a/tests/core/test_queue.py +++ b/tests/core/test_queue.py @@ -15,24 +15,22 @@ def setUp(self): step=action_step, callback=simple_callback ) + self.index = 0 def test_instantiating_queue_class(self): expected_tasks = [] queue = Queue() - self.assertIsInstance(queue, Queue) self.assertListEqual(queue.tasks, expected_tasks) def test_queue_add(self): queue = Queue() - queue.add(item=self.task) - self.assertEqual(queue.tasks[0], self.task) + self.assertEqual(queue.tasks[self.index], self.task) def test_queue_remove(self): queue = Queue() - queue.add(item=self.task) queue.remove() @@ -41,7 +39,6 @@ def test_queue_remove(self): def test_queue_size(self): expected_size = 1 queue = Queue() - queue.add(item=self.task) self.assertEqual(queue.size(), expected_size) @@ -53,26 +50,21 @@ def test_queue_reverse(self): callback=simple_callback ) queue = Queue() - queue.add(item=self.task) queue.add(item=expected_task) - queue.reverse() - self.assertEqual(queue.tasks[0], expected_task) + self.assertEqual(queue.tasks[self.index], expected_task) def test_queue_clear(self): queue = Queue() - queue.add(item=self.task) - queue.clear() self.assertFalse(queue.tasks) def test_queue_get(self): queue = Queue() - queue.add(item=self.task) self.assertListEqual(queue.get(), [self.task]) @@ -86,6 +78,7 @@ def setUp(self): step=action_step, callback=simple_callback ) + self.index = 0 def test_instantiating_queue_group_class(self): expected_queue = {} @@ -101,7 +94,7 @@ def test_queue_group_add(self): self.assertIsNotNone(queue_group.queue.get(TASK_GROUP_NAME)) self.assertIsInstance(queue_group.queue.get(TASK_GROUP_NAME), Queue) - self.assertEqual(queue_group.queue[TASK_GROUP_NAME].tasks[0], self.task) + self.assertEqual(queue_group.queue[TASK_GROUP_NAME].tasks[self.index], self.task) def test_queue_group_size(self): expected_size = 1 @@ -120,7 +113,6 @@ def test_queue_group_count(self): group_name="new_group" ) queue = QueueGroup() - queue.add(item=self.task) queue.add(item=task) @@ -128,7 +120,6 @@ def test_queue_group_count(self): def test_queue_group_tasks(self): queue = QueueGroup() - queue.add(item=self.task) self.assertIsNotNone(queue.tasks()) From 4994c2a0a310cb09e396c9c9f97893086fd0d353 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Mon, 18 Aug 2025 23:41:39 -0300 Subject: [PATCH 19/20] =?UTF-8?q?=F0=9F=93=98=20DOCS:=20Updated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/nav/reference/notify-default.md | 2 +- dotflow/core/exception.py | 2 +- dotflow/core/task.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/nav/reference/notify-default.md b/docs/nav/reference/notify-default.md index 4958819..f2a6a83 100644 --- a/docs/nav/reference/notify-default.md +++ b/docs/nav/reference/notify-default.md @@ -1,3 +1,3 @@ -# NotifyTelegram +# Notify Default ::: dotflow.providers.notify_default.NotifyDefault diff --git a/dotflow/core/exception.py b/dotflow/core/exception.py index b4a5d0b..ad0e24e 100644 --- a/dotflow/core/exception.py +++ b/dotflow/core/exception.py @@ -1,7 +1,7 @@ """Exception module""" MESSAGE_UNKNOWN_ERROR = "Unknown error, please check logs for more information." -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/#3-task-function." +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/tutorial/first-steps/#3-task-function." MESSAGE_NOT_CALLABLE_OBJECT = "Problem validating the '{name}' object type; this is not a callable object" MESSAGE_EXECUTION_NOT_EXIST = "The execution mode does not exist. Allowed parameter is 'sequential', 'background' and 'parallel'." MESSAGE_IMPORT_MODULE_ERROR = "Error importing Python module '{module}'." diff --git a/dotflow/core/task.py b/dotflow/core/task.py index dab2dbd..8378b36 100644 --- a/dotflow/core/task.py +++ b/dotflow/core/task.py @@ -68,12 +68,12 @@ class Task(TaskInstance): step (Callable): A argument that receives an object of the callable type, which is basically a function. You can see in this - [example](https://dotflow-io.github.io/dotflow/#3-task-function). + [example](https://dotflow-io.github.io/dotflow/nav/tutorial/first-steps/#3-task-function). callback (Callable): Any callable object that receives **args** or **kwargs**, which is basically a function. You can see in this - [example](https://dotflow-io.github.io/dotflow/#2-callback-function). + [example](https://dotflow-io.github.io/dotflow/nav/tutorial/first-steps/#2-callback-function). initial_context (Any): Any python object. @@ -293,12 +293,12 @@ def add( step (Callable): A argument that receives an object of the callable type, which is basically a function. You can see in this - [example](https://dotflow-io.github.io/dotflow/#3-task-function). + [example](https://dotflow-io.github.io/dotflow/nav/tutorial/first-steps/#3-task-function). callback (Callable): Any callable object that receives **args** or **kwargs**, which is basically a function. You can see in this - [example](https://dotflow-io.github.io/dotflow/#2-callback-function). + [example](https://dotflow-io.github.io/dotflow/nav/tutorial/first-steps/#2-callback-function). initial_context (Context): The argument exists to include initial data in the execution From 5eec6c924729cf031e2cfa93c9d1bdc27a3f6d52 Mon Sep 17 00:00:00 2001 From: FernandoCelmer Date: Tue, 19 Aug 2025 00:19:41 -0300 Subject: [PATCH 20/20] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20FEATURE:=20Data=20ty?= =?UTF-8?q?pe=20improvement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotflow/abc/logs.py | 3 ++- dotflow/core/types/enum.py | 18 ++++++++++++++++++ dotflow/core/types/execution.py | 3 ++- dotflow/core/types/status.py | 15 ++------------- dotflow/core/types/storage.py | 3 ++- dotflow/core/types/worflow.py | 5 +++-- dotflow/providers/notify_telegram.py | 14 +++++++++++++- dotflow/types.py | 7 ++++++- 8 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 dotflow/core/types/enum.py diff --git a/dotflow/abc/logs.py b/dotflow/abc/logs.py index b0a98ec..379d526 100644 --- a/dotflow/abc/logs.py +++ b/dotflow/abc/logs.py @@ -1,10 +1,11 @@ """Logs ABC""" from typing import Literal -from enum import StrEnum from abc import ABC, abstractmethod +from dotflow.core.types.enum import StrEnum + class TypeLog(StrEnum): diff --git a/dotflow/core/types/enum.py b/dotflow/core/types/enum.py new file mode 100644 index 0000000..3d8bf4b --- /dev/null +++ b/dotflow/core/types/enum.py @@ -0,0 +1,18 @@ +"""Enum Type module""" + +import sys + +from enum import Enum + + +class StrEnumType(str, Enum): + ... + + +if sys.version_info >= (3, 11): + from enum import StrEnum as StrEnum +else: + StrEnum = StrEnumType + + +__all__ = ["StrEnum"] diff --git a/dotflow/core/types/execution.py b/dotflow/core/types/execution.py index 339f0c1..321ea7c 100644 --- a/dotflow/core/types/execution.py +++ b/dotflow/core/types/execution.py @@ -1,8 +1,9 @@ """Execution Mode Type module""" -from enum import StrEnum from typing import Literal +from dotflow.core.types.enum import StrEnum + class ExecutionModeType(StrEnum): """ diff --git a/dotflow/core/types/status.py b/dotflow/core/types/status.py index 6a38458..07923f2 100644 --- a/dotflow/core/types/status.py +++ b/dotflow/core/types/status.py @@ -1,8 +1,9 @@ """Status Task Type module""" -from enum import StrEnum from typing_extensions import Literal +from dotflow.core.types.enum import StrEnum + class StatusTaskType(StrEnum): """ @@ -19,18 +20,6 @@ class StatusTaskType(StrEnum): RETRY = "Retry" FAILED = "Failed" - @classmethod - def get_symbol(cls, value: str) -> str: - status = { - StatusTaskType.NOT_STARTED: "⚪", - StatusTaskType.IN_PROGRESS: "🔵", - StatusTaskType.COMPLETED: "✅", - StatusTaskType.PAUSED: "◼️", - StatusTaskType.RETRY: "❗", - StatusTaskType.FAILED: "❌" - } - return status.get(value) - TYPE_STATUS_TASK = Literal[ StatusTaskType.NOT_STARTED, diff --git a/dotflow/core/types/storage.py b/dotflow/core/types/storage.py index f2d51f1..dc0971a 100644 --- a/dotflow/core/types/storage.py +++ b/dotflow/core/types/storage.py @@ -1,8 +1,9 @@ """Storage Type module""" -from enum import StrEnum from typing import Literal +from dotflow.core.types.enum import StrEnum + class StorageType(StrEnum): """ diff --git a/dotflow/core/types/worflow.py b/dotflow/core/types/worflow.py index 2010cb8..25b8940 100644 --- a/dotflow/core/types/worflow.py +++ b/dotflow/core/types/worflow.py @@ -1,8 +1,9 @@ """Workflow Status Type module""" -from enum import StrEnum from typing import Literal +from dotflow.core.types.enum import StrEnum + class WorkflowStatusType(StrEnum): """ @@ -21,4 +22,4 @@ class WorkflowStatusType(StrEnum): WorkflowStatusType.NEW, WorkflowStatusType.IN_PROGRESS, WorkflowStatusType.COMPLETED -] \ No newline at end of file +] diff --git a/dotflow/providers/notify_telegram.py b/dotflow/providers/notify_telegram.py index 339d673..2dd6cc0 100644 --- a/dotflow/providers/notify_telegram.py +++ b/dotflow/providers/notify_telegram.py @@ -10,6 +10,18 @@ from dotflow.logging import logger +def get_symbol(value: str) -> str: + status = { + StatusTaskType.NOT_STARTED: "⚪", + StatusTaskType.IN_PROGRESS: "🔵", + StatusTaskType.COMPLETED: "✅", + StatusTaskType.PAUSED: "◼️", + StatusTaskType.RETRY: "❗", + StatusTaskType.FAILED: "❌" + } + return status.get(value) + + class NotifyTelegram(Notify): MESSAGE = "{symbol} {status}\n```json\n{task}```\n{workflow_id}-{task_id}" @@ -50,7 +62,7 @@ def send(self, task: Any) -> None: def _get_text(self, task: Any) -> str: return self.MESSAGE.format( - symbol=StatusTaskType.get_symbol(task.status), + symbol=get_symbol(task.status), status=task.status, workflow_id=task.workflow_id, task_id=task.task_id, diff --git a/dotflow/types.py b/dotflow/types.py index 13bfa3f..53a7ee3 100644 --- a/dotflow/types.py +++ b/dotflow/types.py @@ -1,9 +1,14 @@ """Types module""" +from dotflow.core.types.execution import ExecutionModeType from dotflow.core.types.status import StatusTaskType from dotflow.core.types.storage import StorageType +from dotflow.core.types.worflow import WorkflowStatusType + __all__ = [ + "ExecutionModeType", "StatusTaskType", - "StorageType" + "StorageType", + "WorkflowStatusType" ]