osbot-utils 1.81.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.
@@ -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
- import functools
5
- import inspect
3
+
6
4
  import sys
7
5
  import types
8
- import typing
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
- immutable_types = (bool, int, float, complex, str, tuple, frozenset, bytes, NoneType, EnumMeta)
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
- """Return current class dictionary of class level variables and their values."""
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 immutable_types and var_name.startswith('__') is False: # if var_type is not one of the immutable_types or is an __ internal
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, 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}'"
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
- """Return entire (including base classes) dictionary of class level variables and their values."""
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
- """Update instance attributes with values from provided keyword arguments."""
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):
@@ -361,3 +388,5 @@ def serialize_to_dict(obj):
361
388
  return data
362
389
  else:
363
390
  raise TypeError(f"Type {type(obj)} not serializable")
391
+ #return f"UNSERIALIZABLE({type(obj).__name__})" # todo: see if there are valid use cases for this
392
+
@@ -1,8 +1,9 @@
1
1
  #todo: refactor with index_by
2
- from functools import wraps
3
2
 
4
3
 
5
4
  def group_by(function): # returns the list provided grouped by the key provided in group_by
5
+ from functools import wraps
6
+
6
7
  @wraps(function)
7
8
  def wrapper(*args,**kwargs):
8
9
  key = None
@@ -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
- Use this for cases where we want the cache to be tied to the Class instance (i.e. not global for all executions)
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
- 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'
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 functools import wraps
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,7 +1,9 @@
1
- from osbot_utils.utils.Misc import random_guid, is_guid
1
+
2
2
 
3
3
  class Random_Guid(str):
4
4
  def __new__(cls, value=None):
5
+ from osbot_utils.utils.Misc import random_guid, is_guid
6
+
5
7
  if value is None:
6
8
  value = random_guid()
7
9
  if is_guid(value):
@@ -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):
@@ -8,6 +8,7 @@ from osbot_utils.helpers.Random_Guid import Random_Gu
8
8
  from osbot_utils.helpers.generators.Generator_Context_Manager import Generator_Context_Manager
9
9
  from osbot_utils.helpers.generators.Model__Generator_State import Model__Generator_State
10
10
  from osbot_utils.helpers.generators.Model__Generator_Target import Model__Generator_Target
11
+ from osbot_utils.utils.Lists import list_group_by
11
12
 
12
13
 
13
14
  class Generator_Manager(Type_Safe): # Class for managing multiple generator targets
@@ -92,6 +93,21 @@ class Generator_Manager(Type_Safe):
92
93
  stopped_count += 1 # Increment the stopped count if successful
93
94
  return stopped_count # Return the total number of stopped generators
94
95
 
96
+ def status(self):
97
+ items = []
98
+ for _, generator in self.generators.items():
99
+ item = dict(target_method_name = generator.target.__name__,
100
+ target_state = generator.state.value,
101
+ target_id = generator.target_id )
102
+ items.append(item)
103
+ items__by_state = list_group_by(items, 'target_state')
104
+ result = {}
105
+ for state, items in items__by_state.items():
106
+ result[state] = len(items)
107
+ result['data'] = items__by_state
108
+ return result
109
+
110
+
95
111
  def target_id(self, target: GeneratorType) -> Union[Random_Guid, None]: # Method to get the ID of a specific generator
96
112
  with self.lock: # Acquire the lock for thread-safe access
97
113
  for generator in list(self.generators.values()): # Iterate over the generator targets
@@ -1,18 +1,14 @@
1
- import sqlite3
2
-
3
- from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
4
- from osbot_utils.decorators.methods.cache import cache
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(Kwargs_To_Self):
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 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
- from osbot_utils.helpers.sqlite.tables.Sqlite__Table__Files import Sqlite__Table__Files
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
- from osbot_utils.utils.Dev import pprint
2
- from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
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(Kwargs_To_Self):
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