abstract-utilities 0.2.2.387__py3-none-any.whl → 0.2.2.480__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.

Potentially problematic release.


This version of abstract-utilities might be problematic. Click here for more details.

Files changed (63) hide show
  1. abstract_utilities/__init__.py +14 -43
  2. abstract_utilities/abstract_classes.py +49 -0
  3. abstract_utilities/class_utils.py +38 -3
  4. abstract_utilities/cmd_utils/imports/__init__.py +1 -0
  5. abstract_utilities/cmd_utils/imports/imports.py +10 -0
  6. abstract_utilities/cmd_utils/pexpect_utils.py +310 -0
  7. abstract_utilities/cmd_utils/user_utils.py +1 -1
  8. abstract_utilities/dynimport.py +7 -15
  9. abstract_utilities/env_utils/__init__.py +3 -0
  10. abstract_utilities/env_utils/abstractEnv.py +129 -0
  11. abstract_utilities/env_utils/envy_it.py +33 -0
  12. abstract_utilities/env_utils/imports/__init__.py +2 -0
  13. abstract_utilities/env_utils/imports/imports.py +8 -0
  14. abstract_utilities/env_utils/imports/utils.py +122 -0
  15. abstract_utilities/file_utils/__init__.py +3 -0
  16. abstract_utilities/file_utils/file_utils/__init__.py +8 -0
  17. abstract_utilities/file_utils/file_utils/file_filters.py +104 -0
  18. abstract_utilities/{robust_reader → file_utils/file_utils}/file_reader.py +5 -19
  19. abstract_utilities/{robust_readers/file_filters.py → file_utils/file_utils/file_utils.py} +5 -4
  20. abstract_utilities/{robust_readers → file_utils/file_utils}/filter_params.py +1 -38
  21. abstract_utilities/file_utils/file_utils/find_collect.py +154 -0
  22. abstract_utilities/file_utils/file_utils/imports/__init__.py +3 -0
  23. abstract_utilities/file_utils/file_utils/imports/constants.py +39 -0
  24. abstract_utilities/file_utils/file_utils/imports/file_functions.py +10 -0
  25. abstract_utilities/file_utils/file_utils/imports/imports.py +39 -0
  26. abstract_utilities/file_utils/file_utils/imports/module_imports.py +14 -0
  27. abstract_utilities/file_utils/file_utils/imports.py +10 -0
  28. abstract_utilities/file_utils/file_utils/map_utils.py +29 -0
  29. abstract_utilities/{robust_reader → file_utils/file_utils}/pdf_utils.py +1 -9
  30. abstract_utilities/file_utils/file_utils/type_checks.py +91 -0
  31. abstract_utilities/file_utils/imports/__init__.py +4 -0
  32. abstract_utilities/file_utils/imports/classes.py +381 -0
  33. abstract_utilities/file_utils/imports/clean_imps.py +158 -0
  34. abstract_utilities/file_utils/imports/constants.py +39 -0
  35. abstract_utilities/file_utils/imports/file_functions.py +10 -0
  36. abstract_utilities/file_utils/imports/imports.py +65 -0
  37. abstract_utilities/file_utils/imports/module_imports.py +13 -0
  38. abstract_utilities/file_utils/req.py +329 -0
  39. abstract_utilities/log_utils.py +1 -1
  40. abstract_utilities/path_utils.py +90 -6
  41. abstract_utilities/read_write_utils.py +250 -157
  42. abstract_utilities/robust_reader/__init__.py +1 -1
  43. abstract_utilities/robust_reader/imports/__init__.py +1 -0
  44. abstract_utilities/robust_reader/imports/imports.py +3 -0
  45. abstract_utilities/robust_readers/__init__.py +0 -1
  46. abstract_utilities/robust_readers/import_utils/__init__.py +1 -0
  47. abstract_utilities/robust_readers/import_utils/clean_imports.py +175 -0
  48. abstract_utilities/robust_readers/imports.py +8 -0
  49. abstract_utilities/robust_readers/initFuncGen.py +92 -76
  50. abstract_utilities/safe_utils.py +133 -0
  51. abstract_utilities/ssh_utils/__init__.py +3 -0
  52. abstract_utilities/ssh_utils/classes.py +127 -0
  53. abstract_utilities/ssh_utils/imports.py +10 -0
  54. abstract_utilities/ssh_utils/pexpect_utils.py +315 -0
  55. abstract_utilities/ssh_utils/utils.py +188 -0
  56. abstract_utilities/string_clean.py +40 -1
  57. abstract_utilities/string_utils.py +51 -0
  58. abstract_utilities/type_utils.py +25 -2
  59. {abstract_utilities-0.2.2.387.dist-info → abstract_utilities-0.2.2.480.dist-info}/METADATA +1 -1
  60. abstract_utilities-0.2.2.480.dist-info/RECORD +92 -0
  61. abstract_utilities-0.2.2.387.dist-info/RECORD +0 -52
  62. {abstract_utilities-0.2.2.387.dist-info → abstract_utilities-0.2.2.480.dist-info}/WHEEL +0 -0
  63. {abstract_utilities-0.2.2.387.dist-info → abstract_utilities-0.2.2.480.dist-info}/top_level.txt +0 -0
