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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
File without changes
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""
|
|
2
|
+
list_utils.py
|
|
3
|
+
|
|
4
|
+
This module provides a set of utility functions tailored for manipulating lists. It offers capabilities to:
|
|
5
|
+
|
|
6
|
+
- Sort lists and retrieve elements at specific positions.
|
|
7
|
+
- Combine two lists.
|
|
8
|
+
- Ensure that objects are contained within nested lists.
|
|
9
|
+
- Add multiple values to a list.
|
|
10
|
+
|
|
11
|
+
The `list_utils` module is designed to simplify and abstract common list operations, enhancing code readability and reusability.
|
|
12
|
+
|
|
13
|
+
Note: While Python's native list methods are comprehensive, the functions here provide additional checks and transformations that might be commonly needed in various applications.
|
|
14
|
+
|
|
15
|
+
This module is part of the `abstract_utilities` package.
|
|
16
|
+
|
|
17
|
+
Author: putkoff
|
|
18
|
+
Date: 05/31/2023
|
|
19
|
+
Version: 0.1.2
|
|
20
|
+
"""
|
|
21
|
+
def get_sort(ls: list, k: int = 0):
|
|
22
|
+
"""
|
|
23
|
+
Sorts a list in ascending order and returns the element at index k.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
ls (list): The list to be sorted.
|
|
27
|
+
k (int, optional): The index of the element to return. Defaults to 0.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
any: The element at index k after sorting the list.
|
|
31
|
+
"""
|
|
32
|
+
ls.sort()
|
|
33
|
+
return ls[k]
|
|
34
|
+
|
|
35
|
+
def combineList(ls: list, lsN: list) -> list:
|
|
36
|
+
"""
|
|
37
|
+
Combines two lists and returns the combined list.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
ls (list): The first list.
|
|
41
|
+
lsN (list): The second list.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
list: The combined list.
|
|
45
|
+
"""
|
|
46
|
+
for k in range(len(lsN)):
|
|
47
|
+
ls.append(lsN[k])
|
|
48
|
+
return ls
|
|
49
|
+
|
|
50
|
+
def find_original_case(input_list, search_string):
|
|
51
|
+
"""
|
|
52
|
+
Searches for a case-insensitive match of 'search_string' within 'input_list'
|
|
53
|
+
and returns the first matching element in its original case.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
input_list (list): The list of strings to search within.
|
|
57
|
+
search_string (str): The case-insensitive string to search for.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
str or None: The first element in 'input_list' that matches 'search_string'
|
|
61
|
+
in its original case, or None if no match is found.
|
|
62
|
+
"""
|
|
63
|
+
for obj in input_list:
|
|
64
|
+
if obj.lower() == search_string.lower():
|
|
65
|
+
return obj
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
def ensure_nested_list(obj):
|
|
69
|
+
"""
|
|
70
|
+
Ensure that the input object is a nested list.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
obj (any): The object to ensure as a nested list.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
list: A nested list containing the object or the original list if it's already nested.
|
|
77
|
+
"""
|
|
78
|
+
# Check if the input object is a list
|
|
79
|
+
if not isinstance(obj, list):
|
|
80
|
+
# If it's not a list, create a new nested list containing the object
|
|
81
|
+
return [obj]
|
|
82
|
+
# If it is a list, check if any of its elements are non-list objects
|
|
83
|
+
for element in obj:
|
|
84
|
+
if not isinstance(element, list):
|
|
85
|
+
# If at least one element is not a list, wrap the original list in a new list
|
|
86
|
+
return [obj]
|
|
87
|
+
# If all elements are lists, return the original list
|
|
88
|
+
return obj
|
|
89
|
+
def make_list_add(obj,values):
|
|
90
|
+
"""
|
|
91
|
+
Add multiple values to a list and return the resulting list.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
obj (list): The original list.
|
|
95
|
+
values (iterable): Values to be added to the list.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
list: The modified list containing the original elements and the added values.
|
|
99
|
+
"""
|
|
100
|
+
obj = list(obj)
|
|
101
|
+
for each in list(values):
|
|
102
|
+
obj.append(each)
|
|
103
|
+
return obj
|
|
104
|
+
|
|
105
|
+
def recursive_json_list(json_list:dict,desired_values:dict)->list:
|
|
106
|
+
"""
|
|
107
|
+
Filters the json list based on the desired_values.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
- list: A filtered json list of desired_values held within json object keys.
|
|
111
|
+
"""
|
|
112
|
+
# Start with the full list of json objects
|
|
113
|
+
# For each key in the selection values, filter the list if the key's value is set
|
|
114
|
+
recursed_list=[]
|
|
115
|
+
for json_obj in json_list:
|
|
116
|
+
bool_count = 0
|
|
117
|
+
for desired_key, desired_value in desired_values.items():
|
|
118
|
+
if desired_key in json_obj:
|
|
119
|
+
if json_obj[desired_key] == desired_value:
|
|
120
|
+
bool_count +=1
|
|
121
|
+
static = desired_value
|
|
122
|
+
if bool_count == len(list(desired_values.keys())):
|
|
123
|
+
recursed_list.append(json_obj)
|
|
124
|
+
return recursed_list
|
|
125
|
+
|
|
126
|
+
def filter_json_list_values(json_list:list,keys:list)->None:
|
|
127
|
+
"""
|
|
128
|
+
Updates the GUI dropdown lists based on the filtered RPC list.
|
|
129
|
+
"""
|
|
130
|
+
# Get the filtered list of json values based on current key selections
|
|
131
|
+
all_keys={}
|
|
132
|
+
for key in keys:
|
|
133
|
+
unique_values=[]
|
|
134
|
+
for json_obj in json_list:
|
|
135
|
+
if key in json_obj:
|
|
136
|
+
if json_obj[key] not in unique_values:
|
|
137
|
+
unique_values.append(json_obj[key])
|
|
138
|
+
all_keys[key]= unique_values
|
|
139
|
+
return all_keys
|
|
140
|
+
def get_highest_value_obj(obj_list, function):
|
|
141
|
+
return max(obj_list, key=function)
|
|
142
|
+
|
|
143
|
+
def safe_list_return(current_list,list_num=0):
|
|
144
|
+
if len(current_list) >= list_num+1:
|
|
145
|
+
return current_list[int(list_num)]
|
|
146
|
+
def get_actual_number(reference_object,number_value):
|
|
147
|
+
try:
|
|
148
|
+
reference_length = len(reference_object)
|
|
149
|
+
except:
|
|
150
|
+
reference_length = 0
|
|
151
|
+
if reference_length ==0:
|
|
152
|
+
return None
|
|
153
|
+
return max(0, min(number_value, reference_length-1))
|
|
154
|
+
def compare_lists(list_1,list_2):
|
|
155
|
+
if len(list_1)>len(list_2):
|
|
156
|
+
return False
|
|
157
|
+
for each in list_1:
|
|
158
|
+
if each not in list_2:
|
|
159
|
+
return False
|
|
160
|
+
return True
|
|
161
|
+
def remove_from_list(list_obj, key, value):
|
|
162
|
+
return [obj for obj in list_obj if not (isinstance(obj, dict) and obj.get(key) == value)]
|
|
163
|
+
|
|
164
|
+
def list_set(obj):
|
|
165
|
+
try:
|
|
166
|
+
obj = list(set(obj))
|
|
167
|
+
except Exception as e:
|
|
168
|
+
print(f"{e}")
|
|
169
|
+
return obj
|
|
170
|
+
|
|
171
|
+
def get_symetric_difference(obj_1,obj_2):
|
|
172
|
+
set1 = set(obj_1)
|
|
173
|
+
set2 = set(obj_2)
|
|
174
|
+
# Find elements that are unique to each list
|
|
175
|
+
unique_elements = set1.symmetric_difference(set2)
|
|
176
|
+
# Convert the set back to a list, if needed
|
|
177
|
+
return list(unique_elements)
|
|
178
|
+
def make_list(obj:any) -> list:
|
|
179
|
+
"""
|
|
180
|
+
Converts the input object to a list. If the object is already a list, it is returned as is.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
obj: The object to convert.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
list: The object as a list.
|
|
187
|
+
"""
|
|
188
|
+
if isinstance(obj,str):
|
|
189
|
+
if ',' in obj:
|
|
190
|
+
obj = obj.split(',')
|
|
191
|
+
if isinstance(obj,set) or isinstance(obj,tuple):
|
|
192
|
+
return list(obj)
|
|
193
|
+
if isinstance(obj, list):
|
|
194
|
+
return obj
|
|
195
|
+
return [obj]
|
|
196
|
+
|
|
197
|
+
def make_list_it(obj=None):
|
|
198
|
+
obj = make_list(obj or [])
|
|
199
|
+
return obj
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from .imports import *
|
|
2
|
+
class AbstractLogManager(metaclass=SingletonMeta):
|
|
3
|
+
def __init__(self):
|
|
4
|
+
# Create a logger; use __name__ to have a module-specific logger if desired.
|
|
5
|
+
self.logger = logging.getLogger("AbstractLogManager")
|
|
6
|
+
self.logger.setLevel(logging.DEBUG) # Set to lowest level to let handlers filter as needed.
|
|
7
|
+
|
|
8
|
+
# Create a console handler with a default level.
|
|
9
|
+
self.console_handler = logging.StreamHandler()
|
|
10
|
+
# Default level: show warnings and above.
|
|
11
|
+
self.console_handler.setLevel(logging.WARNING)
|
|
12
|
+
|
|
13
|
+
# Formatter for the logs.
|
|
14
|
+
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
|
15
|
+
self.console_handler.setFormatter(formatter)
|
|
16
|
+
|
|
17
|
+
# If there are no handlers already attached, add our console handler.
|
|
18
|
+
if not self.logger.hasHandlers():
|
|
19
|
+
self.logger.addHandler(self.console_handler)
|
|
20
|
+
|
|
21
|
+
def set_debug(self, enabled: bool) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Enable or disable DEBUG level messages.
|
|
24
|
+
When enabled, the console handler will output DEBUG messages and above.
|
|
25
|
+
When disabled, it falls back to INFO or WARNING (adjust as needed).
|
|
26
|
+
"""
|
|
27
|
+
if enabled:
|
|
28
|
+
self.console_handler.setLevel(logging.DEBUG)
|
|
29
|
+
self.logger.debug("DEBUG logging enabled.")
|
|
30
|
+
else:
|
|
31
|
+
# For example, disable DEBUG by raising the level to INFO.
|
|
32
|
+
self.console_handler.setLevel(logging.INFO)
|
|
33
|
+
self.logger.info("DEBUG logging disabled; INFO level active.")
|
|
34
|
+
|
|
35
|
+
def set_info(self, enabled: bool) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Enable or disable INFO level messages.
|
|
38
|
+
When enabled, INFO and above are shown; when disabled, only WARNING and above.
|
|
39
|
+
"""
|
|
40
|
+
if enabled:
|
|
41
|
+
# Lower the handler level to INFO if currently higher.
|
|
42
|
+
self.console_handler.setLevel(logging.INFO)
|
|
43
|
+
self.logger.info("INFO logging enabled.")
|
|
44
|
+
else:
|
|
45
|
+
self.console_handler.setLevel(logging.WARNING)
|
|
46
|
+
self.logger.warning("INFO logging disabled; only WARNING and above will be shown.")
|
|
47
|
+
|
|
48
|
+
def set_warning(self, enabled: bool) -> None:
|
|
49
|
+
"""
|
|
50
|
+
Enable or disable WARNING level messages.
|
|
51
|
+
When disabled, only ERROR and CRITICAL messages are shown.
|
|
52
|
+
"""
|
|
53
|
+
if enabled:
|
|
54
|
+
# WARNING messages enabled means handler level is WARNING.
|
|
55
|
+
self.console_handler.setLevel(logging.WARNING)
|
|
56
|
+
self.logger.warning("WARNING logging enabled.")
|
|
57
|
+
else:
|
|
58
|
+
self.console_handler.setLevel(logging.ERROR)
|
|
59
|
+
self.logger.error("WARNING logging disabled; only ERROR and CRITICAL messages will be shown.")
|
|
60
|
+
|
|
61
|
+
def get_logger(self) -> logging.Logger:
|
|
62
|
+
"""Return the configured logger instance."""
|
|
63
|
+
return self.logger
|
|
64
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from .imports import *
|
|
2
|
+
from .logger_callable import *
|
|
3
|
+
def initialize_call_log(value=None,
|
|
4
|
+
data=None,
|
|
5
|
+
logMsg=None,
|
|
6
|
+
log_level=None):
|
|
7
|
+
"""
|
|
8
|
+
Inspect the stack to find the first caller *outside* this module,
|
|
9
|
+
then log its function name and file path.
|
|
10
|
+
"""
|
|
11
|
+
# Grab the current stack
|
|
12
|
+
stack = inspect.stack()
|
|
13
|
+
caller_name = "<unknown>"
|
|
14
|
+
caller_path = "<unknown>"
|
|
15
|
+
log_level = log_level or 'info'
|
|
16
|
+
try:
|
|
17
|
+
# Starting at index=1 to skip initialize_call_log itself
|
|
18
|
+
for frame_info in stack[1:]:
|
|
19
|
+
modname = frame_info.frame.f_globals.get("__name__", "")
|
|
20
|
+
# Skip over frames in your logging modules:
|
|
21
|
+
if not modname.startswith("abstract_utilities.log_utils") \
|
|
22
|
+
and not modname.startswith("abstract_flask.request_utils") \
|
|
23
|
+
and not modname.startswith("logging"):
|
|
24
|
+
caller_name = frame_info.function
|
|
25
|
+
caller_path = frame_info.filename
|
|
26
|
+
break
|
|
27
|
+
finally:
|
|
28
|
+
# Avoid reference cycles
|
|
29
|
+
del stack
|
|
30
|
+
|
|
31
|
+
logMsg = logMsg or "initializing"
|
|
32
|
+
full_message = (
|
|
33
|
+
f"{logMsg}\n"
|
|
34
|
+
f"calling_function: {caller_name}\n"
|
|
35
|
+
f"path: {caller_path}\n"
|
|
36
|
+
f"data: {data}"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
print_or_log(full_message,level=log_level)
|
|
40
|
+
def _normalize(obj):
|
|
41
|
+
"""Recursively turn PosixPath into str, sets into list, etc."""
|
|
42
|
+
if isinstance(obj, Path):
|
|
43
|
+
return str(obj)
|
|
44
|
+
if isinstance(obj, dict):
|
|
45
|
+
return {k: _normalize(v) for k, v in obj.items()}
|
|
46
|
+
if isinstance(obj, (list, tuple, set)):
|
|
47
|
+
return [_normalize(x) for x in obj]
|
|
48
|
+
return obj
|
|
49
|
+
def get_json_call_response(value=None, status_code=None, data=None, logMsg=None, callLog=False):
|
|
50
|
+
response_body = {}
|
|
51
|
+
if status_code == 200:
|
|
52
|
+
response_body["success"] = True
|
|
53
|
+
response_body["result"] = _normalize(value)
|
|
54
|
+
logMsg = logMsg or "success"
|
|
55
|
+
if callLog:
|
|
56
|
+
initialize_call_log(value=value,
|
|
57
|
+
data=data,
|
|
58
|
+
logMsg=logMsg,
|
|
59
|
+
log_level='info')
|
|
60
|
+
else:
|
|
61
|
+
response_body["success"] = False
|
|
62
|
+
response_body["error"] = _normalize(value)
|
|
63
|
+
logMsg = logMsg or f"ERROR: {logMsg}"
|
|
64
|
+
initialize_call_log(value=value,
|
|
65
|
+
data=data,
|
|
66
|
+
logMsg=logMsg,
|
|
67
|
+
log_level='error')
|
|
68
|
+
return jsonify(response_body), status_code
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from .imports import *
|
|
2
|
+
def get_logFile(bpName: str = None, maxBytes: int = 100_000, backupCount: int = 3) -> logging.Logger:
|
|
3
|
+
"""
|
|
4
|
+
If bpName is None, use the “caller module’s basename” as the logger name.
|
|
5
|
+
Otherwise, use the explicitly provided bpName.
|
|
6
|
+
"""
|
|
7
|
+
if bpName is None:
|
|
8
|
+
# Find the first frame outside logging_utils.py
|
|
9
|
+
frame_idx = _find_caller_frame_index()
|
|
10
|
+
frame_info = inspect.stack()[frame_idx]
|
|
11
|
+
caller_path = frame_info.filename # e.g. "/home/joe/project/app/routes.py"
|
|
12
|
+
bpName = os.path.splitext(os.path.basename(caller_path))[0]
|
|
13
|
+
del frame_info
|
|
14
|
+
|
|
15
|
+
log_dir = mkdirs("logs")
|
|
16
|
+
log_path = os.path.join(log_dir, f"{bpName}.log")
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(bpName)
|
|
19
|
+
logger.setLevel(logging.INFO)
|
|
20
|
+
|
|
21
|
+
if not logger.handlers:
|
|
22
|
+
handler = RotatingFileHandler(log_path, maxBytes=maxBytes, backupCount=backupCount)
|
|
23
|
+
handler.setLevel(logging.INFO)
|
|
24
|
+
|
|
25
|
+
fmt = "%(asctime)s - %(levelname)s - %(pathname)s - %(message)s"
|
|
26
|
+
formatter = logging.Formatter(fmt)
|
|
27
|
+
handler.setFormatter(formatter)
|
|
28
|
+
logger.addHandler(handler)
|
|
29
|
+
|
|
30
|
+
console = logging.StreamHandler()
|
|
31
|
+
console.setLevel(logging.INFO)
|
|
32
|
+
console.setFormatter(formatter)
|
|
33
|
+
logger.addHandler(console)
|
|
34
|
+
|
|
35
|
+
return logger
|
|
36
|
+
|
|
37
|
+
def _find_caller_frame_index():
|
|
38
|
+
"""
|
|
39
|
+
Scan up the call stack until we find a frame whose module is NOT logging_utils.
|
|
40
|
+
Return that index in inspect.stack().
|
|
41
|
+
"""
|
|
42
|
+
for idx, frame_info in enumerate(inspect.stack()):
|
|
43
|
+
# Ignore the very first frame (idx=0), which is this function itself.
|
|
44
|
+
if idx == 0:
|
|
45
|
+
continue
|
|
46
|
+
module = inspect.getmodule(frame_info.frame)
|
|
47
|
+
# If module is None (e.g. interactive), skip it;
|
|
48
|
+
# else get module.__name__ and compare:
|
|
49
|
+
module_name = module.__name__ if module else None
|
|
50
|
+
|
|
51
|
+
# Replace 'yourpackage.logging_utils' with whatever your actual module path is:
|
|
52
|
+
if module_name != __name__ and not module_name.startswith("logging"):
|
|
53
|
+
# We found a frame that isn’t in this helper module or the stdlib logging.
|
|
54
|
+
return idx
|
|
55
|
+
# Fallback to 1 (the immediate caller) if nothing else matches:
|
|
56
|
+
return 1
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from .imports import *
|
|
2
|
+
from .log_file import *
|
|
3
|
+
def get_logger_callable(logger, level="info"):
|
|
4
|
+
if logger is None:
|
|
5
|
+
return None
|
|
6
|
+
elif isinstance(logger, logging.Logger):
|
|
7
|
+
return getattr(logger, level.lower(), None)
|
|
8
|
+
elif callable(logger) and hasattr(logger, "__self__") and isinstance(logger.__self__, logging.Logger):
|
|
9
|
+
return logger
|
|
10
|
+
else:
|
|
11
|
+
return None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _find_caller_frame_index():
|
|
15
|
+
"""
|
|
16
|
+
Return the index in inspect.stack() of the first frame
|
|
17
|
+
that’s not in this module or the logging stdlib.
|
|
18
|
+
"""
|
|
19
|
+
for idx, frame_info in enumerate(inspect.stack()):
|
|
20
|
+
fn = frame_info.filename
|
|
21
|
+
if not fn.endswith("logging_utils.py") and "logging" not in os.path.basename(fn):
|
|
22
|
+
return idx
|
|
23
|
+
return 0 # fallback
|
|
24
|
+
|
|
25
|
+
def get_caller_info():
|
|
26
|
+
"""
|
|
27
|
+
Returns (caller_path, caller_idx).
|
|
28
|
+
caller_idx is the index into inspect.stack() where the call came from.
|
|
29
|
+
"""
|
|
30
|
+
idx = _find_caller_frame_index()
|
|
31
|
+
frame = inspect.stack()[idx]
|
|
32
|
+
return frame.filename, idx
|
|
33
|
+
|
|
34
|
+
def print_or_log(message, logger=True, level="info"):
|
|
35
|
+
# 1) grab both the path and the numeric index
|
|
36
|
+
caller_path, caller_idx = get_caller_info()
|
|
37
|
+
|
|
38
|
+
# 2) decide which logger object to use
|
|
39
|
+
if logger is True:
|
|
40
|
+
bpName = os.path.splitext(os.path.basename(caller_path))[0]
|
|
41
|
+
logger = get_logFile(bpName)
|
|
42
|
+
|
|
43
|
+
# 3) pick the right logging method
|
|
44
|
+
log_callable = get_logger_callable(logger, level=level)
|
|
45
|
+
if log_callable:
|
|
46
|
+
# pass the integer stacklevel = caller_idx + 1
|
|
47
|
+
log_callable(message, stacklevel=caller_idx + 1)
|
|
48
|
+
else:
|
|
49
|
+
print(message)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from ...type_utils import det_bool_T,is_number
|