osbot-utils 1.82.0__py3-none-any.whl → 1.83.0__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/base_classes/Type_Safe.py +58 -31
- osbot_utils/decorators/lists/group_by.py +2 -1
- osbot_utils/decorators/lists/index_by.py +3 -3
- osbot_utils/decorators/methods/cache_on_self.py +28 -28
- osbot_utils/decorators/methods/remove_return_value.py +3 -6
- osbot_utils/helpers/Random_Guid.py +3 -1
- osbot_utils/helpers/Timestamp_Now.py +4 -1
- osbot_utils/helpers/sqlite/Sqlite__Database.py +9 -9
- osbot_utils/helpers/sqlite/domains/Sqlite__DB__Files.py +6 -4
- osbot_utils/helpers/trace/Trace_Call__Config.py +5 -3
- osbot_utils/utils/Dev.py +10 -5
- osbot_utils/utils/Env.py +40 -10
- osbot_utils/utils/Files.py +46 -13
- osbot_utils/utils/Json.py +28 -6
- osbot_utils/utils/Misc.py +62 -26
- osbot_utils/utils/Objects.py +36 -21
- osbot_utils/utils/Toml.py +5 -7
- osbot_utils/version +1 -1
- {osbot_utils-1.82.0.dist-info → osbot_utils-1.83.0.dist-info}/METADATA +2 -2
- {osbot_utils-1.82.0.dist-info → osbot_utils-1.83.0.dist-info}/RECORD +22 -22
- {osbot_utils-1.82.0.dist-info → osbot_utils-1.83.0.dist-info}/LICENSE +0 -0
- {osbot_utils-1.82.0.dist-info → osbot_utils-1.83.0.dist-info}/WHEEL +0 -0
@@ -1,31 +1,14 @@
|
|
1
1
|
# todo: find a way to add these documentations strings to a separate location so that
|
2
|
-
# the code is not polluted with them (like in the example below)
|
3
2
|
# the data is available in IDE's code complete
|
4
|
-
|
5
|
-
import inspect
|
3
|
+
|
6
4
|
import sys
|
7
5
|
import types
|
8
|
-
import
|
9
|
-
from decimal import Decimal
|
10
|
-
from enum import Enum, EnumMeta
|
11
|
-
from typing import List
|
12
|
-
from osbot_utils.base_classes.Type_Safe__List import Type_Safe__List
|
13
|
-
from osbot_utils.helpers.Random_Guid import Random_Guid
|
14
|
-
from osbot_utils.helpers.Random_Guid_Short import Random_Guid_Short
|
15
|
-
from osbot_utils.helpers.Safe_Id import Safe_Id
|
16
|
-
from osbot_utils.helpers.Timestamp_Now import Timestamp_Now
|
17
|
-
from osbot_utils.utils.Dev import pprint
|
18
|
-
from osbot_utils.utils.Json import json_parse, json_to_bytes, json_to_gz
|
19
|
-
from osbot_utils.utils.Misc import list_set
|
20
|
-
from osbot_utils.utils.Objects import default_value, value_type_matches_obj_annotation_for_attr, \
|
21
|
-
raise_exception_on_obj_type_annotation_mismatch, obj_is_attribute_annotation_of_type, enum_from_value, \
|
22
|
-
obj_is_type_union_compatible, value_type_matches_obj_annotation_for_union_attr, \
|
23
|
-
convert_dict_to_value_from_obj_annotation, dict_to_obj, convert_to_value_from_obj_annotation, \
|
24
|
-
obj_attribute_annotation
|
6
|
+
from osbot_utils.utils.Objects import default_value # todo: remove test mocking requirement for this to be here (instead of on the respective method)
|
25
7
|
|
26
8
|
# Backport implementations of get_origin and get_args for Python 3.7
|
27
9
|
if sys.version_info < (3, 8): # pragma: no cover
|
28
10
|
def get_origin(tp):
|
11
|
+
import typing
|
29
12
|
if isinstance(tp, typing._GenericAlias):
|
30
13
|
return tp.__origin__
|
31
14
|
elif tp is typing.Generic:
|
@@ -34,6 +17,7 @@ if sys.version_info < (3, 8): # pragma
|
|
34
17
|
return None
|
35
18
|
|
36
19
|
def get_args(tp):
|
20
|
+
import typing
|
37
21
|
if isinstance(tp, typing._GenericAlias):
|
38
22
|
return tp.__args__
|
39
23
|
else:
|
@@ -46,7 +30,7 @@ if sys.version_info >= (3, 10):
|
|
46
30
|
else: # pragma: no cover
|
47
31
|
NoneType = type(None)
|
48
32
|
|
49
|
-
|
33
|
+
|
50
34
|
|
51
35
|
|
52
36
|
#todo: see if we can also add type safety to method execution
|
@@ -56,9 +40,11 @@ immutable_types = (bool, int, float, complex, str, tuple, frozenset, bytes, None
|
|
56
40
|
class Type_Safe:
|
57
41
|
|
58
42
|
def __init__(self, **kwargs):
|
43
|
+
from osbot_utils.utils.Objects import raise_exception_on_obj_type_annotation_mismatch
|
59
44
|
|
60
45
|
for (key, value) in self.__cls_kwargs__().items(): # assign all default values to self
|
61
46
|
if value is not None: # when the value is explicitly set to None on the class static vars, we can't check for type safety
|
47
|
+
|
62
48
|
raise_exception_on_obj_type_annotation_mismatch(self, key, value)
|
63
49
|
if hasattr(self, key):
|
64
50
|
existing_value = getattr(self, key)
|
@@ -79,6 +65,11 @@ class Type_Safe:
|
|
79
65
|
def __exit__(self, exc_type, exc_val, exc_tb): pass
|
80
66
|
|
81
67
|
def __setattr__(self, name, value):
|
68
|
+
from osbot_utils.utils.Objects import convert_dict_to_value_from_obj_annotation
|
69
|
+
from osbot_utils.utils.Objects import convert_to_value_from_obj_annotation
|
70
|
+
from osbot_utils.utils.Objects import value_type_matches_obj_annotation_for_attr
|
71
|
+
from osbot_utils.utils.Objects import value_type_matches_obj_annotation_for_union_attr
|
72
|
+
|
82
73
|
if not hasattr(self, '__annotations__'): # can't do type safety checks if the class does not have annotations
|
83
74
|
return super().__setattr__(name, value)
|
84
75
|
|
@@ -101,11 +92,20 @@ class Type_Safe:
|
|
101
92
|
super().__setattr__(name, value)
|
102
93
|
|
103
94
|
def __attr_names__(self):
|
95
|
+
from osbot_utils.utils.Misc import list_set
|
96
|
+
|
104
97
|
return list_set(self.__locals__())
|
105
98
|
|
106
99
|
@classmethod
|
107
|
-
def __cls_kwargs__(cls, include_base_classes=True):
|
108
|
-
|
100
|
+
def __cls_kwargs__(cls, include_base_classes=True): # Return current class dictionary of class level variables and their values
|
101
|
+
import functools
|
102
|
+
import inspect
|
103
|
+
from enum import EnumMeta
|
104
|
+
from osbot_utils.utils.Objects import obj_is_type_union_compatible
|
105
|
+
|
106
|
+
IMMUTABLE_TYPES = (bool, int, float, complex, str, tuple, frozenset, bytes, NoneType, EnumMeta)
|
107
|
+
|
108
|
+
|
109
109
|
kwargs = {}
|
110
110
|
|
111
111
|
for base_cls in inspect.getmro(cls):
|
@@ -134,11 +134,11 @@ class Type_Safe:
|
|
134
134
|
if var_type and not isinstance(var_value, var_type): # check type
|
135
135
|
exception_message = f"variable '{var_name}' is defined as type '{var_type}' but has value '{var_value}' of type '{type(var_value)}'"
|
136
136
|
raise ValueError(exception_message)
|
137
|
-
if var_type not in
|
137
|
+
if var_type not in IMMUTABLE_TYPES and var_name.startswith('__') is False: # if var_type is not one of the IMMUTABLE_TYPES or is an __ internal
|
138
138
|
#todo: fix type safety bug that I believe is caused here
|
139
|
-
if obj_is_type_union_compatible(var_type,
|
140
|
-
if type(var_type) not in
|
141
|
-
exception_message = f"variable '{var_name}' is defined as type '{var_type}' which is not supported by Kwargs_To_Self, with only the following immutable types being supported: '{
|
139
|
+
if obj_is_type_union_compatible(var_type, IMMUTABLE_TYPES) is False: # if var_type is not something like Optional[Union[int, str]]
|
140
|
+
if type(var_type) not in IMMUTABLE_TYPES:
|
141
|
+
exception_message = f"variable '{var_name}' is defined as type '{var_type}' which is not supported by Kwargs_To_Self, with only the following immutable types being supported: '{IMMUTABLE_TYPES}'"
|
142
142
|
raise ValueError(exception_message)
|
143
143
|
if include_base_classes is False:
|
144
144
|
break
|
@@ -146,6 +146,9 @@ class Type_Safe:
|
|
146
146
|
|
147
147
|
@classmethod
|
148
148
|
def __default__value__(cls, var_type):
|
149
|
+
import typing
|
150
|
+
from osbot_utils.base_classes.Type_Safe__List import Type_Safe__List
|
151
|
+
|
149
152
|
if var_type is typing.Set: # todo: refactor the dict, set and list logic, since they are 90% the same
|
150
153
|
return set()
|
151
154
|
if get_origin(var_type) is set:
|
@@ -164,8 +167,8 @@ class Type_Safe:
|
|
164
167
|
else:
|
165
168
|
return default_value(var_type) # for all other cases call default_value, which will try to create a default instance
|
166
169
|
|
167
|
-
def __default_kwargs__(self):
|
168
|
-
|
170
|
+
def __default_kwargs__(self): # Return entire (including base classes) dictionary of class level variables and their values.
|
171
|
+
import inspect
|
169
172
|
kwargs = {}
|
170
173
|
cls = type(self)
|
171
174
|
for base_cls in inspect.getmro(cls): # Traverse the inheritance hierarchy and collect class-level attributes
|
@@ -217,9 +220,13 @@ class Type_Safe:
|
|
217
220
|
# todo: see if there should be a prefix on these methods, to make it easier to spot them
|
218
221
|
# of if these are actually that useful that they should be added like this
|
219
222
|
def bytes(self):
|
223
|
+
from osbot_utils.utils.Json import json_to_bytes
|
224
|
+
|
220
225
|
return json_to_bytes(self.json())
|
221
226
|
|
222
227
|
def bytes_gz(self):
|
228
|
+
from osbot_utils.utils.Json import json_to_gz
|
229
|
+
|
223
230
|
return json_to_gz(self.json())
|
224
231
|
|
225
232
|
def json(self):
|
@@ -240,8 +247,8 @@ class Type_Safe:
|
|
240
247
|
for k,v in self.__cls_kwargs__().items():
|
241
248
|
setattr(self, k, v)
|
242
249
|
|
243
|
-
def update_from_kwargs(self, **kwargs):
|
244
|
-
|
250
|
+
def update_from_kwargs(self, **kwargs): # Update instance attributes with values from provided keyword arguments.
|
251
|
+
from osbot_utils.utils.Objects import value_type_matches_obj_annotation_for_attr
|
245
252
|
for key, value in kwargs.items():
|
246
253
|
if value is not None:
|
247
254
|
if hasattr(self,'__annotations__'): # can only do type safety checks if the class does not have annotations
|
@@ -278,6 +285,16 @@ class Type_Safe:
|
|
278
285
|
|
279
286
|
# todo: this needs refactoring, since the logic and code is getting quite complex (to be inside methods like this)
|
280
287
|
def deserialize_from_dict(self, data, raise_on_not_found=False):
|
288
|
+
from decimal import Decimal
|
289
|
+
from enum import EnumMeta
|
290
|
+
from osbot_utils.base_classes.Type_Safe__List import Type_Safe__List
|
291
|
+
from osbot_utils.helpers.Random_Guid import Random_Guid
|
292
|
+
from osbot_utils.helpers.Random_Guid_Short import Random_Guid_Short
|
293
|
+
from osbot_utils.utils.Objects import obj_is_attribute_annotation_of_type
|
294
|
+
from osbot_utils.utils.Objects import obj_attribute_annotation
|
295
|
+
from osbot_utils.utils.Objects import enum_from_value
|
296
|
+
from osbot_utils.helpers.Safe_Id import Safe_Id
|
297
|
+
from osbot_utils.helpers.Timestamp_Now import Timestamp_Now
|
281
298
|
|
282
299
|
for key, value in data.items():
|
283
300
|
if hasattr(self, key) and isinstance(getattr(self, key), Type_Safe):
|
@@ -327,16 +344,22 @@ class Type_Safe:
|
|
327
344
|
return self
|
328
345
|
|
329
346
|
def obj(self):
|
347
|
+
from osbot_utils.utils.Objects import dict_to_obj
|
348
|
+
|
330
349
|
return dict_to_obj(self.json())
|
331
350
|
|
332
351
|
def serialize_to_dict(self): # todo: see if we need this method or if the .json() is enough
|
333
352
|
return serialize_to_dict(self)
|
334
353
|
|
335
354
|
def print(self):
|
355
|
+
from osbot_utils.utils.Dev import pprint
|
356
|
+
|
336
357
|
pprint(serialize_to_dict(self))
|
337
358
|
|
338
359
|
@classmethod
|
339
360
|
def from_json(cls, json_data, raise_on_not_found=False):
|
361
|
+
from osbot_utils.utils.Json import json_parse
|
362
|
+
|
340
363
|
if type(json_data) is str:
|
341
364
|
json_data = json_parse(json_data)
|
342
365
|
if json_data: # if there is no data or is {} then don't create an object (since this could be caused by bad data being provided)
|
@@ -345,6 +368,10 @@ class Type_Safe:
|
|
345
368
|
|
346
369
|
# todo: see if it is possible to add recursive protection to this logic
|
347
370
|
def serialize_to_dict(obj):
|
371
|
+
from decimal import Decimal
|
372
|
+
from enum import Enum
|
373
|
+
from typing import List
|
374
|
+
|
348
375
|
if isinstance(obj, (str, int, float, bool, bytes, Decimal)) or obj is None:
|
349
376
|
return obj
|
350
377
|
elif isinstance(obj, Enum):
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# todo: find way to also allow the used of function().index_by(key) to working
|
2
2
|
# : maybe using Fluent_List
|
3
|
-
from functools import wraps
|
4
|
-
|
5
|
-
from osbot_utils.fluent.Fluent_Dict import Fluent_Dict
|
6
3
|
|
7
4
|
|
8
5
|
def index_by(function): # returns the list provided indexed by the key provided in index_by
|
6
|
+
from functools import wraps
|
7
|
+
from osbot_utils.fluent.Fluent_Dict import Fluent_Dict
|
8
|
+
|
9
9
|
def apply(key, values):
|
10
10
|
if key:
|
11
11
|
results = {}
|
@@ -1,9 +1,5 @@
|
|
1
|
-
import inspect
|
2
|
-
from functools import wraps
|
3
|
-
from osbot_utils.utils.Misc import str_md5
|
4
1
|
from typing import Any, Callable, TypeVar
|
5
2
|
|
6
|
-
|
7
3
|
CACHE_ON_SELF_KEY_PREFIX = '__cache_on_self__'
|
8
4
|
CACHE_ON_SELF_TYPES = [int, float, bytearray, bytes, bool,
|
9
5
|
complex, str]
|
@@ -17,12 +13,14 @@ CACHE_ON_SELF_TYPES = [int, float, bytearray, bytes, bool,
|
|
17
13
|
|
18
14
|
T = TypeVar('T', bound=Callable[..., Any]) # so that we have type hinting when using this class
|
19
15
|
|
20
|
-
def cache_on_self(function: T) -> T:
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
def cache_on_self(function: T) -> T: # Use this for cases where we want the cache to be tied to the Class instance (i.e. not global for all executions)
|
17
|
+
import inspect
|
18
|
+
from functools import wraps
|
19
|
+
|
24
20
|
@wraps(function)
|
25
21
|
def wrapper(*args, **kwargs):
|
22
|
+
|
23
|
+
|
26
24
|
if len(args) == 0 or inspect.isclass(type(args[0])) is False:
|
27
25
|
raise Exception("In Method_Wrappers.cache_on_self could not find self")
|
28
26
|
|
@@ -58,23 +56,25 @@ def cache_on_self__kwargs_to_str(kwargs):
|
|
58
56
|
return kwargs_values_as_str
|
59
57
|
|
60
58
|
def cache_on_self__get_cache_in_key(function, args=None, kwargs=None):
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
59
|
+
from osbot_utils.utils.Misc import str_md5
|
60
|
+
|
61
|
+
key_name = function.__name__
|
62
|
+
args_md5 = ''
|
63
|
+
kwargs_md5 = ''
|
64
|
+
args_values_as_str = cache_on_self__args_to_str(args)
|
65
|
+
kwargs_values_as_str = cache_on_self__kwargs_to_str(kwargs)
|
66
|
+
if args_values_as_str:
|
67
|
+
args_md5 = str_md5(args_values_as_str)
|
68
|
+
if kwargs_values_as_str:
|
69
|
+
kwargs_md5 = str_md5(kwargs_values_as_str)
|
70
|
+
return f'{CACHE_ON_SELF_KEY_PREFIX}_{key_name}_{args_md5}_{kwargs_md5}'
|
71
|
+
|
72
|
+
# class_name = self_obj.__class__.__name__
|
73
|
+
#
|
74
|
+
# function_name = function_obj.__name__
|
75
|
+
# if params:
|
76
|
+
# params_as_string = '_'.join(str(x) for x in params).replace('/',' ')
|
77
|
+
# params_md5 = str_md5(params_as_string)
|
78
|
+
# return f'{class_name}_{function_name}_{params_md5}.gz'
|
79
|
+
# else:
|
80
|
+
# return f'{class_name}_{function_name}.gz'
|
@@ -1,14 +1,11 @@
|
|
1
|
-
from
|
1
|
+
class remove_return_value: # removes the field from the return value of the function (if it exists)
|
2
2
|
|
3
|
-
|
4
|
-
class remove_return_value:
|
5
|
-
"""
|
6
|
-
removes the field from the return value of the function (if it exists)
|
7
|
-
"""
|
8
3
|
def __init__(self, field_name):
|
9
4
|
self.field_name = field_name # field to remove
|
10
5
|
|
11
6
|
def __call__(self, function):
|
7
|
+
from functools import wraps
|
8
|
+
|
12
9
|
@wraps(function) # makes __name__ work ok
|
13
10
|
def wrapper(*args,**kwargs): # wrapper function
|
14
11
|
data = function(*args,**kwargs) # calls wrapped function with original params
|
@@ -1,12 +1,15 @@
|
|
1
|
-
from osbot_utils.utils.Misc import timestamp_now
|
2
1
|
|
3
2
|
class Timestamp_Now(int):
|
4
3
|
def __new__(cls, value=None):
|
4
|
+
from osbot_utils.utils.Misc import timestamp_now
|
5
|
+
|
5
6
|
if value is None:
|
6
7
|
value = timestamp_now()
|
7
8
|
return int.__new__(cls, value)
|
8
9
|
|
9
10
|
def __init__(self, value=None):
|
11
|
+
from osbot_utils.utils.Misc import timestamp_now
|
12
|
+
|
10
13
|
self.value = value if value is not None else timestamp_now()
|
11
14
|
|
12
15
|
def __str__(self):
|
@@ -1,18 +1,14 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
from osbot_utils.
|
4
|
-
from osbot_utils.
|
5
|
-
from osbot_utils.decorators.methods.cache_on_self import cache_on_self
|
6
|
-
|
7
|
-
from osbot_utils.utils.Files import current_temp_folder, path_combine, folder_create, file_exists, file_delete
|
8
|
-
from osbot_utils.utils.Misc import random_filename
|
1
|
+
from osbot_utils.base_classes.Type_Safe import Type_Safe
|
2
|
+
from osbot_utils.decorators.methods.cache_on_self import cache_on_self
|
3
|
+
from osbot_utils.utils.Files import current_temp_folder, path_combine, folder_create, file_exists, file_delete
|
4
|
+
from osbot_utils.utils.Misc import random_filename
|
9
5
|
|
10
6
|
SQLITE_DATABASE_PATH__IN_MEMORY = ':memory:'
|
11
7
|
FOLDER_NAME_TEMP_DATABASES = '_temp_sqlite_databases'
|
12
8
|
TEMP_DATABASE__FILE_NAME_PREFIX = 'random_sqlite_db__'
|
13
9
|
TEMP_DATABASE__FILE_EXTENSION = '.sqlite'
|
14
10
|
|
15
|
-
class Sqlite__Database(
|
11
|
+
class Sqlite__Database(Type_Safe):
|
16
12
|
db_path : str = None
|
17
13
|
closed : bool = False
|
18
14
|
connected : bool = False
|
@@ -38,6 +34,8 @@ class Sqlite__Database(Kwargs_To_Self):
|
|
38
34
|
|
39
35
|
@cache_on_self
|
40
36
|
def connect(self):
|
37
|
+
import sqlite3
|
38
|
+
|
41
39
|
connection_string = self.connection_string()
|
42
40
|
connection = sqlite3.connect(connection_string)
|
43
41
|
connection.row_factory = self.dict_factory # this returns a dict as the row value of every query
|
@@ -90,6 +88,8 @@ class Sqlite__Database(Kwargs_To_Self):
|
|
90
88
|
return path_temp_databases
|
91
89
|
|
92
90
|
def save_to(self, path):
|
91
|
+
import sqlite3
|
92
|
+
|
93
93
|
connection = self.connection()
|
94
94
|
file_conn = sqlite3.connect(path)
|
95
95
|
connection.backup(file_conn)
|
@@ -1,7 +1,7 @@
|
|
1
|
-
from osbot_utils.decorators.lists.index_by
|
2
|
-
from osbot_utils.decorators.methods.cache_on_self
|
3
|
-
from osbot_utils.helpers.sqlite.domains.Sqlite__DB__Local
|
4
|
-
|
1
|
+
from osbot_utils.decorators.lists.index_by import index_by
|
2
|
+
from osbot_utils.decorators.methods.cache_on_self import cache_on_self
|
3
|
+
from osbot_utils.helpers.sqlite.domains.Sqlite__DB__Local import Sqlite__DB__Local
|
4
|
+
|
5
5
|
|
6
6
|
|
7
7
|
class Sqlite__DB__Files(Sqlite__DB__Local):
|
@@ -35,6 +35,8 @@ class Sqlite__DB__Files(Sqlite__DB__Local):
|
|
35
35
|
|
36
36
|
@cache_on_self
|
37
37
|
def table_files(self):
|
38
|
+
from osbot_utils.helpers.sqlite.tables.Sqlite__Table__Files import Sqlite__Table__Files
|
39
|
+
|
38
40
|
return Sqlite__Table__Files(database=self).setup()
|
39
41
|
|
40
42
|
@index_by
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
2
|
-
from osbot_utils.base_classes.
|
1
|
+
|
2
|
+
from osbot_utils.base_classes.Type_Safe import Type_Safe
|
3
|
+
from osbot_utils.utils.Dev import pprint # todo: fix test requirement of mock to use this method
|
4
|
+
|
3
5
|
|
4
6
|
PRINT_MAX_STRING_LENGTH = 100
|
5
7
|
PRINT_PADDING__DURATION = 100
|
6
8
|
PRINT_PADDING_PARENT_INFO = 60
|
7
9
|
|
8
|
-
class Trace_Call__Config(
|
10
|
+
class Trace_Call__Config(Type_Safe):
|
9
11
|
capture_locals : bool = False
|
10
12
|
capture_duration : bool
|
11
13
|
capture_extra_data : bool
|
osbot_utils/utils/Dev.py
CHANGED
@@ -1,26 +1,29 @@
|
|
1
|
-
import json
|
2
|
-
import pprint as original_pprint
|
3
|
-
|
4
|
-
from osbot_utils.utils.Misc import date_time_now
|
5
|
-
|
6
1
|
|
7
2
|
class Dev:
|
8
3
|
@staticmethod
|
9
4
|
def jformat(data):
|
5
|
+
import json
|
6
|
+
|
10
7
|
return json.dumps(data, indent=4) # use json.dumps to format
|
11
8
|
|
12
9
|
@staticmethod
|
13
10
|
def jprint(data):
|
11
|
+
import json
|
12
|
+
|
14
13
|
print() # add a line before
|
15
14
|
print(json.dumps(data, indent=4)) # use json.dumps to format
|
16
15
|
return data
|
17
16
|
|
18
17
|
@staticmethod
|
19
18
|
def pformat(data):
|
19
|
+
import pprint as original_pprint
|
20
|
+
|
20
21
|
return original_pprint.pformat(data, indent=2) # use a pprint to format
|
21
22
|
|
22
23
|
@staticmethod
|
23
24
|
def pprint(*args):
|
25
|
+
import pprint as original_pprint
|
26
|
+
|
24
27
|
print() # add a line before
|
25
28
|
for arg in args:
|
26
29
|
original_pprint.pprint(arg, indent=2) # use a pprint to format
|
@@ -36,6 +39,8 @@ class Dev:
|
|
36
39
|
|
37
40
|
@staticmethod
|
38
41
|
def print_now():
|
42
|
+
from osbot_utils.utils.Misc import date_time_now
|
43
|
+
|
39
44
|
print(date_time_now())
|
40
45
|
|
41
46
|
jformat = Dev.jformat
|
osbot_utils/utils/Env.py
CHANGED
@@ -1,12 +1,6 @@
|
|
1
|
-
|
2
|
-
import os
|
3
|
-
import sys
|
4
|
-
from sys import platform
|
5
|
-
from osbot_utils.utils.Files import all_parent_folders, file_exists
|
6
|
-
from osbot_utils.utils.Misc import list_set
|
7
|
-
from osbot_utils.utils.Str import strip_quotes
|
8
|
-
|
9
1
|
def del_env(key):
|
2
|
+
import os
|
3
|
+
|
10
4
|
if key in os.environ:
|
11
5
|
del os.environ[key]
|
12
6
|
|
@@ -14,6 +8,8 @@ def env__home():
|
|
14
8
|
return get_env('HOME', '')
|
15
9
|
|
16
10
|
def env__home__is__root():
|
11
|
+
import os
|
12
|
+
|
17
13
|
return os.getenv('HOME') == '/root'
|
18
14
|
|
19
15
|
def env__old_pwd():
|
@@ -28,6 +24,8 @@ def env__old_pwd__remove(value):
|
|
28
24
|
return value
|
29
25
|
|
30
26
|
def env__terminal__is__xterm():
|
27
|
+
import os
|
28
|
+
|
31
29
|
return os.getenv('TERM') == 'xterm'
|
32
30
|
|
33
31
|
def env__terminal__is_not__xterm():
|
@@ -35,16 +33,21 @@ def env__terminal__is_not__xterm():
|
|
35
33
|
|
36
34
|
|
37
35
|
def platform_darwin():
|
36
|
+
from sys import platform
|
38
37
|
return platform == 'darwin'
|
39
38
|
|
40
39
|
def env_value(var_name):
|
41
40
|
return env_vars().get(var_name, None)
|
42
41
|
|
43
42
|
def env_var_set(var_name):
|
43
|
+
import os
|
44
|
+
|
44
45
|
value = os.getenv(var_name)
|
45
46
|
return value is not None and value != ''
|
46
47
|
|
47
48
|
def env_vars_list():
|
49
|
+
from osbot_utils.utils.Misc import list_set
|
50
|
+
|
48
51
|
return list_set(env_vars())
|
49
52
|
|
50
53
|
def env_vars(reload_vars=False):
|
@@ -52,6 +55,8 @@ def env_vars(reload_vars=False):
|
|
52
55
|
if reload_vars reload data from .env file
|
53
56
|
then return dictionary with current environment variables
|
54
57
|
"""
|
58
|
+
import os
|
59
|
+
|
55
60
|
if reload_vars:
|
56
61
|
load_dotenv()
|
57
62
|
vars = os.environ
|
@@ -61,7 +66,12 @@ def env_vars(reload_vars=False):
|
|
61
66
|
return data
|
62
67
|
|
63
68
|
def env_load_from_file(path, override=False):
|
69
|
+
import os
|
70
|
+
from osbot_utils.utils.Files import file_exists
|
71
|
+
|
64
72
|
if file_exists(path):
|
73
|
+
from osbot_utils.utils.Str import strip_quotes
|
74
|
+
|
65
75
|
with open(path) as f:
|
66
76
|
for line in f:
|
67
77
|
line = line.strip()
|
@@ -75,6 +85,8 @@ def env_load_from_file(path, override=False):
|
|
75
85
|
os.environ[key.strip()] = value.strip()
|
76
86
|
|
77
87
|
def env_unload_from_file(path):
|
88
|
+
import os
|
89
|
+
|
78
90
|
if os.path.exists(path):
|
79
91
|
with open(path) as f:
|
80
92
|
for line in f:
|
@@ -87,19 +99,33 @@ def env_unload_from_file(path):
|
|
87
99
|
del os.environ[key]
|
88
100
|
|
89
101
|
def find_dotenv_file(start_path=None, env_file_to_find='.env'):
|
102
|
+
import os
|
103
|
+
from osbot_utils.utils.Files import all_parent_folders
|
104
|
+
|
90
105
|
directories = all_parent_folders(path=start_path, include_path=True) # Define the possible directories to search for the .env file (which is this and all parent folders)
|
91
106
|
for directory in directories: # Iterate through the directories and load the .env file if found
|
92
107
|
env_path = os.path.join(directory,env_file_to_find) # Define the path to the .env file
|
93
108
|
if os.path.exists(env_path): # If we found one
|
94
109
|
return env_path # return the path to the .env file
|
95
110
|
|
111
|
+
def get_env(key, default=None):
|
112
|
+
import os
|
113
|
+
return os.getenv(key, default=default)
|
114
|
+
|
96
115
|
def in_github_action():
|
116
|
+
import os
|
117
|
+
|
97
118
|
return os.getenv('GITHUB_ACTIONS') == 'true'
|
98
119
|
|
99
120
|
def in_pytest_with_coverage():
|
121
|
+
import os
|
122
|
+
|
100
123
|
return os.getenv('COVERAGE_RUN') == 'true'
|
101
124
|
|
102
125
|
def in_python_debugger():
|
126
|
+
import os
|
127
|
+
import sys
|
128
|
+
|
103
129
|
if sys.gettrace() is not None: # Check for a trace function
|
104
130
|
return True
|
105
131
|
pycharm_hosted = os.getenv('PYCHARM_HOSTED') == '1' # Check for PyCharm specific environment variables and other potential indicators
|
@@ -121,10 +147,15 @@ def not_in_github_action():
|
|
121
147
|
return in_github_action() is False
|
122
148
|
|
123
149
|
def set_env(key, value):
|
150
|
+
import os
|
151
|
+
|
124
152
|
os.environ[key] = value
|
125
153
|
return value
|
126
154
|
|
127
155
|
def unload_dotenv(dotenv_path=None):
|
156
|
+
import os
|
157
|
+
from osbot_utils.utils.Files import all_parent_folders
|
158
|
+
|
128
159
|
if dotenv_path: # If a specific dotenv path is provided, unload from it
|
129
160
|
env_unload_from_file(dotenv_path)
|
130
161
|
else:
|
@@ -136,5 +167,4 @@ def unload_dotenv(dotenv_path=None):
|
|
136
167
|
break # Stop after unloading the first .env file
|
137
168
|
|
138
169
|
is_env_var_set = env_var_set
|
139
|
-
env_load = load_dotenv
|
140
|
-
get_env = os.getenv
|
170
|
+
env_load = load_dotenv
|