osbot-utils 1.80.0__py3-none-any.whl → 1.82.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.
@@ -361,3 +361,5 @@ def serialize_to_dict(obj):
361
361
  return data
362
362
  else:
363
363
  raise TypeError(f"Type {type(obj)} not serializable")
364
+ #return f"UNSERIALIZABLE({type(obj).__name__})" # todo: see if there are valid use cases for this
365
+
@@ -0,0 +1,21 @@
1
+ from osbot_utils.helpers.generators.Model__Generator_State import Model__Generator_State
2
+
3
+
4
+ class Generator_Context_Manager:
5
+ def __init__(self, manager, generator_func):
6
+ self.manager = manager
7
+ self.generator_func = generator_func
8
+ self.target_id = None
9
+
10
+ def __enter__(self):
11
+ self.target_id = self.manager.add(self.generator_func) # Add the generator to the manager
12
+ return self.manager.generator(self.target_id).target # Return the generator's reference
13
+
14
+ def __exit__(self, exc_type, exc_val, exc_tb):
15
+ with self.manager.lock:
16
+ generator = self.manager.generator(self.target_id)
17
+ if generator and generator.state == Model__Generator_State.RUNNING:
18
+ generator.state = Model__Generator_State.COMPLETED
19
+
20
+
21
+ #self.manager.cleanup()
@@ -0,0 +1,115 @@
1
+ import threading
2
+ from _thread import RLock # Reentrant lock for thread-safe access
3
+ from types import GeneratorType
4
+ from typing import Dict # Typing imports for type hints
5
+ from typing import Union
6
+ from osbot_utils.base_classes.Type_Safe import Type_Safe # Type_Safe base class for type-safe attributes
7
+ from osbot_utils.helpers.Random_Guid import Random_Guid # Helper for generating unique IDs
8
+ from osbot_utils.helpers.generators.Generator_Context_Manager import Generator_Context_Manager
9
+ from osbot_utils.helpers.generators.Model__Generator_State import Model__Generator_State
10
+ from osbot_utils.helpers.generators.Model__Generator_Target import Model__Generator_Target
11
+ from osbot_utils.utils.Lists import list_group_by
12
+
13
+
14
+ class Generator_Manager(Type_Safe): # Class for managing multiple generator targets
15
+ generators: Dict[Random_Guid, Model__Generator_Target] # Dictionary mapping target IDs to generator targets
16
+ lock : RLock = None # Reentrant lock for thread-safe access to shared data
17
+
18
+ def __init__(self, **kwargs): # Constructor method
19
+ super().__init__(**kwargs)
20
+ self.lock = threading.RLock() # return an object of type _thread.RLock
21
+
22
+
23
+ def active(self) -> Dict[Random_Guid, Model__Generator_Target]: # Method to get all active (running) generators
24
+ with self.lock: # Acquire the lock for thread-safe access
25
+ return {k: v for k, v in self.generators.items() if v.state == Model__Generator_State.RUNNING} # Return a dictionary of running generators
26
+
27
+ def add(self, target: GeneratorType) -> Random_Guid: # Method to add a new generator to the manager
28
+ with self.lock: # Acquire the lock for thread-safe access
29
+ existing_target_id = self.target_id(target) # Check if the target already exists
30
+ if existing_target_id: # If the target already exists
31
+ raise ValueError(f"In Generator_Manager.add_generator, " # Raise an error with the ID of the existing target
32
+ f"target already exists with ID: {existing_target_id}")
33
+
34
+ generator = Model__Generator_Target(target=target, state=Model__Generator_State.RUNNING) # Create a new Generator_Target with RUNNING state
35
+ self.generators[generator.target_id] = generator # Add the generator to the dictionary
36
+ return generator.target_id # Return the unique ID of the added generator
37
+
38
+ def capture(self, generator_func: GeneratorType): # Use this method to manage a generator's lifecycle via a context manager.
39
+ return Generator_Context_Manager(self, generator_func)
40
+
41
+ def cleanup(self) -> int: # Method to remove all completed or stopped generators
42
+ with self.lock: # Acquire the lock for thread-safe access
43
+ cleaned_count = 0 # Counter for the number of generators cleaned up
44
+ for target_id in list(self.generators.keys()): # Iterate over the keys of the dictionary
45
+ generator = self.generator(target_id) # Get the generator by its ID
46
+ if generator and generator.state in [Model__Generator_State.STOPPED,
47
+ Model__Generator_State.COMPLETED]: # Check if the generator is stopped or completed
48
+ self.generators.pop(target_id, None) # Remove the generator from the dictionary
49
+ cleaned_count += 1 # Increment the cleaned count
50
+ return cleaned_count # Return the total number of cleaned generators
51
+
52
+ def find_generator(self, target: GeneratorType) -> Model__Generator_Target:
53
+ with self.lock:
54
+ for generator in list(self.generators.values()):
55
+ if generator.target == target:
56
+ return generator
57
+
58
+ def generator(self, target_id: Random_Guid) -> Model__Generator_Target: # Method to get a generator by its ID
59
+ with self.lock: # Acquire the lock for thread-safe access
60
+ return self.generators.get(target_id) # Return the generator or None if it doesn't exist
61
+
62
+ def remove(self, target_id: Random_Guid) -> bool: # Method to remove a generator if it is stopped or completed
63
+ with self.lock: # Acquire the lock for thread-safe access
64
+ generator = self.generator(target_id) # Get the generator by its ID
65
+ if not generator: # If the generator doesn't exist
66
+ return False # Silently return False
67
+ if generator.state in [Model__Generator_State.STOPPED, Model__Generator_State.COMPLETED]: # Check if the generator is in a removable state
68
+ del self.generators[target_id] # Remove the generator from the dictionary
69
+ return True # Return True to indicate successful removal
70
+ return False # Return False if the generator was not removable
71
+
72
+ def should_stop(self, target_id: Random_Guid) -> bool: # Method to check if a generator should stop
73
+ with self.lock: # Acquire the lock for thread-safe access
74
+ generator = self.generator(target_id) # Get the generator by its ID
75
+ if not generator: # If the generator doesn't exist
76
+ raise ValueError(f"In Generator_Manager.should_stop, " # Raise an error indicating missing generator
77
+ f"Generator with ID {target_id} does not exist.")
78
+ return generator.state != Model__Generator_State.RUNNING # Return True if the generator is not running
79
+
80
+ def stop(self, target_id: Random_Guid) -> bool: # Method to stop a running generator
81
+ with self.lock: # Acquire the lock for thread-safe access
82
+ generator = self.generator(target_id) # Get the generator by its ID
83
+ if generator and generator.state == Model__Generator_State.RUNNING: # If the generator is running
84
+ generator.state = Model__Generator_State.STOPPING # Set its state to STOPPING
85
+ return True # Return True to indicate the generator is stopping
86
+ return False # Return False if the generator could not be stopped
87
+
88
+ def stop_all(self) -> int: # Method to stop all running generators
89
+ with self.lock: # Acquire the lock for thread-safe access
90
+ stopped_count = 0 # Counter for the number of generators stopped
91
+ for target_id in list(self.generators.keys()): # Iterate over the keys of the dictionary
92
+ if self.stop(target_id): # Attempt to stop each generator
93
+ stopped_count += 1 # Increment the stopped count if successful
94
+ return stopped_count # Return the total number of stopped generators
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
+
111
+ def target_id(self, target: GeneratorType) -> Union[Random_Guid, None]: # Method to get the ID of a specific generator
112
+ with self.lock: # Acquire the lock for thread-safe access
113
+ for generator in list(self.generators.values()): # Iterate over the generator targets
114
+ if generator.target == target: # Check if the target matches
115
+ return generator.target_id # Return the matching target's ID
@@ -0,0 +1,10 @@
1
+ from enum import Enum
2
+
3
+ class Model__Generator_State(Enum): # Enum representing possible states of a generator
4
+ CREATED : str = "created" # Initial state when the generator is created
5
+ RUNNING : str = "running" # State when the generator is actively running
6
+ STOPPING : str = "stopping" # State when the generator is in the process of stopping
7
+ STOPPED : str = "stopped" # State when the generator is fully stopped
8
+ COMPLETED : str = "completed" # State when the generator has completed its execution
9
+ ERROR : str = "error" # State when the generator encounters an error
10
+
@@ -0,0 +1,10 @@
1
+ from types import GeneratorType
2
+ from osbot_utils.base_classes.Type_Safe import Type_Safe
3
+ from osbot_utils.helpers.Random_Guid import Random_Guid
4
+ from osbot_utils.helpers.generators.Model__Generator_State import Model__Generator_State
5
+
6
+
7
+ class Model__Generator_Target(Type_Safe): # Class representing a generator target and its metadata
8
+ target_id : Random_Guid # Unique ID for the generator target
9
+ target : GeneratorType = None # The generator instance being managed
10
+ state : Model__Generator_State = Model__Generator_State.CREATED # Current state of the generator (default is CREATED)
File without changes
osbot_utils/version CHANGED
@@ -1 +1 @@
1
- v1.80.0
1
+ v1.82.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: osbot_utils
3
- Version: 1.80.0
3
+ Version: 1.82.0
4
4
  Summary: OWASP Security Bot - Utils
