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,301 @@
|
|
1
|
+
import inspect
|
2
|
+
import logging
|
3
|
+
import sys
|
4
|
+
import types
|
5
|
+
from logging import Logger, StreamHandler, FileHandler
|
6
|
+
from logging.handlers import MemoryHandler
|
7
|
+
|
8
|
+
from osbot_utils.decorators.lists.group_by import group_by
|
9
|
+
from osbot_utils.decorators.lists.index_by import index_by
|
10
|
+
from osbot_utils.decorators.methods.cache_on_function import cache_on_function
|
11
|
+
from osbot_utils.decorators.methods.cache_on_self import cache_on_self
|
12
|
+
from osbot_utils.utils.Misc import random_string
|
13
|
+
from osbot_utils.utils.Files import temp_file
|
14
|
+
from osbot_utils.utils.Objects import obj_dict
|
15
|
+
|
16
|
+
DEFAULT_LOG_LEVEL = logging.DEBUG
|
17
|
+
DEFAULT_LOG_FORMAT = '%(asctime)s\t|\t%(name)s\t|\t%(levelname)s\t|\t%(message)s'
|
18
|
+
MEMORY_LOGGER_CAPACITY = 1024*10
|
19
|
+
MEMORY_LOGGER_FLUSH_LEVEL = logging.ERROR
|
20
|
+
|
21
|
+
# for reference here are the log levels
|
22
|
+
# CRITICAL 50
|
23
|
+
# ERROR 40
|
24
|
+
# WARNING 30
|
25
|
+
# INFO 20
|
26
|
+
# DEBUG 10
|
27
|
+
# NOTSET 0
|
28
|
+
|
29
|
+
class Python_Logger_Config:
|
30
|
+
|
31
|
+
def __init__(self):
|
32
|
+
self.elastic_host = None
|
33
|
+
self.elastic_password = None
|
34
|
+
self.elastic_port = None
|
35
|
+
self.elastic_username = None
|
36
|
+
#self.log_to_aws_s3 = False # todo
|
37
|
+
#self.log_to_aws_cloud_trail = False # todo
|
38
|
+
#self.log_to_aws_firehose = False # todo
|
39
|
+
self.log_to_console = False # todo
|
40
|
+
self.log_to_file = False # todo
|
41
|
+
#self.log_to_elastic = False # todo
|
42
|
+
self.log_to_memory = False
|
43
|
+
self.path_logs = None
|
44
|
+
self.log_format = DEFAULT_LOG_FORMAT
|
45
|
+
self.log_level = DEFAULT_LOG_LEVEL
|
46
|
+
|
47
|
+
|
48
|
+
class Python_Logger:
|
49
|
+
config : Python_Logger_Config
|
50
|
+
logger : Logger
|
51
|
+
logger_name : str
|
52
|
+
critical : types.FunctionType # these will be replaced by Python_Logger_Config.setup_log_methods
|
53
|
+
debug : types.FunctionType
|
54
|
+
error : types.FunctionType
|
55
|
+
exception : types.FunctionType
|
56
|
+
info : types.FunctionType
|
57
|
+
ok : types.FunctionType
|
58
|
+
warning : types.FunctionType
|
59
|
+
|
60
|
+
def __init__(self, logger_name= None, logger_config : Python_Logger_Config = None):
|
61
|
+
self.set_logger_name(logger_name)
|
62
|
+
#self.logger_name = logger_name or random_string(prefix="Python_Logger_")
|
63
|
+
self.set_config(logger_config)
|
64
|
+
# self.logger = None
|
65
|
+
self.setup() # todo: understand side effect of setting up logger on __init__
|
66
|
+
|
67
|
+
def disable(self):
|
68
|
+
self.logger.disabled = True
|
69
|
+
return self
|
70
|
+
|
71
|
+
def set_logger_name(self, logger_name):
|
72
|
+
if logger_name: # if the value is provided, use it
|
73
|
+
self.logger_name = logger_name
|
74
|
+
return self
|
75
|
+
for frame_info in inspect.stack(): # Look for the first frame that is outside this Python_Logger class
|
76
|
+
if 'self' in frame_info.frame.f_locals:
|
77
|
+
caller_self = frame_info.frame.f_locals['self']
|
78
|
+
caller_module = caller_self.__class__.__name__
|
79
|
+
if caller_module != 'Python_Logger':
|
80
|
+
self.logger_name = 'Python_Logger__' + caller_module
|
81
|
+
return self
|
82
|
+
|
83
|
+
self.logger_name = random_string(prefix="Python_Logger_")
|
84
|
+
return self
|
85
|
+
|
86
|
+
def manager_get_loggers(self):
|
87
|
+
return Logger.manager.loggerDict
|
88
|
+
|
89
|
+
def manager_remove_logger(self):
|
90
|
+
logger_dict = Logger.manager.loggerDict
|
91
|
+
if self.logger_name in logger_dict: # need to do it manually here since Logger.manager doesn't seem to have a way to remove loggers
|
92
|
+
del logger_dict[self.logger_name]
|
93
|
+
return True
|
94
|
+
return False
|
95
|
+
|
96
|
+
def setup(self, logger_name=None, log_level=None,add_console_logger=False, add_memory_logger=True):
|
97
|
+
if logger_name:
|
98
|
+
self.logger_name = logger_name
|
99
|
+
self.logger = logging.getLogger(self.logger_name)
|
100
|
+
self.setup_log_methods()
|
101
|
+
self.set_log_level(log_level)
|
102
|
+
if add_console_logger:
|
103
|
+
self.add_console_logger()
|
104
|
+
if add_memory_logger:
|
105
|
+
self.add_handler_memory()
|
106
|
+
return self
|
107
|
+
|
108
|
+
def setup_log_methods(self):
|
109
|
+
# adds these helper methods like this so that the filename and function values are accurate
|
110
|
+
setattr(self, "critical" , self.logger.critical )
|
111
|
+
setattr(self, "debug" , self.logger.debug )
|
112
|
+
setattr(self, "error" , self.logger.error )
|
113
|
+
setattr(self, "exception" , self.logger.exception )
|
114
|
+
setattr(self, "info" , self.logger.info )
|
115
|
+
setattr(self, "ok" , self.logger.info )
|
116
|
+
setattr(self, "warning" , self.logger.warning )
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
# self.info = self.logger.info
|
123
|
+
# self.warning = self.logger.warning
|
124
|
+
# self.error = self.logger.error
|
125
|
+
# self.exception = self.logger.exception
|
126
|
+
# self.critical = self.logger.critical
|
127
|
+
|
128
|
+
|
129
|
+
# Setters
|
130
|
+
def set_config(self, config):
|
131
|
+
if type(config) is Python_Logger_Config:
|
132
|
+
self.config = config
|
133
|
+
else:
|
134
|
+
self.config = Python_Logger_Config()
|
135
|
+
return self.config
|
136
|
+
|
137
|
+
def set_log_format(self, log_format):
|
138
|
+
if log_format:
|
139
|
+
self.config.log_format = log_format
|
140
|
+
|
141
|
+
def set_log_level(self, level=None):
|
142
|
+
level = level or self.config.log_level
|
143
|
+
if self.logger:
|
144
|
+
self.logger.setLevel(level)
|
145
|
+
return True
|
146
|
+
return False
|
147
|
+
|
148
|
+
# Getters
|
149
|
+
|
150
|
+
def log_handler(self, handler_type):
|
151
|
+
for handler in self.log_handlers():
|
152
|
+
if type(handler) is handler_type:
|
153
|
+
return handler
|
154
|
+
return None
|
155
|
+
|
156
|
+
def log_handler_console(self):
|
157
|
+
return self.log_handler(StreamHandler)
|
158
|
+
|
159
|
+
def log_handler_file(self):
|
160
|
+
return self.log_handler(logging.FileHandler)
|
161
|
+
|
162
|
+
def log_handler_memory(self):
|
163
|
+
return self.log_handler(MemoryHandler)
|
164
|
+
|
165
|
+
def log_handlers(self):
|
166
|
+
if self.logger:
|
167
|
+
return self.logger.handlers
|
168
|
+
return []
|
169
|
+
|
170
|
+
def log_handlers_remove(self, handler):
|
171
|
+
if handler and handler in self.log_handlers():
|
172
|
+
self.logger.removeHandler(handler)
|
173
|
+
return True
|
174
|
+
return False
|
175
|
+
|
176
|
+
def log_handlers_remove_type(self, handler_type):
|
177
|
+
handler = self.log_handler(handler_type)
|
178
|
+
return self.log_handlers_remove(handler)
|
179
|
+
|
180
|
+
def log_formatter(self):
|
181
|
+
return logging.Formatter(self.config.log_format)
|
182
|
+
|
183
|
+
def log_level(self):
|
184
|
+
return self.config.log_level
|
185
|
+
|
186
|
+
# Actions
|
187
|
+
|
188
|
+
def add_console_logger(self):
|
189
|
+
self.config.log_to_console = True
|
190
|
+
return self.add_handler_console()
|
191
|
+
|
192
|
+
def add_memory_logger(self):
|
193
|
+
self.config.log_to_memory = True
|
194
|
+
return self.add_handler_memory()
|
195
|
+
|
196
|
+
def add_file_logger(self,path_log_file=None):
|
197
|
+
self.config.log_to_file = True
|
198
|
+
return self.add_handler_file(path_log_file=path_log_file)
|
199
|
+
|
200
|
+
def remove_memory_logger(self):
|
201
|
+
memory_logger = self.log_handler_memory()
|
202
|
+
if self.log_handlers_remove(memory_logger):
|
203
|
+
self.config.log_to_file = False
|
204
|
+
return True
|
205
|
+
return False
|
206
|
+
|
207
|
+
|
208
|
+
# Handlers
|
209
|
+
def add_handler_console(self):
|
210
|
+
if self.logger and self.config.log_to_console:
|
211
|
+
handler = StreamHandler(sys.stdout)
|
212
|
+
handler.setLevel(logging.DEBUG)
|
213
|
+
handler.setFormatter(self.log_formatter())
|
214
|
+
self.logger.addHandler(handler)
|
215
|
+
return True
|
216
|
+
return False
|
217
|
+
|
218
|
+
def add_handler_file(self, path_log_file=None):
|
219
|
+
if self.logger and self.config.log_to_file:
|
220
|
+
if path_log_file is None:
|
221
|
+
path_log_file = temp_file(extension='.log')
|
222
|
+
handler = FileHandler(path_log_file)
|
223
|
+
handler.setLevel(self.log_level())
|
224
|
+
handler.setFormatter(self.log_formatter())
|
225
|
+
self.logger.addHandler(handler)
|
226
|
+
return True
|
227
|
+
return False
|
228
|
+
|
229
|
+
def add_handler_memory(self, memory_capacity=None):
|
230
|
+
if self.log_handler_memory() is None:
|
231
|
+
if self.logger and self.config.log_to_memory:
|
232
|
+
capacity = memory_capacity or MEMORY_LOGGER_CAPACITY
|
233
|
+
flush_level = MEMORY_LOGGER_FLUSH_LEVEL
|
234
|
+
target = None # we want the messages to only be kept in memory
|
235
|
+
memory_handler = MemoryHandler(capacity=capacity, flushLevel=flush_level, target=target,flushOnClose=True)
|
236
|
+
memory_handler.setLevel(self.log_level())
|
237
|
+
self.logger.addHandler(memory_handler)
|
238
|
+
return True
|
239
|
+
return False
|
240
|
+
|
241
|
+
# Utils
|
242
|
+
def memory_handler(self) -> MemoryHandler:
|
243
|
+
return self.log_handler_memory()
|
244
|
+
|
245
|
+
def memory_handler_buffer(self):
|
246
|
+
if self.config.log_to_memory:
|
247
|
+
return self.memory_handler().buffer
|
248
|
+
return []
|
249
|
+
|
250
|
+
def memory_handler_clear(self):
|
251
|
+
if self.config.log_to_memory:
|
252
|
+
memory_handler = self.memory_handler()
|
253
|
+
memory_handler.buffer = []
|
254
|
+
return True
|
255
|
+
return False
|
256
|
+
def memory_handler_exceptions(self):
|
257
|
+
return self.memory_handler_logs(index_by='levelname').get('EXCEPTIONS', {})
|
258
|
+
|
259
|
+
@index_by
|
260
|
+
@group_by
|
261
|
+
def memory_handler_logs(self):
|
262
|
+
logs = []
|
263
|
+
for log_record in self.memory_handler_buffer():
|
264
|
+
logs.append(obj_dict(log_record))
|
265
|
+
return logs
|
266
|
+
|
267
|
+
def memory_handler_last_log_entry(self):
|
268
|
+
memory_buffer = self.memory_handler_buffer()
|
269
|
+
if memory_buffer: # Check if the buffer is not empty
|
270
|
+
last_log_record = memory_buffer[-1] # get the last record
|
271
|
+
return obj_dict(last_log_record) # convert record into a nice json object
|
272
|
+
return {}
|
273
|
+
|
274
|
+
def memory_handler_messages(self):
|
275
|
+
return [log_entry.get('message') for log_entry in self.memory_handler_logs()]
|
276
|
+
|
277
|
+
# Logging methods
|
278
|
+
|
279
|
+
# def debug (self, msg='', *args, **kwargs): return self._log('debug' , msg, *args, **kwargs)
|
280
|
+
# #def info (self, msg='', *args, **kwargs): return self.__log__('info' , msg, *args, **kwargs)
|
281
|
+
# def warning (self, msg='', *args, **kwargs): return self._log('warning' , msg, *args, **kwargs)
|
282
|
+
# def error (self, msg='', *args, **kwargs): return self._log('error' , msg, *args, **kwargs)
|
283
|
+
# def exception(self, msg='', *args, **kwargs): return self._log('exception' , msg, *args, **kwargs)
|
284
|
+
# def critical (self, msg='', *args, **kwargs): return self._log('critical' , msg, *args, **kwargs)
|
285
|
+
#
|
286
|
+
# def __log__(self, level, msg, *args, **kwargs):
|
287
|
+
# if self.logger:
|
288
|
+
# log_method = getattr(self.logger, level)
|
289
|
+
# log_method(msg, *args, **kwargs)
|
290
|
+
# return True
|
291
|
+
# return False
|
292
|
+
|
293
|
+
@cache_on_function
|
294
|
+
def logger_info():
|
295
|
+
python_logger = Python_Logger().setup()
|
296
|
+
return python_logger.logger.info
|
297
|
+
|
298
|
+
@cache_on_function
|
299
|
+
def logger_error():
|
300
|
+
python_logger = Python_Logger().setup()
|
301
|
+
return python_logger.logger.error
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# todo refactor into Status class
|
2
|
+
import traceback
|
3
|
+
|
4
|
+
from osbot_utils.utils.Python_Logger import Python_Logger
|
5
|
+
|
6
|
+
class Status:
|
7
|
+
def __init__(self):
|
8
|
+
self.logger = Python_Logger().setup()
|
9
|
+
self.call_logger_method = False
|
10
|
+
self.root_logger_handlers = None
|
11
|
+
#self.logger.add_memory_logger()
|
12
|
+
|
13
|
+
def root_logger(self):
|
14
|
+
return self.logger.logger.parent
|
15
|
+
|
16
|
+
def clear_root_logger_handlers(self):
|
17
|
+
self.root_logger_handlers = self.root_logger().handlers
|
18
|
+
self.root_logger().handlers = []
|
19
|
+
|
20
|
+
def restore_root_logger_handlers(self):
|
21
|
+
if self.root_logger_handlers:
|
22
|
+
self.root_logger().handlers = self.root_logger_handlers
|
23
|
+
def status_message(self, status, message:str=None, data=None, error=None):
|
24
|
+
return { 'data' : data ,
|
25
|
+
'error' : error ,
|
26
|
+
'message': message ,
|
27
|
+
'status' : status
|
28
|
+
}
|
29
|
+
|
30
|
+
def last_message(self):
|
31
|
+
return self.logger.memory_handler_last_log_entry()
|
32
|
+
|
33
|
+
def log_message(self, status, message:str='', data=None, error=None, stacklevel=3): # stacklevel is usually 3 because we want to get the caller of the method that called this on
|
34
|
+
logger_message = f'[osbot] [{status}] ' + str(message)
|
35
|
+
logger_method = self.logger.__getattribute__(status)
|
36
|
+
status_message = self.status_message(status=status, message=message, data=data, error=error)
|
37
|
+
if self.call_logger_method:
|
38
|
+
kwargs = {} # todo: add option to capture stack trace and other helpful debug data
|
39
|
+
if status =='exception':
|
40
|
+
kwargs = dict(exc_info=True, stacklevel=stacklevel)
|
41
|
+
logger_method(logger_message, **kwargs)
|
42
|
+
return status_message
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
osbot_status = Status() # todo map out the performatin implications of doing this
|
48
|
+
osbot_logger = osbot_status.logger
|
49
|
+
|
50
|
+
def status_critical (message:str='', data=None,error=None): return osbot_status.log_message(status='critical' , message=message, data=data, error=error)
|
51
|
+
def status_debug (message:str='', data=None,error=None): return osbot_status.log_message(status='debug' , message=message, data=data, error=error)
|
52
|
+
def status_error (message:str='', data=None,error=None): return osbot_status.log_message(status='error' , message=message, data=data, error=error)
|
53
|
+
def status_exception(message:str='', data=None,error=None): return osbot_status.log_message(status='exception', message=message, data=data, error=error)
|
54
|
+
def status_info (message:str='', data=None,error=None): return osbot_status.log_message(status='info' , message=message, data=data, error=error)
|
55
|
+
def status_ok (message:str='', data=None,error=None): return osbot_status.log_message(status='ok' , message=message, data=data, error=error)
|
56
|
+
def status_warning (message:str='', data=None,error=None): return osbot_status.log_message(status='warning' , message=message, data=data, error=error)
|
57
|
+
|
58
|
+
log_critical = status_critical # level 50
|
59
|
+
log_error = status_error # level 40
|
60
|
+
log_exception = status_exception # level 40
|
61
|
+
log_warning = status_warning # level 30
|
62
|
+
log_info = status_info # level 20
|
63
|
+
log_ok = status_ok # level 20
|
64
|
+
log_debug = status_debug # level 10
|
65
|
+
|
66
|
+
|
67
|
+
def send_status_to_logger(value: bool = True):
|
68
|
+
osbot_status.call_logger_method = value
|
69
|
+
|
70
|
+
#def log_error (message):#logger().error (message) # level 40
|
71
|
+
#def log_info (message): logger().info (message) # level 20
|
72
|
+
#def log_warning (message): logger().warning (message) # level 30
|
73
|
+
|
74
|
+
|
75
|
+
# def status_info (message:str='', data=None,error=None): osbot_logger.info ('[osbot] [info] ' + str(message)); return status_message('info', message=message, data=data, error=error)
|
76
|
+
# def status_ok (message:str='', data=None,error=None): osbot_logger.info ('[osbot] [ok] ' + str(message)); return status_message('ok', message=message, data=data, error=error)
|
77
|
+
# def status_warning(message:str='', data=None,error=None): osbot_logger.warning('[osbot] [warning] ' + str(message)); return status_message('warning', message=message, data=data, error=error)
|
78
|
+
|
79
|
+
#todo: add status_exception that automatically picks up the exception from the stack trace
|
osbot_utils/utils/Str.py
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
import textwrap
|
2
|
+
from html import escape, unescape
|
3
|
+
|
4
|
+
from osbot_utils.utils.Files import safe_file_name
|
5
|
+
|
6
|
+
|
7
|
+
def html_escape(value):
|
8
|
+
return escape(value)
|
9
|
+
|
10
|
+
def html_unescape(value):
|
11
|
+
return unescape(value)
|
12
|
+
|
13
|
+
def str_dedent(value, strip=True):
|
14
|
+
result = textwrap.dedent(value)
|
15
|
+
if strip:
|
16
|
+
result = result.strip()
|
17
|
+
return result
|
18
|
+
|
19
|
+
def str_index(target:str, source:str):
|
20
|
+
try:
|
21
|
+
return target.index(source)
|
22
|
+
except:
|
23
|
+
return -1
|
24
|
+
|
25
|
+
def str_join(delimiter, values):
|
26
|
+
return delimiter.join(values)
|
27
|
+
|
28
|
+
def str_max_width(target, value):
|
29
|
+
return str(target)[:value]
|
30
|
+
|
31
|
+
def str_safe(value):
|
32
|
+
return safe_file_name(value)
|
33
|
+
|
34
|
+
def str_starts_with(source, prefix):
|
35
|
+
if source is None or prefix is None:
|
36
|
+
return False
|
37
|
+
else:
|
38
|
+
return source.startswith(prefix)
|
39
|
+
|
40
|
+
def str_unicode_escape(target):
|
41
|
+
return str(target).encode('unicode_escape').decode("utf-8")
|
42
|
+
|
43
|
+
def str_cap_snake_case(snake_str):
|
44
|
+
"""
|
45
|
+
Converts a snake_case string to Capitalized_Snake_Case.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
snake_str (str): The snake_case string to be converted.
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
str: The converted string in Capitalized_Snake_Case.
|
52
|
+
"""
|
53
|
+
return "_".join(word.capitalize() for word in snake_str.split("_"))
|
54
|
+
|
55
|
+
|
56
|
+
def trim(target):
|
57
|
+
if type(target) is str:
|
58
|
+
return target.strip()
|
59
|
+
return ""
|
60
|
+
|
61
|
+
html_encode = html_escape
|
62
|
+
html_decode = html_unescape
|
63
|
+
safe_str = str_safe
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import osbot_utils
|
2
|
+
from osbot_utils.utils.Files import file_contents, path_combine
|
3
|
+
|
4
|
+
class Version:
|
5
|
+
|
6
|
+
FILE_NAME_VERSION = 'version'
|
7
|
+
|
8
|
+
def path_code_root(self):
|
9
|
+
return osbot_utils.path
|
10
|
+
|
11
|
+
def path_version_file(self):
|
12
|
+
return path_combine(self.path_code_root(), self.FILE_NAME_VERSION)
|
13
|
+
|
14
|
+
def value(self):
|
15
|
+
version = file_contents(self.path_version_file()) or ""
|
16
|
+
return version.strip()
|
osbot_utils/utils/Zip.py
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
import io
|
2
|
+
import os
|
3
|
+
import shutil
|
4
|
+
import zipfile
|
5
|
+
from os.path import abspath
|
6
|
+
|
7
|
+
from osbot_utils.utils.Files import temp_folder, folder_files, temp_file, is_file
|
8
|
+
|
9
|
+
|
10
|
+
def unzip_file(zip_file, target_folder=None, format='zip'):
|
11
|
+
target_folder = target_folder or temp_folder()
|
12
|
+
shutil.unpack_archive(zip_file, extract_dir=target_folder, format=format)
|
13
|
+
return target_folder
|
14
|
+
|
15
|
+
def zip_bytes_add_file(zip_bytes, zip_file_path, file_contents):
|
16
|
+
if type(file_contents) is str:
|
17
|
+
file_contents = file_contents.encode('utf-8')
|
18
|
+
elif type(file_contents) is not bytes:
|
19
|
+
return None
|
20
|
+
zip_buffer = io.BytesIO(zip_bytes)
|
21
|
+
with zipfile.ZipFile(zip_buffer, 'a', zipfile.ZIP_DEFLATED) as zf:
|
22
|
+
zf.writestr(zip_file_path, file_contents)
|
23
|
+
|
24
|
+
return zip_buffer.getvalue()
|
25
|
+
|
26
|
+
def zip_bytes_get_file(zip_bytes, zip_file_path):
|
27
|
+
zip_buffer = io.BytesIO(zip_bytes)
|
28
|
+
with zipfile.ZipFile(zip_buffer, 'r') as zf:
|
29
|
+
return zf.read(zip_file_path)
|
30
|
+
|
31
|
+
def zip_bytes_file_list(zip_bytes):
|
32
|
+
zip_buffer_from_bytes = io.BytesIO(zip_bytes)
|
33
|
+
with zipfile.ZipFile(zip_buffer_from_bytes, 'r') as zf:
|
34
|
+
return sorted(zf.namelist())
|
35
|
+
|
36
|
+
def zip_bytes_to_file(zip_bytes, target_file=None):
|
37
|
+
if target_file is None:
|
38
|
+
target_file = temp_file(extension='.zip')
|
39
|
+
with open(target_file, 'wb') as f:
|
40
|
+
f.write(zip_bytes)
|
41
|
+
return target_file
|
42
|
+
|
43
|
+
def zip_files_to_bytes(target_files, root_folder=None):
|
44
|
+
zip_buffer = io.BytesIO() # Create a BytesIO buffer to hold the zipped file
|
45
|
+
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf: # Create a ZipFile object with the buffer as the target
|
46
|
+
for entry in target_files:
|
47
|
+
if type(entry) is str: # if entry is a string, assume it's a file path
|
48
|
+
file_path = entry
|
49
|
+
file_root_folder = root_folder
|
50
|
+
else:
|
51
|
+
file_path = entry.get('file')
|
52
|
+
file_root_folder = entry.get('root_folder') or root_folder
|
53
|
+
if file_root_folder:
|
54
|
+
arcname = file_path.replace(file_root_folder,'') # Define the arcname, which is the name inside the zip file
|
55
|
+
else:
|
56
|
+
arcname = file_path # if root_path is not provided, use the full file path
|
57
|
+
zf.write(file_path, arcname) # Add the file to the zip file
|
58
|
+
zip_buffer.seek(0)
|
59
|
+
return zip_buffer
|
60
|
+
|
61
|
+
def zip_folder(root_dir, format='zip'):
|
62
|
+
return shutil.make_archive(base_name=root_dir, format=format, root_dir=root_dir)
|
63
|
+
|
64
|
+
def zip_folder_to_bytes(root_dir): # todo add unit test
|
65
|
+
zip_buffer = io.BytesIO() # Create a BytesIO buffer to hold the zipped file
|
66
|
+
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf: # Create a ZipFile object with the buffer as the target
|
67
|
+
for foldername, subfolders, filenames in os.walk(root_dir): # Walk the root_dir and add all files and folders to the zip file
|
68
|
+
for filename in filenames:
|
69
|
+
absolute_path = os.path.join(foldername, filename) # Create the complete filepath
|
70
|
+
arcname = os.path.relpath(absolute_path, root_dir) # Define the arcname, which is the name inside the zip file
|
71
|
+
zf.write(absolute_path, arcname) # Add the file to the zip file
|
72
|
+
zip_buffer.seek(0) # Reset buffer position
|
73
|
+
return zip_buffer
|
74
|
+
|
75
|
+
def zip_file_list(path):
|
76
|
+
if is_file(path):
|
77
|
+
with zipfile.ZipFile(path) as zip_file:
|
78
|
+
return sorted(zip_file.namelist())
|
79
|
+
return []
|
80
|
+
|
81
|
+
def zip_files(base_folder, file_pattern="*.*", target_file=None):
|
82
|
+
base_folder = abspath(base_folder)
|
83
|
+
file_list = folder_files(base_folder, file_pattern)
|
84
|
+
|
85
|
+
if len(file_list): # if there were files found
|
86
|
+
target_file = target_file or temp_file(extension='zip')
|
87
|
+
with zipfile.ZipFile(target_file,'w') as zip:
|
88
|
+
for file_name in file_list:
|
89
|
+
zip_file_path = file_name.replace(base_folder,'')
|
90
|
+
zip.write(file_name, zip_file_path)
|
91
|
+
|
92
|
+
return target_file
|
93
|
+
|
94
|
+
|
95
|
+
# extra function's mappings
|
96
|
+
file_unzip = unzip_file
|
97
|
+
folder_zip = zip_folder
|
osbot_utils/version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v1.7.7
|