automizor 0.3.1__py3-none-any.whl → 0.4.1__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.
- automizor/__init__.py +1 -1
- automizor/exceptions.py +69 -0
- automizor/job/__init__.py +0 -2
- automizor/job/_job.py +15 -18
- automizor/log/__init__.py +95 -0
- automizor/log/_log.py +91 -0
- automizor/storage/__init__.py +101 -2
- automizor/storage/_storage.py +169 -34
- automizor/utils/__init__.py +31 -0
- automizor/vault/__init__.py +35 -6
- automizor/vault/_container.py +3 -2
- automizor/vault/_vault.py +68 -68
- {automizor-0.3.1.dist-info → automizor-0.4.1.dist-info}/METADATA +2 -2
- automizor-0.4.1.dist-info/RECORD +17 -0
- {automizor-0.3.1.dist-info → automizor-0.4.1.dist-info}/WHEEL +1 -1
- automizor/job/_exceptions.py +0 -2
- automizor/job.py +0 -132
- automizor/storage/_exceptions.py +0 -2
- automizor/storage.py +0 -0
- automizor/vault/_exceptions.py +0 -2
- automizor/vault/_secret.py +0 -45
- automizor/vault.py +0 -190
- automizor-0.3.1.dist-info/RECORD +0 -20
- {automizor-0.3.1.dist-info → automizor-0.4.1.dist-info}/LICENSE +0 -0
- {automizor-0.3.1.dist-info → automizor-0.4.1.dist-info}/top_level.txt +0 -0
automizor/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
version = "0.
|
1
|
+
version = "0.4.1"
|
automizor/exceptions.py
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
from requests import Response
|
2
|
+
|
3
|
+
|
4
|
+
class AutomizorError(Exception):
|
5
|
+
def __init__(self, message, error=None):
|
6
|
+
if error:
|
7
|
+
message = f"{message}: {error}"
|
8
|
+
super().__init__(message)
|
9
|
+
|
10
|
+
def __str__(self):
|
11
|
+
return f"{self.args[0]}"
|
12
|
+
|
13
|
+
@classmethod
|
14
|
+
def from_response(cls, response: Response, message: str):
|
15
|
+
_STATUS_EXCEPTION_MAP = {
|
16
|
+
400: InvalidRequest,
|
17
|
+
401: Unauthorized,
|
18
|
+
403: Forbidden,
|
19
|
+
404: NotFound,
|
20
|
+
429: RateLimitExceeded,
|
21
|
+
500: InternalServerError,
|
22
|
+
502: BadGateway,
|
23
|
+
503: ServiceUnavailable,
|
24
|
+
}
|
25
|
+
|
26
|
+
try:
|
27
|
+
error = dict(response.json()).get("detail", "Unknown error.")
|
28
|
+
except Exception: # pylint: disable=broad-except
|
29
|
+
error = response.text
|
30
|
+
|
31
|
+
return _STATUS_EXCEPTION_MAP.get(response.status_code, UnexpectedError)(
|
32
|
+
message, error
|
33
|
+
)
|
34
|
+
|
35
|
+
|
36
|
+
class BadGateway(AutomizorError):
|
37
|
+
pass
|
38
|
+
|
39
|
+
|
40
|
+
class Forbidden(AutomizorError):
|
41
|
+
pass
|
42
|
+
|
43
|
+
|
44
|
+
class InternalServerError(AutomizorError):
|
45
|
+
pass
|
46
|
+
|
47
|
+
|
48
|
+
class InvalidRequest(AutomizorError):
|
49
|
+
pass
|
50
|
+
|
51
|
+
|
52
|
+
class NotFound(AutomizorError):
|
53
|
+
pass
|
54
|
+
|
55
|
+
|
56
|
+
class RateLimitExceeded(AutomizorError):
|
57
|
+
pass
|
58
|
+
|
59
|
+
|
60
|
+
class ServiceUnavailable(AutomizorError):
|
61
|
+
pass
|
62
|
+
|
63
|
+
|
64
|
+
class Unauthorized(AutomizorError):
|
65
|
+
pass
|
66
|
+
|
67
|
+
|
68
|
+
class UnexpectedError(AutomizorError):
|
69
|
+
pass
|
automizor/job/__init__.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from functools import lru_cache
|
2
2
|
|
3
|
-
from ._exceptions import AutomizorJobError
|
4
3
|
from ._job import JSON
|
5
4
|
|
6
5
|
|
@@ -44,7 +43,6 @@ def set_result(name: str, value: JSON):
|
|
44
43
|
|
45
44
|
|
46
45
|
__all__ = [
|
47
|
-
"AutomizorJobError",
|
48
46
|
"get_context",
|
49
47
|
"set_result",
|
50
48
|
]
|
automizor/job/_job.py
CHANGED
@@ -4,7 +4,8 @@ from typing import Dict, List, Union
|
|
4
4
|
|
5
5
|
import requests
|
6
6
|
|
7
|
-
from .
|
7
|
+
from automizor.exceptions import AutomizorError
|
8
|
+
from automizor.utils import get_api_config, get_headers
|
8
9
|
|
9
10
|
JSON = Union[str, int, float, bool, None, Dict[str, "JSON"], List["JSON"]]
|
10
11
|
|
@@ -30,8 +31,7 @@ class Job:
|
|
30
31
|
To use this class effectively, ensure that the following environment variables are
|
31
32
|
set in your environment:
|
32
33
|
|
33
|
-
- ``
|
34
|
-
- ``AUTOMIZOR_API_TOKEN``: The authentication token for API access.
|
34
|
+
- ``AUTOMIZOR_AGENT_TOKEN``: The token for authenticating against the `Automizor API`.
|
35
35
|
- ``AUTOMIZOR_CONTEXT_FILE``: The path to a local file containing job context, if used.
|
36
36
|
- ``AUTOMIZOR_JOB_ID``: The identifier for the current job, used when fetching context via API.
|
37
37
|
|
@@ -50,18 +50,12 @@ class Job:
|
|
50
50
|
"""
|
51
51
|
|
52
52
|
def __init__(self):
|
53
|
-
self.
|
54
|
-
self.
|
55
|
-
self._context_file = os.getenv("AUTOMIZOR_CONTEXT_FILE")
|
56
|
-
self._job_id = os.getenv("AUTOMIZOR_JOB_ID")
|
53
|
+
self._context_file = os.getenv("AUTOMIZOR_CONTEXT_FILE", None)
|
54
|
+
self._job_id = os.getenv("AUTOMIZOR_JOB_ID", None)
|
57
55
|
|
56
|
+
self.url, self.token = get_api_config()
|
58
57
|
self.session = requests.Session()
|
59
|
-
self.session.headers.update(
|
60
|
-
{
|
61
|
-
"Authorization": f"Token {self._api_token}",
|
62
|
-
"Content-Type": "application/json",
|
63
|
-
}
|
64
|
-
)
|
58
|
+
self.session.headers.update(get_headers(self.token))
|
65
59
|
|
66
60
|
def get_context(self) -> dict:
|
67
61
|
"""
|
@@ -73,8 +67,7 @@ class Job:
|
|
73
67
|
This is useful in environments where direct access to the `Automizor API` is
|
74
68
|
not possible or preferred.
|
75
69
|
2. The `Automizor API`, using the job ID (`AUTOMIZOR_JOB_ID`) to fetch the specific
|
76
|
-
job context.
|
77
|
-
(`AUTOMIZOR_API_TOKEN`) settings.
|
70
|
+
job context.
|
78
71
|
|
79
72
|
Returns:
|
80
73
|
A dictionary containing the job context.
|
@@ -118,17 +111,21 @@ class Job:
|
|
118
111
|
data[name] = value
|
119
112
|
|
120
113
|
with open(file_path, "w", encoding="utf-8") as file:
|
121
|
-
json.dump(data, file, ensure_ascii=False
|
114
|
+
json.dump(data, file, ensure_ascii=False)
|
122
115
|
|
123
116
|
def _read_file_context(self) -> dict:
|
124
117
|
with open(self._context_file, "r", encoding="utf-8") as file:
|
125
118
|
return json.load(file)
|
126
119
|
|
127
120
|
def _read_job_context(self) -> dict:
|
128
|
-
url = f"https://{self.
|
121
|
+
url = f"https://{self.url}/api/v1/rpa/job/{self._job_id}/"
|
129
122
|
try:
|
130
123
|
response = self.session.get(url, timeout=10)
|
131
124
|
response.raise_for_status()
|
132
125
|
return response.json().get("context", {})
|
126
|
+
except requests.HTTPError as exc:
|
127
|
+
raise AutomizorError.from_response(
|
128
|
+
exc.response, "Failed to get job context"
|
129
|
+
) from exc
|
133
130
|
except Exception as exc:
|
134
|
-
raise
|
131
|
+
raise AutomizorError("Failed to get job context") from exc
|
@@ -0,0 +1,95 @@
|
|
1
|
+
from functools import lru_cache
|
2
|
+
|
3
|
+
from ._log import JSON
|
4
|
+
|
5
|
+
|
6
|
+
@lru_cache
|
7
|
+
def _get_log():
|
8
|
+
from ._log import Log
|
9
|
+
|
10
|
+
return Log()
|
11
|
+
|
12
|
+
|
13
|
+
def debug(msg: JSON):
|
14
|
+
"""
|
15
|
+
Writes a debug log message with a level of "DEBUG".
|
16
|
+
|
17
|
+
Parameters:
|
18
|
+
msg (JSON): The log message to write. This can be a string, number, boolean, dictionary,
|
19
|
+
list, or None.
|
20
|
+
"""
|
21
|
+
|
22
|
+
_get_log().write_log("DEBUG", msg)
|
23
|
+
|
24
|
+
|
25
|
+
def info(msg: JSON):
|
26
|
+
"""
|
27
|
+
Writes an info log message with a level of "INFO".
|
28
|
+
|
29
|
+
Parameters:
|
30
|
+
msg (JSON): The log message to write. This can be a string, number, boolean, dictionary,
|
31
|
+
list, or None.
|
32
|
+
"""
|
33
|
+
|
34
|
+
_get_log().write_log("INFO", msg)
|
35
|
+
|
36
|
+
|
37
|
+
def warning(msg: JSON):
|
38
|
+
"""
|
39
|
+
Writes a warning log message with a level of "WARNING".
|
40
|
+
|
41
|
+
Parameters:
|
42
|
+
msg (JSON): The log message to write. This can be a string, number, boolean, dictionary,
|
43
|
+
list, or None.
|
44
|
+
"""
|
45
|
+
|
46
|
+
_get_log().write_log("WARNING", msg)
|
47
|
+
|
48
|
+
|
49
|
+
def error(msg: JSON):
|
50
|
+
"""
|
51
|
+
Writes an error log message with a level of "ERROR".
|
52
|
+
|
53
|
+
Parameters:
|
54
|
+
msg (JSON): The log message to write. This can be a string, number, boolean, dictionary,
|
55
|
+
list, or None.
|
56
|
+
"""
|
57
|
+
|
58
|
+
_get_log().write_log("ERROR", msg)
|
59
|
+
|
60
|
+
|
61
|
+
def critical(msg: JSON):
|
62
|
+
"""
|
63
|
+
Writes a critical log message with a level of "CRITICAL".
|
64
|
+
|
65
|
+
Parameters:
|
66
|
+
msg (JSON): The log message to write. This can be a string, number, boolean, dictionary,
|
67
|
+
list, or None.
|
68
|
+
"""
|
69
|
+
|
70
|
+
_get_log().write_log("CRITICAL", msg)
|
71
|
+
|
72
|
+
|
73
|
+
def set_level(level: str):
|
74
|
+
"""
|
75
|
+
Set the log level for filtering log messages.
|
76
|
+
|
77
|
+
Parameters:
|
78
|
+
level (str): The log level to set. Valid log levels are "DEBUG", "INFO",
|
79
|
+
"WARNING", "ERROR", and "CRITICAL".
|
80
|
+
|
81
|
+
Raises:
|
82
|
+
ValueError: If an invalid log level is provided.
|
83
|
+
"""
|
84
|
+
|
85
|
+
_get_log().set_level(level)
|
86
|
+
|
87
|
+
|
88
|
+
__all__ = [
|
89
|
+
"debug",
|
90
|
+
"info",
|
91
|
+
"warning",
|
92
|
+
"error",
|
93
|
+
"critical",
|
94
|
+
"set_level",
|
95
|
+
]
|
automizor/log/_log.py
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from typing import Dict, List, Union
|
4
|
+
|
5
|
+
LOG_LEVELS = {
|
6
|
+
"DEBUG": 1,
|
7
|
+
"INFO": 2,
|
8
|
+
"WARNING": 3,
|
9
|
+
"ERROR": 4,
|
10
|
+
"CRITICAL": 5,
|
11
|
+
}
|
12
|
+
|
13
|
+
JSON = Union[str, int, float, bool, None, Dict[str, "JSON"], List["JSON"]]
|
14
|
+
|
15
|
+
|
16
|
+
class Log:
|
17
|
+
"""
|
18
|
+
`Log` is a class that facilitates logging messages to a local file, which can be
|
19
|
+
useful for debugging and monitoring purposes. It provides a simple interface for
|
20
|
+
writing log messages with different levels, such as "info", "warning", "error", etc.
|
21
|
+
|
22
|
+
The log messages are stored in a JSON file, which can be easily parsed and analyzed
|
23
|
+
later. This class does not provide any log rotation or cleanup mechanisms, so it is
|
24
|
+
recommended to manage log files manually or use external log management tools.
|
25
|
+
|
26
|
+
Example usage:
|
27
|
+
|
28
|
+
.. code-block:: python
|
29
|
+
|
30
|
+
from automizor import log
|
31
|
+
|
32
|
+
# Set log level to INFO
|
33
|
+
log.set_level("INFO")
|
34
|
+
|
35
|
+
# Write a log message
|
36
|
+
log.info({"key": "value"})
|
37
|
+
"""
|
38
|
+
|
39
|
+
def __init__(self):
|
40
|
+
self.level = "INFO"
|
41
|
+
|
42
|
+
def set_level(self, level: str):
|
43
|
+
"""
|
44
|
+
Set the log level for filtering log messages.
|
45
|
+
|
46
|
+
Parameters:
|
47
|
+
level (str): The log level to set. Valid log levels are "DEBUG", "INFO",
|
48
|
+
"WARNING", "ERROR", and "CRITICAL".
|
49
|
+
|
50
|
+
Raises:
|
51
|
+
ValueError: If an invalid log level is provided.
|
52
|
+
"""
|
53
|
+
|
54
|
+
if level not in LOG_LEVELS:
|
55
|
+
raise ValueError(f"Invalid log level: {level}")
|
56
|
+
|
57
|
+
self.level = level
|
58
|
+
|
59
|
+
def write_log(self, level: str, msg: JSON):
|
60
|
+
"""
|
61
|
+
Write a log message with the specified log level.
|
62
|
+
|
63
|
+
Parameters:
|
64
|
+
level (str): The log level of the message. Valid log levels are "DEBUG", "INFO",
|
65
|
+
"WARNING", "ERROR", and "CRITICAL".
|
66
|
+
msg (JSON): The log message to write. This can be a string, number, boolean, dictionary,
|
67
|
+
list, or None.
|
68
|
+
|
69
|
+
Raises:
|
70
|
+
ValueError: If an invalid log level is provided.
|
71
|
+
"""
|
72
|
+
|
73
|
+
if level not in LOG_LEVELS:
|
74
|
+
raise ValueError(f"Invalid log level: {level}")
|
75
|
+
|
76
|
+
if LOG_LEVELS[level] < LOG_LEVELS[self.level]:
|
77
|
+
return
|
78
|
+
|
79
|
+
data = []
|
80
|
+
file_path = "output/log.json"
|
81
|
+
try:
|
82
|
+
if os.path.exists(file_path):
|
83
|
+
with open(file_path, "r", encoding="utf-8") as file:
|
84
|
+
data = json.load(file)
|
85
|
+
except json.JSONDecodeError:
|
86
|
+
pass
|
87
|
+
|
88
|
+
data.append({"level": level, "msg": msg})
|
89
|
+
|
90
|
+
with open(file_path, "w", encoding="utf-8") as file:
|
91
|
+
json.dump(data, file, ensure_ascii=False)
|
automizor/storage/__init__.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
import json
|
2
|
+
import mimetypes
|
1
3
|
from functools import lru_cache
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import List
|
2
6
|
|
3
|
-
from ._exceptions import AutomizorStorageError
|
4
7
|
from ._storage import JSON
|
5
8
|
|
6
9
|
|
@@ -11,6 +14,30 @@ def _get_storage():
|
|
11
14
|
return Storage()
|
12
15
|
|
13
16
|
|
17
|
+
def list_assets() -> List[str]:
|
18
|
+
"""
|
19
|
+
Retrieves a list of all asset names.
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
A list of all asset names.
|
23
|
+
"""
|
24
|
+
|
25
|
+
storage = _get_storage()
|
26
|
+
return storage.list_assets()
|
27
|
+
|
28
|
+
|
29
|
+
def delete_asset(name: str):
|
30
|
+
"""
|
31
|
+
Deletes the specified asset.
|
32
|
+
|
33
|
+
Parameters:
|
34
|
+
name: The name identifier of the asset to delete.
|
35
|
+
"""
|
36
|
+
|
37
|
+
storage = _get_storage()
|
38
|
+
storage.delete_asset(name)
|
39
|
+
|
40
|
+
|
14
41
|
def get_bytes(name: str) -> bytes:
|
15
42
|
"""
|
16
43
|
Retrieves the specified asset as raw bytes.
|
@@ -72,10 +99,82 @@ def get_text(name: str) -> str:
|
|
72
99
|
return storage.get_text(name)
|
73
100
|
|
74
101
|
|
102
|
+
def set_bytes(name: str, data: bytes, content_type="application/octet-stream"):
|
103
|
+
"""
|
104
|
+
Uploads raw bytes as an asset.
|
105
|
+
|
106
|
+
Parameters:
|
107
|
+
name: The name identifier of the asset to upload.
|
108
|
+
data: The raw byte content to upload.
|
109
|
+
content_type: The MIME type of the asset.
|
110
|
+
"""
|
111
|
+
|
112
|
+
storage = _get_storage()
|
113
|
+
storage.set_bytes(name, data, content_type)
|
114
|
+
|
115
|
+
|
116
|
+
def set_file(name: str, path: str, content_type: str = None):
|
117
|
+
"""
|
118
|
+
Uploads a file as an asset.
|
119
|
+
|
120
|
+
Parameters:
|
121
|
+
name: The name identifier of the asset to upload.
|
122
|
+
path: The filesystem path of the file to upload.
|
123
|
+
content_type: The MIME type of the asset.
|
124
|
+
"""
|
125
|
+
|
126
|
+
content = Path(path).read_bytes()
|
127
|
+
if content_type is None:
|
128
|
+
content_type, _ = mimetypes.guess_type(path)
|
129
|
+
if content_type is None:
|
130
|
+
content_type = "application/octet-stream"
|
131
|
+
|
132
|
+
storage = _get_storage()
|
133
|
+
storage.set_bytes(name, content, content_type)
|
134
|
+
|
135
|
+
|
136
|
+
def set_json(name: str, value: JSON, **kwargs):
|
137
|
+
"""
|
138
|
+
Uploads JSON data as an asset.
|
139
|
+
|
140
|
+
Parameters:
|
141
|
+
name: The name identifier of the asset to upload.
|
142
|
+
value: The JSON data to upload.
|
143
|
+
kwargs: Additional keyword arguments to pass to json.dumps.
|
144
|
+
"""
|
145
|
+
|
146
|
+
content = json.dumps(value, **kwargs).encode("utf-8")
|
147
|
+
content_type = "application/json"
|
148
|
+
|
149
|
+
storage = _get_storage()
|
150
|
+
storage.set_bytes(name, content, content_type)
|
151
|
+
|
152
|
+
|
153
|
+
def set_text(name: str, text: str):
|
154
|
+
"""
|
155
|
+
Uploads text content as an asset.
|
156
|
+
|
157
|
+
Parameters:
|
158
|
+
name: The name identifier of the asset to upload.
|
159
|
+
text: The text content to upload.
|
160
|
+
"""
|
161
|
+
|
162
|
+
content = text.encode("utf-8")
|
163
|
+
content_type = "text/plain"
|
164
|
+
|
165
|
+
storage = _get_storage()
|
166
|
+
storage.set_bytes(name, content, content_type)
|
167
|
+
|
168
|
+
|
75
169
|
__all__ = [
|
76
|
-
"
|
170
|
+
"list_assets",
|
171
|
+
"delete_asset",
|
77
172
|
"get_bytes",
|
78
173
|
"get_file",
|
79
174
|
"get_json",
|
80
175
|
"get_text",
|
176
|
+
"set_bytes",
|
177
|
+
"set_file",
|
178
|
+
"set_json",
|
179
|
+
"set_text",
|
81
180
|
]
|