osbot-utils 2.2.0__py3-none-any.whl → 2.4.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,5 +1,4 @@
1
1
  import uuid
2
-
3
2
  from osbot_utils.utils.Misc import is_guid
4
3
 
5
4
  GUID__NAMESPACE = uuid.UUID('2cfec064-537a-4ff7-8fdc-2fc9e2606f3d')
@@ -142,7 +142,7 @@ class Type_Safe:
142
142
  from enum import EnumMeta
143
143
  from osbot_utils.utils.Objects import obj_is_type_union_compatible
144
144
 
145
- IMMUTABLE_TYPES = (bool, int, float, complex, str, tuple, frozenset, bytes, NoneType, EnumMeta)
145
+ IMMUTABLE_TYPES = (bool, int, float, complex, str, tuple, frozenset, bytes, NoneType, EnumMeta, type)
146
146
 
147
147
 
148
148
  kwargs = {}
@@ -172,7 +172,15 @@ class Type_Safe:
172
172
  if var_value is not None: # allow None assignments on ctor since that is a valid use case
173
173
  if get_origin(var_type) is Annotated:
174
174
  continue
175
- if var_type and not isinstance(var_value, var_type): # check type
175
+ if get_origin(var_type) is type: # Special handling for Type[T]
176
+ if not isinstance(var_value, type):
177
+ exception_message = f"variable '{var_name}' is defined as Type[T] but has value '{var_value}' which is not a type"
178
+ raise ValueError(exception_message)
179
+ type_arg = get_args(var_type)[0]
180
+ if not issubclass(var_value, type_arg):
181
+ exception_message = f"variable '{var_name}' is defined as {var_type} but value {var_value} is not a subclass of {type_arg}"
182
+ raise ValueError(exception_message)
183
+ elif var_type and not isinstance(var_value, var_type): # check type
176
184
  exception_message = f"variable '{var_name}' is defined as type '{var_type}' but has value '{var_value}' of type '{type(var_value)}'"
177
185
  raise ValueError(exception_message)
178
186
  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
@@ -1,15 +1,22 @@
1
1
  import inspect # For function introspection
2
2
  from enum import Enum
3
- from typing import get_args, get_origin, Optional, Union, List, Any, Dict # For type hinting utilities
3
+ from typing import get_args, get_origin, Union, List, Any # For type hinting utilities
4
4
 
5
5
 
6
6
  class Type_Safe_Method: # Class to handle method type safety validation
7
7
  def __init__(self, func): # Initialize with function
8
8
  self.func = func # Store original function
9
9
  self.sig = inspect.signature(func) # Get function signature
10
- self.annotations = func.__annotations__ # Get type annotations
10
+ self.annotations = func.__annotations__ # Get function annotations
11
+
12
+ def check_for_any_use(self):
13
+ for param_name, type_hint in self.annotations.items():
14
+ if type_hint is any: # Detect incorrect usage of lowercase any
15
+ raise ValueError(f"Parameter '{param_name}' uses lowercase 'any' instead of 'Any' from typing module. "
16
+ f"Please use 'from typing import Any' and annotate as '{param_name}: Any'")
11
17
 
12
18
  def handle_type_safety(self, args: tuple, kwargs: dict): # Main method to handle type safety
19
+ self.check_for_any_use()
13
20
  bound_args = self.bind_args(args, kwargs) # Bind arguments to parameters
14
21
  for param_name, param_value in bound_args.arguments.items(): # Iterate through arguments
15
22
  if param_name != 'self': # Skip self parameter
@@ -107,5 +114,6 @@ class Type_Safe_Method:
107
114
 
108
115
  def validate_direct_type(self, param_name: str, # Validate direct type match
109
116
  param_value: Any, expected_type: Any): # Type parameters
110
- if not isinstance(param_value, expected_type): # Check type match
111
- raise ValueError(f"Parameter '{param_name}' expected type {expected_type}, but got {type(param_value)}") # Raise error if no match
117
+ if expected_type is not Any:
118
+ if not isinstance(param_value, expected_type): # Check type match
119
+ raise ValueError(f"Parameter '{param_name}' expected type {expected_type}, but got {type(param_value)}") # Raise error if no match
File without changes
@@ -25,7 +25,7 @@ if sys.version_info < (3, 8):
25
25
  else:
26
26
  return ()
27
27
  else:
28
- from typing import get_origin, get_args, List, Tuple, Dict
28
+ from typing import get_origin, get_args, List, Tuple, Dict, Type, _GenericAlias
29
29
 
30
30
 
31
31
  def are_types_compatible_for_assigment(source_type, target_type):
@@ -128,6 +128,16 @@ def convert_to_value_from_obj_annotation(target, attr_name, value):
128
128
  if hasattr(obj_annotations,'get'):
129
129
  attribute_annotation = obj_annotations.get(attr_name)
130
130
  if attribute_annotation:
131
+ origin = get_origin(attribute_annotation) # Add handling for Type[T] annotations
132
+ if origin is type and isinstance(value, str):
133
+ try: # Convert string path to actual type
134
+ if len(value.rsplit('.', 1)) > 1:
135
+ module_name, class_name = value.rsplit('.', 1)
136
+ module = __import__(module_name, fromlist=[class_name])
137
+ return getattr(module, class_name)
138
+ except (ValueError, ImportError, AttributeError) as e:
139
+ raise ValueError(f"Could not convert '{value}' to type: {str(e)}")
140
+
131
141
  if attribute_annotation in TYPE_SAFE__CONVERT_VALUE__SUPPORTED_TYPES: # for now hard-coding this to just these types until we understand the side effects
132
142
  return attribute_annotation(value)
133
143
  return value
@@ -373,6 +383,8 @@ def obj_is_type_union_compatible(var_type, compatible_types):
373
383
  from typing import Union
374
384
 
375
385
  origin = get_origin(var_type)
386
+ if isinstance(var_type, _GenericAlias) and origin is type: # Add handling for Type[T]
387
+ return type in compatible_types # Allow if 'type' is in compatible types
376
388
  if origin is Union: # For Union types, including Optionals
377
389
  args = get_args(var_type) # Get the argument types
378
390
  for arg in args: # Iterate through each argument in the Union
@@ -454,8 +466,11 @@ def value_type_matches_obj_annotation_for_attr(target, attr_name, value):
454
466
  annotations = all_annotations(target)
455
467
  attr_type = annotations.get(attr_name)
456
468
  if attr_type:
457
- origin_attr_type = get_origin(attr_type) # to handle when type definition contains a generic
458
- if origin_attr_type is Annotated: # if the type is Annotated
469
+ origin_attr_type = get_origin(attr_type) # to handle when type definition contains a generic
470
+ if origin_attr_type is type: # Add handling for Type[T]
471
+ type_arg = get_args(attr_type)[0] # Get T from Type[T]
472
+ return isinstance(value, type) and issubclass(value, type_arg) # Check that value is a type and is subclass of type_arg
473
+ if origin_attr_type is Annotated: # if the type is Annotated
459
474
  args = get_args(attr_type)
460
475
  origin_attr_type = args[0]
461
476
 