@@ -1,33 +1,22 @@
1
1
  # attach_functions.py — single helper you can import anywhere
2
2
  # attach_dynamic.py
3
3
  from __future__ import annotations
4
- from types import ModuleType
5
- from typing import Iterable
6
- from .file_filters import define_defaults,get_files_and_dirs
4
+ from .imports import *
5
+ def call_for_all_tabs(root = None,tab_control=True):
6
+ root = root or get_caller_dir()
7
+ get_for_all_tabs(root,tab_control=tab_control)
7
8
 
8
- import inspect
9
- from ..read_write_utils import *
10
- import textwrap, pkgutil, os, re, textwrap, sys, types, importlib, importlib.util, inspect
11
- from typing import *
12
- ABSPATH = os.path.abspath(__file__)
13
- ABSROOT = os.path.dirname(ABSPATH)
14
- def get_caller_path():
15
- frame = inspect.stack()[1]
16
- return os.path.abspath(frame.filename)
17
- def get_caller_dir():
18
- frame = inspect.stack()[1]
19
- abspath = os.path.abspath(frame.filename)
20
- return os.path.dirname(abspath)
21
- def call_for_all_tabs():
22
-
23
- root = get_caller_dir()
24
- get_for_all_tabs(root)
25
-
26
- ABSPATH = os.path.abspath(__file__)
27
- ABSROOT = os.path.dirname(ABSPATH)
28
- def clean_imports():
9
+ def get_clean_list(*args):
10
+ objs = []
11
+ for arg in args:
12
+ objs+= make_list(arg)
13
+ return list(set(objs))
14
+ def clean_imports(*args,**kwargs):
15
+ for pkg,imps in kwargs.items():
16
+ f"from {pkg} import make_list(imps)"
17
+
29
18
  alls = str(list(set("""os,re,subprocess,sys,re,traceback,pydot, enum, inspect, sys, traceback, threading,json,traceback,logging,requests""".replace('\n','').replace(' ','').replace('\t','').split(','))))[1:-1].replace('"','').replace("'",'')
30
- input(alls)
19
+ return
31
20
  def isTab(item):
32
21
  item_lower = item.lower()
33
22
  for key in ['console','tab']:
@@ -57,11 +46,17 @@ def ifFunctionsInFile(root):
57
46
  return item
58
47
 
59
48
 
60
- def get_for_all_tabs(root = None):
49
+ def get_for_all_tabs(root = None,tab_control=True):
61
50
  root = root or caller_path()
62
51
  if os.path.isfile(root):
63
52
  root = os.path.dirname(root)
64
- all_tabs = get_dirs(root = root)
53
+ if tab_control:
54
+ all_tabs = get_dirs(root = root)
55
+ else:
56
+ dirname = root
57
+ if root and os.path.isfile(root):
58
+ dirname = os.path.dirname(root)
59
+ all_tabs = [dirname]
65
60
  for ROOT in all_tabs:
66
61
  FUNCS_DIR = ifFunctionsInFile(ROOT)
67
62
  if FUNCS_DIR == None:
