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,496 @@
|
|
1
|
+
import base64
|
2
|
+
import hashlib
|
3
|
+
import importlib
|
4
|
+
import logging
|
5
|
+
import os
|
6
|
+
import random
|
7
|
+
import string
|
8
|
+
import sys
|
9
|
+
import textwrap
|
10
|
+
import re
|
11
|
+
import uuid
|
12
|
+
import warnings
|
13
|
+
from datetime import datetime, timedelta, UTC
|
14
|
+
from secrets import token_bytes
|
15
|
+
from time import sleep
|
16
|
+
from typing import Iterable
|
17
|
+
from urllib.parse import quote_plus, unquote_plus
|
18
|
+
|
19
|
+
def ansi_text_visible_length(text):
|
20
|
+
ansi_escape = re.compile(r'\x1b\[[0-9;]*m') # This regex matches the escape sequences used for text formatting
|
21
|
+
visible_text = ansi_escape.sub('', text) # Remove the escape sequences
|
22
|
+
return len(visible_text) # Return the length of the remaining text
|
23
|
+
|
24
|
+
def append_random_string(target, length=6, prefix='-'):
|
25
|
+
return f'{target}{random_string(length, prefix)}'
|
26
|
+
|
27
|
+
def attr_value_from_module_name(module_name, attr_name, default_value=None):
|
28
|
+
module = importlib.import_module(module_name)
|
29
|
+
if hasattr(module, attr_name):
|
30
|
+
return getattr(module, attr_name)
|
31
|
+
return default_value
|
32
|
+
|
33
|
+
def bytes_md5(target : bytes):
|
34
|
+
return hashlib.md5(target).hexdigest()
|
35
|
+
|
36
|
+
def bytes_sha256(target : bytes):
|
37
|
+
return hashlib.sha256(target).hexdigest()
|
38
|
+
|
39
|
+
def bytes_sha384(target : bytes):
|
40
|
+
return hashlib.sha384(target).hexdigest()
|
41
|
+
|
42
|
+
def base64_to_bytes(bytes_base64):
|
43
|
+
if type(bytes_base64) is str:
|
44
|
+
bytes_base64 = bytes_base64.encode()
|
45
|
+
return base64.decodebytes(bytes_base64)
|
46
|
+
|
47
|
+
def base64_to_str(target, encoding='ascii'):
|
48
|
+
return bytes_to_str(base64_to_bytes(target), encoding=encoding)
|
49
|
+
|
50
|
+
def bytes_to_base64(target):
|
51
|
+
return base64.b64encode(target).decode()
|
52
|
+
|
53
|
+
def bytes_to_str(target, encoding='ascii'):
|
54
|
+
return target.decode(encoding=encoding)
|
55
|
+
|
56
|
+
def convert_to_number(value):
|
57
|
+
if value:
|
58
|
+
try:
|
59
|
+
if value[0] in ['£','$','€']:
|
60
|
+
return float(re.sub(r'[^\d.]', '', value))
|
61
|
+
else:
|
62
|
+
return float(value)
|
63
|
+
except:
|
64
|
+
return 0
|
65
|
+
else:
|
66
|
+
return 0
|
67
|
+
|
68
|
+
def date_time_from_to_str(date_time_str, format_from, format_to, print_conversion_error=False):
|
69
|
+
try:
|
70
|
+
date_time = datetime.strptime(date_time_str, format_from)
|
71
|
+
return date_time.strftime(format_to)
|
72
|
+
except ValueError as value_error:
|
73
|
+
if print_conversion_error:
|
74
|
+
print(f"[date_time_from_to_str]: Error: {value_error}") # todo: use log handler
|
75
|
+
return None
|
76
|
+
|
77
|
+
|
78
|
+
def date_time_to_str(date_time, date_time_format='%Y-%m-%d %H:%M:%S.%f', milliseconds_numbers=3):
|
79
|
+
if date_time:
|
80
|
+
date_time_str = date_time.strftime(date_time_format)
|
81
|
+
return time_str_milliseconds(datetime_str=date_time_str, datetime_format=date_time_format, milliseconds_numbers=milliseconds_numbers)
|
82
|
+
else:
|
83
|
+
return ''
|
84
|
+
|
85
|
+
def date_now(use_utc=True, return_str=True):
|
86
|
+
value = date_time_now(use_utc=use_utc, return_str=False)
|
87
|
+
if return_str:
|
88
|
+
return date_to_str(date=value)
|
89
|
+
return value
|
90
|
+
|
91
|
+
def date_time_now(use_utc=True, return_str=True, milliseconds_numbers=0, date_time_format='%Y-%m-%d %H:%M:%S.%f'):
|
92
|
+
if use_utc:
|
93
|
+
value = datetime.now(UTC)
|
94
|
+
#value = datetime.utcnow() # todo: this has been marked for depreciation in python 11
|
95
|
+
# value = datetime.now(UTC) # but this doesn't seem to work in python 10.x : E ImportError: cannot import name 'UTC' from 'datetime' (/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/datetime.py)
|
96
|
+
|
97
|
+
else:
|
98
|
+
value = datetime.now()
|
99
|
+
if return_str:
|
100
|
+
return date_time_to_str(value, milliseconds_numbers=milliseconds_numbers, date_time_format=date_time_format)
|
101
|
+
return value
|
102
|
+
|
103
|
+
# def date_time_parse(value):
|
104
|
+
# if type(value) is datetime:
|
105
|
+
# return value
|
106
|
+
# return parser.parse(value)
|
107
|
+
|
108
|
+
def date_time_less_time_delta(date_time, days=0, hours=0, minutes=0, seconds=0, date_time_format='%Y-%m-%d %H:%M:%S' , return_str=True):
|
109
|
+
new_date_time = date_time - timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
|
110
|
+
if return_str:
|
111
|
+
return date_time_to_str(new_date_time, date_time_format=date_time_format)
|
112
|
+
return new_date_time
|
113
|
+
|
114
|
+
def date_time_now_less_time_delta(days=0,hours=0, minutes=0, seconds=0, date_time_format='%Y-%m-%d %H:%M:%S', return_str=True):
|
115
|
+
return date_time_less_time_delta(datetime.now(UTC),days=days, hours=hours, minutes=minutes, seconds=seconds,date_time_format=date_time_format, return_str=return_str)
|
116
|
+
|
117
|
+
def date_to_str(date, date_format='%Y-%m-%d'):
|
118
|
+
return date.strftime(date_format)
|
119
|
+
|
120
|
+
#note: this is here at the moment due to a circular dependency with lists and objects
|
121
|
+
def list_set(target):
|
122
|
+
if hasattr(target, '__iter__'):
|
123
|
+
return sorted(list(set(target)))
|
124
|
+
return []
|
125
|
+
|
126
|
+
def time_str_milliseconds(datetime_str, datetime_format, milliseconds_numbers=0):
|
127
|
+
if '.%f' in datetime_format and -1 < milliseconds_numbers < 6:
|
128
|
+
chars_to_remove = milliseconds_numbers-6
|
129
|
+
if milliseconds_numbers == 0:
|
130
|
+
chars_to_remove -= 1
|
131
|
+
return datetime_str[:chars_to_remove]
|
132
|
+
return datetime_str
|
133
|
+
|
134
|
+
def flist(target):
|
135
|
+
from osbot_utils.fluent.Fluent_List import Fluent_List
|
136
|
+
return Fluent_List(target)
|
137
|
+
|
138
|
+
# todo: check if this should still be here
|
139
|
+
def get_random_color(max=5):
|
140
|
+
if max > 5: max = 5 # add support for more than 5 colors
|
141
|
+
colors = ['skyblue', 'darkseagreen', 'palevioletred', 'coral', 'darkgray']
|
142
|
+
return colors[random_number(0, max-1)]
|
143
|
+
|
144
|
+
def in_github_action():
|
145
|
+
return os.getenv('GITHUB_ACTIONS') == 'true'
|
146
|
+
|
147
|
+
def is_debugging():
|
148
|
+
return sys.gettrace() is not None
|
149
|
+
|
150
|
+
def is_number(value):
|
151
|
+
try:
|
152
|
+
if type(value) is int or type(value) is float :
|
153
|
+
int(value)
|
154
|
+
return True
|
155
|
+
except:
|
156
|
+
pass
|
157
|
+
return False
|
158
|
+
|
159
|
+
def is_int(value):
|
160
|
+
try:
|
161
|
+
int(value)
|
162
|
+
return True
|
163
|
+
except ValueError:
|
164
|
+
return False
|
165
|
+
|
166
|
+
def is_float(value):
|
167
|
+
try:
|
168
|
+
float(value)
|
169
|
+
return True
|
170
|
+
except ValueError:
|
171
|
+
return False
|
172
|
+
|
173
|
+
def is_guid(value):
|
174
|
+
try:
|
175
|
+
uuid_obj = uuid.UUID(value)
|
176
|
+
return str(uuid_obj) == value.lower()
|
177
|
+
except ValueError:
|
178
|
+
return False
|
179
|
+
|
180
|
+
|
181
|
+
def ignore_warning__unclosed_ssl():
|
182
|
+
warnings.filterwarnings("ignore", category=ResourceWarning, message="unclosed.*<ssl.SSLSocket.*>")
|
183
|
+
|
184
|
+
|
185
|
+
def last_letter(text):
|
186
|
+
if text and (type(text) is str) and len(text) > 0:
|
187
|
+
return text[-1]
|
188
|
+
|
189
|
+
|
190
|
+
# def log_critical(message): logger().critical(message) # level 50
|
191
|
+
# def log_debug (message): logger().debug (message) # level 10
|
192
|
+
# def log_error (message): logger().error (message) # level 40
|
193
|
+
# def log_info (message): logger().info (message) # level 20
|
194
|
+
# def log_warning (message): logger().warning (message) # level 30
|
195
|
+
|
196
|
+
def log_to_console(level="INFO"):
|
197
|
+
logger_set_level(level)
|
198
|
+
logger_add_handler__console()
|
199
|
+
print() # add extra print so that in pytest the first line is not hidden
|
200
|
+
|
201
|
+
def log_to_file(level="INFO"):
|
202
|
+
logger_set_level(level)
|
203
|
+
return logger_add_handler__file()
|
204
|
+
|
205
|
+
def logger():
|
206
|
+
return logging.getLogger()
|
207
|
+
|
208
|
+
def logger_add_handler(handler):
|
209
|
+
logger().addHandler(handler)
|
210
|
+
|
211
|
+
def logger_add_handler__console():
|
212
|
+
logger_add_handler(logging.StreamHandler())
|
213
|
+
|
214
|
+
def logger_add_handler__file(log_file=None):
|
215
|
+
from osbot_utils.utils.Files import temp_file
|
216
|
+
log_file = log_file or temp_file(extension=".log")
|
217
|
+
logger_add_handler(logging.FileHandler(filename=log_file))
|
218
|
+
return log_file
|
219
|
+
|
220
|
+
def logger_set_level(level):
|
221
|
+
logger().setLevel(level)
|
222
|
+
|
223
|
+
def logger_set_level_critical(): logger_set_level('CRITICAL') # level 50
|
224
|
+
def logger_set_level_debug (): logger_set_level('DEBUG' ) # level 10
|
225
|
+
def logger_set_level_error (): logger_set_level('ERROR' ) # level 40
|
226
|
+
def logger_set_level_info (): logger_set_level('INFO' ) # level 20
|
227
|
+
def logger_set_level_warning (): logger_set_level('WARNING' ) # level 30
|
228
|
+
|
229
|
+
def lower(target : str):
|
230
|
+
if target:
|
231
|
+
return target.lower()
|
232
|
+
return ""
|
233
|
+
|
234
|
+
def size(target=None):
|
235
|
+
if target and hasattr(target, '__len__'):
|
236
|
+
return len(target)
|
237
|
+
return 0
|
238
|
+
|
239
|
+
def str_md5(text : str):
|
240
|
+
if text:
|
241
|
+
return bytes_md5(text.encode())
|
242
|
+
return ''
|
243
|
+
|
244
|
+
def none_or_empty(target,field):
|
245
|
+
if target and field:
|
246
|
+
value = target.get(field)
|
247
|
+
return (value is None) or value == ''
|
248
|
+
return True
|
249
|
+
|
250
|
+
def print_date_now(use_utc=True):
|
251
|
+
print(date_time_now(use_utc=use_utc))
|
252
|
+
|
253
|
+
def print_time_now(use_utc=True):
|
254
|
+
print(time_now(use_utc=use_utc))
|
255
|
+
|
256
|
+
def str_sha256(text: str):
|
257
|
+
if text:
|
258
|
+
return bytes_sha256(text.encode())
|
259
|
+
return None
|
260
|
+
|
261
|
+
def str_sha384(text:str):
|
262
|
+
if text:
|
263
|
+
return bytes_sha384(text.encode())
|
264
|
+
return
|
265
|
+
|
266
|
+
def str_sha384_as_base64(text:str, include_prefix=True):
|
267
|
+
if text:
|
268
|
+
hash_object = hashlib.sha384(text.encode())
|
269
|
+
digest = hash_object.digest() # Getting the digest of the hash
|
270
|
+
digest_base64 = base64.b64encode(digest).decode() # Converting the digest to Base64 encoding
|
271
|
+
if include_prefix:
|
272
|
+
return "sha384-" + digest_base64
|
273
|
+
return digest_base64
|
274
|
+
return
|
275
|
+
|
276
|
+
def time_delta_to_str(time_delta):
|
277
|
+
microseconds = time_delta.microseconds
|
278
|
+
milliseconds = int(microseconds / 1000)
|
279
|
+
total_seconds = int(time_delta.total_seconds())
|
280
|
+
return f'{total_seconds}s {milliseconds}ms'
|
281
|
+
|
282
|
+
def time_delta_in_days_hours_or_minutes(time_delta):
|
283
|
+
total_seconds = int(time_delta.total_seconds())
|
284
|
+
days , seconds = divmod(total_seconds, 86400)
|
285
|
+
hours , seconds = divmod(seconds , 3600 )
|
286
|
+
minutes, seconds = divmod(seconds , 60 )
|
287
|
+
if days > 0:
|
288
|
+
return f"{days}d {hours}h {minutes}m"
|
289
|
+
elif hours > 0:
|
290
|
+
return f"{hours:4}h {minutes}m"
|
291
|
+
elif minutes >0:
|
292
|
+
return f"{minutes}m"
|
293
|
+
elif seconds >0:
|
294
|
+
return f"{seconds}s"
|
295
|
+
|
296
|
+
|
297
|
+
def time_now(use_utc=True, milliseconds_numbers=1):
|
298
|
+
if use_utc:
|
299
|
+
datetime_now = datetime.now(UTC)
|
300
|
+
else:
|
301
|
+
datetime_now = datetime.now()
|
302
|
+
return time_to_str(datetime_value=datetime_now,milliseconds_numbers=milliseconds_numbers)
|
303
|
+
|
304
|
+
def time_to_str(datetime_value, time_format='%H:%M:%S.%f', milliseconds_numbers=3):
|
305
|
+
time_str = datetime_value.strftime(time_format)
|
306
|
+
return time_str_milliseconds(datetime_str=time_str, datetime_format=time_format, milliseconds_numbers=milliseconds_numbers)
|
307
|
+
|
308
|
+
def timestamp_utc_now():
|
309
|
+
return int(datetime.now(UTC).timestamp() * 1000)
|
310
|
+
|
311
|
+
def timestamp_utc_now_less_delta(days=0,hours=0, minutes=0, seconds=0):
|
312
|
+
date_time = date_time_now_less_time_delta(days=days,hours=hours, minutes=minutes, seconds=seconds, return_str=False)
|
313
|
+
return datetime_to_timestamp(date_time)
|
314
|
+
|
315
|
+
def datetime_to_timestamp(datetime):
|
316
|
+
return int(datetime.timestamp() * 1000)
|
317
|
+
|
318
|
+
def timestamp_to_datetime(timestamp):
|
319
|
+
timestamp = float(timestamp) # handle cases when timestamp is a Decimal
|
320
|
+
return datetime.fromtimestamp(timestamp/1000)
|
321
|
+
|
322
|
+
def timestamp_to_str(timestamp, date_time_format='%Y-%m-%d %H:%M:%S.%f'):
|
323
|
+
date_time = timestamp_to_datetime(timestamp)
|
324
|
+
return datetime_to_str(date_time, date_time_format=date_time_format)
|
325
|
+
|
326
|
+
def timestamp_to_str_date(timestamp, date_format='%Y-%m-%d'):
|
327
|
+
return timestamp_to_str(timestamp, date_format)
|
328
|
+
|
329
|
+
def timestamp_to_str_time(timestamp, time_format='%H:%M:%S'):
|
330
|
+
return timestamp_to_str(timestamp, time_format)
|
331
|
+
|
332
|
+
def to_string(target):
|
333
|
+
if target:
|
334
|
+
return str(target)
|
335
|
+
return ''
|
336
|
+
|
337
|
+
def random_bytes(length=24):
|
338
|
+
return token_bytes(length)
|
339
|
+
|
340
|
+
def random_filename(extension='.tmp', length=10):
|
341
|
+
from osbot_utils.utils.Files import file_extension_fix
|
342
|
+
extension = file_extension_fix(extension)
|
343
|
+
return '{0}{1}'.format(''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) , extension)
|
344
|
+
|
345
|
+
def random_port(min=20000,max=65000):
|
346
|
+
return random_number(min, max)
|
347
|
+
|
348
|
+
def random_number(min=1,max=65000):
|
349
|
+
return random.randint(min, max)
|
350
|
+
|
351
|
+
def random_password(length=24, prefix=''):
|
352
|
+
password = prefix + ''.join(random.choices(string.ascii_lowercase +
|
353
|
+
string.ascii_uppercase +
|
354
|
+
string.punctuation +
|
355
|
+
string.digits ,
|
356
|
+
k=length))
|
357
|
+
# replace these chars with _ (to make prevent errors in command prompts and urls)
|
358
|
+
items = ['"', '\'', '`','\\','/','}','?','#',';',':']
|
359
|
+
for item in items:
|
360
|
+
password = password.replace(item, '_')
|
361
|
+
return password
|
362
|
+
|
363
|
+
def random_string(length:int=8, prefix:str='', postfix:str=''):
|
364
|
+
if is_int(length):
|
365
|
+
length -= 1 # so that we get the exact length when the value is provided
|
366
|
+
else:
|
367
|
+
length = 7 # default length
|
368
|
+
value = '_' + ''.join(random.choices(string.ascii_uppercase, k=length)).lower()
|
369
|
+
return f"{prefix}{value}{postfix}"
|
370
|
+
|
371
|
+
def random_string_and_numbers(length:int=6,prefix:str=''):
|
372
|
+
return prefix + ''.join(random.choices(string.ascii_uppercase + string.digits, k=length))
|
373
|
+
|
374
|
+
def random_text(prefix:str=None,length:int=12, lowercase=False):
|
375
|
+
if prefix is None: prefix = 'text_'
|
376
|
+
if last_letter(prefix) not in ['_','/']:
|
377
|
+
prefix += '_'
|
378
|
+
value = random_string_and_numbers(length=length, prefix=prefix)
|
379
|
+
if lowercase:
|
380
|
+
return lower(value)
|
381
|
+
return value
|
382
|
+
|
383
|
+
def random_uuid():
|
384
|
+
return str(uuid.uuid4())
|
385
|
+
|
386
|
+
def remove(target_string, string_to_remove): # todo: refactor to str_*
|
387
|
+
return replace(target_string, string_to_remove, '')
|
388
|
+
|
389
|
+
def remove_multiple_spaces(target): # todo: refactor to str_*
|
390
|
+
return re.sub(' +', ' ', target)
|
391
|
+
|
392
|
+
def replace(target_string, string_to_find, string_to_replace): # todo: refactor to str_*
|
393
|
+
return target_string.replace(string_to_find, string_to_replace)
|
394
|
+
|
395
|
+
def remove_html_tags(html):
|
396
|
+
if html:
|
397
|
+
TAG_RE = re.compile(r'<[^>]+>')
|
398
|
+
return TAG_RE.sub('', html).replace(' ', ' ')
|
399
|
+
|
400
|
+
def split_lines(text):
|
401
|
+
return text.replace('\r\n','\n').split('\n')
|
402
|
+
|
403
|
+
def split_spaces(target):
|
404
|
+
return remove_multiple_spaces(target).split(' ')
|
405
|
+
|
406
|
+
def sorted_set(target : Iterable):
|
407
|
+
if target:
|
408
|
+
return sorted(set(target))
|
409
|
+
return []
|
410
|
+
|
411
|
+
def str_to_base64(target):
|
412
|
+
return bytes_to_base64(str_to_bytes(target))
|
413
|
+
|
414
|
+
def str_to_bytes(target):
|
415
|
+
return target.encode()
|
416
|
+
|
417
|
+
def str_to_date(str_date, format='%Y-%m-%d %H:%M:%S.%f'):
|
418
|
+
return datetime.strptime(str_date,format)
|
419
|
+
|
420
|
+
def str_to_date_time(str_date, format='%Y-%m-%d %H:%M:%S'):
|
421
|
+
return datetime.strptime(str_date,format)
|
422
|
+
|
423
|
+
def str_to_int(str_data):
|
424
|
+
return int(float(str_data))
|
425
|
+
|
426
|
+
|
427
|
+
def to_int(value, default=0):
|
428
|
+
try:
|
429
|
+
return int(value)
|
430
|
+
except:
|
431
|
+
return default
|
432
|
+
|
433
|
+
def under_debugger():
|
434
|
+
return 'pydevd' in sys.modules
|
435
|
+
|
436
|
+
|
437
|
+
def url_encode(data):
|
438
|
+
if type(data) is str:
|
439
|
+
return quote_plus(data)
|
440
|
+
|
441
|
+
def url_decode(data):
|
442
|
+
if type(data) is str:
|
443
|
+
return unquote_plus(data)
|
444
|
+
|
445
|
+
def utc_now():
|
446
|
+
return datetime.now(UTC)
|
447
|
+
|
448
|
+
def upper(target : str):
|
449
|
+
if target:
|
450
|
+
return target.upper()
|
451
|
+
return ""
|
452
|
+
|
453
|
+
def wait(seconds):
|
454
|
+
if seconds and seconds > 0:
|
455
|
+
sleep(seconds)
|
456
|
+
|
457
|
+
def word_wrap(text,length = 40):
|
458
|
+
if text:
|
459
|
+
wrapped_text = ""
|
460
|
+
for line in text.splitlines(): # handle case when there are newlines inside the text value
|
461
|
+
wrapped_text += '\n'.join(textwrap.wrap(line, length))
|
462
|
+
wrapped_text += '\n'
|
463
|
+
return wrapped_text
|
464
|
+
return ''
|
465
|
+
|
466
|
+
def word_wrap_escaped(text,length = 40):
|
467
|
+
if text:
|
468
|
+
return '\\n'.join(textwrap.wrap(text, length))
|
469
|
+
|
470
|
+
bytes_to_string = bytes_to_str
|
471
|
+
|
472
|
+
convert_to_float = convert_to_number
|
473
|
+
|
474
|
+
datetime_now = date_time_now
|
475
|
+
datetime_to_str = date_time_to_str
|
476
|
+
datetime_from_timestamp = timestamp_to_datetime
|
477
|
+
datetime_utc_now = utc_now
|
478
|
+
date_time_to_timestamp = datetime_to_timestamp
|
479
|
+
date_time_from_timestamp = timestamp_to_datetime
|
480
|
+
date_time_from_time_stamp = timestamp_to_datetime
|
481
|
+
date_time_utc_now = utc_now
|
482
|
+
|
483
|
+
|
484
|
+
new_guid = random_uuid
|
485
|
+
|
486
|
+
str_lines = split_lines
|
487
|
+
str_remove = remove
|
488
|
+
|
489
|
+
random_id = random_string
|
490
|
+
random_int = random_number
|
491
|
+
random_guid = random_uuid
|
492
|
+
random_value = random_string
|
493
|
+
|
494
|
+
time_utc = time_now
|
495
|
+
wait_for = wait
|
496
|
+
|