5
5
  Home-page: https://github.com/owasp-sbot/OSBot-Utils
6
6
  License: MIT
@@ -23,7 +23,7 @@ Description-Content-Type: text/markdown
23
23
 
24
24
  Powerful Python util methods and classes that simplify common apis and tasks.
25
25
 
26
- ![Current Release](https://img.shields.io/badge/release-v1.80.0-blue)
26
+ ![Current Release](https://img.shields.io/badge/release-v1.82.0-blue)
27
27
  [![codecov](https://codecov.io/gh/owasp-sbot/OSBot-Utils/graph/badge.svg?token=GNVW0COX1N)](https://codecov.io/gh/owasp-sbot/OSBot-Utils)
28
28
 
29
29
 
@@ -2,7 +2,7 @@ osbot_utils/__init__.py,sha256=DdJDmQc9zbQUlPVyTJOww6Ixrn9n4bD3ami5ItQfzJI,16
2
2
  osbot_utils/base_classes/Cache_Pickle.py,sha256=kPCwrgUbf_dEdxUz7vW1GuvIPwlNXxuRhb-H3AbSpII,5884
3
3
  osbot_utils/base_classes/Kwargs_To_Disk.py,sha256=HHoy05NC_w35WcT-OnSKoSIV_cLqaU9rdjH0_KNTM0E,1096
4
4
  osbot_utils/base_classes/Kwargs_To_Self.py,sha256=weFNsBfBNV9W_qBkN-IdBD4yYcJV_zgTxBRO-ZlcPS4,141
5
- osbot_utils/base_classes/Type_Safe.py,sha256=lAWsPoNGoLIrzjJ5xIHP_rlpTmeTP0eXbvnBKGEB1s0,22335
5
+ osbot_utils/base_classes/Type_Safe.py,sha256=ozt5ydM6iHKX-Xkj_h3CJQ0QhUFf91xEWkUL3vANBLU,22454
6
6
  osbot_utils/base_classes/Type_Safe__List.py,sha256=iWyoc2xjHkTJrZTVnPse9Rljte2tF67oNq8yA7jnAhY,5996
7
7
  osbot_utils/base_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  osbot_utils/context_managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -169,6 +169,11 @@ osbot_utils/helpers/flows/models/Flow_Run__Event.py,sha256=-F_H_Tq119qa9tk8IoX5U
169
169
  osbot_utils/helpers/flows/models/Flow_Run__Event_Data.py,sha256=Xvv_vtOL-siSdt5nkRCLA7uHxlsA3bTm7ZD_EOkCVAU,405
170
170
  osbot_utils/helpers/flows/models/Flow_Run__Event_Type.py,sha256=-iHKNQ-Ar2UfiCraFYQNHdBQ9fNcTTRoh3kRpXRS_Gc,375
171
171
  osbot_utils/helpers/flows/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
+ osbot_utils/helpers/generators/Generator_Context_Manager.py,sha256=STpCn2Zh5G2TmyYMjeLMjfpkSYUUeU6mzV_O6Hu4g4Q,863
173
+ osbot_utils/helpers/generators/Generator_Manager.py,sha256=eQzyZVxZjglzPmoxLJ7ib1pD8187YcKBIr7mljpSUF4,11731
174
+ osbot_utils/helpers/generators/Model__Generator_State.py,sha256=cS9tigdBIAvexip2VC8B0meegslzlu-cOosTDiEpe54,903
175
+ osbot_utils/helpers/generators/Model__Generator_Target.py,sha256=JKa_PEQeVNZDSMBAobADaor4zXAhM_XWPHrQ2NMApZs,819
176
+ osbot_utils/helpers/generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
177
  osbot_utils/helpers/html/Dict_To_Css.py,sha256=u6B4Mx7PXr-gDrTrs1hgknnvsZVK4Fic5LqedKjo-lk,1097
173
178
  osbot_utils/helpers/html/Dict_To_Html.py,sha256=OlRSaDGOeseBNTxRB2ho5whqEacMXeAXWOfeVSEYqC4,3355
174
179
  osbot_utils/helpers/html/Dict_To_Tags.py,sha256=L8O8c0RPzP92EfeACk3pjXJfnlz-Rg38o2Gf9tS2UfM,3745
@@ -306,8 +311,8 @@ osbot_utils/utils/Toml.py,sha256=-_Yv5T8ZhGGoDSSoNEdFhSsXiK_JPjGkPijm4JoeHSk,166
306
311
  osbot_utils/utils/Version.py,sha256=Ww6ChwTxqp1QAcxOnztkTicShlcx6fbNsWX5xausHrg,422
307
312
  osbot_utils/utils/Zip.py,sha256=pR6sKliUY0KZXmqNzKY2frfW-YVQEVbLKiyqQX_lc-8,14052
308
313
  osbot_utils/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
309
- osbot_utils/version,sha256=kZW8YcxtUgv8JJ5SMq_RAgJKr-SRwTryyrVMJjtd39o,8
310
- osbot_utils-1.80.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
311
- osbot_utils-1.80.0.dist-info/METADATA,sha256=GIgAeq1697iuS-tNNl4zTwOz4xA3-GzE2cX__pZc0fg,1317
312
- osbot_utils-1.80.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
313
- osbot_utils-1.80.0.dist-info/RECORD,,
314
+ osbot_utils/version,sha256=e35OcH4nQ1oWf1FG09fvfF_3Ok_im-bbOEnUGv2vOi0,8
315
+ osbot_utils-1.82.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
316
+ osbot_utils-1.82.0.dist-info/METADATA,sha256=xKqrIfMr1ibNv8sycCvTDPeSYEHhChKdpwk-ioqvzFs,1317
317
+ osbot_utils-1.82.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
318
+ osbot_utils-1.82.0.dist-info/RECORD,,