abstract-utilities 0.2.2.419__tar.gz → 0.2.2.421__tar.gz
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-0.2.2.419 → abstract_utilities-0.2.2.421}/PKG-INFO +1 -1
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/setup.py +1 -1
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/__init__.py +1 -2
- abstract_utilities-0.2.2.421/src/abstract_utilities/file_utils/__init__.py +3 -0
- abstract_utilities-0.2.2.421/src/abstract_utilities/file_utils/file_utils/file_utils.py +208 -0
- abstract_utilities-0.2.2.421/src/abstract_utilities/file_utils/imports/classes.py +406 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/imports/module_imports.py +3 -3
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/ssh_utils/utils.py +72 -6
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities.egg-info/PKG-INFO +1 -1
- abstract_utilities-0.2.2.419/src/abstract_utilities/file_utils/__init__.py +0 -3
- abstract_utilities-0.2.2.419/src/abstract_utilities/file_utils/file_utils/file_utils.py +0 -194
- abstract_utilities-0.2.2.419/src/abstract_utilities/file_utils/imports/classes.py +0 -128
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/README.md +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/pyproject.toml +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/setup.cfg +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/abstract_classes.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/class_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/cmd_utils/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/cmd_utils/cmd_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/cmd_utils/imports/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/cmd_utils/imports/imports.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/cmd_utils/pexpect_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/cmd_utils/user_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/collator_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/compare_utils/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/compare_utils/best_match.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/compare_utils/compare_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/compare_utils/find_value.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/doit.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/dynimport.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/env_utils/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/env_utils/abstractEnv.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/env_utils/envy_it.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/env_utils/imports/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/env_utils/imports/imports.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/env_utils/imports/utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/error_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/file_utils/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/file_utils/file_filters.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/file_utils/file_reader.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/file_utils/filter_params.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/file_utils/imports.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/file_utils/map_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/file_utils/pdf_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/imports/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/imports/constants.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/imports/imports.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/file_utils/req.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/global_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/hash_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/history_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/json_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/list_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/log_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/math_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/parse_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/path_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/read_write_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_reader/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_reader/file_reader2.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_reader/file_readers.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_reader/imports/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_reader/imports/imports.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_reader/sadfsad.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/import_utils/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/import_utils/dot_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/import_utils/function_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/import_utils/import_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/import_utils/impot_functions.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/import_utils/safe_import_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/import_utils/sysroot_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/import_utils/utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/imports.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/robust_readers/initFuncGen.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/ssh_utils/__init__.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/ssh_utils/classes.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/ssh_utils/imports.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/ssh_utils/pexpect_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/string_clean.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/tetsts.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/thread_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/time_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/type_utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/utils.py +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities.egg-info/SOURCES.txt +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities.egg-info/dependency_links.txt +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities.egg-info/requires.txt +0 -0
- {abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: abstract_utilities
|
|
3
|
-
Version: 0.2.2.
|
|
3
|
+
Version: 0.2.2.421
|
|
4
4
|
Summary: abstract_utilities is a collection of utility modules providing a variety of functions to aid in tasks such as data comparison, list manipulation, JSON handling, string manipulation, mathematical computations, and time operations.
|
|
5
5
|
Home-page: https://github.com/AbstractEndeavors/abstract_utilities
|
|
6
6
|
Author: putkoff
|
|
@@ -4,7 +4,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
4
4
|
long_description = fh.read()
|
|
5
5
|
setuptools.setup(
|
|
6
6
|
name='abstract_utilities',
|
|
7
|
-
version='0.2.2.
|
|
7
|
+
version='0.2.2.421',
|
|
8
8
|
author='putkoff',
|
|
9
9
|
author_email='partners@abstractendeavors.com',
|
|
10
10
|
description='abstract_utilities is a collection of utility modules providing a variety of functions to aid in tasks such as data comparison, list manipulation, JSON handling, string manipulation, mathematical computations, and time operations.',
|
{abstract_utilities-0.2.2.419 → abstract_utilities-0.2.2.421}/src/abstract_utilities/__init__.py
RENAMED
|
@@ -3,9 +3,8 @@ from datetime import timedelta
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from typing import *
|
|
5
5
|
from .hash_utils import *
|
|
6
|
-
from .dynimport import get_abstract_import,import_symbols_to_parent,call_for_all_tabs
|
|
6
|
+
##from .dynimport import get_abstract_import,import_symbols_to_parent,call_for_all_tabs
|
|
7
7
|
|
|
8
|
-
from .robust_readers import *
|
|
9
8
|
from .json_utils import (unified_json_loader,
|
|
10
9
|
find_keys,
|
|
11
10
|
get_key_values_from_path,
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from .imports import *
|
|
2
|
+
from .filter_params import *
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# -------------------------------------------------------------
|
|
6
|
+
# Wrapper: respects your original API and naming conventions
|
|
7
|
+
# -------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
def get_allowed_predicate(allowed=None):
|
|
10
|
+
if allowed is not False:
|
|
11
|
+
if allowed is True:
|
|
12
|
+
allowed = None
|
|
13
|
+
allowed = allowed or make_allowed_predicate()
|
|
14
|
+
else:
|
|
15
|
+
def allowed(*args):
|
|
16
|
+
return True
|
|
17
|
+
return allowed
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# -------------------------------------------------------------
|
|
21
|
+
# Remote-aware globbing
|
|
22
|
+
# -------------------------------------------------------------
|
|
23
|
+
def get_globs(items, recursive: bool = True, allowed=None, **kwargs):
|
|
24
|
+
"""
|
|
25
|
+
Behaves like your original get_globs(), but can traverse both
|
|
26
|
+
local and remote paths transparently via normalize_items().
|
|
27
|
+
"""
|
|
28
|
+
glob_paths = []
|
|
29
|
+
roots = [p for p in make_list(items) if p]
|
|
30
|
+
|
|
31
|
+
kwargs.setdefault("mindepth", 1)
|
|
32
|
+
if not recursive:
|
|
33
|
+
kwargs.setdefault("maxdepth", 1)
|
|
34
|
+
|
|
35
|
+
for fs, root, _ in normalize_items(roots, **kwargs):
|
|
36
|
+
# use the backend's recursive walker
|
|
37
|
+
nu_items = fs.glob_recursive(root, **kwargs)
|
|
38
|
+
if allowed:
|
|
39
|
+
nu_items = [n for n in nu_items if n and allowed(n)]
|
|
40
|
+
glob_paths += nu_items
|
|
41
|
+
return glob_paths
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# -------------------------------------------------------------
|
|
45
|
+
# Allowed filters
|
|
46
|
+
# -------------------------------------------------------------
|
|
47
|
+
def get_allowed_files(items, allowed=True, **kwargs):
|
|
48
|
+
allowed = get_allowed_predicate(allowed=allowed)
|
|
49
|
+
out = []
|
|
50
|
+
for fs, item, _ in normalize_items(items, **kwargs):
|
|
51
|
+
if fs.isfile(item) and allowed(item):
|
|
52
|
+
out.append(item)
|
|
53
|
+
return out
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_allowed_dirs(items, allowed=False, **kwargs):
|
|
57
|
+
allowed = get_allowed_predicate(allowed=allowed)
|
|
58
|
+
out = []
|
|
59
|
+
for fs, item, _ in normalize_items(items, **kwargs):
|
|
60
|
+
if fs.isdir(item) and allowed(item):
|
|
61
|
+
out.append(item)
|
|
62
|
+
return out
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# -------------------------------------------------------------
|
|
66
|
+
# Filtered sets
|
|
67
|
+
# -------------------------------------------------------------
|
|
68
|
+
def get_filtered_files(items, allowed=None, files=None, **kwargs):
|
|
69
|
+
allowed = get_allowed_predicate(allowed=allowed)
|
|
70
|
+
files = set(files or [])
|
|
71
|
+
out = []
|
|
72
|
+
for fs, root, _ in normalize_items(items, **kwargs):
|
|
73
|
+
for p in fs.glob_recursive(root, **kwargs):
|
|
74
|
+
if p in files:
|
|
75
|
+
continue
|
|
76
|
+
if allowed(p) and fs.isfile(p):
|
|
77
|
+
out.append(p)
|
|
78
|
+
return out
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_filtered_dirs(items, allowed=None, dirs=None, **kwargs):
|
|
82
|
+
allowed = get_allowed_predicate(allowed=allowed)
|
|
83
|
+
dirs = set(dirs or [])
|
|
84
|
+
out = []
|
|
85
|
+
for fs, root, _ in normalize_items(items, **kwargs):
|
|
86
|
+
for p in fs.glob_recursive(root, **kwargs):
|
|
87
|
+
if p in dirs:
|
|
88
|
+
continue
|
|
89
|
+
if allowed(p) and fs.isdir(p):
|
|
90
|
+
out.append(p)
|
|
91
|
+
return out
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# -------------------------------------------------------------
|
|
95
|
+
# Recursive expansion
|
|
96
|
+
# -------------------------------------------------------------
|
|
97
|
+
def get_all_allowed_files(items, allowed=None, **kwargs):
|
|
98
|
+
dirs = get_all_allowed_dirs(items, allowed=allowed, **kwargs)
|
|
99
|
+
files = get_allowed_files(items, allowed=allowed, **kwargs)
|
|
100
|
+
seen = set(files)
|
|
101
|
+
for fs, directory, _ in normalize_items(dirs, **kwargs):
|
|
102
|
+
for p in fs.glob_recursive(directory, **kwargs):
|
|
103
|
+
if p in seen:
|
|
104
|
+
continue
|
|
105
|
+
if allowed and not allowed(p):
|
|
106
|
+
continue
|
|
107
|
+
if fs.isfile(p):
|
|
108
|
+
files.append(p)
|
|
109
|
+
seen.add(p)
|
|
110
|
+
return files
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_all_allowed_dirs(items, allowed=None, **kwargs):
|
|
114
|
+
allowed = get_allowed_predicate(allowed=allowed)
|
|
115
|
+
out = []
|
|
116
|
+
seen = set()
|
|
117
|
+
for fs, root, _ in normalize_items(items, **kwargs):
|
|
118
|
+
if fs.isdir(root) and allowed(root):
|
|
119
|
+
out.append(root)
|
|
120
|
+
seen.add(root)
|
|
121
|
+
for p in fs.glob_recursive(root, **kwargs):
|
|
122
|
+
if p in seen:
|
|
123
|
+
continue
|
|
124
|
+
if allowed(p) and fs.isdir(p):
|
|
125
|
+
out.append(p)
|
|
126
|
+
seen.add(p)
|
|
127
|
+
return out
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# -------------------------------------------------------------
|
|
131
|
+
# Unified directory scan
|
|
132
|
+
# -------------------------------------------------------------
|
|
133
|
+
def get_files_and_dirs(
|
|
134
|
+
directory: str=None,
|
|
135
|
+
cfg: Optional["ScanConfig"] = None,
|
|
136
|
+
allowed_exts: Optional[Set[str]] = False,
|
|
137
|
+
unallowed_exts: Optional[Set[str]] = False,
|
|
138
|
+
exclude_types: Optional[Set[str]] = False,
|
|
139
|
+
exclude_dirs: Optional[List[str]] = False,
|
|
140
|
+
exclude_patterns: Optional[List[str]] = False,
|
|
141
|
+
add=False,
|
|
142
|
+
recursive: bool = True,
|
|
143
|
+
include_files: bool = True,
|
|
144
|
+
roots=None,
|
|
145
|
+
**kwargs
|
|
146
|
+
):
|
|
147
|
+
"""
|
|
148
|
+
Same public signature as your original get_files_and_dirs(),
|
|
149
|
+
but powered by backend objects (LocalFS or SSHFS).
|
|
150
|
+
"""
|
|
151
|
+
cfg = cfg or define_defaults(
|
|
152
|
+
allowed_exts=allowed_exts,
|
|
153
|
+
unallowed_exts=unallowed_exts,
|
|
154
|
+
exclude_types=exclude_types,
|
|
155
|
+
exclude_dirs=exclude_dirs,
|
|
156
|
+
exclude_patterns=exclude_patterns,
|
|
157
|
+
add=add
|
|
158
|
+
)
|
|
159
|
+
allowed = make_allowed_predicate(cfg)
|
|
160
|
+
items = []
|
|
161
|
+
files = []
|
|
162
|
+
roots = make_list(roots)
|
|
163
|
+
directory = make_list(directory)+roots
|
|
164
|
+
if recursive:
|
|
165
|
+
items = get_globs(directory, recursive=recursive, allowed=allowed, **kwargs)
|
|
166
|
+
else:
|
|
167
|
+
for fs, base, _ in normalize_items(make_list(directory), **kwargs):
|
|
168
|
+
try:
|
|
169
|
+
items += [fs.join(base, name) for name in fs.listdir(base)]
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
dirs = get_allowed_dirs(items, allowed=allowed, **kwargs)
|
|
174
|
+
if include_files:
|
|
175
|
+
files = get_allowed_files(items, allowed=allowed, **kwargs)
|
|
176
|
+
return dirs, files
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# -------------------------------------------------------------
|
|
180
|
+
# Unchanged predicate builder
|
|
181
|
+
# -------------------------------------------------------------
|
|
182
|
+
def make_allowed_predicate(cfg: ScanConfig) -> Callable[[str], bool]:
|
|
183
|
+
def allowed(path: str) -> bool:
|
|
184
|
+
p = Path(path)
|
|
185
|
+
name = p.name.lower()
|
|
186
|
+
path_str = str(p).lower()
|
|
187
|
+
|
|
188
|
+
# A) directory exclusions
|
|
189
|
+
if cfg.exclude_dirs:
|
|
190
|
+
for dpat in cfg.exclude_dirs:
|
|
191
|
+
if dpat in path_str or fnmatch.fnmatch(name, dpat.lower()):
|
|
192
|
+
if p.is_dir() or dpat in path_str:
|
|
193
|
+
return False
|
|
194
|
+
|
|
195
|
+
# B) filename pattern exclusions
|
|
196
|
+
if cfg.exclude_patterns:
|
|
197
|
+
for pat in cfg.exclude_patterns:
|
|
198
|
+
if fnmatch.fnmatch(name, pat.lower()):
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
# C) extension gates
|
|
202
|
+
if p.is_file():
|
|
203
|
+
ext = p.suffix.lower()
|
|
204
|
+
if (cfg.allowed_exts and ext not in cfg.allowed_exts) or \
|
|
205
|
+
(cfg.unallowed_exts and ext in cfg.unallowed_exts):
|
|
206
|
+
return False
|
|
207
|
+
return True
|
|
208
|
+
return allowed
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
from .imports import *
|
|
2
|
+
from .module_imports import *
|
|
3
|
+
from .constants import *
|
|
4
|
+
def get_item_check_cmd(path, file=True, directory=False, exists=False):
|
|
5
|
+
if (directory and file) or exists:
|
|
6
|
+
typ = "e"
|
|
7
|
+
elif file:
|
|
8
|
+
typ = "f"
|
|
9
|
+
elif directory:
|
|
10
|
+
typ = "d"
|
|
11
|
+
elif isinstance(file, str):
|
|
12
|
+
if "f" in file:
|
|
13
|
+
typ = "f"
|
|
14
|
+
elif "d" in file:
|
|
15
|
+
typ = "d"
|
|
16
|
+
else:
|
|
17
|
+
typ = "e"
|
|
18
|
+
else:
|
|
19
|
+
typ = "e"
|
|
20
|
+
return f"test -{typ} {shlex.quote(path)} && echo __OK__ || true"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_all_item_check_cmd(path, file=True, directory=True, exists=True):
|
|
24
|
+
collects = []
|
|
25
|
+
out_js = {}
|
|
26
|
+
|
|
27
|
+
if file:
|
|
28
|
+
collects.append("file")
|
|
29
|
+
if directory:
|
|
30
|
+
collects.append("dir")
|
|
31
|
+
if exists:
|
|
32
|
+
collects.append("exists")
|
|
33
|
+
|
|
34
|
+
if not collects:
|
|
35
|
+
return out_js
|
|
36
|
+
|
|
37
|
+
path = shlex.quote(path)
|
|
38
|
+
for typ in collects:
|
|
39
|
+
t = typ[0] # f, d, or e
|
|
40
|
+
out_js[typ] = f"test -{t} {path} && echo __OK__ || true"
|
|
41
|
+
|
|
42
|
+
return out_js
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def is_file(
|
|
46
|
+
path,
|
|
47
|
+
user_at_host=None,
|
|
48
|
+
password=None,
|
|
49
|
+
key=None,
|
|
50
|
+
env_path=None,
|
|
51
|
+
**kwargs
|
|
52
|
+
):
|
|
53
|
+
contingencies = list(set([user_at_host,password,key,env_path]))
|
|
54
|
+
len_contingencies = len(contingencies)
|
|
55
|
+
is_potential = (len_contingencies >1 or (None not in contingencies))
|
|
56
|
+
if not is_potential:
|
|
57
|
+
return os.path.isfile(path)
|
|
58
|
+
cmd = get_item_check_cmd(path,file=True)
|
|
59
|
+
return run_cmd(cmd=cmd,
|
|
60
|
+
user_at_host=user_at_host,
|
|
61
|
+
password=password,
|
|
62
|
+
key=key,
|
|
63
|
+
env_path=env_path,
|
|
64
|
+
**kwargs
|
|
65
|
+
)
|
|
66
|
+
def is_dir(
|
|
67
|
+
path,
|
|
68
|
+
user_at_host=None,
|
|
69
|
+
password=None,
|
|
70
|
+
key=None,
|
|
71
|
+
env_path=None,
|
|
72
|
+
**kwargs
|
|
73
|
+
):
|
|
74
|
+
contingencies = list(set([user_at_host,password,key,env_path]))
|
|
75
|
+
len_contingencies = len(contingencies)
|
|
76
|
+
is_potential = (len_contingencies >1 or (None not in contingencies))
|
|
77
|
+
if not is_potential:
|
|
78
|
+
return os.path.isdir(path)
|
|
79
|
+
cmd = get_item_check_cmd(path,file=False,directory=True)
|
|
80
|
+
return run_cmd(cmd=cmd,
|
|
81
|
+
user_at_host=user_at_host,
|
|
82
|
+
password=password,
|
|
83
|
+
key=key,
|
|
84
|
+
env_path=env_path,
|
|
85
|
+
**kwargs
|
|
86
|
+
)
|
|
87
|
+
def is_exists(
|
|
88
|
+
path,
|
|
89
|
+
user_at_host=None,
|
|
90
|
+
password=None,
|
|
91
|
+
key=None,
|
|
92
|
+
env_path=None,
|
|
93
|
+
**kwargs
|
|
94
|
+
):
|
|
95
|
+
contingencies = list(set([user_at_host,password,key,env_path]))
|
|
96
|
+
len_contingencies = len(contingencies)
|
|
97
|
+
is_potential = (len_contingencies >1 or (None not in contingencies))
|
|
98
|
+
if not is_potential:
|
|
99
|
+
return os.path.exists(path)
|
|
100
|
+
if is_potential == True:
|
|
101
|
+
cmd = get_item_check_cmd(path,exists=True)
|
|
102
|
+
return run_cmd(cmd=cmd,
|
|
103
|
+
user_at_host=user_at_host,
|
|
104
|
+
password=password,
|
|
105
|
+
key=key,
|
|
106
|
+
env_path=env_path,
|
|
107
|
+
**kwargs
|
|
108
|
+
)
|
|
109
|
+
def is_any(
|
|
110
|
+
path,
|
|
111
|
+
user_at_host=None,
|
|
112
|
+
password=None,
|
|
113
|
+
key=None,
|
|
114
|
+
env_path=None,
|
|
115
|
+
**kwargs
|
|
116
|
+
):
|
|
117
|
+
contingencies = list(set([user_at_host,password,key,env_path]))
|
|
118
|
+
len_contingencies = len(contingencies)
|
|
119
|
+
is_potential = (len_contingencies >1 or (None not in contingencies))
|
|
120
|
+
if not is_potential:
|
|
121
|
+
return os.path.exists(path)
|
|
122
|
+
if is_potential == True:
|
|
123
|
+
out_js = get_all_item_check_cmd(path,file=True,directory=True,exists=True)
|
|
124
|
+
for typ,cmd in out_js.items():
|
|
125
|
+
response = run_cmd(cmd=cmd,
|
|
126
|
+
user_at_host=user_at_host,
|
|
127
|
+
password=password,
|
|
128
|
+
key=key,
|
|
129
|
+
env_path=env_path,
|
|
130
|
+
**kwargs
|
|
131
|
+
)
|
|
132
|
+
result = "__OK__" in (response or "")
|
|
133
|
+
if result:
|
|
134
|
+
return typ
|
|
135
|
+
return None
|
|
136
|
+
class PathBackend(Protocol):
|
|
137
|
+
def join(self, *parts: str) -> str: ...
|
|
138
|
+
def isfile(self, path: str) -> bool: ...
|
|
139
|
+
def isdir(self, path: str) -> bool: ...
|
|
140
|
+
def glob_recursive(self, base: str, **opts) -> List[str]: ...
|
|
141
|
+
def listdir(self, base: str) -> List[str]: ...
|
|
142
|
+
|
|
143
|
+
class LocalFS:
|
|
144
|
+
def __init__(self, get_type=False, get_is_dir=False, get_is_file=False, get_is_exists=False, **kwargs):
|
|
145
|
+
self.get_type = get_type
|
|
146
|
+
self.get_is_dir = get_is_dir
|
|
147
|
+
self.get_is_file = get_is_file
|
|
148
|
+
self.get_is_exists = get_is_exists
|
|
149
|
+
|
|
150
|
+
def join(self, *parts: str) -> str:
|
|
151
|
+
return os.path.join(*parts)
|
|
152
|
+
|
|
153
|
+
def isfile(self, path: str) -> bool:
|
|
154
|
+
return os.path.isfile(path)
|
|
155
|
+
|
|
156
|
+
def isdir(self, path: str) -> bool:
|
|
157
|
+
return os.path.isdir(path)
|
|
158
|
+
|
|
159
|
+
def isexists(self, path: str) -> bool:
|
|
160
|
+
return os.path.exists(path)
|
|
161
|
+
|
|
162
|
+
def istype(self, path: str) -> str | None:
|
|
163
|
+
funcs_js = {"file": os.path.isfile, "dir": os.path.isdir, "exists": os.path.exists}
|
|
164
|
+
for key, func in funcs_js.items():
|
|
165
|
+
if func(path):
|
|
166
|
+
return key
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
def is_included(self, path, **kwargs):
|
|
170
|
+
include_js = {}
|
|
171
|
+
if self.get_type:
|
|
172
|
+
include_js["typ"] = self.istype(path)
|
|
173
|
+
if self.get_is_dir:
|
|
174
|
+
include_js["dir"] = self.isdir(path)
|
|
175
|
+
if self.get_is_file:
|
|
176
|
+
include_js["file"] = self.isfile(path)
|
|
177
|
+
if self.get_is_exists:
|
|
178
|
+
include_js["exists"] = self.isexists(path)
|
|
179
|
+
return include_js
|
|
180
|
+
def glob_recursive(self, base: str, **opts) -> List[str]:
|
|
181
|
+
"""
|
|
182
|
+
opts:
|
|
183
|
+
- maxdepth: int | None
|
|
184
|
+
- mindepth: int (default 1)
|
|
185
|
+
- follow_symlinks: bool
|
|
186
|
+
- include_dirs: bool
|
|
187
|
+
- include_files: bool
|
|
188
|
+
- exclude_hidden: bool
|
|
189
|
+
"""
|
|
190
|
+
maxdepth = opts.get("maxdepth")
|
|
191
|
+
mindepth = opts.get("mindepth", 1)
|
|
192
|
+
follow = opts.get("follow_symlinks", False)
|
|
193
|
+
want_d = opts.get("include_dirs", True)
|
|
194
|
+
want_f = opts.get("include_files", True)
|
|
195
|
+
hide = opts.get("exclude_hidden", False)
|
|
196
|
+
|
|
197
|
+
results: List[str] = []
|
|
198
|
+
base_depth = os.path.normpath(base).count(os.sep)
|
|
199
|
+
|
|
200
|
+
for root, dirs, files in os.walk(base, followlinks=follow):
|
|
201
|
+
depth = os.path.normpath(root).count(os.sep) - base_depth
|
|
202
|
+
if maxdepth is not None and depth > maxdepth:
|
|
203
|
+
dirs[:] = []
|
|
204
|
+
continue
|
|
205
|
+
if want_d and depth >= mindepth:
|
|
206
|
+
for d in dirs:
|
|
207
|
+
if hide and d.startswith("."): continue
|
|
208
|
+
results.append(os.path.join(root, d))
|
|
209
|
+
if want_f and depth >= mindepth:
|
|
210
|
+
for f in files:
|
|
211
|
+
if hide and f.startswith("."): continue
|
|
212
|
+
results.append(os.path.join(root, f))
|
|
213
|
+
return results
|
|
214
|
+
|
|
215
|
+
def listdir(self, base: str) -> List[str]:
|
|
216
|
+
try:
|
|
217
|
+
return [os.path.join(base, name) for name in os.listdir(base)]
|
|
218
|
+
except Exception:
|
|
219
|
+
return []
|
|
220
|
+
def get_spec_kwargs(
|
|
221
|
+
user_at_host=None,
|
|
222
|
+
password=None,
|
|
223
|
+
key=None,
|
|
224
|
+
env_path=None,
|
|
225
|
+
kwargs=None
|
|
226
|
+
):
|
|
227
|
+
kwargs = kwargs or {}
|
|
228
|
+
kwargs["user_at_host"] = kwargs.get("user_at_host") or user_at_host
|
|
229
|
+
kwargs["password"] = kwargs.get("password") or password
|
|
230
|
+
kwargs["key"] = kwargs.get("key") or key
|
|
231
|
+
kwargs["env_path"] = kwargs.get("env_path") or env_path
|
|
232
|
+
return kwargs
|
|
233
|
+
class SSHFS:
|
|
234
|
+
"""Remote POSIX backend via run_remote_cmd."""
|
|
235
|
+
def __init__(self, password=None, key=None, env_path=None,
|
|
236
|
+
get_type=False, get_is_dir=False, get_is_file=False, get_is_exists=False, **kwargs):
|
|
237
|
+
self.user_at_host = kwargs.get('user_at_host') or kwargs.get('user') or kwargs.get('host')
|
|
238
|
+
self.password = password
|
|
239
|
+
self.key = key
|
|
240
|
+
self.env_path = env_path
|
|
241
|
+
self.get_type = get_type
|
|
242
|
+
self.get_is_dir = get_is_dir
|
|
243
|
+
self.get_is_file = get_is_file
|
|
244
|
+
self.get_is_exists = get_is_exists
|
|
245
|
+
|
|
246
|
+
def cell_spec_kwargs(self, func, path, **kwargs):
|
|
247
|
+
kwargs = get_spec_kwargs(
|
|
248
|
+
user_at_host=self.user_at_host,
|
|
249
|
+
password=self.password,
|
|
250
|
+
key=self.key,
|
|
251
|
+
env_path=self.env_path,
|
|
252
|
+
kwargs=kwargs
|
|
253
|
+
)
|
|
254
|
+
return func(path, **kwargs)
|
|
255
|
+
|
|
256
|
+
def is_included(self, path, **kwargs):
|
|
257
|
+
include_js = {}
|
|
258
|
+
if self.get_type:
|
|
259
|
+
include_js["typ"] = self.istype(path, **kwargs)
|
|
260
|
+
if self.get_is_dir:
|
|
261
|
+
include_js["dir"] = self.isdir(path, **kwargs)
|
|
262
|
+
if self.get_is_file:
|
|
263
|
+
include_js["file"] = self.isfile(path, **kwargs)
|
|
264
|
+
if self.get_is_exists:
|
|
265
|
+
include_js["exists"] = self.isexists(path, **kwargs)
|
|
266
|
+
return include_js
|
|
267
|
+
|
|
268
|
+
def join(self, *parts: str) -> str:
|
|
269
|
+
return posixpath.join(*parts)
|
|
270
|
+
|
|
271
|
+
def isfile(self, path: str, **kwargs) -> bool:
|
|
272
|
+
out = self.cell_spec_kwargs(is_file, path, **kwargs)
|
|
273
|
+
return "__OK__" in (out or "")
|
|
274
|
+
|
|
275
|
+
def isdir(self, path: str, **kwargs) -> bool:
|
|
276
|
+
out = self.cell_spec_kwargs(is_dir, path, **kwargs)
|
|
277
|
+
return "__OK__" in (out or "")
|
|
278
|
+
|
|
279
|
+
def isexists(self, path: str, **kwargs) -> bool:
|
|
280
|
+
out = self.cell_spec_kwargs(is_exists, path, **kwargs)
|
|
281
|
+
return "__OK__" in (out or "")
|
|
282
|
+
|
|
283
|
+
def istype(self, path: str, **kwargs) -> str | None:
|
|
284
|
+
out = self.cell_spec_kwargs(is_any, path, **kwargs)
|
|
285
|
+
return out
|
|
286
|
+
|
|
287
|
+
def glob_recursive(self, base: str, **opts) -> List[str]:
|
|
288
|
+
maxdepth = opts.get("maxdepth")
|
|
289
|
+
mindepth = opts.get("mindepth", 1)
|
|
290
|
+
follow = opts.get("follow_symlinks", False)
|
|
291
|
+
want_d = opts.get("include_dirs", True)
|
|
292
|
+
want_f = opts.get("include_files", True)
|
|
293
|
+
hide = opts.get("exclude_hidden", False)
|
|
294
|
+
|
|
295
|
+
parts = []
|
|
296
|
+
if follow:
|
|
297
|
+
parts.append("-L")
|
|
298
|
+
parts += ["find", shlex.quote(base)]
|
|
299
|
+
if mindepth is not None:
|
|
300
|
+
parts += ["-mindepth", str(mindepth)]
|
|
301
|
+
if maxdepth is not None:
|
|
302
|
+
parts += ["-maxdepth", str(maxdepth)]
|
|
303
|
+
|
|
304
|
+
type_filters = []
|
|
305
|
+
if want_d and not want_f:
|
|
306
|
+
type_filters = ["-type", "d"]
|
|
307
|
+
elif want_f and not want_d:
|
|
308
|
+
type_filters = ["-type", "f"]
|
|
309
|
+
|
|
310
|
+
hidden_filter = []
|
|
311
|
+
if hide:
|
|
312
|
+
hidden_filter = ["!", "-regex", r".*/\..*"]
|
|
313
|
+
|
|
314
|
+
cmd = " ".join(parts + type_filters + hidden_filter + ["-printf", r"'%p\n'"]) + " 2>/dev/null"
|
|
315
|
+
out = run_remote_cmd(self.user_at_host, cmd)
|
|
316
|
+
return [line.strip().strip("'") for line in (out or "").splitlines() if line.strip()]
|
|
317
|
+
|
|
318
|
+
def listdir(self, base: str) -> List[str]:
|
|
319
|
+
cmd = f"find {shlex.quote(base)} -maxdepth 1 -mindepth 1 -printf '%p\\n' 2>/dev/null"
|
|
320
|
+
out = run_remote_cmd(self.user_at_host, cmd)
|
|
321
|
+
return [line.strip() for line in (out or "").splitlines() if line.strip()]
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def make_allowed_predicate(cfg: ScanConfig,fs=None) -> Callable[[str], bool]:
|
|
325
|
+
fs = fs or LocalFS()
|
|
326
|
+
def allowed(path: str) -> bool:
|
|
327
|
+
|
|
328
|
+
name = p.name.lower()
|
|
329
|
+
path_str = str(p).lower()
|
|
330
|
+
# A) directories
|
|
331
|
+
if cfg.exclude_dirs:
|
|
332
|
+
for dpat in cfg.exclude_dirs:
|
|
333
|
+
if dpat in path_str or fnmatch.fnmatch(name, dpat.lower()):
|
|
334
|
+
if p.is_dir() or dpat in path_str:
|
|
335
|
+
return False
|
|
336
|
+
|
|
337
|
+
if cfg.exclude_patterns:
|
|
338
|
+
# B) filename patterns
|
|
339
|
+
for pat in cfg.exclude_patterns:
|
|
340
|
+
if fnmatch.fnmatch(name, pat.lower()):
|
|
341
|
+
return False
|
|
342
|
+
|
|
343
|
+
# C) extension gates
|
|
344
|
+
if p.is_file():
|
|
345
|
+
ext = p.suffix.lower()
|
|
346
|
+
if (cfg.allowed_exts and ext not in cfg.allowed_exts) or (cfg.unallowed_exts and ext in cfg.unallowed_exts):
|
|
347
|
+
return False
|
|
348
|
+
return True
|
|
349
|
+
return allowed
|
|
350
|
+
def try_group(pre,item,strings):
|
|
351
|
+
|
|
352
|
+
try:
|
|
353
|
+
m = pre.match(item)
|
|
354
|
+
for i,string in enumerate(strings):
|
|
355
|
+
strings[i] = m.group(string)
|
|
356
|
+
|
|
357
|
+
except:
|
|
358
|
+
return None
|
|
359
|
+
return strings
|
|
360
|
+
def normalize_items(
|
|
361
|
+
paths: Iterable[str],
|
|
362
|
+
user_at_host=None,
|
|
363
|
+
get_type=True,
|
|
364
|
+
get_is_dir=False,
|
|
365
|
+
get_is_file=False,
|
|
366
|
+
get_is_exists=False,
|
|
367
|
+
**kwargs
|
|
368
|
+
) -> List[tuple[PathBackend, str, dict]]:
|
|
369
|
+
pairs: List[tuple[PathBackend, str, dict]] = []
|
|
370
|
+
host = user_at_host or kwargs.get("host") or kwargs.get("user")
|
|
371
|
+
|
|
372
|
+
for item in paths:
|
|
373
|
+
if not item:
|
|
374
|
+
continue
|
|
375
|
+
|
|
376
|
+
strings = try_group(REMOTE_RE, item, ["host", "path"])
|
|
377
|
+
fs_host = None
|
|
378
|
+
nuhost = None
|
|
379
|
+
|
|
380
|
+
if (strings and None not in strings) or host:
|
|
381
|
+
if strings and None not in strings:
|
|
382
|
+
nuhost = strings[0]
|
|
383
|
+
item = strings[1] or item
|
|
384
|
+
nuhost = nuhost or host
|
|
385
|
+
fs_host = SSHFS(
|
|
386
|
+
nuhost,
|
|
387
|
+
user_at_host=user_at_host,
|
|
388
|
+
get_type=get_type,
|
|
389
|
+
get_is_dir=get_is_dir,
|
|
390
|
+
get_is_file=get_is_file,
|
|
391
|
+
get_is_exists=get_is_exists,
|
|
392
|
+
**kwargs
|
|
393
|
+
)
|
|
394
|
+
else:
|
|
395
|
+
fs_host = LocalFS(
|
|
396
|
+
get_type=get_type,
|
|
397
|
+
get_is_dir=get_is_dir,
|
|
398
|
+
get_is_file=get_is_file,
|
|
399
|
+
get_is_exists=get_is_exists
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
includes = fs_host.is_included(item)
|
|
403
|
+
pairs.append((fs_host, item, includes))
|
|
404
|
+
return pairs
|
|
405
|
+
|
|
406
|
+
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from ...string_clean import eatAll
|
|
2
2
|
from ...list_utils import make_list
|
|
3
3
|
from ...type_utils import get_media_exts, is_media_type,MIME_TYPES,is_str
|
|
4
|
-
from ...ssh_utils import
|
|
4
|
+
from ...ssh_utils import *
|
|
5
5
|
from ...env_utils import *
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
from ...abstract_classes import SingletonMeta
|
|
8
|
-
|
|
8
|
+
|