abstract-utilities 0.2.2.495__py3-none-any.whl → 0.2.2.504__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.
- abstract_utilities/__init__.py +5 -9
- abstract_utilities/class_utils/__init__.py +7 -0
- abstract_utilities/class_utils/abstract_classes.py +74 -0
- abstract_utilities/class_utils/caller_utils.py +35 -0
- abstract_utilities/class_utils/class_utils.py +109 -0
- abstract_utilities/class_utils/function_utils.py +153 -0
- abstract_utilities/class_utils/global_utils.py +56 -0
- abstract_utilities/class_utils/imports/__init__.py +2 -0
- abstract_utilities/class_utils/imports/imports.py +2 -0
- abstract_utilities/class_utils/imports/utils.py +40 -0
- abstract_utilities/class_utils/module_utils.py +63 -0
- abstract_utilities/env_utils/imports/imports.py +3 -2
- abstract_utilities/error_utils/__init__.py +2 -0
- abstract_utilities/error_utils/error_utils.py +25 -0
- abstract_utilities/error_utils/imports/__init__.py +2 -0
- abstract_utilities/error_utils/imports/imports.py +1 -0
- abstract_utilities/error_utils/imports/module_imports.py +1 -0
- abstract_utilities/file_utils/imports/imports.py +3 -18
- abstract_utilities/file_utils/imports/module_imports.py +3 -6
- abstract_utilities/file_utils/src/type_checks.py +0 -1
- abstract_utilities/hash_utils/__init__.py +2 -0
- abstract_utilities/hash_utils/hash_utils.py +5 -0
- abstract_utilities/hash_utils/imports/__init__.py +2 -0
- abstract_utilities/hash_utils/imports/imports.py +1 -0
- abstract_utilities/hash_utils/imports/module_imports.py +0 -0
- abstract_utilities/history_utils/__init__.py +2 -0
- abstract_utilities/history_utils/history_utils.py +37 -0
- abstract_utilities/history_utils/imports/__init__.py +2 -0
- abstract_utilities/history_utils/imports/imports.py +1 -0
- abstract_utilities/history_utils/imports/module_imports.py +0 -0
- abstract_utilities/import_utils/imports/imports.py +1 -1
- abstract_utilities/import_utils/imports/module_imports.py +1 -1
- abstract_utilities/import_utils/src/__init__.py +1 -1
- abstract_utilities/import_utils/src/clean_imports.py +31 -5
- abstract_utilities/import_utils/src/dot_utils.py +9 -0
- abstract_utilities/import_utils/src/package_utilss/__init__.py +139 -0
- abstract_utilities/import_utils/src/package_utilss/context_utils.py +27 -0
- abstract_utilities/import_utils/src/package_utilss/import_collectors.py +53 -0
- abstract_utilities/import_utils/src/package_utilss/path_utils.py +28 -0
- abstract_utilities/import_utils/src/package_utilss/safe_import.py +27 -0
- abstract_utilities/import_utils/src/pkg_utils.py +140 -0
- abstract_utilities/imports.py +18 -0
- abstract_utilities/json_utils/__init__.py +2 -0
- abstract_utilities/json_utils/imports/__init__.py +2 -0
- abstract_utilities/json_utils/imports/imports.py +2 -0
- abstract_utilities/json_utils/imports/module_imports.py +5 -0
- abstract_utilities/json_utils/json_utils.py +743 -0
- abstract_utilities/list_utils/__init__.py +2 -0
- abstract_utilities/list_utils/imports/__init__.py +2 -0
- abstract_utilities/list_utils/imports/imports.py +1 -0
- abstract_utilities/list_utils/imports/module_imports.py +0 -0
- abstract_utilities/list_utils/list_utils.py +199 -0
- abstract_utilities/log_utils/__init__.py +5 -0
- abstract_utilities/log_utils/abstractLogManager.py +64 -0
- abstract_utilities/log_utils/call_response.py +68 -0
- abstract_utilities/log_utils/imports/__init__.py +2 -0
- abstract_utilities/log_utils/imports/imports.py +7 -0
- abstract_utilities/log_utils/imports/module_imports.py +2 -0
- abstract_utilities/log_utils/log_file.py +56 -0
- abstract_utilities/log_utils/logger_callable.py +49 -0
- abstract_utilities/math_utils/__init__.py +2 -0
- abstract_utilities/math_utils/imports/__init__.py +2 -0
- abstract_utilities/math_utils/imports/imports.py +2 -0
- abstract_utilities/math_utils/imports/module_imports.py +1 -0
- abstract_utilities/math_utils/math_utils.py +208 -0
- abstract_utilities/parse_utils/__init__.py +2 -0
- abstract_utilities/parse_utils/imports/__init__.py +3 -0
- abstract_utilities/parse_utils/imports/constants.py +10 -0
- abstract_utilities/parse_utils/imports/imports.py +2 -0
- abstract_utilities/parse_utils/imports/module_imports.py +4 -0
- abstract_utilities/parse_utils/parse_utils.py +516 -0
- abstract_utilities/path_utils/__init__.py +2 -0
- abstract_utilities/path_utils/imports/__init__.py +2 -0
- abstract_utilities/path_utils/imports/imports.py +1 -0
- abstract_utilities/path_utils/imports/module_imports.py +6 -0
- abstract_utilities/path_utils/path_utils.py +715 -0
- abstract_utilities/path_utils.py +94 -2
- abstract_utilities/read_write_utils/__init__.py +1 -0
- abstract_utilities/read_write_utils/imports/__init__.py +2 -0
- abstract_utilities/read_write_utils/imports/imports.py +2 -0
- abstract_utilities/read_write_utils/imports/module_imports.py +5 -0
- abstract_utilities/read_write_utils/read_write_utils.py +338 -0
- abstract_utilities/read_write_utils.py +2 -4
- abstract_utilities/safe_utils/__init__.py +2 -0
- abstract_utilities/safe_utils/imports/__init__.py +3 -0
- abstract_utilities/safe_utils/imports/imports.py +1 -0
- abstract_utilities/safe_utils/imports/module_imports.py +2 -0
- abstract_utilities/safe_utils/safe_utils.py +130 -0
- abstract_utilities/ssh_utils/__init__.py +2 -1
- abstract_utilities/ssh_utils/classes.py +0 -1
- abstract_utilities/ssh_utils/cmd_utils.py +207 -0
- abstract_utilities/ssh_utils/imports/__init__.py +3 -0
- abstract_utilities/ssh_utils/imports/imports.py +5 -0
- abstract_utilities/ssh_utils/imports/module_imports.py +5 -0
- abstract_utilities/ssh_utils/imports/utils.py +189 -0
- abstract_utilities/ssh_utils/pexpect_utils.py +11 -18
- abstract_utilities/string_utils/__init__.py +4 -0
- abstract_utilities/string_utils/clean_utils.py +28 -0
- abstract_utilities/string_utils/eat_utils.py +103 -0
- abstract_utilities/string_utils/imports/__init__.py +3 -0
- abstract_utilities/string_utils/imports/imports.py +2 -0
- abstract_utilities/string_utils/imports/module_imports.py +2 -0
- abstract_utilities/string_utils/imports/utils.py +81 -0
- abstract_utilities/string_utils/replace_utils.py +27 -0
- abstract_utilities/thread_utils/__init__.py +2 -0
- abstract_utilities/thread_utils/imports/__init__.py +2 -0
- abstract_utilities/thread_utils/imports/imports.py +2 -0
- abstract_utilities/thread_utils/imports/module_imports.py +2 -0
- abstract_utilities/thread_utils/thread_utils.py +140 -0
- abstract_utilities/time_utils/__init__.py +2 -0
- abstract_utilities/time_utils/imports/__init__.py +2 -0
- abstract_utilities/time_utils/imports/imports.py +3 -0
- abstract_utilities/time_utils/imports/module_imports.py +1 -0
- abstract_utilities/time_utils/time_utils.py +392 -0
- abstract_utilities/type_utils/__init__.py +3 -0
- abstract_utilities/type_utils/alpha_utils.py +59 -0
- abstract_utilities/type_utils/imports/__init__.py +2 -0
- abstract_utilities/type_utils/imports/imports.py +4 -0
- abstract_utilities/type_utils/imports/module_imports.py +1 -0
- abstract_utilities/type_utils/num_utils.py +19 -0
- abstract_utilities/type_utils/type_utils.py +981 -0
- {abstract_utilities-0.2.2.495.dist-info → abstract_utilities-0.2.2.504.dist-info}/METADATA +1 -1
- abstract_utilities-0.2.2.504.dist-info/RECORD +229 -0
- abstract_utilities-0.2.2.495.dist-info/RECORD +0 -123
- {abstract_utilities-0.2.2.495.dist-info → abstract_utilities-0.2.2.504.dist-info}/WHEEL +0 -0
- {abstract_utilities-0.2.2.495.dist-info → abstract_utilities-0.2.2.504.dist-info}/top_level.txt +0 -0
abstract_utilities/path_utils.py
CHANGED
|
@@ -22,13 +22,15 @@ Author: putkoff
|
|
|
22
22
|
Date: 05/31/2023
|
|
23
23
|
Version: 0.1.2
|
|
24
24
|
"""
|
|
25
|
-
import os
|
|
25
|
+
import os,shlex
|
|
26
|
+
from .ssh_utils import run_cmd
|
|
26
27
|
from .string_clean import eatAll
|
|
27
28
|
from .list_utils import make_list
|
|
28
29
|
from .type_utils import get_media_exts, is_media_type,MIME_TYPES
|
|
29
30
|
from .safe_utils import safe_join
|
|
30
31
|
from .class_utils import get_caller_path,get_caller_dir
|
|
31
|
-
|
|
32
|
+
from .abstract_classes import SingletonMeta,run_pruned_func
|
|
33
|
+
from .string_utils import get_from_kwargs
|
|
32
34
|
def get_os_info():
|
|
33
35
|
"""
|
|
34
36
|
Get Operating System Information
|
|
@@ -625,6 +627,96 @@ def create_base_dir(directory=None, child=None):
|
|
|
625
627
|
return full_path
|
|
626
628
|
|
|
627
629
|
|
|
630
|
+
def get_user_pass_host_key(**kwargs):
|
|
631
|
+
args = ['password','user_at_host','host','key','user']
|
|
632
|
+
kwargs['del_kwarg']=kwargs.get('del_kwarg',False)
|
|
633
|
+
values,kwargs = get_from_kwargs(*args,**kwargs)
|
|
634
|
+
return values
|
|
635
|
+
|
|
636
|
+
# --- Base remote checker -----------------------------------------------------
|
|
637
|
+
def _remote_test(path: str, test_flag: str, timeout: int = 5,*args, **kwargs) -> bool:
|
|
638
|
+
"""
|
|
639
|
+
Run a remote shell test (e.g. -f, -d) via SSH.
|
|
640
|
+
Returns True if test succeeds, False otherwise.
|
|
641
|
+
"""
|
|
642
|
+
try:
|
|
643
|
+
kwargs['cmd']=f"[ {test_flag} {shlex.quote(path)} ] && echo 1 || echo 0"
|
|
644
|
+
kwargs['text']=True
|
|
645
|
+
kwargs['timeout']=timeout
|
|
646
|
+
kwargs['stderr']=subprocess.DEVNULL
|
|
647
|
+
result = run_pruned_func(run_cmd,**kwargs)
|
|
648
|
+
return result.strip() == "1"
|
|
649
|
+
except Exception:
|
|
650
|
+
return False
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
# --- Individual path checks --------------------------------------------------
|
|
654
|
+
def is_remote_file(path: str,*args, **kwargs) -> bool:
|
|
655
|
+
"""True if remote path is a file."""
|
|
656
|
+
return _remote_test(path, "-f", **kwargs)
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def is_remote_dir(path: str,*args, **kwargs) -> bool:
|
|
660
|
+
"""True if remote path is a directory."""
|
|
661
|
+
return _remote_test(path, "-d", **kwargs)
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def is_local_file(path: str) -> bool:
|
|
665
|
+
"""True if local path is a file."""
|
|
666
|
+
return os.path.isfile(path)
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
def is_local_dir(path: str) -> bool:
|
|
670
|
+
"""True if local path is a directory."""
|
|
671
|
+
return os.path.isdir(path)
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
# --- Unified interface -------------------------------------------------------
|
|
675
|
+
|
|
676
|
+
def is_file(path: str,*args,**kwargs) -> bool:
|
|
677
|
+
"""Determine if path is a file (works local or remote)."""
|
|
678
|
+
if get_user_pass_host_key(**kwargs):
|
|
679
|
+
return is_remote_file(path, **kwargs)
|
|
680
|
+
return is_local_file(path)
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def is_dir(path: str, *args,**kwargs) -> bool:
|
|
684
|
+
"""Determine if path is a directory (works local or remote)."""
|
|
685
|
+
if get_user_pass_host_key(**kwargs):
|
|
686
|
+
return is_remote_dir(path, **kwargs)
|
|
687
|
+
return is_local_dir(path)
|
|
688
|
+
|
|
689
|
+
def is_exists(path: str, *args,**kwargs) -> bool:
|
|
690
|
+
if is_file(path,**kwargs):
|
|
691
|
+
return True
|
|
692
|
+
if is_dir(path,**kwargs):
|
|
693
|
+
return True
|
|
694
|
+
return False
|
|
695
|
+
# --- Optional: keep your original all-in-one wrapper ------------------------
|
|
696
|
+
def check_path_type(
|
|
697
|
+
path: str,
|
|
698
|
+
*args,
|
|
699
|
+
**kwargs
|
|
700
|
+
) -> str:
|
|
701
|
+
"""
|
|
702
|
+
Return 'file', 'directory', 'missing', or 'unknown'.
|
|
703
|
+
Uses isolated is_file/is_dir functions.
|
|
704
|
+
"""
|
|
705
|
+
if get_user_pass_host_key(**kwargs):
|
|
706
|
+
if is_remote_file(path,**kwargs):
|
|
707
|
+
return "file"
|
|
708
|
+
elif is_remote_dir(path,**kwargs):
|
|
709
|
+
return "directory"
|
|
710
|
+
else:
|
|
711
|
+
return "missing"
|
|
712
|
+
else:
|
|
713
|
+
if os.path.isfile(path):
|
|
714
|
+
return "file"
|
|
715
|
+
elif os.path.isdir(path):
|
|
716
|
+
return "directory"
|
|
717
|
+
elif not os.path.exists(path):
|
|
718
|
+
return "missing"
|
|
719
|
+
return "unknown"
|
|
628
720
|
|
|
629
721
|
def get_file_parts(path):
|
|
630
722
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .read_write_utils import *
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""
|
|
2
|
+
read_write_utils.py
|
|
3
|
+
-------------------
|
|
4
|
+
Unified read/write utility for safe file operations.
|
|
5
|
+
Supports:
|
|
6
|
+
- Writing content to a file
|
|
7
|
+
- Reading content from a file
|
|
8
|
+
- Creating and reading if missing
|
|
9
|
+
- Detecting file/content params via positional args or kwargs
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
from abstract_utilities.read_write_utils import *
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from .imports import *
|
|
16
|
+
_FILE_PATH_KEYS = ['file', 'filepath', 'file_path', 'path', 'directory', 'f', 'dst', 'dest']
|
|
17
|
+
_CONTENTS_KEYS = ['cont', 'content', 'contents', 'data', 'datas', 'dat', 'src', 'source']
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# --- Helper utilities --------------------------------------------------------
|
|
21
|
+
def string_in_keys(strings, kwargs):
|
|
22
|
+
"""Find a matching keyword in kwargs that contains any of the given substrings."""
|
|
23
|
+
for key in kwargs:
|
|
24
|
+
for s in strings:
|
|
25
|
+
if s.lower() in key.lower():
|
|
26
|
+
return key
|
|
27
|
+
return None
|
|
28
|
+
def make_dirs(path, exist_ok=True, **kwargs):
|
|
29
|
+
remote = get_user_pass_host_key(**kwargs)
|
|
30
|
+
|
|
31
|
+
if remote:
|
|
32
|
+
kwargs['cmd'] = f"mkdir -p {path}"
|
|
33
|
+
|
|
34
|
+
resp = run_pruned_func(run_cmd, **kwargs)
|
|
35
|
+
|
|
36
|
+
else:
|
|
37
|
+
os.makedirs(path, exist_ok=exist_ok)
|
|
38
|
+
return path
|
|
39
|
+
def make_path(path, home_dir=None, file=None, **kwargs):
|
|
40
|
+
if not path:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
basename = os.path.basename(path)
|
|
44
|
+
parts = [p for p in path.split('/') if p]
|
|
45
|
+
|
|
46
|
+
# Detect whether this is a file or a folder
|
|
47
|
+
is_file = file if file is not None else ('.' in basename)
|
|
48
|
+
pieces = parts[:-1] if is_file else parts
|
|
49
|
+
|
|
50
|
+
full_dir = home_dir or '/'
|
|
51
|
+
for piece in pieces:
|
|
52
|
+
full_dir = os.path.join(full_dir, piece)
|
|
53
|
+
make_dirs(full_dir, exist_ok=True, **kwargs)
|
|
54
|
+
|
|
55
|
+
if is_file:
|
|
56
|
+
full_dir = os.path.join(full_dir, basename)
|
|
57
|
+
|
|
58
|
+
return full_dir
|
|
59
|
+
def get_rel_path(src,src_rel,dst,**kwargs):
|
|
60
|
+
if src.startswith(src_rel):
|
|
61
|
+
nu_src = src[len(src_rel):]
|
|
62
|
+
nu_src= eatAll(nu_src,'/')
|
|
63
|
+
directory= eatOuter(dst,'/')
|
|
64
|
+
rel_path = os.path.join(dst,nu_src)
|
|
65
|
+
return rel_path
|
|
66
|
+
def make_relative_path(src,src_rel,dst,**kwargs):
|
|
67
|
+
|
|
68
|
+
if src.startswith(src_rel):
|
|
69
|
+
rel_path = get_rel_path(src,src_rel,dst)
|
|
70
|
+
|
|
71
|
+
path = make_path(rel_path,**kwargs)
|
|
72
|
+
|
|
73
|
+
return path
|
|
74
|
+
|
|
75
|
+
def path_join(*args):
|
|
76
|
+
path = None
|
|
77
|
+
for i,arg in enumerate(args):
|
|
78
|
+
if arg:
|
|
79
|
+
if i == 0:
|
|
80
|
+
path = arg
|
|
81
|
+
else:
|
|
82
|
+
path = os.path.join(path,arg)
|
|
83
|
+
return path
|
|
84
|
+
|
|
85
|
+
def get_path(paths,**kwargs):
|
|
86
|
+
"""Return the first valid path among given paths."""
|
|
87
|
+
for path in paths:
|
|
88
|
+
if isinstance(path, str):
|
|
89
|
+
if is_file(path,**kwargs):
|
|
90
|
+
return path
|
|
91
|
+
dirname = os.path.dirname(path)
|
|
92
|
+
if is_exists(dirname,**kwargs):
|
|
93
|
+
return path
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def break_down_find_existing(path,**kwargs):
|
|
98
|
+
"""Return the first non-existent subpath within a path chain."""
|
|
99
|
+
test_path = ''
|
|
100
|
+
for part in path.split(os.sep):
|
|
101
|
+
test_path = os.path.join(test_path, part)
|
|
102
|
+
if not is_exists(test_path,**kwargs):
|
|
103
|
+
return test_path if test_path else None
|
|
104
|
+
return test_path
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# --- Parameter parsing --------------------------------------------------------
|
|
108
|
+
def check_read_write_params(*args, **kwargs):
|
|
109
|
+
"""
|
|
110
|
+
Determine file_path and contents from arguments.
|
|
111
|
+
Returns a tuple: (file_path, contents)
|
|
112
|
+
"""
|
|
113
|
+
file_key = string_in_keys(_FILE_PATH_KEYS, kwargs)
|
|
114
|
+
content_key = string_in_keys(_CONTENTS_KEYS, kwargs)
|
|
115
|
+
|
|
116
|
+
file_path = kwargs.get(file_key) if file_key else None
|
|
117
|
+
contents = kwargs.get(content_key) if content_key else None
|
|
118
|
+
|
|
119
|
+
# Handle positional args (fallback)
|
|
120
|
+
if file_path is None and len(args) > 0:
|
|
121
|
+
file_path = args[0]
|
|
122
|
+
if contents is None and len(args) > 1:
|
|
123
|
+
contents = args[1]
|
|
124
|
+
|
|
125
|
+
if file_path is None:
|
|
126
|
+
raise ValueError("Missing file_path argument.")
|
|
127
|
+
return file_path, contents
|
|
128
|
+
|
|
129
|
+
def write_to_path(
|
|
130
|
+
file_path: str,
|
|
131
|
+
contents: str,
|
|
132
|
+
*,
|
|
133
|
+
user_at_host: str = None,
|
|
134
|
+
cwd: str | None = None,
|
|
135
|
+
password=None,
|
|
136
|
+
key=None,
|
|
137
|
+
env_path=None,
|
|
138
|
+
**kwargs
|
|
139
|
+
) -> str:
|
|
140
|
+
"""
|
|
141
|
+
Completely overwrite a file (locally or remotely).
|
|
142
|
+
Supports sudo and password-based remote execution.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
# sanitize for shell safety
|
|
146
|
+
quoted_path = shlex.quote(file_path)
|
|
147
|
+
quoted_data = shlex.quote(str(contents))
|
|
148
|
+
|
|
149
|
+
# shell command that fully overwrites
|
|
150
|
+
# (no append, replaces contents entirely)
|
|
151
|
+
base_cmd = f'sudo sh -c "echo {quoted_data} > {quoted_path}"'
|
|
152
|
+
|
|
153
|
+
# optional sudo password injection
|
|
154
|
+
full_cmd = get_print_sudo_cmd(
|
|
155
|
+
cmd=base_cmd,
|
|
156
|
+
password=password,
|
|
157
|
+
key=key,
|
|
158
|
+
env_path=env_path
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# local or remote dispatch
|
|
162
|
+
if user_at_host:
|
|
163
|
+
return run_remote_cmd(
|
|
164
|
+
user_at_host=user_at_host,
|
|
165
|
+
cmd=full_cmd,
|
|
166
|
+
cwd=cwd,
|
|
167
|
+
password=password,
|
|
168
|
+
key=key,
|
|
169
|
+
env_path=env_path,
|
|
170
|
+
**kwargs
|
|
171
|
+
)
|
|
172
|
+
else:
|
|
173
|
+
return run_local_cmd(
|
|
174
|
+
cmd=full_cmd,
|
|
175
|
+
cwd=cwd,
|
|
176
|
+
password=password,
|
|
177
|
+
key=key,
|
|
178
|
+
env_path=env_path,
|
|
179
|
+
**kwargs
|
|
180
|
+
)
|
|
181
|
+
### --- Core functionality -------------------------------------------------------
|
|
182
|
+
##def write_to_file(*args, **kwargs):
|
|
183
|
+
## """
|
|
184
|
+
## Write contents to a file (create if missing).
|
|
185
|
+
##
|
|
186
|
+
## Returns the file_path written.
|
|
187
|
+
## """
|
|
188
|
+
## file_path, contents = check_read_write_params(*args, **kwargs)
|
|
189
|
+
## if contents is None:
|
|
190
|
+
## raise ValueError("Missing contents to write.")
|
|
191
|
+
##
|
|
192
|
+
## os.makedirs(os.path.dirname(file_path) or ".", exist_ok=True)
|
|
193
|
+
## with open(file_path, "w", encoding="utf-8") as f:
|
|
194
|
+
## f.write(str(contents))
|
|
195
|
+
## return file_path
|
|
196
|
+
# --- Core functionality -------------------------------------------------------
|
|
197
|
+
def write_to_file(*args, **kwargs):
|
|
198
|
+
"""
|
|
199
|
+
Write contents to a file (create if missing).
|
|
200
|
+
|
|
201
|
+
Returns the file_path written.
|
|
202
|
+
"""
|
|
203
|
+
file_path, contents = check_read_write_params(*args, **kwargs)
|
|
204
|
+
values,kwargs = get_from_kwargs(['file_path','contents'],del_kwarg=True,**kwargs)
|
|
205
|
+
dirname = os.path.dirname(file_path)
|
|
206
|
+
|
|
207
|
+
if contents is None:
|
|
208
|
+
raise ValueError("Missing contents to write.")
|
|
209
|
+
user_at_host = kwargs.get("user_at_host")
|
|
210
|
+
if get_user_pass_host_key(**kwargs):
|
|
211
|
+
make_dirs(dirname, exist_ok=True,**kwargs)
|
|
212
|
+
kwargs["cwd"] = kwargs.get('cwd') or os.path.dirname(file_path)
|
|
213
|
+
# sanitize for shell safety
|
|
214
|
+
quoted_path = shlex.quote(file_path)
|
|
215
|
+
quoted_data = shlex.quote(str(contents))
|
|
216
|
+
# shell command that fully overwrites
|
|
217
|
+
# (no append, replaces contents entirely)
|
|
218
|
+
kwargs["cmd"] = f'sh -c "echo {quoted_data} > {quoted_path}"'
|
|
219
|
+
if not kwargs.get('password') and not kwargs.get('key'):
|
|
220
|
+
kwargs["cmd"]=f'sudo {kwargs["cmd"]}'
|
|
221
|
+
result = run_pruned_func(run_cmd,**kwargs)
|
|
222
|
+
if 'file_path' in kwargs:
|
|
223
|
+
del kwargs['file_path']
|
|
224
|
+
if not is_file(file_path,**kwargs) or str(contents) != read_from_file(file_path,**kwargs):
|
|
225
|
+
kwargs["cmd"]=f'sudo {kwargs["cmd"]}'
|
|
226
|
+
result = run_pruned_func(run_cmd,**kwargs)
|
|
227
|
+
return result
|
|
228
|
+
|
|
229
|
+
make_dirs(dirname or ".", exist_ok=True)
|
|
230
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
231
|
+
f.write(str(contents))
|
|
232
|
+
return file_path
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def read_from_file(file_path,**kwargs):
|
|
236
|
+
if get_user_pass_host_key(**kwargs):
|
|
237
|
+
kwargs["cwd"] = kwargs.get('cwd') or os.path.dirname(file_path)
|
|
238
|
+
basename = os.path.basename(file_path)
|
|
239
|
+
kwargs["cmd"] = f'cat {basename}'
|
|
240
|
+
return run_pruned_func(run_cmd,**kwargs)
|
|
241
|
+
"""Read text content from a file."""
|
|
242
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
243
|
+
return f.read()
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def copy_dirs(dirs, dst_root, src_rel=None, **kwargs):
|
|
247
|
+
"""
|
|
248
|
+
Recursively copy directory structures (without files) from dirs → dst_root.
|
|
249
|
+
"""
|
|
250
|
+
for src in dirs:
|
|
251
|
+
# build destination path preserving relative structure
|
|
252
|
+
dst_path = make_relative_path(src, src_rel, dst_root, **kwargs) if src_rel else dst_root
|
|
253
|
+
make_path(dst_path, **kwargs) # ensures directory exists
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def copy_file(src, dst_root, src_rel=None, **kwargs):
|
|
258
|
+
"""
|
|
259
|
+
Copy a single file to dst_root, preserving relative structure if src_rel provided.
|
|
260
|
+
Supports remote copy via read/write.
|
|
261
|
+
"""
|
|
262
|
+
# derive destination file path
|
|
263
|
+
dst_path = make_relative_path(src, src_rel, dst_root, **kwargs) if src_rel else os.path.join(dst_root, os.path.basename(src))
|
|
264
|
+
make_path(dst_path, **kwargs)
|
|
265
|
+
|
|
266
|
+
if get_user_pass_host_key(**kwargs): # remote mode
|
|
267
|
+
contents = read_from_file(src, **kwargs)
|
|
268
|
+
write_to_file(contents=contents, file_path=dst_path, **kwargs)
|
|
269
|
+
else: # local
|
|
270
|
+
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
|
271
|
+
shutil.copy2(src, dst_path)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
return dst_path
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def copy_files(files, dst_root, src_rel=None, **kwargs):
|
|
278
|
+
"""
|
|
279
|
+
Copy a list of files to dst_root.
|
|
280
|
+
"""
|
|
281
|
+
for src in files:
|
|
282
|
+
copy_file(src=src, dst_root=dst_root, src_rel=src_rel, **kwargs)
|
|
283
|
+
|
|
284
|
+
def create_and_read_file(*args, **kwargs):
|
|
285
|
+
"""
|
|
286
|
+
Create the file (if missing) and read contents from it.
|
|
287
|
+
"""
|
|
288
|
+
file_path, contents = check_read_write_params(*args, **kwargs)
|
|
289
|
+
if not os.path.isfile(file_path):
|
|
290
|
+
write_to_file(file_path, contents or "")
|
|
291
|
+
return read_from_file(file_path)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def is_file_extension(obj: str) -> bool:
|
|
295
|
+
"""Return True if obj looks like a filename with extension."""
|
|
296
|
+
if not isinstance(obj, str):
|
|
297
|
+
return False
|
|
298
|
+
root, ext = os.path.splitext(obj)
|
|
299
|
+
return bool(root and ext)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def delete_file(file_path: str):
|
|
303
|
+
"""Safely delete a file if it exists."""
|
|
304
|
+
if os.path.isfile(file_path):
|
|
305
|
+
os.remove(file_path)
|
|
306
|
+
return True
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def get_content_lines(*args, **kwargs):
|
|
311
|
+
"""Return a list of lines from string or file path."""
|
|
312
|
+
file_path, contents = check_read_write_params(*args, **kwargs)
|
|
313
|
+
if os.path.isfile(file_path):
|
|
314
|
+
contents = read_from_file(filepath)
|
|
315
|
+
|
|
316
|
+
if isinstance(contents, str):
|
|
317
|
+
return contents.splitlines()
|
|
318
|
+
elif isinstance(contents, list):
|
|
319
|
+
return contents
|
|
320
|
+
return []
|
|
321
|
+
def collate_text_docs(directory=None):
|
|
322
|
+
return [read_from_file(item) for item in get_all_files(directory=directory)]
|
|
323
|
+
def get_content(*paths):
|
|
324
|
+
item_path = os.path.join(*paths)
|
|
325
|
+
if os.path.isfile(item_path):
|
|
326
|
+
try:
|
|
327
|
+
content = read_from_file(item_path)
|
|
328
|
+
return content
|
|
329
|
+
except:
|
|
330
|
+
pass
|
|
331
|
+
return None
|
|
332
|
+
def get_text_or_read(text=None,file_path=None):
|
|
333
|
+
text = text or ''
|
|
334
|
+
imports_js = {}
|
|
335
|
+
if not text and file_path and os.path.isfile(file_path):
|
|
336
|
+
text=read_from_file(file_path)
|
|
337
|
+
return text
|
|
338
|
+
##
|
|
@@ -12,14 +12,12 @@ Usage:
|
|
|
12
12
|
from abstract_utilities.read_write_utils import *
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
import os
|
|
16
|
-
import shlex
|
|
15
|
+
import os,shlex
|
|
17
16
|
from .string_clean import *
|
|
18
17
|
from .ssh_utils.utils import run_cmd,get_print_sudo_cmd,run_local_cmd,run_remote_cmd
|
|
19
|
-
from .file_utils import is_file,is_dir,get_user_pass_host_key,is_exists
|
|
20
18
|
from .abstract_classes import run_pruned_func
|
|
21
19
|
from .string_utils import get_from_kwargs
|
|
22
|
-
from .path_utils import get_all_files
|
|
20
|
+
from .path_utils import get_all_files,is_file,is_dir,get_user_pass_host_key,is_exists
|
|
23
21
|
_FILE_PATH_KEYS = ['file', 'filepath', 'file_path', 'path', 'directory', 'f', 'dst', 'dest']
|
|
24
22
|
_CONTENTS_KEYS = ['cont', 'content', 'contents', 'data', 'datas', 'dat', 'src', 'source']
|
|
25
23
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from typing import *
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
abstract_safeops.py
|
|
3
|
+
-------------------
|
|
4
|
+
Utility functions for safely splitting, slicing, and retrieving elements from iterable or string objects
|
|
5
|
+
without raising exceptions on invalid input or out-of-range indices.
|
|
6
|
+
|
|
7
|
+
Designed for compatibility with the abstract_ ecosystem (e.g. abstract_utilities, abstract_math, etc.).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .imports import *
|
|
11
|
+
_BASE_DIR = get_caller_dir()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def safe_split(
|
|
15
|
+
string: Any,
|
|
16
|
+
char: Any,
|
|
17
|
+
i: Optional[int] = None,
|
|
18
|
+
default: Union[bool, Any] = False
|
|
19
|
+
) -> Union[str, List[str], Any, None]:
|
|
20
|
+
"""
|
|
21
|
+
Safely split a string by a character and optionally return index i.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
string: Input string (or any object convertible to string).
|
|
25
|
+
char: Delimiter to split on.
|
|
26
|
+
i: Optional index to retrieve from the split result.
|
|
27
|
+
default: If True, return the original string on error.
|
|
28
|
+
If any other value, return that instead of raising.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
The split list, or the element at index i, or default behavior on error.
|
|
32
|
+
"""
|
|
33
|
+
if string is None or char is None:
|
|
34
|
+
return string
|
|
35
|
+
|
|
36
|
+
s, c = str(string), str(char)
|
|
37
|
+
if c not in s:
|
|
38
|
+
return string
|
|
39
|
+
|
|
40
|
+
parts = s.split(c)
|
|
41
|
+
|
|
42
|
+
if i is None:
|
|
43
|
+
return parts
|
|
44
|
+
|
|
45
|
+
if is_number(i):
|
|
46
|
+
idx = int(i)
|
|
47
|
+
if 0 <= idx < len(parts):
|
|
48
|
+
return parts[idx]
|
|
49
|
+
|
|
50
|
+
if default:
|
|
51
|
+
return string if default is True else default
|
|
52
|
+
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def safe_slice(
|
|
57
|
+
obj: Any,
|
|
58
|
+
i: Optional[int] = None,
|
|
59
|
+
k: Optional[int] = None,
|
|
60
|
+
default: Union[bool, Any] = False
|
|
61
|
+
) -> Any:
|
|
62
|
+
"""
|
|
63
|
+
Safely slice an iterable object or string, with fallback behavior on invalid indices.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
obj: Iterable or string-like object.
|
|
67
|
+
i: Start index (can be negative).
|
|
68
|
+
k: End index (can be negative).
|
|
69
|
+
default: If True, returns the original object on error.
|
|
70
|
+
If any other value, return that value on error.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
The sliced object, or default behavior on error.
|
|
74
|
+
"""
|
|
75
|
+
# Null or invalid base case
|
|
76
|
+
if obj is None or isinstance(obj, bool):
|
|
77
|
+
return obj if default is True else default if default else None
|
|
78
|
+
|
|
79
|
+
# Non-iterable guard
|
|
80
|
+
if not hasattr(obj, "__getitem__"):
|
|
81
|
+
return obj if default is True else default if default else None
|
|
82
|
+
|
|
83
|
+
obj_len = len(obj)
|
|
84
|
+
|
|
85
|
+
# Normalize negative indices
|
|
86
|
+
if isinstance(i, int) and i < 0:
|
|
87
|
+
i = obj_len + i
|
|
88
|
+
if isinstance(k, int) and k < 0:
|
|
89
|
+
k = obj_len + k
|
|
90
|
+
|
|
91
|
+
# Bound indices
|
|
92
|
+
if i is not None:
|
|
93
|
+
i = max(0, min(i, obj_len))
|
|
94
|
+
if k is not None:
|
|
95
|
+
k = max(0, min(k, obj_len))
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
return obj[i:k]
|
|
99
|
+
except Exception:
|
|
100
|
+
return obj if default is True else default if default else None
|
|
101
|
+
|
|
102
|
+
def safe_join(*paths):
|
|
103
|
+
paths = list(paths)
|
|
104
|
+
paths = [path for path in paths if path]
|
|
105
|
+
return os.path.join(*paths)
|
|
106
|
+
def safe_get(
|
|
107
|
+
obj: Any,
|
|
108
|
+
key: Union[int, str, None] = None,
|
|
109
|
+
default: Union[bool, Any] = False
|
|
110
|
+
) -> Any:
|
|
111
|
+
"""
|
|
112
|
+
Generalized safe getter for both indexable and mapping types.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
obj: The object to access (list, dict, string, etc.).
|
|
116
|
+
key: Index or key to retrieve.
|
|
117
|
+
default: Fallback value or True for "return obj".
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Retrieved element, or default value on failure.
|
|
121
|
+
"""
|
|
122
|
+
if obj is None or key is None:
|
|
123
|
+
return obj if default is True else default if default else None
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
if isinstance(obj, dict):
|
|
127
|
+
return obj.get(key, obj if default is True else default if default else None)
|
|
128
|
+
return obj[key]
|
|
129
|
+
except Exception:
|
|
130
|
+
return obj if default is True else default if default else None
|