@@ -71,52 +66,38 @@ def get_for_all_tabs(root = None):
71
66
  apply_inits(ROOT)
72
67
 
73
68
 
74
- def apply_inits(ROOT):
75
- FUNCS_DIR = ifFunctionsInFile(ROOT)
76
-
77
-
78
- if_fun_dir = isDir(FUNCS_DIR)
79
- if if_fun_dir != None:
80
-
81
- if if_fun_dir:
82
- CFG = define_defaults(allowed_exts='.py',
83
- unallowed_exts = True,
84
- exclude_types = True,
85
- exclude_dirs = True,
86
- exclude_patterns = True)
87
- _,filepaths = get_files_and_dirs(FUNCS_DIR,cfg=CFG)
88
-
89
- else:
90
- filepaths = [FUNCS_DIR]
91
-
92
- # Parse top-level def names
93
- def extract_funcs(path: str):
94
- funcs = []
95
- for line in read_from_file(path).splitlines():
96
- m = re.match(r"^def\s+([A-Za-z_]\w*)\s*\(self", line)
97
- if m:
98
- funcs.append(m.group(1))
99
- return funcs
100
-
101
- # Build functions/__init__.py that re-exports all discovered functions
102
- import_lines = []
103
- all_funcs = []
104
- for fp in filepaths:
105
- module = os.path.splitext(os.path.basename(fp))[0]
106
- funcs = extract_funcs(fp)
107
- if funcs:
108
- import_lines.append(f"from .{module} import ({', '.join(funcs)})")
109
- all_funcs.extend(funcs)
110
- if if_fun_dir:
111
- functions_init = "\n".join(import_lines) + ("\n" if import_lines else "")
112
- write_to_file(contents=functions_init, file_path=os.path.join(FUNCS_DIR, "__init__.py"))
113
-
114
- # Prepare the tuple literal of function names for import + loop
115
- uniq_funcs = sorted(set(all_funcs))
116
- func_tuple = ", ".join(uniq_funcs) + ("," if len(uniq_funcs) == 1 else "")
117
-
118
- # Generate apiConsole/initFuncs.py using the safer setattr-loop
119
- init_funcs_src = textwrap.dedent(f"""\
69
+ def write_init_functions(import_lines,functions_dir):
70
+ functions_init = "\n".join(import_lines) + ("\n" if import_lines else "")
71
+ init_file_path = os.path.join(functions_dir, "__init__.py")
72
+ write_to_file(contents=functions_init, file_path=init_file_path)
73
+ return {"functions_init":functions_init,"init_file_path":init_file_path}
74
+ def extract_funcs(filepaths):
75
+ funcs = []
76
+ for line in read_from_file(path).splitlines():
77
+ m = re.match(r"^def\s+([A-Za-z_]\w*)\s*\(self", line)
78
+ if m:
79
+ funcs.append(m.group(1))
80
+ return funcs
81
+ def get_all_funcs(
82
+ filepaths,
83
+ all_funcs=None,
84
+ import_lines=None
85
+ ):
86
+ import_lines = import_lines or []
87
+ all_funcs = all_funcs or []
88
+ for fp in filepaths:
89
+ basename = os.path.basename(fp)
90
+ module = os.path.splitext(basename)[0]
91
+ funcs = extract_funcs(fp)
92
+ if funcs:
93
+ import_lines.append(f"from .{module} import ({', '.join(funcs)})")
94
+ all_funcs.extend(funcs)
95
+ uniq_funcs = sorted(set(all_funcs))
96
+ func_tuple=", ".join(uniq_funcs) + ("," if len(uniq_funcs) == 1 else "")
97
+ all_funcs_js = {"import_lines":import_lines,"all_funcs":all_funcs,"uniq_funcs":uniq_funcs,"func_tuple":func_tuple}
98
+ return all_funcs_js
99
+ def get_init_funcs_str(func_tuple):
100
+ init_funcs_str = textwrap.dedent(f"""\
120
101
 
121
102
 
122
103
  from .functions import ({func_tuple})
@@ -129,7 +110,42 @@ def apply_inits(ROOT):
129
110
  logger.info(f"{{e}}")
130
111
  return self
131
112
  """)
132
-
133
- write_to_file(contents=init_funcs_src, file_path=os.path.join(ROOT, "initFuncs.py"))
134
-
113
+ return init_funcs_str
114
+ def get_function_file_paths(functions_dir):
115
+ filepaths=[]
116
+ if_fun_dir = isDir(functions_dir)
117
+ if if_fun_dir != None:
118
+ input(if_fun_dir)
119
+ if if_fun_dir:
120
+ CFG = define_defaults(allowed_exts='.py',
121
+ unallowed_exts = True,
122
+ exclude_types = True,
123
+ exclude_dirs = True,
124
+ exclude_patterns = True)
125
+ input(CFG)
126
+ _,filepaths = get_files_and_dirs(functions_dir,cfg=CFG)
127
+ else:
128
+ filepaths = [FUNCS_DIR]
129
+ input(filepaths)
130
+ return filepaths
131
+ def apply_inits(root=None,tab_control=True):
132
+ root = root or get_caller_dir()
133
+ FUNCS_DIR = ifFunctionsInFile(root)
134
+ if_fun_dir = isDir(FUNCS_DIR)
135
+ if if_fun_dir != None:
136
+ file_paths = get_function_file_paths(FUNCS_DIR)
137
+
138
+ all_funcs_js = get_all_funcs(
139
+ filepaths=file_paths
140
+ )
141
+ if if_fun_dir:
142
+ init_func_js = write_init_functions(import_lines=all_funcs_js.get('import_lines'),functions_dir=FUNCS_DIR)
143
+ all_funcs_js.update(init_func_js)
144
+ func_tuple = all_funcs_js.get("func_tuple")
145
+ init_funcs_str = get_init_funcs_str(func_tuple)
146
+ init_funcs_file_path = os.path.join(root, "initFuncs.py")
147
+ all_funcs_js["funcs_str"]=init_funcs_str
148
+ all_funcs_js["funcs_file_path"]=init_funcs_file_path
149
+ write_to_file(contents=init_funcs_str, file_path=init_funcs_file_path)
150
+ return all_funcs_js
135
151
 
@@ -0,0 +1,133 @@
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 typing import *
11
+ from .type_utils import is_number
12
+ from .class_utils import get_caller_dir
13
+
14
+ _BASE_DIR = get_caller_dir()
15
+
16
+
17
+ def safe_split(
18
+ string: Any,
19
+ char: Any,
20
+ i: Optional[int] = None,
21
+ default: Union[bool, Any] = False
22
+ ) -> Union[str, List[str], Any, None]:
23
+ """
24
+ Safely split a string by a character and optionally return index i.
25
+
26
+ Args:
27
+ string: Input string (or any object convertible to string).
28
+ char: Delimiter to split on.
29
+ i: Optional index to retrieve from the split result.
30
+ default: If True, return the original string on error.
31
+ If any other value, return that instead of raising.
32
+
33
+ Returns:
34
+ The split list, or the element at index i, or default behavior on error.
35
+ """
36
+ if string is None or char is None:
37
+ return string
38
+
39
+ s, c = str(string), str(char)
40
+ if c not in s:
41
+ return string
42
+
43
+ parts = s.split(c)
44
+
45
+ if i is None:
46
+ return parts
47
+
48
+ if is_number(i):
49
+ idx = int(i)
50
+ if 0 <= idx < len(parts):
51
+ return parts[idx]
52
+
53
+ if default:
54
+ return string if default is True else default
55
+
56
+ return None
57
+
58
+
59
+ def safe_slice(
60
+ obj: Any,
61
+ i: Optional[int] = None,
62
+ k: Optional[int] = None,
63
+ default: Union[bool, Any] = False
64
+ ) -> Any:
65
+ """
66
+ Safely slice an iterable object or string, with fallback behavior on invalid indices.
67
+
68
+ Args:
69
+ obj: Iterable or string-like object.
70
+ i: Start index (can be negative).
71
+ k: End index (can be negative).
72
+ default: If True, returns the original object on error.
73
+ If any other value, return that value on error.
74
+
75
+ Returns:
76
+ The sliced object, or default behavior on error.
77
+ """
78
+ # Null or invalid base case
79
+ if obj is None or isinstance(obj, bool):
80
+ return obj if default is True else default if default else None
81
+
82
+ # Non-iterable guard
83
+ if not hasattr(obj, "__getitem__"):
84
+ return obj if default is True else default if default else None
85
+
86
+ obj_len = len(obj)
87
+
88
+ # Normalize negative indices
89
+ if isinstance(i, int) and i < 0:
90
+ i = obj_len + i
91
+ if isinstance(k, int) and k < 0:
92
+ k = obj_len + k
93
+
94
+ # Bound indices
95
+ if i is not None:
96
+ i = max(0, min(i, obj_len))
97
+ if k is not None:
98
+ k = max(0, min(k, obj_len))
99
+
100
+ try:
101
+ return obj[i:k]
102
+ except Exception:
103
+ return obj if default is True else default if default else None
104
+
105
+ def safe_join(*paths):
106
+ paths = list(paths)
107
+ paths = [path for path in paths if path]
108
+ return os.path.join(*paths)
109
+ def safe_get(
110
+ obj: Any,
111
+ key: Union[int, str, None] = None,
112
+ default: Union[bool, Any] = False
113
+ ) -> Any:
114
+ """
115
+ Generalized safe getter for both indexable and mapping types.
116
+
117
+ Args:
118
+ obj: The object to access (list, dict, string, etc.).
119
+ key: Index or key to retrieve.
120
+ default: Fallback value or True for "return obj".
121
+
122
+ Returns:
123
+ Retrieved element, or default value on failure.
124
+ """
125
+ if obj is None or key is None:
126
+ return obj if default is True else default if default else None
127
+
128
+ try:
129
+ if isinstance(obj, dict):
130
+ return obj.get(key, obj if default is True else default if default else None)
131
+ return obj[key]
132
+ except Exception:
133
+ return obj if default is True else default if default else None
@@ -0,0 +1,3 @@
1
+ from .classes import *
2
+ from .utils import *
3
+ from .pexpect_utils import *
@@ -0,0 +1,127 @@
1
+ from .imports import *
2
+ from .utils import run_local_cmd, run_ssh_cmd,run_any_cmd,run_cmd
3
+
4
+ class PathBackend(Protocol):
5
+ def join(self, *parts: str) -> str: ...
6
+ def isfile(self, path: str) -> bool: ...
7
+ def isdir(self, path: str) -> bool: ...
8
+ def glob_recursive(self, base: str, **opts) -> List[str]: ...
9
+ def listdir(self, base: str) -> List[str]: ...
10
+
11
+ class LocalFS:
12
+ def join(self, *parts: str) -> str:
13
+ return os.path.join(*parts)
14
+ def isfile(self, path: str) -> bool:
15
+ return os.path.isfile(path)
16
+ def isdir(self, path: str) -> bool:
17
+ return os.path.isdir(path)
18
+ def glob_recursive(self, base: str, **opts) -> List[str]:
19
+ """
20
+ opts:
21
+ - maxdepth: int | None
22
+ - mindepth: int (default 1)
23
+ - follow_symlinks: bool
24
+ - include_dirs: bool
25
+ - include_files: bool
26
+ - exclude_hidden: bool
27
+ """
28
+ maxdepth = opts.get("maxdepth")
29
+ mindepth = opts.get("mindepth", 1)
30
+ follow = opts.get("follow_symlinks", False)
31
+ want_d = opts.get("include_dirs", True)
32
+ want_f = opts.get("include_files", True)
33
+ hide = opts.get("exclude_hidden", False)
34
+
35
+ results: List[str] = []
36
+ base_depth = os.path.normpath(base).count(os.sep)
37
+
38
+ for root, dirs, files in os.walk(base, followlinks=follow):
39
+ depth = os.path.normpath(root).count(os.sep) - base_depth
40
+ if maxdepth is not None and depth > maxdepth:
41
+ dirs[:] = []
42
+ continue
43
+ if want_d and depth >= mindepth:
44
+ for d in dirs:
45
+ if hide and d.startswith("."): continue
46
+ results.append(os.path.join(root, d))
47
+ if want_f and depth >= mindepth:
48
+ for f in files:
49
+ if hide and f.startswith("."): continue
50
+ results.append(os.path.join(root, f))
51
+ return results
52
+
53
+ def listdir(self, base: str) -> List[str]:
54
+ try:
55
+ return [os.path.join(base, name) for name in os.listdir(base)]
56
+ except Exception:
57
+ return []
58
+
59
+ class SSHFS:
60
+ """Remote POSIX backend via your run_remote_cmd."""
61
+ def __init__(self, user_at_host: str):
62
+ self.user_at_host = user_at_host
63
+
64
+ def join(self, *parts: str) -> str:
65
+ return posixpath.join(*parts)
66
+
67
+ def isfile(self, path: str) -> bool:
68
+ cmd = f"test -f {shlex.quote(path)} && echo __OK__ || true"
69
+ out = run_remote_cmd(self.user_at_host, cmd)
70
+ return "__OK__" in (out or "")
71
+
72
+ def isdir(self, path: str) -> bool:
73
+ cmd = f"test -d {shlex.quote(path)} && echo __OK__ || true"
74
+ out = run_remote_cmd(self.user_at_host, cmd)
75
+ return "__OK__" in (out or "")
76
+
77
+ def glob_recursive(self, base: str, **opts) -> List[str]:
78
+ maxdepth = opts.get("maxdepth")
79
+ mindepth = opts.get("mindepth", 1)
80
+ follow = opts.get("follow_symlinks", False)
81
+ want_d = opts.get("include_dirs", True)
82
+ want_f = opts.get("include_files", True)
83
+ hide = opts.get("exclude_hidden", False)
84
+
85
+ parts = []
86
+ if follow:
87
+ parts.append("-L")
88
+ parts += ["find", shlex.quote(base)]
89
+ if mindepth is not None:
90
+ parts += ["-mindepth", str(mindepth)]
91
+ if maxdepth is not None:
92
+ parts += ["-maxdepth", str(maxdepth)]
93
+
94
+ type_filters = []
95
+ if want_d and not want_f:
96
+ type_filters = ["-type", "d"]
97
+ elif want_f and not want_d:
98
+ type_filters = ["-type", "f"]
99
+
100
+ hidden_filter = []
101
+ if hide:
102
+ hidden_filter = ["!", "-regex", r".*/\..*"]
103
+
104
+ cmd = " ".join(parts + type_filters + hidden_filter + ["-printf", r"'%p\n'"]) + " 2>/dev/null"
105
+ out = run_remote_cmd(self.user_at_host, cmd)
106
+ return [line.strip().strip("'") for line in (out or "").splitlines() if line.strip()]
107
+
108
+ def listdir(self, base: str) -> List[str]:
109
+ cmd = f"find {shlex.quote(base)} -maxdepth 1 -mindepth 1 -printf '%p\\n' 2>/dev/null"
110
+ out = run_remote_cmd(self.user_at_host, cmd)
111
+ return [line.strip() for line in (out or "").splitlines() if line.strip()]
112
+
113
+ # ---- auto-detect "user@host:/abs/path" ----
114
+ REMOTE_RE = re.compile(r"^(?P<host>[^:\s]+@[^:\s]+):(?P<path>/.*)$")
115
+
116
+ def normalize_items(paths: Iterable[str],user_at_host=None,**kwargs) -> List[tuple[PathBackend, str]]:
117
+ pairs: List[tuple[PathBackend, str]] = []
118
+ host = user_at_host or kwargs.get('host')
119
+
120
+ for item in paths:
121
+ if not item: continue
122
+ m = REMOTE_RE.match(item)
123
+ if m:
124
+ pairs.append((SSHFS(m.group("host") or user_at_host), m.group("path")))
125
+ else:
126
+ pairs.append((LocalFS(), item))
127
+ return pairs
@@ -0,0 +1,10 @@
1
+ # remote_fs.py
2
+ from __future__ import annotations
3
+ from typing import *
4
+ import subprocess, shlex, os, fnmatch, glob, posixpath, re
5
+ # exec_api.py
6
+ # ---- import your existing pieces ----
7
+ from ..type_utils import make_list
8
+ from ..time_utils import get_sleep
9
+ from ..env_utils import *
10
+ from ..string_clean import eatOuter