osbot-utils 1.7.7__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.
- osbot_utils/__init__.py +1 -0
- osbot_utils/base_classes/Cache_Pickle.py +129 -0
- osbot_utils/base_classes/Kwargs_To_Disk.py +27 -0
- osbot_utils/base_classes/Kwargs_To_Self.py +308 -0
- osbot_utils/base_classes/Type_Safe__List.py +14 -0
- osbot_utils/base_classes/__init__.py +0 -0
- osbot_utils/context_managers/__init__.py +0 -0
- osbot_utils/context_managers/capture_duration.py +33 -0
- osbot_utils/decorators/__init__.py +0 -0
- osbot_utils/decorators/classes/__init__.py +0 -0
- osbot_utils/decorators/classes/singleton.py +9 -0
- osbot_utils/decorators/lists/__init__.py +0 -0
- osbot_utils/decorators/lists/filter_list.py +12 -0
- osbot_utils/decorators/lists/group_by.py +21 -0
- osbot_utils/decorators/lists/index_by.py +27 -0
- osbot_utils/decorators/methods/__init__.py +0 -0
- osbot_utils/decorators/methods/cache.py +19 -0
- osbot_utils/decorators/methods/cache_on_function.py +56 -0
- osbot_utils/decorators/methods/cache_on_self.py +78 -0
- osbot_utils/decorators/methods/cache_on_tmp.py +71 -0
- osbot_utils/decorators/methods/capture_exception.py +37 -0
- osbot_utils/decorators/methods/capture_status.py +20 -0
- osbot_utils/decorators/methods/catch.py +13 -0
- osbot_utils/decorators/methods/context.py +11 -0
- osbot_utils/decorators/methods/depreciated.py +79 -0
- osbot_utils/decorators/methods/function_type_check.py +62 -0
- osbot_utils/decorators/methods/obj_as_context.py +6 -0
- osbot_utils/decorators/methods/remove_return_value.py +22 -0
- osbot_utils/decorators/methods/required_fields.py +19 -0
- osbot_utils/fluent/Fluent_Dict.py +19 -0
- osbot_utils/fluent/Fluent_List.py +44 -0
- osbot_utils/fluent/__init__.py +1 -0
- osbot_utils/graphs/__init__.py +0 -0
- osbot_utils/graphs/mermaid/Mermaid.py +75 -0
- osbot_utils/graphs/mermaid/Mermaid__Edge.py +49 -0
- osbot_utils/graphs/mermaid/Mermaid__Graph.py +93 -0
- osbot_utils/graphs/mermaid/Mermaid__Node.py +69 -0
- osbot_utils/graphs/mermaid/Mermaid__Renderer.py +54 -0
- osbot_utils/graphs/mermaid/configs/Mermaid__Edge__Config.py +7 -0
- osbot_utils/graphs/mermaid/configs/Mermaid__Node__Config.py +9 -0
- osbot_utils/graphs/mermaid/configs/Mermaid__Render__Config.py +7 -0
- osbot_utils/graphs/mermaid/examples/Mermaid_Examples__FlowChart.py +98 -0
- osbot_utils/graphs/mermaid/models/Mermaid__Diagram_Direction.py +9 -0
- osbot_utils/graphs/mermaid/models/Mermaid__Diagram__Type.py +17 -0
- osbot_utils/graphs/mermaid/models/Mermaid__Node__Shape.py +30 -0
- osbot_utils/graphs/mgraph/MGraph.py +53 -0
- osbot_utils/graphs/mgraph/MGraph__Config.py +7 -0
- osbot_utils/graphs/mgraph/MGraph__Data.py +139 -0
- osbot_utils/graphs/mgraph/MGraph__Edge.py +27 -0
- osbot_utils/graphs/mgraph/MGraph__Node.py +33 -0
- osbot_utils/graphs/mgraph/MGraph__Random_Graphs.py +27 -0
- osbot_utils/graphs/mgraph/MGraph__Serializer.py +43 -0
- osbot_utils/graphs/mgraph/MGraphs.py +17 -0
- osbot_utils/graphs/mgraph/__init__.py +0 -0
- osbot_utils/helpers/CPrint.py +98 -0
- osbot_utils/helpers/Dict_To_Attr.py +7 -0
- osbot_utils/helpers/Local_Cache.py +111 -0
- osbot_utils/helpers/Local_Caches.py +54 -0
- osbot_utils/helpers/Print_Table.py +369 -0
- osbot_utils/helpers/Python_Audit.py +45 -0
- osbot_utils/helpers/Random_Seed.py +27 -0
- osbot_utils/helpers/SCP.py +58 -0
- osbot_utils/helpers/SSH.py +151 -0
- osbot_utils/helpers/Type_Registry.py +16 -0
- osbot_utils/helpers/__init__.py +0 -0
- osbot_utils/helpers/ast/Ast.py +35 -0
- osbot_utils/helpers/ast/Ast_Base.py +124 -0
- osbot_utils/helpers/ast/Ast_Data.py +28 -0
- osbot_utils/helpers/ast/Ast_Load.py +62 -0
- osbot_utils/helpers/ast/Ast_Merge.py +26 -0
- osbot_utils/helpers/ast/Ast_Node.py +117 -0
- osbot_utils/helpers/ast/Ast_Visit.py +85 -0
- osbot_utils/helpers/ast/Call_Tree.py +38 -0
- osbot_utils/helpers/ast/__init__.py +145 -0
- osbot_utils/helpers/ast/nodes/Ast_Add.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Alias.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_And.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Argument.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Arguments.py +10 -0
- osbot_utils/helpers/ast/nodes/Ast_Assert.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Assign.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Attribute.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Aug_Assign.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Bin_Op.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Bool_Op.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Break.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Call.py +17 -0
- osbot_utils/helpers/ast/nodes/Ast_Class_Def.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Compare.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Comprehension.py +10 -0
- osbot_utils/helpers/ast/nodes/Ast_Constant.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Continue.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Dict.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Eq.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Except_Handler.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Expr.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_For.py +10 -0
- osbot_utils/helpers/ast/nodes/Ast_Function_Def.py +17 -0
- osbot_utils/helpers/ast/nodes/Ast_Generator_Exp.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Gt.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_GtE.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_If.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_If_Exp.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Import.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Import_From.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_In.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Is.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Is_Not.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Keyword.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Lambda.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_List.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_List_Comp.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Load.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Lt.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_LtE.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Mod.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Module.py +20 -0
- osbot_utils/helpers/ast/nodes/Ast_Mult.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Name.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Not.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Not_Eq.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Not_In.py +6 -0
- osbot_utils/helpers/ast/nodes/Ast_Or.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Pass.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Pow.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Raise.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Return.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Set.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Slice.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Starred.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Store.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Sub.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Subscript.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_Try.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Tuple.py +9 -0
- osbot_utils/helpers/ast/nodes/Ast_Unary_Op.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_While.py +8 -0
- osbot_utils/helpers/ast/nodes/Ast_With.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_With_Item.py +7 -0
- osbot_utils/helpers/ast/nodes/Ast_Yield.py +7 -0
- osbot_utils/helpers/ast/nodes/__init__.py +0 -0
- osbot_utils/helpers/html/Dict_To_Css.py +20 -0
- osbot_utils/helpers/html/Dict_To_Html.py +59 -0
- osbot_utils/helpers/html/Dict_To_Tags.py +88 -0
- osbot_utils/helpers/html/Html_To_Dict.py +75 -0
- osbot_utils/helpers/html/Html_To_Tag.py +20 -0
- osbot_utils/helpers/html/Tag__Base.py +91 -0
- osbot_utils/helpers/html/Tag__Body.py +5 -0
- osbot_utils/helpers/html/Tag__Div.py +5 -0
- osbot_utils/helpers/html/Tag__H.py +9 -0
- osbot_utils/helpers/html/Tag__HR.py +5 -0
- osbot_utils/helpers/html/Tag__Head.py +32 -0
- osbot_utils/helpers/html/Tag__Html.py +42 -0
- osbot_utils/helpers/html/Tag__Link.py +17 -0
- osbot_utils/helpers/html/Tag__Style.py +25 -0
- osbot_utils/helpers/html/__init__.py +0 -0
- osbot_utils/helpers/pubsub/Event__Queue.py +95 -0
- osbot_utils/helpers/pubsub/PubSub__Client.py +53 -0
- osbot_utils/helpers/pubsub/PubSub__Room.py +13 -0
- osbot_utils/helpers/pubsub/PubSub__Server.py +94 -0
- osbot_utils/helpers/pubsub/PubSub__Sqlite.py +24 -0
- osbot_utils/helpers/pubsub/__init__.py +0 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event.py +15 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Connect.py +7 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Disconnect.py +7 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Join_Room.py +8 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Leave_Room.py +8 -0
- osbot_utils/helpers/pubsub/schemas/Schema__Event__Message.py +7 -0
- osbot_utils/helpers/pubsub/schemas/Schema__PubSub__Client.py +8 -0
- osbot_utils/helpers/pubsub/schemas/__init__.py +0 -0
- osbot_utils/helpers/sqlite/Capture_Sqlite_Error.py +51 -0
- osbot_utils/helpers/sqlite/Sqlite__Cursor.py +87 -0
- osbot_utils/helpers/sqlite/Sqlite__Database.py +137 -0
- osbot_utils/helpers/sqlite/Sqlite__Field.py +70 -0
- osbot_utils/helpers/sqlite/Sqlite__Globals.py +5 -0
- osbot_utils/helpers/sqlite/Sqlite__Table.py +293 -0
- osbot_utils/helpers/sqlite/Sqlite__Table__Create.py +96 -0
- osbot_utils/helpers/sqlite/Temp_Sqlite__Database__Disk.py +17 -0
- osbot_utils/helpers/sqlite/Temp_Sqlite__Table.py +23 -0
- osbot_utils/helpers/sqlite/__init__.py +0 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests.py +214 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests__Patch.py +63 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Files.py +23 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Graph.py +47 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Json.py +83 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Local.py +20 -0
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Requests.py +39 -0
- osbot_utils/helpers/sqlite/domains/__init__.py +0 -0
- osbot_utils/helpers/sqlite/domains/schemas/Schema__Table__Requests.py +12 -0
- osbot_utils/helpers/sqlite/domains/schemas/__init__.py +0 -0
- osbot_utils/helpers/sqlite/models/Sqlite__Field__Type.py +37 -0
- osbot_utils/helpers/sqlite/models/__init__.py +0 -0
- osbot_utils/helpers/sqlite/sample_data/Sqlite__Sample_Data__Chinook.py +116 -0
- osbot_utils/helpers/sqlite/sample_data/__init__.py +0 -0
- osbot_utils/helpers/sqlite/sql_builder/SQL_Builder.py +159 -0
- osbot_utils/helpers/sqlite/sql_builder/SQL_Builder__Select.py +12 -0
- osbot_utils/helpers/sqlite/sql_builder/__init__.py +0 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Config.py +63 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Edges.py +46 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Files.py +45 -0
- osbot_utils/helpers/sqlite/tables/Sqlite__Table__Nodes.py +52 -0
- osbot_utils/helpers/sqlite/tables/__init__.py +0 -0
- osbot_utils/helpers/trace/Trace_Call.py +120 -0
- osbot_utils/helpers/trace/Trace_Call__Config.py +94 -0
- osbot_utils/helpers/trace/Trace_Call__Graph.py +26 -0
- osbot_utils/helpers/trace/Trace_Call__Handler.py +215 -0
- osbot_utils/helpers/trace/Trace_Call__Print_Lines.py +85 -0
- osbot_utils/helpers/trace/Trace_Call__Print_Traces.py +170 -0
- osbot_utils/helpers/trace/Trace_Call__Stack.py +166 -0
- osbot_utils/helpers/trace/Trace_Call__Stack_Node.py +59 -0
- osbot_utils/helpers/trace/Trace_Call__Stats.py +71 -0
- osbot_utils/helpers/trace/Trace_Call__View_Model.py +75 -0
- osbot_utils/helpers/trace/Trace_Files.py +33 -0
- osbot_utils/helpers/trace/__init__.py +0 -0
- osbot_utils/testing/Catch.py +54 -0
- osbot_utils/testing/Duration.py +69 -0
- osbot_utils/testing/Hook_Method.py +118 -0
- osbot_utils/testing/Log_To_Queue.py +46 -0
- osbot_utils/testing/Log_To_String.py +37 -0
- osbot_utils/testing/Logging.py +81 -0
- osbot_utils/testing/Patch_Print.py +52 -0
- osbot_utils/testing/Profiler.py +89 -0
- osbot_utils/testing/Stderr.py +19 -0
- osbot_utils/testing/Stdout.py +19 -0
- osbot_utils/testing/Temp_File.py +46 -0
- osbot_utils/testing/Temp_Folder.py +114 -0
- osbot_utils/testing/Temp_Sys_Path.py +13 -0
- osbot_utils/testing/Temp_Web_Server.py +83 -0
- osbot_utils/testing/Temp_Zip.py +45 -0
- osbot_utils/testing/Temp_Zip_In_Memory.py +90 -0
- osbot_utils/testing/Unit_Test.py +34 -0
- osbot_utils/testing/Unzip_File.py +30 -0
- osbot_utils/testing/__init__.py +0 -0
- osbot_utils/utils/Assert.py +52 -0
- osbot_utils/utils/Call_Stack.py +187 -0
- osbot_utils/utils/Csv.py +32 -0
- osbot_utils/utils/Dev.py +47 -0
- osbot_utils/utils/Exceptions.py +7 -0
- osbot_utils/utils/Files.py +528 -0
- osbot_utils/utils/Functions.py +113 -0
- osbot_utils/utils/Http.py +136 -0
- osbot_utils/utils/Int.py +6 -0
- osbot_utils/utils/Json.py +171 -0
- osbot_utils/utils/Json_Cache.py +59 -0
- osbot_utils/utils/Lists.py +198 -0
- osbot_utils/utils/Misc.py +496 -0
- osbot_utils/utils/Objects.py +341 -0
- osbot_utils/utils/Png.py +29 -0
- osbot_utils/utils/Process.py +73 -0
- osbot_utils/utils/Python_Logger.py +301 -0
- osbot_utils/utils/Status.py +79 -0
- osbot_utils/utils/Str.py +63 -0
- osbot_utils/utils/Version.py +16 -0
- osbot_utils/utils/Zip.py +97 -0
- osbot_utils/utils/__init__.py +16 -0
- osbot_utils/version +1 -0
- osbot_utils-1.7.7.dist-info/LICENSE +201 -0
- osbot_utils-1.7.7.dist-info/METADATA +46 -0
- osbot_utils-1.7.7.dist-info/RECORD +260 -0
- osbot_utils-1.7.7.dist-info/WHEEL +4 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
import random
|
2
|
+
|
3
|
+
from osbot_utils.utils.Misc import random_string
|
4
|
+
|
5
|
+
from osbot_utils.utils.Files import path_combine, temp_folder_current, safe_file_name, folder_exists, folder_create, \
|
6
|
+
folder_delete_recursively, files_list, file_create, folder_files, folders_recursive, files_find, files_recursive, \
|
7
|
+
temp_file_in_folder, create_folder, filter_parent_folder
|
8
|
+
from osbot_utils.utils.Zip import zip_folder
|
9
|
+
|
10
|
+
|
11
|
+
class Temp_Folder:
|
12
|
+
|
13
|
+
def __init__(self, folder_name=None, parent_folder=None, temp_prefix='', delete_on_exit=True, temp_files_to_add=0):
|
14
|
+
if type(parent_folder) is Temp_Folder:
|
15
|
+
parent_folder = parent_folder.path()
|
16
|
+
self.folder_name = folder_name or f"temp_folder_{random_string(prefix=temp_prefix)}"
|
17
|
+
self.parent_folder = parent_folder or temp_folder_current()
|
18
|
+
self.full_path = path_combine(self.parent_folder, safe_file_name(self.folder_name))
|
19
|
+
self.delete_on_exit = delete_on_exit
|
20
|
+
self.temp_files_to_add = temp_files_to_add
|
21
|
+
|
22
|
+
|
23
|
+
def __enter__(self):
|
24
|
+
folder_create(self.full_path)
|
25
|
+
self.add_temp_files_and_folders(max_total_files=self.temp_files_to_add)
|
26
|
+
return self
|
27
|
+
|
28
|
+
def __exit__(self, type, value, traceback):
|
29
|
+
if self.delete_on_exit:
|
30
|
+
folder_delete_recursively(self.full_path)
|
31
|
+
|
32
|
+
def __repr__(self):
|
33
|
+
return f"<Temp_Folder: {self.full_path}>"
|
34
|
+
|
35
|
+
def __str__(self):
|
36
|
+
return self.full_path
|
37
|
+
|
38
|
+
def add_temp_files(self, count=0):
|
39
|
+
if count is None: count = 1
|
40
|
+
for i in range(count):
|
41
|
+
self.add_file()
|
42
|
+
|
43
|
+
def add_temp_files_and_folders(self, target_folder=None, max_depth_param=5, max_files_per_folder=4, max_total_files=20, current_depth=0):
|
44
|
+
if max_total_files <=0: return max_total_files
|
45
|
+
if target_folder is None: target_folder = self.full_path
|
46
|
+
|
47
|
+
# Base case: if max_depth_param is 0 or we've reached max_total_files, we stop
|
48
|
+
if max_depth_param == 0 or max_total_files <= 0:
|
49
|
+
return max_total_files
|
50
|
+
|
51
|
+
# Randomly decide the number of subfolders and files for this folder
|
52
|
+
num_subfolders = random.randint(1, max_depth_param)
|
53
|
+
num_files = random.randint(1, min(max_files_per_folder, max_total_files))
|
54
|
+
#print(f'Creating {num_subfolders} subfolders and {num_files} files in {target_folder}')
|
55
|
+
# Create the random files
|
56
|
+
for _ in range(num_files):
|
57
|
+
temp_file_in_folder(target_folder,prefix= f'temp_file__{max_total_files}')
|
58
|
+
max_total_files -= 1
|
59
|
+
if max_total_files <= 0:
|
60
|
+
return max_total_files
|
61
|
+
|
62
|
+
current_depth +=1
|
63
|
+
# Recursively create subfolders and their contents
|
64
|
+
for _ in range(0, num_subfolders):
|
65
|
+
subfolder_name = f"_[{current_depth}]_temp_folder_{str(random.randint(0, 512))}_"
|
66
|
+
subfolder_path = path_combine(target_folder, subfolder_name)
|
67
|
+
create_folder(subfolder_path)
|
68
|
+
|
69
|
+
# Recursive call with decremented depth and updated max_total_files
|
70
|
+
max_total_files = self.add_temp_files_and_folders(subfolder_path, max_depth_param - 1, max_files_per_folder, max_total_files, current_depth=current_depth)
|
71
|
+
|
72
|
+
if max_total_files <= 0:
|
73
|
+
break
|
74
|
+
|
75
|
+
return max_total_files
|
76
|
+
|
77
|
+
def add_file(self, file_name=None, contents=None):
|
78
|
+
if file_name is None: file_name = f"temp_file_{random_string()}.txt"
|
79
|
+
if contents is None: contents = random_string()
|
80
|
+
file_path = path_combine(self.full_path, safe_file_name(file_name))
|
81
|
+
return file_create(file_path, contents)
|
82
|
+
|
83
|
+
def add_folder(self, name=None):
|
84
|
+
if name is None: name = f"temp_folder_{random_string()}"
|
85
|
+
new_folder = path_combine(self.path(), safe_file_name(name))
|
86
|
+
return folder_create(new_folder)
|
87
|
+
|
88
|
+
|
89
|
+
def exists(self):
|
90
|
+
return folder_exists(self.full_path)
|
91
|
+
|
92
|
+
def path(self):
|
93
|
+
return self.full_path
|
94
|
+
|
95
|
+
def files(self, show_parent_folder=False, include_folders=False):
|
96
|
+
all_files = files_recursive(self.path(), include_folders=include_folders)
|
97
|
+
if show_parent_folder:
|
98
|
+
return all_files
|
99
|
+
return filter_parent_folder(all_files, self.path())
|
100
|
+
|
101
|
+
def files_and_folders(self, show_parent_folder=False):
|
102
|
+
all_files_and_folders = files_recursive(self.path(), include_folders=True)
|
103
|
+
if show_parent_folder:
|
104
|
+
return all_files_and_folders
|
105
|
+
return filter_parent_folder(all_files_and_folders, self.path())
|
106
|
+
|
107
|
+
def folders(self, show_parent_folder=False):
|
108
|
+
all_folders = folders_recursive(self.path())
|
109
|
+
if show_parent_folder:
|
110
|
+
return all_folders
|
111
|
+
return filter_parent_folder(all_folders, self.path())
|
112
|
+
|
113
|
+
def zip(self):
|
114
|
+
return zip_folder(self.path())
|
@@ -0,0 +1,83 @@
|
|
1
|
+
from contextlib import contextmanager
|
2
|
+
from functools import partial
|
3
|
+
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
4
|
+
from threading import Thread
|
5
|
+
from urllib.parse import urljoin
|
6
|
+
|
7
|
+
from osbot_utils.utils.Files import file_create, path_combine, temp_filename, file_create_all_parent_folders
|
8
|
+
|
9
|
+
from osbot_utils.utils.Misc import random_port, random_string
|
10
|
+
|
11
|
+
from osbot_utils.utils.Http import port_is_open, GET
|
12
|
+
|
13
|
+
|
14
|
+
class Temp_Web_Server:
|
15
|
+
server : ThreadingHTTPServer
|
16
|
+
server_thread : Thread
|
17
|
+
|
18
|
+
def __init__(self, host: str = None, port: int = None, root_folder: str = None, server_name = None, http_handler = None, wait_for_stop=False):
|
19
|
+
self.host = host or "127.0.0.1"
|
20
|
+
self.port = port or random_port()
|
21
|
+
self.root_folder = root_folder or "."
|
22
|
+
self.server_name = server_name or "Temp_Web_Server"
|
23
|
+
self.http_handler = http_handler or SimpleHTTPRequestHandler
|
24
|
+
self.wait_for_stop = wait_for_stop
|
25
|
+
|
26
|
+
def __enter__(self):
|
27
|
+
self.start()
|
28
|
+
return self
|
29
|
+
|
30
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
31
|
+
self.stop()
|
32
|
+
|
33
|
+
def add_file(self, relative_file_path=None, file_contents=None):
|
34
|
+
if relative_file_path is None:
|
35
|
+
relative_file_path = temp_filename()
|
36
|
+
if file_contents is None:
|
37
|
+
file_contents = random_string()
|
38
|
+
full_path = path_combine(self.root_folder, relative_file_path) # todo: fix the path transversal vulnerability that exists in this function #security
|
39
|
+
file_create_all_parent_folders(full_path)
|
40
|
+
file_create(path=full_path, contents=file_contents)
|
41
|
+
return full_path
|
42
|
+
|
43
|
+
def GET(self, path=''):
|
44
|
+
url = self.url(path)
|
45
|
+
try:
|
46
|
+
return GET(url)
|
47
|
+
except Exception as error:
|
48
|
+
print(error) # todo: add support for using logging
|
49
|
+
return None
|
50
|
+
|
51
|
+
def GET_contains(self, content, path=''):
|
52
|
+
page_html = self.GET(path=path)
|
53
|
+
if type(content) is list:
|
54
|
+
for item in content:
|
55
|
+
if item not in page_html:
|
56
|
+
return False
|
57
|
+
return True
|
58
|
+
return content in page_html
|
59
|
+
|
60
|
+
def server_port_open(self):
|
61
|
+
return port_is_open(host=self.host, port=self.port)
|
62
|
+
|
63
|
+
def stop(self):
|
64
|
+
self.server.server_close()
|
65
|
+
if self.wait_for_stop:
|
66
|
+
self.server.shutdown()
|
67
|
+
self.server_thread.join()
|
68
|
+
else:
|
69
|
+
self.server._BaseServer__shutdown_request = True # simulate what happens inside self.server.shutdown()
|
70
|
+
|
71
|
+
def start(self):
|
72
|
+
if self.http_handler is SimpleHTTPRequestHandler:
|
73
|
+
handler_config = partial(self.http_handler, directory=self.root_folder)
|
74
|
+
else:
|
75
|
+
handler_config = partial(self.http_handler)
|
76
|
+
self.server = ThreadingHTTPServer((self.host, self.port), handler_config)
|
77
|
+
self.server_thread = Thread(target=self.server.serve_forever, name=self.server_name)
|
78
|
+
self.server_thread.start()
|
79
|
+
|
80
|
+
def url(self,path=''):
|
81
|
+
base_url = f"http://{self.host}:{self.port}"
|
82
|
+
url = urljoin(base_url, path)
|
83
|
+
return url
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from osbot_utils.testing.Temp_Folder import Temp_Folder
|
2
|
+
from osbot_utils.utils.Files import Files, is_folder, file_exists, file_name
|
3
|
+
from osbot_utils.utils.Zip import zip_folder, zip_file_list
|
4
|
+
|
5
|
+
|
6
|
+
class Temp_Zip():
|
7
|
+
def __init__(self, target=None, target_zip_file=None, delete_zip_file=True):
|
8
|
+
if type(target) is Temp_Folder:
|
9
|
+
target = target.path()
|
10
|
+
self.target = target
|
11
|
+
self.delete_zip_file = delete_zip_file
|
12
|
+
self.target_zip_file = target_zip_file
|
13
|
+
self.zip_file = None
|
14
|
+
self.zip_bytes = None
|
15
|
+
self.target_zipped = False
|
16
|
+
|
17
|
+
def __enter__(self):
|
18
|
+
if is_folder(self.target):
|
19
|
+
self.zip_file = zip_folder(self.target)
|
20
|
+
self.target_zipped = True
|
21
|
+
return self
|
22
|
+
|
23
|
+
def __exit__(self, type, value, traceback):
|
24
|
+
if Files.exists(self.zip_file) and self.delete_zip_file:
|
25
|
+
Files.delete(self.zip_file)
|
26
|
+
|
27
|
+
def __repr__(self):
|
28
|
+
return f"<Temp_Zip: {self.path()}>"
|
29
|
+
|
30
|
+
def file_name(self):
|
31
|
+
return file_name(self.zip_file)
|
32
|
+
|
33
|
+
def path(self):
|
34
|
+
return self.zip_file
|
35
|
+
|
36
|
+
def files(self):
|
37
|
+
return zip_file_list(self.zip_file)
|
38
|
+
|
39
|
+
def print_path(self):
|
40
|
+
print()
|
41
|
+
print(self.path())
|
42
|
+
return self
|
43
|
+
|
44
|
+
def zip_file_exists(self):
|
45
|
+
return file_exists(self.zip_file)
|
@@ -0,0 +1,90 @@
|
|
1
|
+
import io
|
2
|
+
|
3
|
+
from osbot_utils.testing.Temp_File import Temp_File
|
4
|
+
from osbot_utils.testing.Temp_Folder import Temp_Folder
|
5
|
+
from osbot_utils.utils.Files import is_file, is_folder, files_recursive, filter_parent_folder, temp_file
|
6
|
+
from osbot_utils.utils.Zip import zip_files_to_bytes, zip_bytes_file_list, zip_bytes_add_file, zip_bytes_get_file
|
7
|
+
|
8
|
+
|
9
|
+
class Temp_Zip_In_Memory:
|
10
|
+
|
11
|
+
def __init__(self, targets=None, targets_as_bytes=None):
|
12
|
+
self.targets = targets or []
|
13
|
+
self.targets_as_content = targets_as_bytes or []
|
14
|
+
self.root_folder = None
|
15
|
+
|
16
|
+
def __enter__(self):
|
17
|
+
return self
|
18
|
+
|
19
|
+
def __exit__(self, type, value, traceback):
|
20
|
+
pass
|
21
|
+
|
22
|
+
def add_file(self, file, root_folder=None):
|
23
|
+
if type(file) is Temp_File:
|
24
|
+
file = file.path()
|
25
|
+
if is_file(file):
|
26
|
+
self.add_target(file, root_folder)
|
27
|
+
return self
|
28
|
+
|
29
|
+
def add_file_from_content(self, file_path, file_contents):
|
30
|
+
self.targets_as_content.append({'file_path': file_path, 'file_contents': file_contents})
|
31
|
+
return self
|
32
|
+
|
33
|
+
def add_folder(self, folder, root_folder=None):
|
34
|
+
if type(folder) is Temp_Folder:
|
35
|
+
folder = folder.path()
|
36
|
+
if is_folder(folder):
|
37
|
+
self.add_target(folder, root_folder)
|
38
|
+
return self
|
39
|
+
|
40
|
+
def add_target(self, target, root_folder=None):
|
41
|
+
if target:
|
42
|
+
self.targets.append({'target': target, 'root_folder': root_folder})
|
43
|
+
return self
|
44
|
+
|
45
|
+
def create_zip_file(self, target_zip_file=None):
|
46
|
+
if target_zip_file is None:
|
47
|
+
target_zip_file = temp_file(extension='.zip')
|
48
|
+
with open(target_zip_file, 'wb') as f:
|
49
|
+
f.write(self.zip_bytes())
|
50
|
+
return target_zip_file
|
51
|
+
|
52
|
+
def set_root_folder(self, root_folder):
|
53
|
+
if type(root_folder) is Temp_Folder:
|
54
|
+
self.root_folder = root_folder.path()
|
55
|
+
else:
|
56
|
+
self.root_folder = root_folder
|
57
|
+
return self
|
58
|
+
|
59
|
+
def target_files(self):
|
60
|
+
return [entry.get('file') for entry in self.target_files_with_root_folder()]
|
61
|
+
|
62
|
+
def target_files_with_root_folder(self):
|
63
|
+
all_files = []
|
64
|
+
for entry in self.targets:
|
65
|
+
target = entry.get('target')
|
66
|
+
root_folder = entry.get('root_folder') or self.root_folder
|
67
|
+
if is_file(target):
|
68
|
+
all_files.append({'file': target, 'root_folder': root_folder})
|
69
|
+
elif is_folder(target):
|
70
|
+
for file in files_recursive(target):
|
71
|
+
all_files.append({'file': file, 'root_folder': root_folder})
|
72
|
+
return all_files
|
73
|
+
|
74
|
+
def zip_bytes(self):
|
75
|
+
zip_bytes = self.zip_buffer().getvalue()
|
76
|
+
for items in self.targets_as_content:
|
77
|
+
file_path = items.get('file_path')
|
78
|
+
file_contents = items.get('file_contents')
|
79
|
+
zip_bytes = zip_bytes_add_file(zip_bytes, file_path, file_contents)
|
80
|
+
return zip_bytes
|
81
|
+
|
82
|
+
def zip_bytes_file_content(self, file_path):
|
83
|
+
return zip_bytes_get_file(self.zip_bytes(), file_path)
|
84
|
+
|
85
|
+
def zip_bytes_files(self):
|
86
|
+
return zip_bytes_file_list(self.zip_bytes())
|
87
|
+
|
88
|
+
def zip_buffer(self):
|
89
|
+
targets = self.target_files_with_root_folder()
|
90
|
+
return zip_files_to_bytes(targets, root_folder=self.root_folder)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import base64
|
2
|
+
from unittest import TestCase
|
3
|
+
from osbot_utils.utils.Dev import pprint
|
4
|
+
|
5
|
+
|
6
|
+
class Unit_Test(TestCase):
|
7
|
+
"""Unit test helpers
|
8
|
+
|
9
|
+
- self.result will be written to the output
|
10
|
+
- self.png_data will be saved to '/tmp/unit-test.png'"""
|
11
|
+
|
12
|
+
def setUp(self):
|
13
|
+
self.png_file = '/tmp/unit-test.png'
|
14
|
+
self.result = None
|
15
|
+
self.png_data = None
|
16
|
+
|
17
|
+
def tearDown(self):
|
18
|
+
if self.result is not None:
|
19
|
+
pprint(self.result)
|
20
|
+
if self.png_data is not None:
|
21
|
+
if type(self.png_data) is bytes:
|
22
|
+
with open(self.png_file, "wb") as file:
|
23
|
+
file.write(self.png_data)
|
24
|
+
pprint(f'Png data with size {len(self.png_data)} saved to {self.png_file}')
|
25
|
+
elif type(self.png_data) is str:
|
26
|
+
try:
|
27
|
+
with open(self.png_file, "wb") as file:
|
28
|
+
file.write(base64.decodebytes(self.png_data.encode()))
|
29
|
+
pprint(f'Png data with size {len(self.png_data)} saved to {self.png_file}')
|
30
|
+
except Exception as error:
|
31
|
+
pprint(f'png save error: {error}')
|
32
|
+
pprint(self.png_data)
|
33
|
+
else:
|
34
|
+
pprint(f'Error Png data was not a string: {self.png_data}')
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from osbot_utils.utils.Files import Files, folder_exists, folder_delete_all, temp_folder, files_recursive
|
2
|
+
from osbot_utils.utils.Zip import unzip_file
|
3
|
+
|
4
|
+
|
5
|
+
class Unzip_File:
|
6
|
+
def __init__(self, zip_file=None, target_folder=None, delete_target_folder=True):
|
7
|
+
self.target_folder = target_folder
|
8
|
+
self.zip_file = zip_file
|
9
|
+
self.delete_target_folder = delete_target_folder
|
10
|
+
self.files_unzipped = False
|
11
|
+
self.target_folder_deleted = False
|
12
|
+
|
13
|
+
def __enter__(self):
|
14
|
+
if Files.exists(self.zip_file):
|
15
|
+
if self.target_folder is None:
|
16
|
+
self.target_folder = temp_folder("unzipped_")
|
17
|
+
unzip_file(self.zip_file, self.target_folder)
|
18
|
+
self.files_unzipped = True
|
19
|
+
return self
|
20
|
+
|
21
|
+
def __exit__(self, type, value, traceback):
|
22
|
+
if folder_exists(self.target_folder) and self.delete_target_folder:
|
23
|
+
self.target_folder_deleted = folder_delete_all(self.target_folder)
|
24
|
+
#print("\n\ndeleting", self.target_folder)
|
25
|
+
|
26
|
+
def path(self):
|
27
|
+
return self.target_folder
|
28
|
+
|
29
|
+
def files(self):
|
30
|
+
return files_recursive(self.target_folder)
|
File without changes
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import re
|
2
|
+
import datetime
|
3
|
+
|
4
|
+
from osbot_utils.utils.Objects import class_name
|
5
|
+
|
6
|
+
|
7
|
+
class Assert:
|
8
|
+
def __init__(self ,target):
|
9
|
+
self.target = target
|
10
|
+
|
11
|
+
def is_class(self, name):
|
12
|
+
assert class_name(self.target) in name
|
13
|
+
|
14
|
+
def contains(self, text):
|
15
|
+
assert text in self.target
|
16
|
+
|
17
|
+
def field_is_equal(self, field_name, expected_value=None):
|
18
|
+
field_value = self.target.get(field_name)
|
19
|
+
assert field_value == expected_value , "{0} != {1}".format(field_value, expected_value)
|
20
|
+
return self
|
21
|
+
|
22
|
+
def is_bigger_than(self, value):
|
23
|
+
if type(self.target) is list:
|
24
|
+
list_len = len(self.target)
|
25
|
+
assert list_len > value , "array with len {0} was not bigger than {1}".format(list_len, value)
|
26
|
+
else:
|
27
|
+
assert self.target > value , "value {0} was not bigger than {1}".format(self.target, value)
|
28
|
+
return self
|
29
|
+
|
30
|
+
def is_smaller_than(self, value):
|
31
|
+
if type(self.target) is list:
|
32
|
+
list_len = len(self.target)
|
33
|
+
assert list_len < value , "array with len {0} was not smaller than {1}".format(list_len, value)
|
34
|
+
else:
|
35
|
+
assert self.target < value , "value {0} was not smaller than {1}".format(self.target, value)
|
36
|
+
return self
|
37
|
+
|
38
|
+
def is_equal(self, to):
|
39
|
+
assert self.target == to
|
40
|
+
|
41
|
+
def is_today(self):
|
42
|
+
assert type(self.target) == datetime.datetime
|
43
|
+
assert str(self.target.date()) == str(datetime.datetime.utcnow().date())
|
44
|
+
|
45
|
+
def match_regex(self, regex):
|
46
|
+
assert re.compile(regex).match(self.target) is not None
|
47
|
+
|
48
|
+
def size_is(self, to):
|
49
|
+
assert len(self.target) == to
|
50
|
+
|
51
|
+
def regex_not_match(self,regex):
|
52
|
+
assert re.compile(regex).match(self.target) is None
|
@@ -0,0 +1,187 @@
|
|
1
|
+
import linecache
|
2
|
+
import sys
|
3
|
+
import traceback
|
4
|
+
from osbot_utils.helpers.CPrint import CPrint
|
5
|
+
from osbot_utils.helpers.Print_Table import Print_Table, CHAR_TABLE_HORIZONTAL, CHAR_TABLE_TOP_LEFT, CHAR_TABLE_VERTICAL, CHAR_TABLE_BOTTOM_LEFT
|
6
|
+
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
|
7
|
+
from osbot_utils.utils.Objects import obj_data, class_full_name
|
8
|
+
|
9
|
+
MODULES_TO_STOP_CAPTURE = ['unittest.case']
|
10
|
+
PRINT_STACK_COLOR_THEMES = { 'default' : ('none' , 'none' , 'none' ),
|
11
|
+
'minty' : ('green' , 'grey' , 'cyan' ) ,
|
12
|
+
'meadow' : ('blue' , 'none' ,'green' ) ,
|
13
|
+
'aquamarine' : ('bright_cyan' , 'cyan' ,'blue' ),
|
14
|
+
'autumn' : ('bright_yellow' , 'yellow','red' )}
|
15
|
+
|
16
|
+
class Frame_Data(Kwargs_To_Self):
|
17
|
+
depth : int
|
18
|
+
caller_line : str
|
19
|
+
method_line : str
|
20
|
+
method_name : str
|
21
|
+
line_number : int
|
22
|
+
local_self : str
|
23
|
+
module : str
|
24
|
+
|
25
|
+
def __repr__(self):
|
26
|
+
return f"{self.data()})"
|
27
|
+
|
28
|
+
def data(self):
|
29
|
+
return self.__locals__()
|
30
|
+
|
31
|
+
class Call_Stack(Kwargs_To_Self):
|
32
|
+
capture_locals : bool = False
|
33
|
+
cprint : CPrint
|
34
|
+
cprint_theme : str = 'meadow'
|
35
|
+
frames : list
|
36
|
+
max_depth : int = 10
|
37
|
+
|
38
|
+
__print_order__: list = ['module', 'method_name', 'caller_line', 'method_line', 'local_self', 'line_number', 'depth']
|
39
|
+
|
40
|
+
def __repr__(self):
|
41
|
+
return "Call_Stack"
|
42
|
+
|
43
|
+
def calls(self):
|
44
|
+
calls = []
|
45
|
+
for frame in self.frames:
|
46
|
+
method_name = frame.method_name
|
47
|
+
module = frame.module
|
48
|
+
call = f"{module}.{method_name}"
|
49
|
+
calls.append(call)
|
50
|
+
return calls
|
51
|
+
|
52
|
+
def calls__source_code(self):
|
53
|
+
calls = []
|
54
|
+
for frame in self.frames:
|
55
|
+
calls.append(frame.caller_line)
|
56
|
+
return calls
|
57
|
+
|
58
|
+
|
59
|
+
def capture(self,skip_caller=True):
|
60
|
+
current_frame = sys._getframe().f_back
|
61
|
+
if skip_caller:
|
62
|
+
current_frame = current_frame.f_back
|
63
|
+
return self.capture_frame(current_frame)
|
64
|
+
|
65
|
+
def capture_frame(self, frame):
|
66
|
+
depth = 0
|
67
|
+
while frame:
|
68
|
+
if self.stop_capture(frame, depth):
|
69
|
+
break
|
70
|
+
new_frame = self.new_frame(frame,depth)
|
71
|
+
self.frames.append(new_frame)
|
72
|
+
depth += 1
|
73
|
+
frame = frame.f_back
|
74
|
+
return self
|
75
|
+
|
76
|
+
def stats(self):
|
77
|
+
return { 'depth' : len(self.frames) }
|
78
|
+
|
79
|
+
def stop_capture(self, frame, depth):
|
80
|
+
if frame is None:
|
81
|
+
return True
|
82
|
+
if depth > self.max_depth:
|
83
|
+
return True
|
84
|
+
module = frame.f_globals.get("__name__", "")
|
85
|
+
|
86
|
+
if module in MODULES_TO_STOP_CAPTURE:
|
87
|
+
return True
|
88
|
+
return False
|
89
|
+
|
90
|
+
def new_frame(self, frame, depth):
|
91
|
+
file_path = frame.f_code.co_filename
|
92
|
+
method_name = frame.f_code.co_name
|
93
|
+
caller_line_number = frame.f_lineno
|
94
|
+
method_line_number = frame.f_code.co_firstlineno
|
95
|
+
|
96
|
+
caller_line = linecache.getline(file_path, caller_line_number).strip()
|
97
|
+
method_line = linecache.getline(file_path, method_line_number).strip() # see if we need to add the code to resolve the function from the decorators
|
98
|
+
module = frame.f_globals.get("__name__", "")
|
99
|
+
local_self = class_full_name(frame.f_locals.get('self'))
|
100
|
+
frame_data = Frame_Data( depth = depth ,
|
101
|
+
caller_line = caller_line ,
|
102
|
+
method_line = method_line ,
|
103
|
+
method_name = method_name ,
|
104
|
+
module = module ,
|
105
|
+
local_self = local_self ,
|
106
|
+
line_number = caller_line_number )
|
107
|
+
if self.capture_locals:
|
108
|
+
frame_data.locals = frame.f_locals,
|
109
|
+
return frame_data
|
110
|
+
|
111
|
+
def print(self):
|
112
|
+
for line in self.stack_lines__calls():
|
113
|
+
print(line)
|
114
|
+
|
115
|
+
def print__source_code(self):
|
116
|
+
for line in self.stack_lines__source_code():
|
117
|
+
print(line)
|
118
|
+
|
119
|
+
def stack_lines(self, items):
|
120
|
+
self.cprint.lines = []
|
121
|
+
self.cprint.auto_print = False
|
122
|
+
|
123
|
+
if len(items) == 0:
|
124
|
+
return []
|
125
|
+
|
126
|
+
if len(items) == 1:
|
127
|
+
self.print_with_color('none', f"{CHAR_TABLE_HORIZONTAL} {items[0]}")
|
128
|
+
else:
|
129
|
+
color_top, color_middle, color_bottom = self.stack_colors()
|
130
|
+
self.print_with_color(color_top, text=f"{CHAR_TABLE_TOP_LEFT} {items[0]}")
|
131
|
+
for call in items[1:-1]:
|
132
|
+
self.print_with_color(color_middle, text=f"{CHAR_TABLE_VERTICAL} {call}")
|
133
|
+
self.print_with_color(color_bottom, text=f"{CHAR_TABLE_BOTTOM_LEFT} {items[-1]}")
|
134
|
+
return self.cprint.lines
|
135
|
+
|
136
|
+
def stack_lines__calls(self):
|
137
|
+
calls = self.calls()
|
138
|
+
return self.stack_lines(calls)
|
139
|
+
|
140
|
+
def stack_lines__source_code(self):
|
141
|
+
calls_source_code = self.calls__source_code()
|
142
|
+
return self.stack_lines(calls_source_code)
|
143
|
+
|
144
|
+
def stack_colors(self):
|
145
|
+
color_themes = PRINT_STACK_COLOR_THEMES
|
146
|
+
stack_colors = color_themes.get(self.cprint_theme)
|
147
|
+
if not stack_colors:
|
148
|
+
stack_colors = color_themes.get('default')
|
149
|
+
return stack_colors
|
150
|
+
|
151
|
+
def print_with_color(self, color_name, text):
|
152
|
+
self.cprint.__getattribute__(color_name)(text)
|
153
|
+
|
154
|
+
def print_table(self,headers_to_hide=None):
|
155
|
+
all_data = []
|
156
|
+
for frame in self.frames:
|
157
|
+
all_data.append(frame.data())
|
158
|
+
|
159
|
+
with Print_Table() as _:
|
160
|
+
_.add_data(all_data)
|
161
|
+
_.hide_headers(headers_to_hide)
|
162
|
+
_.print(order=self.__print_order__)
|
163
|
+
|
164
|
+
|
165
|
+
# static methods
|
166
|
+
# todo: refactor these static methods to be in a separate class and Call_Stack into the helpers folder
|
167
|
+
|
168
|
+
def call_stack_current_frame(return_caller=True):
|
169
|
+
if return_caller:
|
170
|
+
return sys._getframe().f_back
|
171
|
+
return sys._getframe()
|
172
|
+
|
173
|
+
def call_stack_format_stack(depth=None):
|
174
|
+
return traceback.format_stack(limit=depth)
|
175
|
+
|
176
|
+
def call_stack_frames(depth=None):
|
177
|
+
return traceback.extract_stack(limit=depth)
|
178
|
+
|
179
|
+
def call_stack_frames_data(depth=None):
|
180
|
+
frames_data = []
|
181
|
+
for frame in call_stack_frames(depth=depth):
|
182
|
+
frames_data.append(obj_data(frame))
|
183
|
+
return frames_data
|
184
|
+
|
185
|
+
def frames_in_threads():
|
186
|
+
return sys._current_frames()
|
187
|
+
|
osbot_utils/utils/Csv.py
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
import csv
|
2
|
+
from io import StringIO
|
3
|
+
from osbot_utils.utils.Http import GET
|
4
|
+
from osbot_utils.utils.Files import file_open
|
5
|
+
from osbot_utils.decorators.lists.group_by import group_by
|
6
|
+
from osbot_utils.decorators.lists.index_by import index_by
|
7
|
+
|
8
|
+
|
9
|
+
@index_by
|
10
|
+
@group_by
|
11
|
+
def load_csv_from_iterable(iterable, delimiter=','):
|
12
|
+
csv_reader = csv.DictReader(iterable, delimiter=delimiter)
|
13
|
+
return [row for row in csv_reader]
|
14
|
+
|
15
|
+
@index_by
|
16
|
+
@group_by
|
17
|
+
def load_csv_from_file(file_path, delimiter=','):
|
18
|
+
iterable = file_open(file_path)
|
19
|
+
return load_csv_from_iterable(iterable, delimiter=delimiter)
|
20
|
+
|
21
|
+
@index_by
|
22
|
+
@group_by
|
23
|
+
def load_csv_from_str(csv_data, delimiter=','):
|
24
|
+
iterable = StringIO(csv_data)
|
25
|
+
return load_csv_from_iterable(iterable, delimiter=delimiter)
|
26
|
+
|
27
|
+
@index_by
|
28
|
+
@group_by
|
29
|
+
def load_csv_from_url(url, headers, delimiter=','):
|
30
|
+
csv_data = GET(url=url, headers=headers)
|
31
|
+
return load_csv_from_str(csv_data=csv_data, delimiter=delimiter)
|
32
|
+
|