abstract-utilities 0.2.2.467__py3-none-any.whl → 0.2.2.513__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.
Files changed (174) hide show
  1. abstract_utilities/__init__.py +6 -10
  2. abstract_utilities/circular_import_finder.py +222 -0
  3. abstract_utilities/circular_import_finder2.py +118 -0
  4. abstract_utilities/class_utils/__init__.py +7 -0
  5. abstract_utilities/class_utils/abstract_classes.py +74 -0
  6. abstract_utilities/class_utils/caller_utils.py +35 -0
  7. abstract_utilities/class_utils/class_utils.py +109 -0
  8. abstract_utilities/class_utils/function_utils.py +153 -0
  9. abstract_utilities/class_utils/global_utils.py +56 -0
  10. abstract_utilities/class_utils/imports/__init__.py +2 -0
  11. abstract_utilities/class_utils/imports/imports.py +2 -0
  12. abstract_utilities/class_utils/imports/utils.py +40 -0
  13. abstract_utilities/class_utils/module_utils.py +63 -0
  14. abstract_utilities/class_utils.py +0 -1
  15. abstract_utilities/directory_utils/__init__.py +4 -0
  16. abstract_utilities/directory_utils/directory_utils.py +94 -0
  17. abstract_utilities/directory_utils/name_utils.py +43 -0
  18. abstract_utilities/directory_utils/size_utils.py +57 -0
  19. abstract_utilities/directory_utils/utils.py +116 -0
  20. abstract_utilities/env_utils/imports/imports.py +5 -3
  21. abstract_utilities/error_utils/__init__.py +2 -0
  22. abstract_utilities/error_utils/error_utils.py +25 -0
  23. abstract_utilities/error_utils/imports/__init__.py +2 -0
  24. abstract_utilities/error_utils/imports/imports.py +1 -0
  25. abstract_utilities/error_utils/imports/module_imports.py +1 -0
  26. abstract_utilities/file_utils/__init__.py +1 -2
  27. abstract_utilities/file_utils/file_utils/imports/__init__.py +0 -2
  28. abstract_utilities/file_utils/file_utils/imports/module_imports.py +2 -1
  29. abstract_utilities/file_utils/file_utils/type_checks.py +34 -24
  30. abstract_utilities/file_utils/imports/__init__.py +0 -1
  31. abstract_utilities/file_utils/imports/clean_imps.py +158 -0
  32. abstract_utilities/file_utils/imports/constants.py +6 -0
  33. abstract_utilities/file_utils/imports/file_functions.py +1 -1
  34. abstract_utilities/file_utils/imports/imports.py +20 -10
  35. abstract_utilities/file_utils/imports/module_imports.py +2 -7
  36. abstract_utilities/file_utils/module_imports.py +12 -0
  37. abstract_utilities/file_utils/src/__init__.py +9 -0
  38. abstract_utilities/file_utils/src/file_filters.py +110 -0
  39. abstract_utilities/file_utils/src/file_reader.py +607 -0
  40. abstract_utilities/file_utils/src/file_utils.py +279 -0
  41. abstract_utilities/file_utils/src/filter_params.py +155 -0
  42. abstract_utilities/file_utils/src/find_collect.py +270 -0
  43. abstract_utilities/file_utils/src/initFunctionsGen.py +286 -0
  44. abstract_utilities/file_utils/src/map_utils.py +29 -0
  45. abstract_utilities/file_utils/src/pdf_utils.py +300 -0
  46. abstract_utilities/file_utils/src/type_checks.py +91 -0
  47. abstract_utilities/hash_utils/__init__.py +2 -0
  48. abstract_utilities/hash_utils/hash_utils.py +5 -0
  49. abstract_utilities/hash_utils/imports/__init__.py +2 -0
  50. abstract_utilities/hash_utils/imports/imports.py +1 -0
  51. abstract_utilities/hash_utils/imports/module_imports.py +0 -0
  52. abstract_utilities/history_utils/__init__.py +2 -0
  53. abstract_utilities/history_utils/history_utils.py +37 -0
  54. abstract_utilities/history_utils/imports/__init__.py +2 -0
  55. abstract_utilities/history_utils/imports/imports.py +1 -0
  56. abstract_utilities/history_utils/imports/module_imports.py +0 -0
  57. abstract_utilities/import_utils/__init__.py +2 -0
  58. abstract_utilities/import_utils/imports/__init__.py +4 -0
  59. abstract_utilities/import_utils/imports/constants.py +2 -0
  60. abstract_utilities/import_utils/imports/imports.py +4 -0
  61. abstract_utilities/import_utils/imports/module_imports.py +6 -0
  62. abstract_utilities/import_utils/imports/utils.py +30 -0
  63. abstract_utilities/import_utils/src/__init__.py +7 -0
  64. abstract_utilities/import_utils/src/clean_imports.py +147 -0
  65. abstract_utilities/import_utils/src/dot_utils.py +69 -0
  66. abstract_utilities/import_utils/src/extract_utils.py +42 -0
  67. abstract_utilities/import_utils/src/import_functions.py +46 -0
  68. abstract_utilities/import_utils/src/import_utils.py +299 -0
  69. abstract_utilities/import_utils/src/package_utils/__init__.py +139 -0
  70. abstract_utilities/import_utils/src/package_utils/context_utils.py +27 -0
  71. abstract_utilities/import_utils/src/package_utils/import_collectors.py +53 -0
  72. abstract_utilities/import_utils/src/package_utils/path_utils.py +28 -0
  73. abstract_utilities/import_utils/src/package_utils/safe_import.py +27 -0
  74. abstract_utilities/import_utils/src/package_utils.py +140 -0
  75. abstract_utilities/import_utils/src/package_utilss/__init__.py +139 -0
  76. abstract_utilities/import_utils/src/package_utilss/context_utils.py +27 -0
  77. abstract_utilities/import_utils/src/package_utilss/import_collectors.py +53 -0
  78. abstract_utilities/import_utils/src/package_utilss/path_utils.py +28 -0
  79. abstract_utilities/import_utils/src/package_utilss/safe_import.py +27 -0
  80. abstract_utilities/import_utils/src/pkg_utils.py +140 -0
  81. abstract_utilities/import_utils/src/sysroot_utils.py +57 -0
  82. abstract_utilities/imports.py +18 -0
  83. abstract_utilities/json_utils/__init__.py +2 -0
  84. abstract_utilities/json_utils/imports/__init__.py +2 -0
  85. abstract_utilities/json_utils/imports/imports.py +2 -0
  86. abstract_utilities/json_utils/imports/module_imports.py +5 -0
  87. abstract_utilities/json_utils/json_utils.py +743 -0
  88. abstract_utilities/list_utils/__init__.py +2 -0
  89. abstract_utilities/list_utils/imports/__init__.py +2 -0
  90. abstract_utilities/list_utils/imports/imports.py +1 -0
  91. abstract_utilities/list_utils/imports/module_imports.py +0 -0
  92. abstract_utilities/list_utils/list_utils.py +199 -0
  93. abstract_utilities/log_utils/__init__.py +5 -0
  94. abstract_utilities/log_utils/abstractLogManager.py +64 -0
  95. abstract_utilities/log_utils/call_response.py +68 -0
  96. abstract_utilities/log_utils/imports/__init__.py +2 -0
  97. abstract_utilities/log_utils/imports/imports.py +7 -0
  98. abstract_utilities/log_utils/imports/module_imports.py +2 -0
  99. abstract_utilities/log_utils/log_file.py +58 -0
  100. abstract_utilities/log_utils/logger_callable.py +49 -0
  101. abstract_utilities/math_utils/__init__.py +2 -0
  102. abstract_utilities/math_utils/imports/__init__.py +2 -0
  103. abstract_utilities/math_utils/imports/imports.py +2 -0
  104. abstract_utilities/math_utils/imports/module_imports.py +1 -0
  105. abstract_utilities/math_utils/math_utils.py +208 -0
  106. abstract_utilities/parse_utils/__init__.py +2 -0
  107. abstract_utilities/parse_utils/imports/__init__.py +3 -0
  108. abstract_utilities/parse_utils/imports/constants.py +10 -0
  109. abstract_utilities/parse_utils/imports/imports.py +2 -0
  110. abstract_utilities/parse_utils/imports/module_imports.py +4 -0
  111. abstract_utilities/parse_utils/parse_utils.py +516 -0
  112. abstract_utilities/path_utils/__init__.py +2 -0
  113. abstract_utilities/path_utils/imports/__init__.py +3 -0
  114. abstract_utilities/path_utils/imports/imports.py +1 -0
  115. abstract_utilities/path_utils/imports/module_imports.py +8 -0
  116. abstract_utilities/path_utils/path_utils.py +251 -0
  117. abstract_utilities/path_utils.py +95 -14
  118. abstract_utilities/read_write_utils/__init__.py +1 -0
  119. abstract_utilities/read_write_utils/imports/__init__.py +2 -0
  120. abstract_utilities/read_write_utils/imports/imports.py +2 -0
  121. abstract_utilities/read_write_utils/imports/module_imports.py +5 -0
  122. abstract_utilities/read_write_utils/read_write_utils.py +338 -0
  123. abstract_utilities/read_write_utils.py +142 -20
  124. abstract_utilities/robust_reader/imports/imports.py +0 -9
  125. abstract_utilities/robust_readers/import_utils/__init__.py +1 -0
  126. abstract_utilities/robust_readers/import_utils/clean_imports.py +175 -0
  127. abstract_utilities/safe_utils/__init__.py +2 -0
  128. abstract_utilities/safe_utils/imports/__init__.py +3 -0
  129. abstract_utilities/safe_utils/imports/imports.py +2 -0
  130. abstract_utilities/safe_utils/imports/module_imports.py +2 -0
  131. abstract_utilities/safe_utils/safe_utils.py +136 -0
  132. abstract_utilities/ssh_utils/__init__.py +3 -1
  133. abstract_utilities/ssh_utils/classes.py +0 -1
  134. abstract_utilities/ssh_utils/cmd_utils.py +207 -0
  135. abstract_utilities/ssh_utils/imports/__init__.py +3 -0
  136. abstract_utilities/ssh_utils/imports/imports.py +5 -0
  137. abstract_utilities/ssh_utils/imports/module_imports.py +6 -0
  138. abstract_utilities/ssh_utils/imports/utils.py +189 -0
  139. abstract_utilities/ssh_utils/pexpect_utils.py +11 -18
  140. abstract_utilities/ssh_utils/type_checks.py +92 -0
  141. abstract_utilities/string_clean.py +40 -1
  142. abstract_utilities/string_utils/__init__.py +4 -0
  143. abstract_utilities/string_utils/clean_utils.py +28 -0
  144. abstract_utilities/string_utils/eat_utils.py +103 -0
  145. abstract_utilities/string_utils/imports/__init__.py +3 -0
  146. abstract_utilities/string_utils/imports/imports.py +2 -0
  147. abstract_utilities/string_utils/imports/module_imports.py +2 -0
  148. abstract_utilities/string_utils/imports/utils.py +81 -0
  149. abstract_utilities/string_utils/replace_utils.py +27 -0
  150. abstract_utilities/string_utils.py +39 -0
  151. abstract_utilities/thread_utils/__init__.py +2 -0
  152. abstract_utilities/thread_utils/imports/__init__.py +2 -0
  153. abstract_utilities/thread_utils/imports/imports.py +2 -0
  154. abstract_utilities/thread_utils/imports/module_imports.py +2 -0
  155. abstract_utilities/thread_utils/thread_utils.py +140 -0
  156. abstract_utilities/time_utils/__init__.py +2 -0
  157. abstract_utilities/time_utils/imports/__init__.py +2 -0
  158. abstract_utilities/time_utils/imports/imports.py +3 -0
  159. abstract_utilities/time_utils/imports/module_imports.py +1 -0
  160. abstract_utilities/time_utils/time_utils.py +392 -0
  161. abstract_utilities/type_utils/__init__.py +3 -0
  162. abstract_utilities/type_utils/alpha_utils.py +59 -0
  163. abstract_utilities/type_utils/imports/__init__.py +2 -0
  164. abstract_utilities/type_utils/imports/imports.py +4 -0
  165. abstract_utilities/type_utils/imports/module_imports.py +1 -0
  166. abstract_utilities/type_utils/num_utils.py +19 -0
  167. abstract_utilities/type_utils/type_utils.py +981 -0
  168. abstract_utilities/type_utils.py +25 -1
  169. {abstract_utilities-0.2.2.467.dist-info → abstract_utilities-0.2.2.513.dist-info}/METADATA +1 -1
  170. abstract_utilities-0.2.2.513.dist-info/RECORD +237 -0
  171. imports/__init__.py +36 -0
  172. abstract_utilities-0.2.2.467.dist-info/RECORD +0 -90
  173. {abstract_utilities-0.2.2.467.dist-info → abstract_utilities-0.2.2.513.dist-info}/WHEEL +0 -0
  174. {abstract_utilities-0.2.2.467.dist-info → abstract_utilities-0.2.2.513.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,279 @@
1
+
2
+ from .filter_params import *
3
+ from ..imports import *
4
+ ##from abstract_utilities import make_list,get_media_exts, is_media_type
5
+ def get_allowed_predicate(allowed=None):
6
+ if allowed != False:
7
+ if allowed == True:
8
+ allowed = None
9
+ allowed = allowed or make_allowed_predicate()
10
+ else:
11
+ def allowed(*args):
12
+ return True
13
+ allowed = allowed
14
+ return allowed
15
+ def get_globs(items,recursive: bool = True,allowed=None):
16
+ glob_paths = []
17
+ items = [item for item in make_list(items) if item]
18
+ for item in items:
19
+ pattern = os.path.join(item, "**/*") # include all files recursively\n
20
+ nuItems = glob.glob(pattern, recursive=recursive)
21
+ if allowed:
22
+ nuItems = [nuItem for nuItem in nuItems if nuItem and allowed(nuItem)]
23
+ glob_paths += nuItems
24
+ return glob_paths
25
+ def get_allowed_files(items,allowed=True):
26
+ allowed = get_allowed_predicate(allowed=allowed)
27
+ return [item for item in items if item and os.path.isfile(item) and allowed(item)]
28
+ def get_allowed_dirs(items,allowed=False):
29
+ allowed = get_allowed_predicate(allowed=allowed)
30
+ return [item for item in items if item and os.path.isdir(item) and allowed(item)]
31
+
32
+ def get_filtered_files(items,allowed=None,files = []):
33
+ allowed = get_allowed_predicate(allowed=allowed)
34
+ glob_paths = get_globs(items)
35
+ return [glob_path for glob_path in glob_paths if glob_path and os.path.isfile(glob_path) and glob_path not in files and allowed(glob_path)]
36
+ def get_filtered_dirs(items,allowed=None,dirs = []):
37
+ allowed = get_allowed_predicate(allowed=allowed)
38
+ glob_paths = get_globs(items)
39
+ return [glob_path for glob_path in glob_paths if glob_path and os.path.isdir(glob_path) and glob_path not in dirs and allowed(glob_path)]
40
+
41
+ def get_all_allowed_files(items,allowed=None):
42
+ dirs = get_all_allowed_dirs(items)
43
+ files = get_allowed_files(items)
44
+ nu_files = []
45
+ for directory in dirs:
46
+ files += get_filtered_files(directory,allowed=allowed,files=files)
47
+ return files
48
+ def get_all_allowed_dirs(items,allowed=None):
49
+ allowed = get_allowed_predicate(allowed=allowed)
50
+ dirs = get_allowed_dirs(items)
51
+ nu_dirs=[]
52
+ for directory in dirs:
53
+ nu_dirs += get_filtered_dirs(directory,allowed=allowed,dirs=nu_dirs)
54
+ return nu_dirs
55
+ def get_files_and_dirs(
56
+ directory: str,
57
+ cfg: Optional["ScanConfig"] = None,
58
+ allowed_exts: Optional[Set[str]] = False,
59
+ unallowed_exts: Optional[Set[str]] = False,
60
+ allowed_types: Optional[Set[str]] = False,
61
+ exclude_types: Optional[Set[str]] = False,
62
+ allowed_dirs: Optional[List[str]] = False,
63
+ exclude_dirs: Optional[List[str]] = False,
64
+ allowed_patterns: Optional[List[str]] = False,
65
+ exclude_patterns: Optional[List[str]] = False,
66
+ add = False,
67
+ recursive: bool = True,
68
+ include_files: bool = True,
69
+ **kwargs
70
+ ):
71
+ cfg = cfg or define_defaults(
72
+ allowed_exts = allowed_exts,
73
+ unallowed_exts = unallowed_exts,
74
+ allowed_types = allowed_types,
75
+ exclude_types = exclude_types,
76
+ allowed_dirs = allowed_dirs,
77
+ exclude_dirs = exclude_dirs,
78
+ allowed_patterns = allowed_patterns,
79
+ exclude_patterns = exclude_patterns,
80
+ add=add
81
+ )
82
+ allowed = make_allowed_predicate(cfg)
83
+ items=[]
84
+ files =[]
85
+ if recursive:
86
+ items = get_globs(directory,recursive=recursive,allowed=allowed)
87
+ else:
88
+ directories = make_list(directory)
89
+ for directory in directories:
90
+ items +=[os.path.join(directory,item) for item in os.listdir(directory)]
91
+ dirs = get_allowed_dirs(items,allowed=allowed)
92
+ if include_files:
93
+ files = get_allowed_files(items,allowed=allowed)
94
+ return dirs,files
95
+ def make_allowed_predicate(cfg: ScanConfig) -> Callable[[str], bool]:
96
+ """
97
+ Build a predicate that returns True if a given path is considered allowed
98
+ under the given ScanConfig. Applies allowed_* and exclude_* logic symmetrically.
99
+ """
100
+ def allowed(path: str=None,p=None) -> bool:
101
+ p = p or Path(path)
102
+ name = p.name.lower()
103
+ path_str = str(p).lower()
104
+
105
+ # --------------------
106
+ # A) directory filters
107
+ # --------------------
108
+ if cfg.exclude_dirs:
109
+ for dpat in cfg.exclude_dirs:
110
+ dpat_l = dpat.lower()
111
+ if dpat_l in path_str or fnmatch.fnmatch(name, dpat_l):
112
+ if p.is_dir() or dpat_l in path_str:
113
+ return False
114
+
115
+ if cfg.allowed_dirs and cfg.allowed_dirs != ["*"]:
116
+ # must be in at least one allowed dir
117
+ if not any(
118
+ fnmatch.fnmatch(path_str, f"*{dpat.lower()}*") for dpat in cfg.allowed_dirs
119
+ ):
120
+ return False
121
+
122
+ # --------------------
123
+ # B) pattern filters
124
+ # --------------------
125
+ if cfg.allowed_patterns and cfg.allowed_patterns != ["*"]:
126
+ if not any(fnmatch.fnmatch(name, pat.lower()) for pat in cfg.allowed_patterns):
127
+ return False
128
+
129
+ if cfg.exclude_patterns:
130
+ for pat in cfg.exclude_patterns:
131
+ if fnmatch.fnmatch(name, pat.lower()):
132
+ return False
133
+
134
+ # --------------------
135
+ # C) extension filters
136
+ # --------------------
137
+ if p.is_file():
138
+ ext = p.suffix.lower()
139
+ if cfg.allowed_exts and ext not in cfg.allowed_exts:
140
+ return False
141
+ if cfg.unallowed_exts and ext in cfg.unallowed_exts:
142
+ return False
143
+
144
+ # --------------------
145
+ # D) type filters (optional)
146
+ # --------------------
147
+ if cfg.allowed_types and cfg.allowed_types != {"*"}:
148
+ if not any(t in path_str for t in cfg.allowed_types):
149
+ return False
150
+ if cfg.exclude_types and cfg.exclude_types != {"*"}:
151
+ if any(t in path_str for t in cfg.exclude_types):
152
+ return False
153
+
154
+ return True
155
+
156
+ return allowed
157
+ def correct_kwargs(**kwargs):
158
+ for key,values in kwargs.items():
159
+ if key.startswith('excluded'):
160
+ post_fix = key.split('_')[-1]
161
+ correct_key = f'exclude_{post_fix}'
162
+ correct_vals = kwargs.get(correct_key)
163
+ kwargs[correct_key]=combine_params(correct_vals,values)
164
+ del kwargs[key]
165
+
166
+ def collect_filepaths(
167
+ directory: List[str],
168
+ cfg: ScanConfig=None,
169
+ allowed_exts: Optional[Set[str]] = False,
170
+ unallowed_exts: Optional[Set[str]] = False,
171
+ allowed_types: Optional[Set[str]] = False,
172
+ exclude_types: Optional[Set[str]] = False,
173
+ allowed_dirs: Optional[List[str]] = False,
174
+ exclude_dirs: Optional[List[str]] = False,
175
+ allowed_patterns: Optional[List[str]] = False,
176
+ exclude_patterns: Optional[List[str]] = False,
177
+ add=False,
178
+ allowed: Optional[Callable[[str], bool]] = None,
179
+ **kwargs
180
+ ) -> List[str]:
181
+ kwargs = correct_kwargs(allowed_exts = allowed_exts,
182
+ unallowed_exts = unallowed_exts,
183
+ allowed_types = allowed_types,
184
+ exclude_types = exclude_types,
185
+ allowed_dirs = allowed_dirs,
186
+ exclude_dirs = exclude_dirs,
187
+ allowed_patterns = allowed_patterns,
188
+ exclude_patterns = exclude_patterns,
189
+ **kwargs)
190
+ cfg = cfg or define_defaults(
191
+ allowed_exts = allowed_exts,
192
+ unallowed_exts = unallowed_exts,
193
+ allowed_types = allowed_types,
194
+ exclude_types = exclude_types,
195
+ allowed_dirs = allowed_dirs,
196
+ exclude_dirs = exclude_dirs,
197
+ allowed_patterns = allowed_patterns,
198
+ exclude_patterns = exclude_patterns,
199
+ add = add
200
+ )
201
+ allowed = allowed or make_allowed_predicate(cfg)
202
+ directories = make_list(directory)
203
+ roots = [r for r in directories if r]
204
+
205
+ # your existing helpers (get_dirs, get_globs, etc.) stay the same
206
+ original_dirs = get_allowed_dirs(roots, allowed=allowed)
207
+ original_globs = get_globs(original_dirs)
208
+ files = get_allowed_files(original_globs, allowed=allowed)
209
+
210
+ for d in get_filtered_dirs(original_dirs, allowed=allowed):
211
+ files += get_filtered_files(d, allowed=allowed, files=files)
212
+
213
+ # de-dupe while preserving order
214
+ seen, out = set(), []
215
+ for f in files:
216
+ if f not in seen:
217
+ seen.add(f)
218
+ out.append(f)
219
+ return out
220
+
221
+
222
+ def _fast_walk(
223
+ root: Path,
224
+ exts: Iterable[str],
225
+ skip_dirs: Iterable[str] = (),
226
+ skip_patterns: Iterable[str] = (),
227
+ ) -> List[Path]:
228
+ exts = tuple(exts)
229
+ skip_dirs = set(sd.lower() for sd in skip_dirs or ())
230
+ skip_patterns = tuple(sp.lower() for sp in (skip_patterns or ()))
231
+
232
+ out = []
233
+ for p in root.rglob("*"):
234
+ # skip directories by name hit
235
+ if p.is_dir():
236
+ name = p.name.lower()
237
+ if name in skip_dirs:
238
+ # rglob doesn't let us prune mid-iteration cleanly; we just won't collect under it
239
+ continue
240
+ # nothing to collect for dirs
241
+ continue
242
+
243
+ # file filters
244
+ name = p.name.lower()
245
+ if any(fnmatch.fnmatch(name, pat) for pat in skip_patterns):
246
+ continue
247
+ if p.suffix.lower() in exts:
248
+ out.append(p)
249
+
250
+ # de-dup and normalize
251
+ return sorted({pp.resolve() for pp in out})
252
+
253
+
254
+ def enumerate_source_files(
255
+ src_root: Path,
256
+ cfg: Optional["ScanConfig"] = None,
257
+ *,
258
+ exts: Optional[Iterable[str]] = None,
259
+ fast_skip_dirs: Optional[Iterable[str]] = None,
260
+ fast_skip_patterns: Optional[Iterable[str]] = None,
261
+ ) -> List[Path]:
262
+ """
263
+ Unified enumerator:
264
+ - If `cfg` is provided: use collect_filepaths(...) with full rules.
265
+ - Else: fast walk using rglob over `exts` (defaults to EXTS) with optional light excludes.
266
+ """
267
+ src_root = Path(src_root)
268
+
269
+ if cfg is not None:
270
+ files = collect_filepaths([str(src_root)], cfg=cfg)
271
+ return sorted({Path(f).resolve() for f in files})
272
+
273
+ # Fast mode
274
+ return _fast_walk(
275
+ src_root,
276
+ exts or EXTS,
277
+ skip_dirs=fast_skip_dirs or (),
278
+ skip_patterns=fast_skip_patterns or (),
279
+ )
@@ -0,0 +1,155 @@
1
+
2
+ from ..imports import *
3
+ import re
4
+ def combine_params(*values,typ=None):
5
+ nu_values = None
6
+ for value in values:
7
+ if value is not None:
8
+ if nu_values is None:
9
+ typ = typ or type(value)
10
+ nu_values = typ()
11
+ else:
12
+ if val_type is set:
13
+ nu_values = nu_values | typ(value)
14
+ if val_type is list:
15
+ nu_values += typ(value)
16
+ return nu_values
17
+ # -------------------------
18
+ # Default sets
19
+ # -------------------------
20
+
21
+ DEFAULT_ALLOWED_EXTS: Set[str] = {
22
+ ".py", ".pyw", ".js", ".jsx", ".ts", ".tsx", ".mjs",
23
+ ".html", ".htm", ".xml", ".css", ".scss", ".sass", ".less",
24
+ ".json", ".yaml", ".yml", ".toml", ".ini", ".cfg",
25
+ ".md", ".markdown", ".rst", ".sh", ".bash", ".env", ".txt"
26
+ }
27
+
28
+ DEFAULT_EXCLUDE_TYPES: Set[str] = {
29
+ "image", "video", "audio", "presentation",
30
+ "spreadsheet", "archive", "executable"
31
+ }
32
+
33
+ _unallowed = set(get_media_exts(DEFAULT_EXCLUDE_TYPES)) | {
34
+ ".bak", ".shp", ".cpg", ".dbf", ".shx", ".geojson",
35
+ ".pyc", ".prj", ".sbn", ".sbx"
36
+ }
37
+ DEFAULT_UNALLOWED_EXTS = {e for e in _unallowed if e not in DEFAULT_ALLOWED_EXTS}
38
+
39
+ DEFAULT_EXCLUDE_DIRS: List[str] = [
40
+ "node_modules", "__pycache__", "backups", "backup",
41
+ "trash", "deprecated", "old", "__init__"
42
+ ]
43
+
44
+ DEFAULT_EXCLUDE_PATTERNS: List[str] = [
45
+ "__init__*", "*.tmp", "*.log", "*.lock", "*.zip", "*~"
46
+ ]
47
+
48
+ DEFAULT_ALLOWED_PATTERNS: List[str] = ["*"]
49
+ DEFAULT_ALLOWED_DIRS: List[str] = ["*"]
50
+ DEFAULT_ALLOWED_TYPES: List[str] = ["*"]
51
+
52
+ REMOTE_RE = re.compile(r"^(?P<host>[^:\s]+@[^:\s]+):(?P<path>/.*)$")
53
+ AllowedPredicate = Optional[Callable[[str], bool]]
54
+
55
+ # -------------------------
56
+ # Config dataclass
57
+ # -------------------------
58
+
59
+ @dataclass
60
+ class ScanConfig:
61
+ allowed_exts: Set[str]
62
+ unallowed_exts: Set[str]
63
+ allowed_types: Set[str]
64
+ exclude_types: Set[str]
65
+ allowed_dirs: List[str] = field(default_factory=list)
66
+ exclude_dirs: List[str] = field(default_factory=list)
67
+ allowed_patterns: List[str] = field(default_factory=list)
68
+ exclude_patterns: List[str] = field(default_factory=list)
69
+
70
+ # -------------------------
71
+ # Utility functions
72
+ # -------------------------
73
+
74
+ def _normalize_listlike(value, typ=list, sep=','):
75
+ """Normalize comma-separated or iterable values into the desired type."""
76
+ if value in [True, None, False]:
77
+ return value
78
+ if isinstance(value, str):
79
+ value = [v.strip() for v in value.split(sep) if v.strip()]
80
+ return typ(value)
81
+
82
+ def ensure_exts(exts):
83
+ if exts in [True, None, False]:
84
+ return exts
85
+ out = []
86
+ for ext in _normalize_listlike(exts, list):
87
+ if not ext.startswith('.'):
88
+ ext = f".{ext}"
89
+ out.append(ext)
90
+ return set(out)
91
+
92
+ def ensure_patterns(patterns):
93
+ """Normalize pattern list and ensure they are valid globs."""
94
+ if patterns in [True, None, False]:
95
+ return patterns
96
+ patterns = _normalize_listlike(patterns, list)
97
+ out = []
98
+ for pattern in patterns:
99
+ if not pattern:
100
+ continue
101
+ if '*' not in pattern and '?' not in pattern:
102
+ # Implicitly make it a prefix match
103
+ if pattern.startswith('.') or pattern.startswith('~'):
104
+ pattern = f"*{pattern}"
105
+ else:
106
+ pattern = f"{pattern}*"
107
+ out.append(pattern)
108
+ return out
109
+
110
+
111
+ def _get_default_modular(value, default, add=False, typ=set):
112
+ """Merge user and default values intelligently."""
113
+ if value in [True, None]:
114
+ return default
115
+ if value is False:
116
+ return typ()
117
+ if add:
118
+ return combine_params(value,default,typ=None)
119
+
120
+ return typ(value)
121
+
122
+ # -------------------------
123
+ # Default derivation logic
124
+ # -------------------------
125
+
126
+ def derive_file_defaults(
127
+ allowed_exts=False, unallowed_exts=False,
128
+ allowed_types=False, exclude_types=False,
129
+ allowed_dirs=False, exclude_dirs=False,
130
+ allowed_patterns=False, exclude_patterns=False,
131
+ add=False
132
+ ):
133
+ allowed_exts = _get_default_modular(ensure_exts(allowed_exts), DEFAULT_ALLOWED_EXTS, add, set)
134
+ unallowed_exts = _get_default_modular(ensure_exts(unallowed_exts), DEFAULT_UNALLOWED_EXTS, add, set)
135
+ allowed_types = _get_default_modular(_normalize_listlike(allowed_types, set), DEFAULT_ALLOWED_TYPES, add, set)
136
+ exclude_types = _get_default_modular(_normalize_listlike(exclude_types, set), DEFAULT_EXCLUDE_TYPES, add, set)
137
+ allowed_dirs = _get_default_modular(_normalize_listlike(allowed_dirs, list), DEFAULT_ALLOWED_DIRS, add, list)
138
+ exclude_dirs = _get_default_modular(_normalize_listlike(exclude_dirs, list), DEFAULT_EXCLUDE_DIRS, add, list)
139
+ allowed_patterns = _get_default_modular(ensure_patterns(allowed_patterns), DEFAULT_ALLOWED_PATTERNS, add, list)
140
+ exclude_patterns = _get_default_modular(ensure_patterns(exclude_patterns), DEFAULT_EXCLUDE_PATTERNS, add, list)
141
+
142
+ return {
143
+ "allowed_exts": allowed_exts,
144
+ "unallowed_exts": unallowed_exts,
145
+ "allowed_types": allowed_types,
146
+ "exclude_types": exclude_types,
147
+ "allowed_dirs": allowed_dirs,
148
+ "exclude_dirs": exclude_dirs,
149
+ "allowed_patterns": allowed_patterns,
150
+ "exclude_patterns": exclude_patterns,
151
+ }
152
+
153
+ def define_defaults(**kwargs):
154
+ defaults = derive_file_defaults(**kwargs)
155
+ return ScanConfig(**defaults)
@@ -0,0 +1,270 @@
1
+ from ..imports import *
2
+ from .filter_params import *
3
+ from .file_filters import enumerate_source_files
4
+ from pathlib import Path
5
+ from typing import Optional, List, Set
6
+
7
+ def check_path_type(
8
+ path: str,
9
+ user: Optional[str] = None,
10
+ host: Optional[str] = None,
11
+ user_as_host: Optional[str] = None,
12
+ use_shell: bool = False
13
+ ) -> Literal["file", "directory", "missing", "unknown"]:
14
+ """
15
+ Determine whether a given path is a file, directory, or missing.
16
+ Works locally or remotely (via SSH).
17
+
18
+ Args:
19
+ path: The path to check.
20
+ user, host, user_as_host: SSH parameters if remote.
21
+ use_shell: Force shell test instead of Python os.path.
22
+ Returns:
23
+ One of: 'file', 'directory', 'missing', or 'unknown'
24
+ """
25
+
26
+ # --- remote check if user/host is given ---
27
+ if user_as_host or (user and host):
28
+ remote_target = user_as_host or f"{user}@{host}"
29
+ cmd = f"if [ -f '{path}' ]; then echo file; elif [ -d '{path}' ]; then echo directory; else echo missing; fi"
30
+ try:
31
+ result = subprocess.check_output(
32
+ ["ssh", remote_target, cmd],
33
+ stderr=subprocess.DEVNULL,
34
+ text=True,
35
+ timeout=5
36
+ ).strip()
37
+ return result if result in ("file", "directory", "missing") else "unknown"
38
+ except Exception:
39
+ return "unknown"
40
+
41
+ # --- local check ---
42
+ if not use_shell:
43
+ if os.path.isfile(path):
44
+ return "file"
45
+ elif os.path.isdir(path):
46
+ return "directory"
47
+ elif not os.path.exists(path):
48
+ return "missing"
49
+ return "unknown"
50
+ else:
51
+ # fallback using shell tests (useful for sandboxed contexts)
52
+ cmd = f"if [ -f '{path}' ]; then echo file; elif [ -d '{path}' ]; then echo directory; else echo missing; fi"
53
+ try:
54
+ output = subprocess.check_output(
55
+ cmd, shell=True, stderr=subprocess.DEVNULL, text=True
56
+ ).strip()
57
+ return output if output in ("file", "directory", "missing") else "unknown"
58
+ except Exception:
59
+ return "unknown"
60
+
61
+
62
+
63
+
64
+ def get_find_cmd(
65
+ directory: str,
66
+ *,
67
+ cfg: Optional["ScanConfig"] = None,
68
+ *,
69
+ allowed_exts: Optional[Set[str]] = False,
70
+ unallowed_exts: Optional[Set[str]] = False,
71
+ allowed_types: Optional[Set[str]] = False,
72
+ exclude_types: Optional[Set[str]] = False,
73
+ allowed_dirs: Optional[List[str]] = False,
74
+ exclude_dirs: Optional[List[str]] = False,
75
+ allowed_patterns: Optional[List[str]] = False,
76
+ exclude_patterns: Optional[List[str]] = False,
77
+ mindepth: Optional[int] = None,
78
+ maxdepth: Optional[int] = None,
79
+ depth: Optional[int] = None,
80
+ file_type: Optional[str] = None, # 'f' or 'd'
81
+ name: Optional[str] = None,
82
+ size: Optional[str] = None,
83
+ mtime: Optional[str] = None,
84
+ perm: Optional[str] = None,
85
+ user: Optional[str] = None,
86
+ **kwargs
87
+ ) -> str:
88
+ """
89
+ Construct a Unix `find` command string from cfg and explicit keyword args.
90
+
91
+ - Honors allowed/excluded patterns, dirs, and extensions from ScanConfig.
92
+ - Automatically applies mindepth/maxdepth/depth/file_type filters.
93
+ """
94
+ cmd = [f"find {shlex.quote(directory)}"]
95
+
96
+ # --- depth ---
97
+ if depth is not None:
98
+ cmd += [f"-mindepth {depth}", f"-maxdepth {depth}"]
99
+ else:
100
+ if mindepth is not None:
101
+ cmd.append(f"-mindepth {mindepth}")
102
+ if maxdepth is not None:
103
+ cmd.append(f"-maxdepth {maxdepth}")
104
+
105
+ # --- file type ---
106
+ if file_type in ("f", "d"):
107
+ cmd.append(f"-type {file_type}")
108
+
109
+ # --- base attributes ---
110
+ if name:
111
+ cmd.append(f"-name {shlex.quote(name)}")
112
+ if size:
113
+ cmd.append(f"-size {shlex.quote(size)}")
114
+ if mtime:
115
+ cmd.append(f"-mtime {shlex.quote(mtime)}")
116
+ if perm:
117
+ cmd.append(f"-perm {shlex.quote(perm)}")
118
+ if user:
119
+ cmd.append(f"-user {shlex.quote(user)}")
120
+ cfg = cfg or define_defaults(
121
+ allowed_exts = allowed_exts,
122
+ unallowed_exts = unallowed_exts,
123
+ allowed_types = allowed_types,
124
+ exclude_types = exclude_types,
125
+ allowed_dirs = allowed_dirs,
126
+ exclude_dirs = exclude_dirs,
127
+ allowed_patterns = allowed_patterns,
128
+ exclude_patterns = exclude_patterns,
129
+ add = add
130
+ )
131
+ # --- cfg-based filters ---
132
+ if cfg:
133
+ # allowed extensions
134
+ if cfg.allowed_exts and cfg.allowed_exts != {"*"}:
135
+ ext_expr = " -o ".join(
136
+ [f"-name '*{e}'" for e in cfg.allowed_exts if e]
137
+ )
138
+ cmd.append(f"\\( {ext_expr} \\)")
139
+
140
+ # disallowed extensions
141
+ if cfg.unallowed_exts:
142
+ for e in cfg.unallowed_exts:
143
+ cmd.append(f"! -name '*{e}'")
144
+
145
+ # allowed directories (match any path)
146
+ if cfg.allowed_dirs and cfg.allowed_dirs != ["*"]:
147
+ dir_expr = " -o ".join(
148
+ [f"-path '*{d}*'" for d in cfg.allowed_dirs if d]
149
+ )
150
+ cmd.append(f"\\( {dir_expr} \\)")
151
+
152
+ # exclude directories
153
+ if cfg.exclude_dirs:
154
+ for d in cfg.exclude_dirs:
155
+ cmd.append(f"! -path '*{d}*'")
156
+
157
+ # allowed patterns
158
+ if cfg.allowed_patterns and cfg.allowed_patterns != ["*"]:
159
+ pat_expr = " -o ".join(
160
+ [f"-name '{p}'" for p in cfg.allowed_patterns if p]
161
+ )
162
+ cmd.append(f"\\( {pat_expr} \\)")
163
+
164
+ # exclude patterns
165
+ if cfg.exclude_patterns:
166
+ for p in cfg.exclude_patterns:
167
+ cmd.append(f"! -name '{p}'")
168
+
169
+ # allowed types
170
+ if cfg.allowed_types and cfg.allowed_types != {"*"}:
171
+ type_expr = " -o ".join(
172
+ [f"-path '*{t}*'" for t in cfg.allowed_types if t]
173
+ )
174
+ cmd.append(f"\\( {type_expr} \\)")
175
+
176
+ # excluded types
177
+ if cfg.exclude_types:
178
+ for t in cfg.exclude_types:
179
+ cmd.append(f"! -path '*{t}*'")
180
+
181
+ return " ".join(cmd)
182
+
183
+
184
+
185
+ def collect_globs(
186
+ directory: str,
187
+ cfg: Optional["ScanConfig"] = None,
188
+ *,
189
+ allowed_exts: Optional[Set[str]] = False,
190
+ unallowed_exts: Optional[Set[str]] = False,
191
+ allowed_types: Optional[Set[str]] = False,
192
+ exclude_types: Optional[Set[str]] = False,
193
+ allowed_dirs: Optional[List[str]] = False,
194
+ exclude_dirs: Optional[List[str]] = False,
195
+ allowed_patterns: Optional[List[str]] = False,
196
+ exclude_patterns: Optional[List[str]] = False,
197
+ patterns: Optional[List[str]] = None,
198
+ mindepth: Optional[int] = None,
199
+ maxdepth: Optional[int] = None,
200
+ depth: Optional[int] = None,
201
+ file_type: Optional[str] = None, # "f", "d", or None
202
+ user_at_host: Optional[str] = None,
203
+ add: bool = False,
204
+ **kwargs
205
+ ) -> List[str] | dict:
206
+ """
207
+ Collect file or directory paths recursively.
208
+
209
+ - If file_type is None → returns {"f": [...], "d": [...]}
210
+ - If file_type is "f" or "d" → returns a list of that type
211
+ - Supports SSH mode via `user_at_host`
212
+ """
213
+ cfg = cfg or define_defaults(
214
+ allowed_exts = allowed_exts,
215
+ unallowed_exts = unallowed_exts,
216
+ allowed_types = allowed_types,
217
+ exclude_types = exclude_types,
218
+ allowed_dirs = allowed_dirs,
219
+ exclude_dirs = exclude_dirs,
220
+ allowed_patterns = allowed_patterns,
221
+ exclude_patterns = exclude_patterns,
222
+ add = add
223
+ )
224
+ allowed = allowed or make_allowed_predicate(cfg)
225
+ directory = str(directory)
226
+
227
+ # Remote path (SSH)
228
+ if user_at_host:
229
+ find_cmd = get_find_cmd(
230
+ directory,
231
+ cfg=cfg,
232
+ mindepth=mindepth,
233
+ maxdepth=maxdepth,
234
+ depth=depth,
235
+ file_type=file_type,
236
+ **{k: v for k, v in kwargs.items() if v},
237
+ )
238
+ return run_cmd(find_cmd, user_at_host=user_at_host)
239
+
240
+ # Local path (Python-native walk)
241
+ root = Path(directory)
242
+ results_js = {"f": [], "d": []}
243
+
244
+ for p in root.rglob("*"):
245
+ if p.is_file():
246
+ kind = "f"
247
+ elif p.is_dir():
248
+ kind = "d"
249
+ else:
250
+ continue
251
+
252
+ # If file_type is specified, skip the other kind
253
+ if file_type and kind != file_type:
254
+ continue
255
+
256
+ if exts and kind == "f" and p.suffix.lower() not in exts:
257
+ continue
258
+
259
+ if patterns and not any(p.match(pat) for pat in patterns):
260
+ continue
261
+
262
+ results_js[kind].append(str(p))
263
+
264
+ # Return based on selection
265
+ if file_type is None:
266
+ return results_js
267
+ else:
268
+ return results_js[file_type]
269
+
270
+