osbot-utils 1.16.0__py3-none-any.whl → 1.20.0__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.
- osbot_utils/base_classes/Kwargs_To_Self.py +3 -54
- osbot_utils/base_classes/Type_Safe.py +6 -0
- osbot_utils/context_managers/disable_root_loggers.py +30 -0
- osbot_utils/helpers/CFormat.py +147 -0
- osbot_utils/helpers/CPrint.py +5 -50
- osbot_utils/helpers/Print_Table.py +1 -1
- osbot_utils/helpers/cache_requests/Cache__Requests__Actions.py +23 -0
- osbot_utils/helpers/cache_requests/Cache__Requests__Config.py +32 -0
- osbot_utils/helpers/cache_requests/Cache__Requests__Data.py +105 -0
- osbot_utils/helpers/cache_requests/Cache__Requests__Invoke.py +55 -0
- osbot_utils/helpers/cache_requests/Cache__Requests__Row.py +64 -0
- osbot_utils/helpers/cache_requests/Cache__Requests__Table.py +16 -0
- osbot_utils/helpers/cache_requests/__init__.py +0 -0
- osbot_utils/helpers/cache_requests/flows/flow__Cache__Requests.py +11 -0
- osbot_utils/helpers/flows/Flow.py +145 -0
- osbot_utils/helpers/flows/Task.py +18 -0
- osbot_utils/helpers/flows/__init__.py +0 -0
- osbot_utils/helpers/sqlite/{domains/schemas → cache}/Schema__Table__Requests.py +6 -4
- osbot_utils/helpers/sqlite/cache/Sqlite__Cache__Requests.py +104 -0
- osbot_utils/helpers/sqlite/{domains → cache}/Sqlite__Cache__Requests__Patch.py +10 -8
- osbot_utils/helpers/sqlite/cache/Sqlite__Cache__Requests__Sqlite.py +18 -0
- osbot_utils/helpers/sqlite/cache/Sqlite__Cache__Requests__Table.py +48 -0
- osbot_utils/helpers/sqlite/{domains → cache}/Sqlite__DB__Requests.py +8 -7
- osbot_utils/helpers/sqlite/cache/TestCase__Sqlite__Cache__Requests.py +35 -0
- osbot_utils/helpers/sqlite/cache/__init__.py +0 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Local.py +6 -2
- osbot_utils/helpers/{SCP.py → ssh/SCP.py} +23 -20
- osbot_utils/helpers/ssh/SSH.py +30 -0
- osbot_utils/helpers/ssh/SSH__Cache__Requests.py +66 -0
- osbot_utils/helpers/ssh/SSH__Execute.py +158 -0
- osbot_utils/helpers/ssh/SSH__Health_Check.py +49 -0
- osbot_utils/helpers/ssh/SSH__Linux.py +106 -0
- osbot_utils/helpers/ssh/SSH__Python.py +48 -0
- osbot_utils/helpers/ssh/TestCase__SSH.py +50 -0
- osbot_utils/helpers/ssh/__init__.py +0 -0
- osbot_utils/helpers/trace/Trace_Call__Print_Lines.py +1 -1
- osbot_utils/testing/Logging.py +15 -5
- osbot_utils/testing/Pytest.py +18 -0
- osbot_utils/utils/Env.py +29 -9
- osbot_utils/utils/Json.py +2 -9
- osbot_utils/utils/Misc.py +17 -16
- osbot_utils/utils/Objects.py +17 -7
- osbot_utils/utils/Python_Logger.py +54 -38
- osbot_utils/utils/Str.py +20 -3
- osbot_utils/utils/Toml.py +33 -0
- osbot_utils/version +1 -1
- {osbot_utils-1.16.0.dist-info → osbot_utils-1.20.0.dist-info}/METADATA +2 -2
- {osbot_utils-1.16.0.dist-info → osbot_utils-1.20.0.dist-info}/RECORD +50 -23
- osbot_utils/helpers/SSH.py +0 -151
- osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests.py +0 -214
- {osbot_utils-1.16.0.dist-info → osbot_utils-1.20.0.dist-info}/LICENSE +0 -0
- {osbot_utils-1.16.0.dist-info → osbot_utils-1.20.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
from osbot_utils.base_classes.Type_Safe import Type_Safe
|
2
|
+
from osbot_utils.context_managers.capture_duration import capture_duration
|
3
|
+
from osbot_utils.decorators.lists.group_by import group_by
|
4
|
+
from osbot_utils.decorators.lists.index_by import index_by
|
5
|
+
from osbot_utils.utils.Dev import pprint
|
6
|
+
from osbot_utils.utils.Env import get_env
|
7
|
+
from osbot_utils.utils.Misc import str_to_int, str_to_bool
|
8
|
+
from osbot_utils.utils.Process import start_process, run_process
|
9
|
+
from osbot_utils.utils.Status import status_error
|
10
|
+
|
11
|
+
ENV_VAR__SSH__HOST = 'SSH__HOST'
|
12
|
+
ENV_VAR__SSH__PORT = 'SSH__PORT'
|
13
|
+
ENV_VAR__SSH__KEY_FILE = 'SSH__KEY_FILE'
|
14
|
+
ENV_VAR__SSH__USER = 'SSH__USER'
|
15
|
+
ENV_VAR__SSH__STRICT_HOST_CHECK = 'SSH__STRICT_HOST_CHECK'
|
16
|
+
|
17
|
+
|
18
|
+
class SSH__Execute(Type_Safe):
|
19
|
+
ssh_host : str
|
20
|
+
ssh_port : int = 22
|
21
|
+
ssh_key_file : str
|
22
|
+
ssh_key_user : str
|
23
|
+
strict_host_check : bool = False
|
24
|
+
|
25
|
+
# execution & other commands # todo refactor into separate class
|
26
|
+
def exec(self, command):
|
27
|
+
return self.execute_command__return_stdout(command)
|
28
|
+
|
29
|
+
def exec__print(self, command):
|
30
|
+
result = self.execute_command__return_stdout(command)
|
31
|
+
self.print_header_for_command(command)
|
32
|
+
print(result)
|
33
|
+
return result
|
34
|
+
|
35
|
+
def execute_command(self, command):
|
36
|
+
if self.ssh_setup_ok() and command:
|
37
|
+
ssh_args = self.execute_command_args(command)
|
38
|
+
with capture_duration() as duration:
|
39
|
+
result = start_process("ssh", ssh_args) # execute command using subprocess.run(...)
|
40
|
+
result['duration'] = duration.data()
|
41
|
+
return result
|
42
|
+
return status_error(error='in execute_command not all required vars were setup')
|
43
|
+
|
44
|
+
def execute_command__print(self, command):
|
45
|
+
self.print_header_for_command(command)
|
46
|
+
result = self.execute_command(command)
|
47
|
+
pprint(result)
|
48
|
+
return result
|
49
|
+
|
50
|
+
def execute_ssh_args(self):
|
51
|
+
ssh_args = []
|
52
|
+
if self.ssh_port:
|
53
|
+
ssh_args += ['-p', str(self.ssh_port)]
|
54
|
+
if self.strict_host_check is False:
|
55
|
+
ssh_args += ['-o', 'StrictHostKeyChecking=no']
|
56
|
+
if self.ssh_key_file:
|
57
|
+
ssh_args += ['-i', self.ssh_key_file]
|
58
|
+
return ssh_args
|
59
|
+
|
60
|
+
def execute_command_args(self, command=None):
|
61
|
+
ssh_args = self.execute_ssh_args()
|
62
|
+
if self.ssh_host:
|
63
|
+
ssh_args += [self.execute_command_target_host()]
|
64
|
+
if command:
|
65
|
+
ssh_args += [command]
|
66
|
+
return ssh_args
|
67
|
+
|
68
|
+
def execute_command_target_host(self):
|
69
|
+
if self.ssh_key_user:
|
70
|
+
return f'{self.ssh_key_user}@{self.ssh_host}'
|
71
|
+
else:
|
72
|
+
return f'{self.ssh_host}'
|
73
|
+
|
74
|
+
def execute_command__return_stdout(self, command):
|
75
|
+
return self.execute_command(command).get('stdout', '').strip()
|
76
|
+
|
77
|
+
def execute_command__return_stderr(self, command):
|
78
|
+
return self.execute_command(command).get('stderr', '').strip()
|
79
|
+
|
80
|
+
@index_by
|
81
|
+
@group_by
|
82
|
+
def execute_command__return_dict(self, command):
|
83
|
+
stdout = self.execute_command(command).get('stdout').strip()
|
84
|
+
return self.parse_stdout_to_dict(stdout)
|
85
|
+
|
86
|
+
@index_by
|
87
|
+
@group_by
|
88
|
+
def execute_command__return_list(self, command):
|
89
|
+
stdout = self.execute_command(command).get('stdout').strip()
|
90
|
+
return self.parse_stdout_to_list(stdout)
|
91
|
+
|
92
|
+
# setup commands # todo refactor into separate class
|
93
|
+
def setup(self):
|
94
|
+
self.setup_using_env_vars()
|
95
|
+
return self
|
96
|
+
|
97
|
+
def setup_using_env_vars(self): # move this to a CONFIG class (see code in SSH__Health_Check)
|
98
|
+
ssh_host = get_env(ENV_VAR__SSH__HOST )
|
99
|
+
ssh_port = get_env(ENV_VAR__SSH__PORT )
|
100
|
+
ssh_key_file = get_env(ENV_VAR__SSH__KEY_FILE )
|
101
|
+
ssh_key_user = get_env(ENV_VAR__SSH__USER )
|
102
|
+
ssh_strict_host_check = get_env(ENV_VAR__SSH__STRICT_HOST_CHECK )
|
103
|
+
if ssh_host:
|
104
|
+
self.ssh_host = ssh_host
|
105
|
+
if ssh_port:
|
106
|
+
self.ssh_port = str_to_int(ssh_port)
|
107
|
+
if ssh_key_file:
|
108
|
+
self.ssh_key_file = ssh_key_file
|
109
|
+
if ssh_key_user:
|
110
|
+
self.ssh_key_user = ssh_key_user
|
111
|
+
if ssh_strict_host_check is not None:
|
112
|
+
self.strict_host_check = str_to_bool(ssh_strict_host_check)
|
113
|
+
|
114
|
+
def parse_stdout_to_dict(self, stdout):
|
115
|
+
lines = stdout.splitlines()
|
116
|
+
headers = lines[0].split()
|
117
|
+
result = []
|
118
|
+
|
119
|
+
for line in lines[1:]: # Split each line into parts based on whitespace
|
120
|
+
parts = line.split() # Combine the parts with headers to create a dictionary
|
121
|
+
entry = {headers[i]: parts[i] for i in range(len(headers))}
|
122
|
+
result.append(entry)
|
123
|
+
|
124
|
+
return result
|
125
|
+
|
126
|
+
def parse_stdout_to_list(self, stdout): # todo: add support for more ways to split the data
|
127
|
+
lines = stdout.splitlines()
|
128
|
+
return lines
|
129
|
+
|
130
|
+
|
131
|
+
def ssh_setup_ok(self):
|
132
|
+
# todo: add check to see if ssh executable exists (this check can be cached)
|
133
|
+
if self.ssh_host and self.ssh_key_file and self.ssh_key_user:
|
134
|
+
return True
|
135
|
+
return False
|
136
|
+
|
137
|
+
def ssh_not__setup_ok(self):
|
138
|
+
return self.ssh_setup_ok() is False
|
139
|
+
|
140
|
+
# print helpers
|
141
|
+
# def print_ls(self, path=''):
|
142
|
+
# pprint(self.ls(path))
|
143
|
+
# return self
|
144
|
+
|
145
|
+
def print_exec(self, command=''):
|
146
|
+
return self.exec__print(command)
|
147
|
+
|
148
|
+
def print_header_for_command(self, command):
|
149
|
+
print('\n')
|
150
|
+
print('*' * (30 + len(command)))
|
151
|
+
print(f'****** stdout for: {command} ******')
|
152
|
+
print('*' * (30 + len(command)))
|
153
|
+
print()
|
154
|
+
|
155
|
+
def remove_server_ssh_host_fingerprint(self): # todo: refactor to utils class
|
156
|
+
cmd_ssh_keyscan = "ssh-keygen"
|
157
|
+
cmd_remove_host = ['-R', f'[{self.ssh_host}]:{self.ssh_port}']
|
158
|
+
return run_process(cmd_ssh_keyscan, cmd_remove_host)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
from osbot_utils.helpers.ssh.SSH__Execute import ENV_VAR__SSH__HOST, ENV_VAR__SSH__KEY_FILE, ENV_VAR__SSH__USER, \
|
2
|
+
ENV_VAR__SSH__PORT, ENV_VAR__SSH__STRICT_HOST_CHECK, SSH__Execute
|
3
|
+
from osbot_utils.utils.Env import get_env
|
4
|
+
from osbot_utils.utils.Misc import list_set
|
5
|
+
from osbot_utils.utils.Status import status_ok, status_error
|
6
|
+
|
7
|
+
ENV_VARS__FOR_SSH = {'ssh_host' : ENV_VAR__SSH__HOST ,
|
8
|
+
'ssh_key_file' : ENV_VAR__SSH__KEY_FILE ,
|
9
|
+
'ssh_key_user' : ENV_VAR__SSH__USER ,
|
10
|
+
'ssh_port' : ENV_VAR__SSH__PORT ,
|
11
|
+
'strict_host_check': ENV_VAR__SSH__STRICT_HOST_CHECK }
|
12
|
+
|
13
|
+
class SSH__Health_Check(SSH__Execute):
|
14
|
+
|
15
|
+
def check_connection(self):
|
16
|
+
text_message = 'test connection' #random_text('echo')
|
17
|
+
response = self.execute_command(f'echo {text_message}')
|
18
|
+
if response.get('status') == 'ok':
|
19
|
+
stderr = response.get('stderr').strip()
|
20
|
+
if stderr == '':
|
21
|
+
stdout = response.get('stdout').strip()
|
22
|
+
if stdout == text_message:
|
23
|
+
return status_ok(message='connection ok')
|
24
|
+
else:
|
25
|
+
return status_error(message=f'expected stdout did not march: {text_message} != {stdout}')
|
26
|
+
else:
|
27
|
+
return status_error(message=f'stderr was not empty', error=stderr, data=response)
|
28
|
+
else:
|
29
|
+
return status_error(message=f'request failed', data=response)
|
30
|
+
|
31
|
+
def env_vars_names(self):
|
32
|
+
return list_set(ENV_VARS__FOR_SSH)
|
33
|
+
|
34
|
+
def env_vars_values(self):
|
35
|
+
values = {}
|
36
|
+
for key, value in ENV_VARS__FOR_SSH.items():
|
37
|
+
env_value = get_env(value)
|
38
|
+
values[key] = env_value
|
39
|
+
return values
|
40
|
+
|
41
|
+
def env_vars_set_ok(self):
|
42
|
+
env_values = self.env_vars_values()
|
43
|
+
if (env_values.get('ssh_host' ) and
|
44
|
+
env_values.get('ssh_key_file') and
|
45
|
+
env_values.get('ssh_key_user') ):
|
46
|
+
return True
|
47
|
+
return False
|
48
|
+
|
49
|
+
|
@@ -0,0 +1,106 @@
|
|
1
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
2
|
+
from osbot_utils.decorators.lists.index_by import index_by
|
3
|
+
from osbot_utils.helpers.ssh.SSH__Execute import SSH__Execute
|
4
|
+
|
5
|
+
|
6
|
+
class SSH__Linux(Kwargs_To_Self):
|
7
|
+
ssh_execute : SSH__Execute
|
8
|
+
|
9
|
+
def apt_update(self):
|
10
|
+
return self.ssh_execute.execute_command__return_stdout('apt-get update')
|
11
|
+
|
12
|
+
def apt_install(self, package_name):
|
13
|
+
return self.ssh_execute.execute_command(f'apt-get install -y {package_name}')
|
14
|
+
|
15
|
+
def cat(self, path=''):
|
16
|
+
command = f'cat {path}'
|
17
|
+
return self.ssh_execute.execute_command__return_stdout(command)
|
18
|
+
|
19
|
+
@index_by
|
20
|
+
def disk_space(self):
|
21
|
+
command = "df -h"
|
22
|
+
stdout = self.ssh_execute.execute_command__return_stdout(command)
|
23
|
+
stdout_disk_space = stdout.replace('Mounted on', 'Mounted_on') # todo, find a better way to do this
|
24
|
+
disk_space = self.ssh_execute.parse_stdout_to_dict(stdout_disk_space)
|
25
|
+
return disk_space
|
26
|
+
|
27
|
+
def dir_exists(self, folder_name):
|
28
|
+
message__folder_exists = "Folder exists"
|
29
|
+
message__folder_not_exists = "Folder does not exist"
|
30
|
+
test_command = f'test -d {folder_name} && echo "{message__folder_exists}" || echo "{message__folder_not_exists}"'
|
31
|
+
result = self.ssh_execute.execute_command__return_stdout(test_command)
|
32
|
+
if result == message__folder_exists:
|
33
|
+
return True
|
34
|
+
if result == message__folder_not_exists:
|
35
|
+
return False
|
36
|
+
|
37
|
+
def echo(self, message):
|
38
|
+
return self.ssh_execute.execute_command__return_stdout(f"echo '{message}'")
|
39
|
+
|
40
|
+
def find(self, path=''):
|
41
|
+
command = f'find {path}'
|
42
|
+
return self.ssh_execute.execute_command__return_list(command)
|
43
|
+
|
44
|
+
def ls(self, path=''):
|
45
|
+
command = f'ls {path}'
|
46
|
+
ls_raw = self.ssh_execute.execute_command__return_stdout(command)
|
47
|
+
return ls_raw.splitlines()
|
48
|
+
|
49
|
+
def memory_usage(self):
|
50
|
+
command = "free -h"
|
51
|
+
memory_usage_raw = self.ssh_execute.execute_command__return_stdout(command) # todo: add fix for data parsing issue
|
52
|
+
return memory_usage_raw.splitlines()
|
53
|
+
|
54
|
+
|
55
|
+
def mkdir(self, folder):
|
56
|
+
command = f'mkdir -p {folder}'
|
57
|
+
return self.ssh_execute.execute_command(command)
|
58
|
+
|
59
|
+
def mv(self, source, destination):
|
60
|
+
command = f'mv {source} {destination}'
|
61
|
+
return self.ssh_execute.execute_command(command)
|
62
|
+
|
63
|
+
def pwd(self):
|
64
|
+
return self.ssh_execute.execute_command__return_stdout('pwd')
|
65
|
+
|
66
|
+
def rm(self, path=''):
|
67
|
+
command = f'rm {path}'
|
68
|
+
return self.ssh_execute.execute_command__return_stderr(command)
|
69
|
+
|
70
|
+
def rmdir(self, folder):
|
71
|
+
command = f'rmdir {folder}'
|
72
|
+
return self.ssh_execute.execute_command(command)
|
73
|
+
|
74
|
+
def running_processes(self,**kwargs):
|
75
|
+
command = "ps aux"
|
76
|
+
return self.ssh_execute.execute_command__return_dict(command, **kwargs)
|
77
|
+
|
78
|
+
def system_uptime(self):
|
79
|
+
command = "uptime"
|
80
|
+
uptime_raw = self.ssh_execute.execute_command__return_stdout(command)
|
81
|
+
return uptime_raw.strip()
|
82
|
+
|
83
|
+
def uname(self):
|
84
|
+
return self.ssh_execute.execute_command__return_stdout('uname')
|
85
|
+
|
86
|
+
def which(self, target):
|
87
|
+
command = f'which {target}' # todo: security-vuln: add protection against code injection
|
88
|
+
return self.ssh_execute.execute_command__return_stdout(command)
|
89
|
+
|
90
|
+
def whoami(self):
|
91
|
+
command = f'whoami'
|
92
|
+
return self.ssh_execute.execute_command__return_stdout(command)
|
93
|
+
|
94
|
+
# todo: add methods below (and respective tests)
|
95
|
+
|
96
|
+
# def ifconfig(self):
|
97
|
+
# command = "export PATH=$PATH:/sbin && ifconfig" # todo add example with PATH modification
|
98
|
+
# return self.execute_command__return_stdout(command)
|
99
|
+
|
100
|
+
# def ifconfig(self): # todo add command to execute in separate bash (see when it is needed)
|
101
|
+
# command = "bash -l -c 'ifconfig'"
|
102
|
+
# return self.execute_command__return_stdout(command)
|
103
|
+
# if port_forward: # todo: add support for port forward (this will need async execution)
|
104
|
+
# local_port = port_forward.get('local_port' )
|
105
|
+
# remote_ip = port_forward.get('remote_ip' )
|
106
|
+
# remote_port = port_forward.get('remote_port')
|
@@ -0,0 +1,48 @@
|
|
1
|
+
from osbot_utils.base_classes.Type_Safe import Type_Safe
|
2
|
+
from osbot_utils.helpers.ssh.SSH__Execute import SSH__Execute
|
3
|
+
from osbot_utils.helpers.ssh.SSH__Linux import SSH__Linux
|
4
|
+
from osbot_utils.utils.Dev import pprint
|
5
|
+
from osbot_utils.utils.Functions import function_source_code
|
6
|
+
from osbot_utils.utils.Lists import list_index_by
|
7
|
+
|
8
|
+
PYTHON3__LINUX__INSTALLER = 'python3'
|
9
|
+
|
10
|
+
class SSH__Python(Type_Safe):
|
11
|
+
ssh_execute: SSH__Execute
|
12
|
+
ssh_linux : SSH__Linux
|
13
|
+
|
14
|
+
def execute_python__code(self, python_code, python_executable='python3'):
|
15
|
+
python_command = f"{python_executable} -c \"{python_code}\""
|
16
|
+
return self.ssh_execute.execute_command(python_command)
|
17
|
+
|
18
|
+
def execute_python__code__return_stdout(self, *args, **kwargs):
|
19
|
+
return self.execute_python__code(*args, **kwargs).get('stdout').strip()
|
20
|
+
|
21
|
+
def execute_python__function(self, function, python_executable='python3'):
|
22
|
+
function_name = function.__name__
|
23
|
+
function_code = function_source_code(function)
|
24
|
+
exec_code = f"{function_code}\nresult= {function_name}(); print(result)"
|
25
|
+
return self.execute_python__code(exec_code)
|
26
|
+
|
27
|
+
def execute_python__function__return_stderr(self, *args, **kwargs):
|
28
|
+
return self.execute_python__function(*args, **kwargs).get('stderr').strip()
|
29
|
+
|
30
|
+
def execute_python__function__return_stdout(self, *args, **kwargs):
|
31
|
+
return self.execute_python__function(*args, **kwargs).get('stdout').strip()
|
32
|
+
|
33
|
+
def install_python3(self):
|
34
|
+
return self.ssh_linux.apt_install(PYTHON3__LINUX__INSTALLER)
|
35
|
+
|
36
|
+
def pip_list(self):
|
37
|
+
pip_list = self.ssh_execute.execute_command__return_dict('pip list')
|
38
|
+
return list_index_by(pip_list, 'Package')
|
39
|
+
|
40
|
+
def pip_install(self, package_name):
|
41
|
+
return self.ssh_execute.execute_command__return_stdout(f'pip install {package_name}')
|
42
|
+
|
43
|
+
def pip_version(self):
|
44
|
+
return self.ssh_execute.execute_command__return_stdout('pip --version')
|
45
|
+
|
46
|
+
def python_version(self):
|
47
|
+
return self.ssh_execute.execute_command__return_stdout('python3 --version')
|
48
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
from unittest import TestCase
|
2
|
+
|
3
|
+
import osbot_utils
|
4
|
+
from osbot_utils.helpers.ssh.SSH import SSH
|
5
|
+
from osbot_utils.helpers.ssh.SSH__Cache__Requests import SSH__Cache__Requests
|
6
|
+
from osbot_utils.helpers.ssh.SSH__Execute import SSH__Execute
|
7
|
+
from osbot_utils.utils.Env import load_dotenv
|
8
|
+
from osbot_utils.utils.Files import path_combine
|
9
|
+
|
10
|
+
ENV_FILE__WITH_ENV_VARS = "../.ssh.env"
|
11
|
+
|
12
|
+
class TestCase__SSH(TestCase):
|
13
|
+
ssh : SSH
|
14
|
+
cache: SSH__Cache__Requests
|
15
|
+
|
16
|
+
@classmethod
|
17
|
+
def setUpClass(cls):
|
18
|
+
cls.load_dotenv()
|
19
|
+
cls.ssh = SSH().setup()
|
20
|
+
if not cls.ssh.ssh_execute().ssh_host:
|
21
|
+
import pytest # we can only import this locally since this dependency doesn't exist in the main osbot_utils codebase
|
22
|
+
pytest.skip("SSH host not set")
|
23
|
+
|
24
|
+
cls.cache = SSH__Cache__Requests()
|
25
|
+
cls.cache.patch_apply()
|
26
|
+
|
27
|
+
@classmethod
|
28
|
+
def tearDownClass(cls):
|
29
|
+
cls.cache.patch_restore()
|
30
|
+
assert SSH__Execute.execute_command.__qualname__ == 'SSH__Execute.execute_command'
|
31
|
+
|
32
|
+
@staticmethod
|
33
|
+
def load_dotenv():
|
34
|
+
env_file_path = path_combine(osbot_utils.path, ENV_FILE__WITH_ENV_VARS)
|
35
|
+
load_dotenv(dotenv_path=env_file_path)
|
36
|
+
|
37
|
+
def cache_disable(self):
|
38
|
+
self.cache.disable()
|
39
|
+
|
40
|
+
def cache_update(self):
|
41
|
+
self.cache.update()
|
42
|
+
|
43
|
+
def ssh_execute(self):
|
44
|
+
return self.ssh.ssh_execute()
|
45
|
+
|
46
|
+
def ssh_linux(self):
|
47
|
+
return self.ssh.ssh_linux()
|
48
|
+
|
49
|
+
def ssh_python(self):
|
50
|
+
return self.ssh.ssh_python()
|
File without changes
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from osbot_utils.utils.Lists import list_sorted
|
2
2
|
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
3
|
-
from osbot_utils.utils.Misc import ansi_text_visible_length
|
4
3
|
from osbot_utils.helpers.trace.Trace_Call__Config import Trace_Call__Config
|
5
4
|
from osbot_utils.helpers.trace.Trace_Call__Print_Traces import text_grey, text_bold_green, text_olive, text_light_grey
|
5
|
+
from osbot_utils.utils.Str import ansi_text_visible_length
|
6
6
|
|
7
7
|
|
8
8
|
class Trace_Call__Print_Lines(Kwargs_To_Self):
|
osbot_utils/testing/Logging.py
CHANGED
@@ -8,8 +8,9 @@ from osbot_utils.decorators.methods.cache_on_self import cache_on_self
|
|
8
8
|
|
9
9
|
#DEFAULT_LOG_FORMAT = '%(asctime)s.%(msecs)03d %(levelname)s - %(message)s'
|
10
10
|
DEFAULT_LOG_FORMAT = '%(levelname)s - %(message)s'
|
11
|
-
DEFAULT_LOG_LEVEL = logging.
|
11
|
+
DEFAULT_LOG_LEVEL = logging.INFO
|
12
12
|
DEFAULT_DATE_FORMAT = '%M:%S'
|
13
|
+
|
13
14
|
class Logging:
|
14
15
|
|
15
16
|
def __init__(self, target=None, log_level: int = None, log_format=None, log_to_console=False, date_format=None):
|
@@ -27,7 +28,7 @@ class Logging:
|
|
27
28
|
stream_handler = logging.StreamHandler(stream=stream)
|
28
29
|
self.logger().addHandler(stream_handler)
|
29
30
|
self.set_logger_level()
|
30
|
-
self.
|
31
|
+
self.set_format_on_stream_handler(stream_handler)
|
31
32
|
|
32
33
|
return stream_handler
|
33
34
|
|
@@ -38,7 +39,7 @@ class Logging:
|
|
38
39
|
self.target = self.target.__name__
|
39
40
|
return logging.getLogger(self.target)
|
40
41
|
|
41
|
-
def enable_log_to_console(self, log_level=
|
42
|
+
def enable_log_to_console(self, log_level=None):
|
42
43
|
self.log_to_sys_stdout()
|
43
44
|
self.set_logger_level(log_level)
|
44
45
|
return self
|
@@ -60,7 +61,7 @@ class Logging:
|
|
60
61
|
return self.add_stream_handler(log_stream)
|
61
62
|
|
62
63
|
|
63
|
-
def
|
64
|
+
def set_format_on_stream_handler(self, stream_handler):
|
64
65
|
formatter = logging.Formatter(fmt=self.log_format, datefmt=self.date_format)
|
65
66
|
stream_handler.setFormatter(formatter)
|
66
67
|
return formatter
|
@@ -71,7 +72,16 @@ class Logging:
|
|
71
72
|
return self
|
72
73
|
|
73
74
|
def set_logger_level(self, level=None):
|
74
|
-
|
75
|
+
if level:
|
76
|
+
self.log_level = level
|
77
|
+
self.logger().setLevel(self.log_level)
|
78
|
+
|
79
|
+
def set_log_format(self, log_format=None, date_format=None):
|
80
|
+
if log_format:
|
81
|
+
self.log_format = log_format
|
82
|
+
if date_format:
|
83
|
+
self.date_format = date_format
|
84
|
+
return self
|
75
85
|
|
76
86
|
|
77
87
|
def info (self,message, *args, **kwargs): self.logger().info (message, *args, **kwargs)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from osbot_utils.utils.Env import get_env, load_dotenv, in_python_debugger
|
2
|
+
|
3
|
+
needs_load_dotenv = True
|
4
|
+
|
5
|
+
def skip_pytest(message=r"Skipping pytest for some reason ¯\_o_/¯"):
|
6
|
+
import pytest # we can only import this locally since this dependency doesn't exist in the main osbot_utils codebase
|
7
|
+
pytest.skip(message)
|
8
|
+
|
9
|
+
def skip_pytest__if_env_var_is_not_set(env_var_name):
|
10
|
+
if needs_load_dotenv:
|
11
|
+
load_dotenv()
|
12
|
+
|
13
|
+
if not get_env(env_var_name):
|
14
|
+
skip_pytest(f"Skipping tests because the {env_var_name} env var doesn't have a value")
|
15
|
+
|
16
|
+
def skip__if_in_python_debugger():
|
17
|
+
if in_python_debugger():
|
18
|
+
skip_pytest("Skipping tests because we are in a debugger")
|
osbot_utils/utils/Env.py
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
# In Misc.py
|
2
|
-
import os
|
3
|
-
from sys import platform
|
4
1
|
|
5
|
-
|
6
|
-
|
7
|
-
from
|
8
|
-
from osbot_utils.utils.
|
2
|
+
import os
|
3
|
+
import sys
|
4
|
+
from sys import platform
|
5
|
+
from osbot_utils.utils.Files import all_parent_folders, file_exists
|
6
|
+
from osbot_utils.utils.Misc import list_set
|
7
|
+
from osbot_utils.utils.Str import strip_quotes
|
9
8
|
|
10
9
|
def env__home_root():
|
11
10
|
return os.getenv('HOME') == '/root'
|
@@ -39,12 +38,14 @@ def env_vars(reload_vars=False):
|
|
39
38
|
return data
|
40
39
|
|
41
40
|
def env_load_from_file(path, override=False):
|
42
|
-
if
|
41
|
+
if file_exists(path):
|
43
42
|
with open(path) as f:
|
44
43
|
for line in f:
|
45
44
|
line = line.strip()
|
46
45
|
if not line or line.startswith('#'): # Strip whitespace and ignore comments
|
47
46
|
continue
|
47
|
+
if line.startswith('export '): # if the line starts with export, we can ignore it and continue
|
48
|
+
line = line[7:]
|
48
49
|
key, value = line.split(sep='=', maxsplit=1) # Split the line into key and value
|
49
50
|
value = strip_quotes(value.strip()) # Handle case when the value is in quotes
|
50
51
|
if override or key.strip() not in os.environ: # Set the environment variable
|
@@ -62,6 +63,20 @@ def env_unload_from_file(path):
|
|
62
63
|
if key in os.environ: # Remove the environment variable if it exists
|
63
64
|
del os.environ[key]
|
64
65
|
|
66
|
+
def in_github_action():
|
67
|
+
return os.getenv('GITHUB_ACTIONS') == 'true'
|
68
|
+
|
69
|
+
def in_python_debugger():
|
70
|
+
if sys.gettrace() is not None: # Check for a trace function
|
71
|
+
return True
|
72
|
+
|
73
|
+
pycharm_hosted = os.getenv('PYCHARM_HOSTED') == '1' # Check for PyCharm specific environment variables and other potential indicators
|
74
|
+
pydevd_load_values_async = os.getenv('PYDEVD_LOAD_VALUES_ASYNC') is not None
|
75
|
+
if pycharm_hosted and pydevd_load_values_async:
|
76
|
+
return True
|
77
|
+
|
78
|
+
return False
|
79
|
+
|
65
80
|
def load_dotenv(dotenv_path=None, override=False):
|
66
81
|
if dotenv_path: # If a specific dotenv path is provided, load from it
|
67
82
|
env_load_from_file(dotenv_path, override)
|
@@ -74,6 +89,10 @@ def load_dotenv(dotenv_path=None, override=False):
|
|
74
89
|
break # Stop after loading the first .env file # Stop after loading the first .env file
|
75
90
|
|
76
91
|
|
92
|
+
def not_in_github_action():
|
93
|
+
return in_github_action() is False
|
94
|
+
|
95
|
+
|
77
96
|
def unload_dotenv(dotenv_path=None):
|
78
97
|
if dotenv_path: # If a specific dotenv path is provided, unload from it
|
79
98
|
env_unload_from_file(dotenv_path)
|
@@ -86,4 +105,5 @@ def unload_dotenv(dotenv_path=None):
|
|
86
105
|
break # Stop after unloading the first .env file
|
87
106
|
|
88
107
|
|
89
|
-
env_load = load_dotenv
|
108
|
+
env_load = load_dotenv
|
109
|
+
get_env = os.getenv
|
osbot_utils/utils/Json.py
CHANGED
@@ -1,16 +1,9 @@
|
|
1
1
|
import json
|
2
|
-
import gzip
|
3
|
-
import logging
|
4
2
|
import os
|
5
3
|
|
6
4
|
from osbot_utils.utils.Misc import str_lines, str_md5, str_sha256
|
7
|
-
from osbot_utils.utils.Status import log_exception
|
8
|
-
|
9
|
-
logger_json = logging.getLogger() # todo: start using this API for capturing error messages from methods bellow
|
10
|
-
|
11
5
|
from osbot_utils.utils.Files import file_create_gz, file_create, load_file_gz, file_contents, file_lines, file_lines_gz
|
12
6
|
|
13
|
-
|
14
7
|
def json_dumps(python_object, indent=4, pretty=True, sort_keys=False, default=str, raise_exception=False):
|
15
8
|
if python_object:
|
16
9
|
try:
|
@@ -19,7 +12,7 @@ def json_dumps(python_object, indent=4, pretty=True, sort_keys=False, default=st
|
|
19
12
|
return json.dumps(python_object, default=default)
|
20
13
|
except Exception as error:
|
21
14
|
error_message = f'Error in load_json: {error}'
|
22
|
-
log_exception(message=error_message, error=error)
|
15
|
+
#log_exception(message=error_message, error=error) # todo: find a better way to do this , since this never worked well
|
23
16
|
if raise_exception:
|
24
17
|
raise error
|
25
18
|
|
@@ -89,7 +82,7 @@ class Json:
|
|
89
82
|
try:
|
90
83
|
return json.loads(json_data)
|
91
84
|
except Exception as error:
|
92
|
-
log_exception(message='Error in load_json', error=error)
|
85
|
+
#log_exception(message='Error in load_json', error=error)
|
93
86
|
if raise_exception:
|
94
87
|
raise error
|
95
88
|
|