rpa-suite 1.6.1__py3-none-any.whl → 1.6.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rpa_suite/__init__.py +1 -1
- rpa_suite/core/__init__.py +5 -1
- rpa_suite/core/artemis.py +445 -0
- rpa_suite/core/asyncrun.py +15 -8
- rpa_suite/core/browser.py +44 -41
- rpa_suite/core/clock.py +46 -13
- rpa_suite/core/date.py +41 -16
- rpa_suite/core/dir.py +29 -72
- rpa_suite/core/email.py +26 -15
- rpa_suite/core/file.py +46 -43
- rpa_suite/core/iris.py +146 -70
- rpa_suite/core/log.py +134 -46
- rpa_suite/core/parallel.py +185 -182
- rpa_suite/core/print.py +119 -96
- rpa_suite/core/regex.py +26 -26
- rpa_suite/core/validate.py +20 -76
- rpa_suite/functions/__init__.py +1 -1
- rpa_suite/suite.py +13 -1
- rpa_suite/utils/__init__.py +1 -1
- rpa_suite/utils/system.py +64 -61
- {rpa_suite-1.6.1.dist-info → rpa_suite-1.6.3.dist-info}/METADATA +8 -18
- rpa_suite-1.6.3.dist-info/RECORD +27 -0
- rpa_suite-1.6.1.dist-info/RECORD +0 -26
- {rpa_suite-1.6.1.dist-info → rpa_suite-1.6.3.dist-info}/WHEEL +0 -0
- {rpa_suite-1.6.1.dist-info → rpa_suite-1.6.3.dist-info}/licenses/LICENSE +0 -0
- {rpa_suite-1.6.1.dist-info → rpa_suite-1.6.3.dist-info}/top_level.txt +0 -0
rpa_suite/__init__.py
CHANGED
@@ -66,7 +66,7 @@ Módulos disponíveis:
|
|
66
66
|
``Iris``: Objeto Iris Automação de funções para converter documentos com OCR + IA baseado em ``docling``
|
67
67
|
"""
|
68
68
|
|
69
|
-
__version__ = "1.6.
|
69
|
+
__version__ = "1.6.2"
|
70
70
|
|
71
71
|
# allows importing the rpa_suite module without the package name
|
72
72
|
from .suite import rpa
|
rpa_suite/core/__init__.py
CHANGED
@@ -48,4 +48,8 @@ if importlib.util.find_spec("selenium") and importlib.util.find_spec("webdriver_
|
|
48
48
|
if importlib.util.find_spec("docling"):
|
49
49
|
from .iris import Iris
|
50
50
|
|
51
|
-
|
51
|
+
# from .iris import Artemis
|
52
|
+
if importlib.util.find_spec("pyautogui"):
|
53
|
+
from .artemis import Artemis
|
54
|
+
|
55
|
+
__version__ = "1.6.2"
|
@@ -0,0 +1,445 @@
|
|
1
|
+
# rpa_suite/core/artemis.py
|
2
|
+
|
3
|
+
# imports standard
|
4
|
+
from enum import Enum
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Tuple, Union, Optional
|
7
|
+
from time import time
|
8
|
+
from time import sleep
|
9
|
+
|
10
|
+
# imports external
|
11
|
+
import pyautogui as artemis_engine
|
12
|
+
|
13
|
+
# imports internos
|
14
|
+
# imports internal
|
15
|
+
from rpa_suite.functions._printer import alert_print, success_print
|
16
|
+
|
17
|
+
# constantes
|
18
|
+
OPENCV_AVAILABLE_FROM_ARTEMIS = False
|
19
|
+
|
20
|
+
|
21
|
+
class ArtemisError(Exception):
|
22
|
+
"""Custom exception for Artemis errors."""
|
23
|
+
def __init__(self, message):
|
24
|
+
super().__init__(f'ArtemisError: {message}')
|
25
|
+
|
26
|
+
|
27
|
+
class Artemis:
|
28
|
+
"""
|
29
|
+
Artemis:
|
30
|
+
----------
|
31
|
+
Intelligent desktop automation based on computer vision.
|
32
|
+
|
33
|
+
Specialized in locating and interacting with visual elements
|
34
|
+
in the graphical interface, optimized for robust RPA automation.
|
35
|
+
"""
|
36
|
+
|
37
|
+
def __init__(self):
|
38
|
+
"""
|
39
|
+
Artemis:
|
40
|
+
----------
|
41
|
+
Intelligent desktop automation based on computer vision.
|
42
|
+
|
43
|
+
Specialized in locating and interacting with visual elements
|
44
|
+
in the graphical interface, optimized for robust RPA automation.
|
45
|
+
"""
|
46
|
+
artemis_engine.FAILSAFE = True # Move mouse to top-left corner to stop
|
47
|
+
artemis_engine.PAUSE = 0.1 # Default pause between commands
|
48
|
+
global OPENCV_AVAILABLE_FROM_ARTEMIS
|
49
|
+
OPENCV_AVAILABLE_FROM_ARTEMIS = self.__check_opencv_availability()
|
50
|
+
|
51
|
+
# pylint: disable=import-outside-toplevel
|
52
|
+
def __check_opencv_availability() -> bool:
|
53
|
+
"""Checks if OpenCV is available in the system."""
|
54
|
+
try:
|
55
|
+
# pylint: disable=import-outside-toplevel
|
56
|
+
import cv2 # pylint: disable=unused-import
|
57
|
+
|
58
|
+
return True
|
59
|
+
except ImportError:
|
60
|
+
return False
|
61
|
+
|
62
|
+
def click_image(
|
63
|
+
self,
|
64
|
+
image_label: str,
|
65
|
+
images_folder: Union[str, Path] = "resource",
|
66
|
+
confidence: float = 0.78,
|
67
|
+
timeout: float = 10.0,
|
68
|
+
click_center: bool = True,
|
69
|
+
click_button: str = "left",
|
70
|
+
double_click: bool = False,
|
71
|
+
search_interval: float = 0.5,
|
72
|
+
region: Optional[Tuple[int, int, int, int]] = None,
|
73
|
+
grayscale: bool = True,
|
74
|
+
display_details: bool = False,
|
75
|
+
verbose: bool = False
|
76
|
+
) -> Union[Tuple[int, int], bool]:
|
77
|
+
"""
|
78
|
+
Locates an image on the screen and clicks on it.
|
79
|
+
|
80
|
+
This function searches for a specific image on the screen using PyAutoGUI
|
81
|
+
and performs a click at the found position. Implements search with timeout
|
82
|
+
and different confidence levels for better accuracy (when OpenCV is available).
|
83
|
+
|
84
|
+
Args:
|
85
|
+
image_label (str): Image file name (with or without extension).
|
86
|
+
Ex: 'ok_button' or 'ok_button.png'
|
87
|
+
images_folder (Union[str, Path], optional): Path to images folder.
|
88
|
+
Default: "resource"
|
89
|
+
confidence (float, optional): Confidence level for location (0.0-1.0).
|
90
|
+
Requires OpenCV installed. If not available, will be ignored.
|
91
|
+
High values = higher precision, lower tolerance.
|
92
|
+
Default: 0.78
|
93
|
+
timeout (float, optional): Time limit in seconds for search.
|
94
|
+
Default: 10.0
|
95
|
+
click_center (bool, optional): If True, clicks at the center of the image.
|
96
|
+
If False, clicks at the top-left corner.
|
97
|
+
Default: True
|
98
|
+
click_button (str, optional): Mouse button ('left', 'right', 'middle').
|
99
|
+
Default: 'left'
|
100
|
+
double_click (bool, optional): If True, performs double click.
|
101
|
+
Default: False
|
102
|
+
search_interval (float, optional): Interval between search attempts.
|
103
|
+
Default: 0.5 seconds
|
104
|
+
region (Optional[Tuple[int, int, int, int]], optional): Screen region to search.
|
105
|
+
Format: (x, y, width, height)
|
106
|
+
Default: None (entire screen)
|
107
|
+
grayscale (bool, optional): If True, searches in grayscale (faster).
|
108
|
+
Default: True
|
109
|
+
display_details (bool, optional): If True, displays details.
|
110
|
+
Default: False
|
111
|
+
verbose (bool, optional): If True, displays verbose output.
|
112
|
+
Default: False
|
113
|
+
|
114
|
+
Returns:
|
115
|
+
Union[Tuple[int, int], bool]: Coordinates (x, y) of the image center if found
|
116
|
+
or False if not found within timeout.
|
117
|
+
|
118
|
+
Raises:
|
119
|
+
ImageClickError: If there's an error in configuration or execution.
|
120
|
+
FileNotFoundError: If the image file is not found.
|
121
|
+
ValueError: If parameters are invalid.
|
122
|
+
|
123
|
+
Note:
|
124
|
+
To use the confidence parameter, install OpenCV: pip install opencv-python
|
125
|
+
Without OpenCV, the function will work with exact pixel matching.
|
126
|
+
|
127
|
+
Example:
|
128
|
+
>>> # Search and click on a button
|
129
|
+
>>> position = click_image('save_button.png', confidence=0.9, timeout=5.0)
|
130
|
+
>>> if position:
|
131
|
+
... print(f"Clicked at position: {position}")
|
132
|
+
... else:
|
133
|
+
... print("Image not found")
|
134
|
+
|
135
|
+
>>> # Search in specific screen region
|
136
|
+
>>> region_result = click_image(
|
137
|
+
... 'menu_icon',
|
138
|
+
... region=(0, 0, 500, 300), # Search only in top-left corner
|
139
|
+
... confidence=0.7
|
140
|
+
... )
|
141
|
+
"""
|
142
|
+
|
143
|
+
# Parameter validation
|
144
|
+
self._validate_parameters(confidence, timeout, search_interval, click_button, region)
|
145
|
+
|
146
|
+
# Resolve full image path
|
147
|
+
image_path = self._resolve_image_path(image_label, images_folder)
|
148
|
+
|
149
|
+
# Warning if confidence will be ignored
|
150
|
+
if confidence != 0.8 and not OPENCV_AVAILABLE_FROM_ARTEMIS:
|
151
|
+
if verbose: alert_print(f"Parameter confidence={confidence} will be ignored. " + "Install OpenCV: pip install opencv-python")
|
152
|
+
|
153
|
+
if verbose: print(f"Starting image search: {image_path}")
|
154
|
+
if display_details:
|
155
|
+
if verbose: print(
|
156
|
+
f"Settings: confidence={'N/A' if not OPENCV_AVAILABLE_FROM_ARTEMIS else confidence}, "
|
157
|
+
+ f"timeout={timeout}s, region={region}"
|
158
|
+
)
|
159
|
+
|
160
|
+
# Temporary PyAutoGUI settings
|
161
|
+
original_pause = artemis_engine.PAUSE
|
162
|
+
artemis_engine.PAUSE = 0.05 # Reduce pause for faster search
|
163
|
+
|
164
|
+
try:
|
165
|
+
# Execute search with timeout
|
166
|
+
position = self._search_image_with_timeout(
|
167
|
+
image_path=image_path,
|
168
|
+
confidence=confidence,
|
169
|
+
timeout=timeout,
|
170
|
+
search_interval=search_interval,
|
171
|
+
region=region,
|
172
|
+
grayscale=grayscale,
|
173
|
+
)
|
174
|
+
|
175
|
+
if not position:
|
176
|
+
if verbose: alert_print(f"Image not found after {timeout}s: {image_path.name}")
|
177
|
+
return False
|
178
|
+
|
179
|
+
# Calculate click position
|
180
|
+
click_position = self._calculate_click_position(position, click_center)
|
181
|
+
|
182
|
+
# Perform click
|
183
|
+
self._perform_click(click_position, click_button, double_click)
|
184
|
+
|
185
|
+
# print(f"Click performed!")
|
186
|
+
return click_position
|
187
|
+
|
188
|
+
except Exception as e:
|
189
|
+
error_msg = f"Error processing image click {image_path.name}: {str(e)}"
|
190
|
+
raise ArtemisError(error_msg) from e
|
191
|
+
|
192
|
+
finally:
|
193
|
+
# Restore original settings
|
194
|
+
artemis_engine.PAUSE = original_pause
|
195
|
+
|
196
|
+
|
197
|
+
def find_image_position(
|
198
|
+
self,
|
199
|
+
image_label: str,
|
200
|
+
images_folder: Union[str, Path] = "resource",
|
201
|
+
confidence: float = 0.8,
|
202
|
+
timeout: float = 5.0,
|
203
|
+
region: Optional[Tuple[int, int, int, int]] = None,
|
204
|
+
grayscale: bool = False,
|
205
|
+
verbose: bool = False,
|
206
|
+
) -> Union[Tuple[int, int], bool]:
|
207
|
+
"""
|
208
|
+
Finds the position of an image on the screen without clicking.
|
209
|
+
|
210
|
+
Utility function to only locate an image without performing a click.
|
211
|
+
Useful for checking element presence or getting coordinates.
|
212
|
+
|
213
|
+
Args:
|
214
|
+
image_label (str): Image file name.
|
215
|
+
images_folder (Union[str, Path], optional): Images folder. Default: "images"
|
216
|
+
confidence (float, optional): Confidence level. Default: 0.8
|
217
|
+
timeout (float, optional): Timeout in seconds. Default: 5.0
|
218
|
+
region (Optional[Tuple], optional): Search region. Default: None
|
219
|
+
grayscale (bool, optional): Search in grayscale. Default: False
|
220
|
+
|
221
|
+
Returns:
|
222
|
+
Union[Tuple[int, int], bool]: Image center coordinates or False.
|
223
|
+
"""
|
224
|
+
|
225
|
+
self._validate_parameters(confidence, timeout, 0.5, "left", region)
|
226
|
+
image_path = self._resolve_image_path(image_label, images_folder)
|
227
|
+
|
228
|
+
try:
|
229
|
+
position = self._search_image_with_timeout(
|
230
|
+
image_path=image_path,
|
231
|
+
confidence=confidence,
|
232
|
+
timeout=timeout,
|
233
|
+
search_interval=0.5,
|
234
|
+
region=region,
|
235
|
+
grayscale=grayscale,
|
236
|
+
)
|
237
|
+
|
238
|
+
if position:
|
239
|
+
return self._calculate_click_position(position, click_center=True)
|
240
|
+
return False
|
241
|
+
|
242
|
+
except Exception as e:
|
243
|
+
error_msg = f"Error searching for image {image_path.name}: {str(e)}"
|
244
|
+
raise ArtemisError(error_msg) from e
|
245
|
+
|
246
|
+
def _validate_parameters(
|
247
|
+
self,
|
248
|
+
confidence: float,
|
249
|
+
timeout: float,
|
250
|
+
search_interval: float,
|
251
|
+
click_button: str,
|
252
|
+
region: Optional[Tuple[int, int, int, int]],
|
253
|
+
) -> None:
|
254
|
+
"""Validates function input parameters."""
|
255
|
+
|
256
|
+
if not 0.0 <= confidence <= 1.0:
|
257
|
+
raise ValueError(f"Confidence must be between 0.0 and 1.0, received: {confidence}")
|
258
|
+
|
259
|
+
if timeout <= 0:
|
260
|
+
raise ValueError(f"Timeout must be positive, received: {timeout}")
|
261
|
+
|
262
|
+
if search_interval <= 0:
|
263
|
+
raise ValueError(f"Search interval must be positive, received: {search_interval}")
|
264
|
+
|
265
|
+
if click_button not in ["left", "right", "middle"]:
|
266
|
+
raise ValueError(f"Click button must be 'left', 'right' or 'middle', received: {click_button}")
|
267
|
+
|
268
|
+
if region is not None:
|
269
|
+
if not isinstance(region, (tuple, list)) or len(region) != 4:
|
270
|
+
raise ValueError("Region must be a tuple with 4 elements: (x, y, width, height)")
|
271
|
+
|
272
|
+
if any(not isinstance(val, int) or val < 0 for val in region):
|
273
|
+
raise ValueError("All region values must be non-negative integers")
|
274
|
+
|
275
|
+
|
276
|
+
def _resolve_image_path(image_label: str, images_folder: Union[str, Path]) -> Path:
|
277
|
+
"""Resolves the full path to the image file."""
|
278
|
+
|
279
|
+
folder_path = Path(images_folder)
|
280
|
+
|
281
|
+
# If image_label already has extension, use directly
|
282
|
+
if "." in image_label:
|
283
|
+
image_path = folder_path / image_label
|
284
|
+
else:
|
285
|
+
# Try different common extensions
|
286
|
+
extensions = [".png", ".jpg", ".jpeg", ".bmp", ".gif"]
|
287
|
+
image_path = None
|
288
|
+
|
289
|
+
for ext in extensions:
|
290
|
+
candidate = folder_path / f"{image_label}{ext}"
|
291
|
+
if candidate.exists():
|
292
|
+
image_path = candidate
|
293
|
+
break
|
294
|
+
|
295
|
+
if not image_path:
|
296
|
+
# If not found, use .png as default for clearer error
|
297
|
+
image_path = folder_path / f"{image_label}.png"
|
298
|
+
|
299
|
+
if not image_path.exists():
|
300
|
+
raise FileNotFoundError(f"Image file not found: {image_path}")
|
301
|
+
|
302
|
+
return image_path
|
303
|
+
|
304
|
+
|
305
|
+
def _search_image_with_timeout(
|
306
|
+
self,
|
307
|
+
image_path: Path,
|
308
|
+
confidence: float,
|
309
|
+
timeout: float,
|
310
|
+
search_interval: float,
|
311
|
+
region: Optional[Tuple[int, int, int, int]],
|
312
|
+
grayscale: bool,
|
313
|
+
verbose: bool = False,
|
314
|
+
) -> Optional[any]:
|
315
|
+
"""Searches for image on screen with timeout, considering OpenCV availability."""
|
316
|
+
|
317
|
+
start_time = time()
|
318
|
+
attempts = 0
|
319
|
+
|
320
|
+
while time() - start_time < timeout:
|
321
|
+
attempts += 1
|
322
|
+
|
323
|
+
try:
|
324
|
+
# Build arguments for locateOnScreen based on OpenCV availability
|
325
|
+
locate_args = {"image": str(image_path), "region": region, "grayscale": grayscale}
|
326
|
+
|
327
|
+
# Add confidence only if OpenCV is available
|
328
|
+
if OPENCV_AVAILABLE_FROM_ARTEMIS:
|
329
|
+
locate_args["confidence"] = confidence
|
330
|
+
|
331
|
+
# Search for image on screen
|
332
|
+
location = artemis_engine.locateOnScreen(**locate_args)
|
333
|
+
|
334
|
+
if location:
|
335
|
+
if verbose: print(f"Image found on attempt {attempts}.")
|
336
|
+
return location
|
337
|
+
|
338
|
+
except artemis_engine.ImageNotFoundException:
|
339
|
+
# Image not found in this attempt
|
340
|
+
pass
|
341
|
+
except TypeError as e:
|
342
|
+
if "confidence" in str(e):
|
343
|
+
# Fallback if confidence error still occurs
|
344
|
+
alert_print("Confidence error detected, trying without parameter...")
|
345
|
+
try:
|
346
|
+
location = artemis_engine.locateOnScreen(str(image_path), region=region, grayscale=grayscale)
|
347
|
+
if location:
|
348
|
+
if verbose: print(f"Image found on attempt {attempts} (without confidence): {location}")
|
349
|
+
return location
|
350
|
+
except Exception as error:
|
351
|
+
ArtemisError(f"Failed attempt without confidence: {error}.") from e
|
352
|
+
else:
|
353
|
+
ArtemisError(f"Error during image search (attempt {attempts}): {e}") from e
|
354
|
+
except Exception as e:
|
355
|
+
ArtemisError(f"Error during image search (attempt {attempts}): {e}") from e
|
356
|
+
|
357
|
+
# Wait before next attempt
|
358
|
+
if time() - start_time < timeout:
|
359
|
+
sleep(search_interval)
|
360
|
+
|
361
|
+
if verbose:
|
362
|
+
success_print(f"Search completed after {attempts} attempts in {timeout}s")
|
363
|
+
return None
|
364
|
+
|
365
|
+
|
366
|
+
def _calculate_click_position(image_box: any, click_center: bool) -> Tuple[int, int]:
|
367
|
+
"""Calculates the exact click position based on image location."""
|
368
|
+
|
369
|
+
if click_center:
|
370
|
+
# Click at image center
|
371
|
+
center_x = image_box.left + image_box.width // 2
|
372
|
+
center_y = image_box.top + image_box.height // 2
|
373
|
+
return (center_x, center_y)
|
374
|
+
# Click at top-left corner
|
375
|
+
return (image_box.left, image_box.top)
|
376
|
+
|
377
|
+
|
378
|
+
def _perform_click(position: Tuple[int, int], click_button: str, double_click: bool, verbose: bool = False) -> None:
|
379
|
+
"""Performs click at specified position."""
|
380
|
+
|
381
|
+
try:
|
382
|
+
x, y = position
|
383
|
+
|
384
|
+
# Move mouse to position (optional, but helps with visualization)
|
385
|
+
artemis_engine.moveTo(x, y, duration=0.1)
|
386
|
+
|
387
|
+
# Perform click
|
388
|
+
if double_click:
|
389
|
+
artemis_engine.doubleClick(x, y, button=click_button)
|
390
|
+
if verbose:
|
391
|
+
success_print(f"Double click performed at ({x}, {y}) with {click_button} button.")
|
392
|
+
else:
|
393
|
+
artemis_engine.click(x, y, button=click_button)
|
394
|
+
if verbose:
|
395
|
+
success_print(f"Click performed at ({x}, {y}) with {click_button} button.")
|
396
|
+
except Exception as e:
|
397
|
+
ArtemisError(f"Error performing click: {str(e)}.") from e
|
398
|
+
|
399
|
+
|
400
|
+
# Convenience functions for specific cases
|
401
|
+
def wait_and_click(
|
402
|
+
self,
|
403
|
+
image_label: str,
|
404
|
+
images_folder: Union[str, Path] = "resource",
|
405
|
+
confidence: float = 0.8,
|
406
|
+
timeout: float = 30.0
|
407
|
+
) -> Union[Tuple[int, int], bool]:
|
408
|
+
|
409
|
+
"""
|
410
|
+
Waits for an image to appear on screen and clicks on it.
|
411
|
+
|
412
|
+
Convenience function for waiting for elements that may take time to appear.
|
413
|
+
"""
|
414
|
+
try:
|
415
|
+
return self.click_image(
|
416
|
+
image_label=image_label,
|
417
|
+
images_folder=images_folder,
|
418
|
+
confidence=confidence,
|
419
|
+
timeout=timeout,
|
420
|
+
search_interval=1.0, # Longer interval for waiting
|
421
|
+
)
|
422
|
+
except Exception as e:
|
423
|
+
ArtemisError(f"Error waiting and clicking: {str(e)}.") from e
|
424
|
+
|
425
|
+
def quick_click(self,
|
426
|
+
image_label: str,
|
427
|
+
images_folder: Union[str, Path] = "resource"
|
428
|
+
) -> Union[Tuple[int, int], bool]:
|
429
|
+
"""
|
430
|
+
Quick click with optimized default settings.
|
431
|
+
|
432
|
+
Convenience function for fast clicks with balanced settings.
|
433
|
+
"""
|
434
|
+
|
435
|
+
try:
|
436
|
+
return self.click_image(
|
437
|
+
image_label=image_label,
|
438
|
+
images_folder=images_folder,
|
439
|
+
confidence=0.8,
|
440
|
+
timeout=3.0,
|
441
|
+
search_interval=0.2,
|
442
|
+
grayscale=True, # Faster
|
443
|
+
)
|
444
|
+
except Exception as e:
|
445
|
+
ArtemisError(f"Error performing quick click: {str(e)}.") from e
|
rpa_suite/core/asyncrun.py
CHANGED
@@ -10,6 +10,10 @@ from functools import wraps
|
|
10
10
|
|
11
11
|
T = TypeVar("T")
|
12
12
|
|
13
|
+
class AsyncRunnerError(Exception):
|
14
|
+
"""Custom exception for AsyncRunner errors."""
|
15
|
+
def __init__(self, message):
|
16
|
+
super().__init__(f'AsyncRunnerError: {message}')
|
13
17
|
|
14
18
|
class AsyncRunner(Generic[T]):
|
15
19
|
"""
|
@@ -19,7 +23,7 @@ class AsyncRunner(Generic[T]):
|
|
19
23
|
Optimized for I/O bound operations (network, files, etc).
|
20
24
|
"""
|
21
25
|
|
22
|
-
def __init__(self):
|
26
|
+
def __init__(self) -> None:
|
23
27
|
"""Start AsyncRunner."""
|
24
28
|
self._task = None
|
25
29
|
self._start_time = None
|
@@ -45,7 +49,7 @@ class AsyncRunner(Generic[T]):
|
|
45
49
|
|
46
50
|
return wrapper
|
47
51
|
|
48
|
-
async def _execute_function(self, function, args, kwargs):
|
52
|
+
async def _execute_function(self, function, args, kwargs) -> None:
|
49
53
|
"""
|
50
54
|
Executes the function and manages results/errors.
|
51
55
|
|
@@ -80,14 +84,17 @@ class AsyncRunner(Generic[T]):
|
|
80
84
|
Returns:
|
81
85
|
self: Returns the instance itself.
|
82
86
|
"""
|
83
|
-
|
84
|
-
|
87
|
+
try:
|
88
|
+
self._result.clear()
|
89
|
+
self._start_time = time.time()
|
85
90
|
|
86
|
-
|
87
|
-
|
88
|
-
|
91
|
+
# Creates and schedules the asynchronous task
|
92
|
+
loop = asyncio.get_event_loop()
|
93
|
+
self._task = loop.create_task(self._execute_function(function, args, kwargs))
|
89
94
|
|
90
|
-
|
95
|
+
return self
|
96
|
+
except Exception as e:
|
97
|
+
AsyncRunnerError(f"Erro ao iniciar a execução da função: {str(e)}.") from e
|
91
98
|
|
92
99
|
def is_running(self) -> bool:
|
93
100
|
"""
|