osbot_utils/version CHANGED
@@ -1 +1 @@
1
- v2.2.0
1
+ v2.4.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: osbot_utils
3
- Version: 2.2.0
3
+ Version: 2.4.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-v2.2.0-blue)
26
+ ![Current Release](https://img.shields.io/badge/release-v2.4.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
 
@@ -29,7 +29,6 @@ osbot_utils/decorators/methods/function_type_check.py,sha256=o8Je6k-tXd3UmbgVL0g
29
29
  osbot_utils/decorators/methods/obj_as_context.py,sha256=59JgxUvEruh_4ROoWHXK22Jv8C4_E5mSxb4aG3JbL50,104
30
30
  osbot_utils/decorators/methods/remove_return_value.py,sha256=EerRwBFDJ3ICKA0BrNzelRECb3n2NUMoFzcn0rQyCUY,1162
31
31
  osbot_utils/decorators/methods/required_fields.py,sha256=PZd9LKd4qBeqxgP9FgTtLNNJ0coZx99hFJyNEKmF1K0,783
32
- osbot_utils/decorators/methods/type_safe.py,sha256=yK1td-9WHFj2pe4dvBtw1cjYOE6z6-VnG5DCStGCO1c,1003
33
32
  osbot_utils/fluent/Fluent_Dict.py,sha256=nZ2z91s39sU2a-TLYpBirRoWgDXHrs0tQ9Bi_ZdKeW0,481
34
33
  osbot_utils/fluent/Fluent_List.py,sha256=PfDDC9sm16CFnNQ8gkhCEsmKcZp8iyQ0YBpSHvYssG8,1089
35
34
  osbot_utils/fluent/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
@@ -37,7 +36,7 @@ osbot_utils/helpers/CFormat.py,sha256=QviXlx3cQF7Wq6bNQmvWPHknDDMQXg5bh5dsRncrg-
37
36
  osbot_utils/helpers/CPrint.py,sha256=ztKPNmT8BGxeyPXSQKRs63PqqbgxKDz_BiZmzFMup9g,1413
38
37
  osbot_utils/helpers/Dependency_Manager.py,sha256=79YRYnVfchewq8iSMJ5dzwW2D5u8chWcIqYE-G9YrSo,1337
39
38
  osbot_utils/helpers/Dict_To_Attr.py,sha256=NdhXl5mJH7-NaBk213amzc5Nfy3tJgW-N_uYIRE4hoc,208
40
- osbot_utils/helpers/Guid.py,sha256=fqiCYHrYff3yZ_Df-WJdXHl1RQtJHb4oCmDkJpcGN_k,828
39
+ osbot_utils/helpers/Guid.py,sha256=0Ay3TYYk2nPr-JRVRCMFxbr8OvoQomv5HjT7o5B7cos,827
41
40
  osbot_utils/helpers/Hashicorp_Secrets.py,sha256=e2fWWHK6bubpAm1sw5y8X5kh2Hk5d4JyZCnUovZip5A,4232
42
41
  osbot_utils/helpers/Local_Cache.py,sha256=0JZZX3fFImcwtbBvxAQl-EbBegSNJRhRMYF6ovTH6zY,3141
43
42
  osbot_utils/helpers/Local_Caches.py,sha256=aQmi1HSM0TH6WQPedG2fbz4KCCJ3DQTU9d18rB1jR0M,1885
@@ -278,13 +277,15 @@ osbot_utils/testing/Temp_Zip_In_Memory.py,sha256=ibDIWt3K4CX558PbkLbX3InHyWYZcwQ
278
277
  osbot_utils/testing/Unit_Test.py,sha256=MReR_wDGbbXFDPz7cmuGflcTxRB6TBnO9mYqRpSq8Pk,1304
279
278
  osbot_utils/testing/Unzip_File.py,sha256=V5H97XO9rlvG5EYOSzAH4kTtAH1ohZ8R8ImvJh46ZNg,1177
280
279
  osbot_utils/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
281
- osbot_utils/type_safe/Type_Safe.py,sha256=irrNiwWQHV4eP2dq9aKYDIETsG375U9U055YPZX1uQw,29658
282
- osbot_utils/type_safe/Type_Safe_Method.py,sha256=8E88of__An9_ZhJKz6Kp22C1mb9WLED0jWNLOII3fJs,10489
280
+ osbot_utils/type_safe/Type_Safe.py,sha256=asybUt2JqDP34b7s6clCcurJpaJg_4oOI4Qt10PQ2bM,30419
281
+ osbot_utils/type_safe/Type_Safe_Method.py,sha256=JLB7NkeuY4x_lIWJyvG1bDApCM_VyAQqrzQXhH7Z-8A,10969
283
282
  osbot_utils/type_safe/Type_Safe__Base.py,sha256=mL8GMaR9tsaUfwk8d-8zp2g-A8kNKiN6kroEFaNvMOk,5518
284
283
  osbot_utils/type_safe/Type_Safe__Dict.py,sha256=I_Ac0JH-ahmQrkADFVyiobTlH1JI31MKHaNszQW4PBo,2396
285
284
  osbot_utils/type_safe/Type_Safe__List.py,sha256=SzSIBkwSOAEpW_V2qh4-f0YHzmgB0T8PczBLbDgZGvg,1340
286
285
  osbot_utils/type_safe/Type_Safe__Validator.py,sha256=cJIPSBarjV716SZUOLvz7Mthjk-aUYKUQtRDtKUBmN4,779
287
286
  osbot_utils/type_safe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
287
+ osbot_utils/type_safe/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
288
+ osbot_utils/type_safe/decorators/type_safe.py,sha256=yK1td-9WHFj2pe4dvBtw1cjYOE6z6-VnG5DCStGCO1c,1003
288
289
  osbot_utils/type_safe/validators/Validator__Max.py,sha256=ctsMADDqq9l9mxwedrOfI9Dg53nPxCsluB0cO8C6bT4,1264
289
290
  osbot_utils/type_safe/validators/Validator__Min.py,sha256=FfVoErZcibqYXvnH-Nl9c2pfPSKFH-CgAhUVBYsxVrc,1970
290
291
  osbot_utils/type_safe/validators/Validator__One_Of.py,sha256=RvEYjf2zT06LVucZeNhzFqTZOh-Bs-C9UNjXd_YLWa8,670
@@ -304,7 +305,7 @@ osbot_utils/utils/Json.py,sha256=0t7Hwefx8bg4JiZVr-xIbWP3BAk6_ZsnY7iV5pnRLDQ,713
304
305
  osbot_utils/utils/Json_Cache.py,sha256=mLPkkDZN-3ZVJiDvV1KBJXILtKkTZ4OepzOsDoBPhWg,2006
305
306
  osbot_utils/utils/Lists.py,sha256=tPz5x5s3sRO97WZ_nsxREBPC5cwaHrhgaYBhsrffTT8,5599
306
307
  osbot_utils/utils/Misc.py,sha256=H_xexJgiTxB3jDeDiW8efGQbO0Zuy8MM0iQ7qXC92JI,17363
307
- osbot_utils/utils/Objects.py,sha256=7QS_rBiLMLiD28be_br-7NrLfR7LtxFImjUGko8MqXM,21933
308
+ osbot_utils/utils/Objects.py,sha256=F3H0ux-IjfyMIA1ou-xc0Ra1Z7oON3fuYRH_qaGsQ4Y,23375
308
309
  osbot_utils/utils/Png.py,sha256=V1juGp6wkpPigMJ8HcxrPDIP4bSwu51oNkLI8YqP76Y,1172
309
310
  osbot_utils/utils/Process.py,sha256=lr3CTiEkN3EiBx3ZmzYmTKlQoPdkgZBRjPulMxG-zdo,2357
310
311
  osbot_utils/utils/Python_Logger.py,sha256=M9Oi62LxfnRSlCN8GhaiwiBINvcSdGy39FCWjyDD-Xg,12792
@@ -316,8 +317,8 @@ osbot_utils/utils/Toml.py,sha256=Rxl8gx7mni5CvBAK-Ai02EKw-GwtJdd3yeHT2kMloik,166
316
317
  osbot_utils/utils/Version.py,sha256=Ww6ChwTxqp1QAcxOnztkTicShlcx6fbNsWX5xausHrg,422
317
318
  osbot_utils/utils/Zip.py,sha256=pR6sKliUY0KZXmqNzKY2frfW-YVQEVbLKiyqQX_lc-8,14052
318
319
  osbot_utils/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
319
- osbot_utils/version,sha256=uVurYQGLXTbYEagaxFAAZWDtcu72lhVJSfloDcK8tAU,7
320
- osbot_utils-2.2.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
321
- osbot_utils-2.2.0.dist-info/METADATA,sha256=k3jcHtdqYaJdLQDq7UJ_l2cIy0O3V6yj7rDUwkz54KE,1315
322
- osbot_utils-2.2.0.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
323
- osbot_utils-2.2.0.dist-info/RECORD,,
320
+ osbot_utils/version,sha256=p1zoKgZOpZRdZ0kkLKwgWGFUdRL6IoXVo1tPBL0f9tY,7
321
+ osbot_utils-2.4.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
322
+ osbot_utils-2.4.0.dist-info/METADATA,sha256=M41QEpJLyE3h5hjXubqfqshQ7-wn0ioHXjgt7AKFuOU,1315
323
+ osbot_utils-2.4.0.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
324
+ osbot_utils-2.4.0.dist-info/RECORD,,