From 7d13fc22a7ee41adc46b6d968d620f4cd6ee9800 Mon Sep 17 00:00:00 2001 From: Dinis Cruz Date: Sun, 12 Jan 2025 01:20:47 +0000 Subject: [PATCH] removed Type_Safe 'auto feature' of trying to find get_ and set_ methods (this is better done outside of this class) --- osbot_utils/type_safe/Type_Safe.py | 34 ++++----- tests/unit/type_safe/test_Type_Safe.py | 99 +++++++++++++------------- 2 files changed, 67 insertions(+), 66 deletions(-) diff --git a/osbot_utils/type_safe/Type_Safe.py b/osbot_utils/type_safe/Type_Safe.py index c29209e3..203a3fa5 100644 --- a/osbot_utils/type_safe/Type_Safe.py +++ b/osbot_utils/type_safe/Type_Safe.py @@ -65,23 +65,23 @@ def __init__(self, **kwargs): def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): pass - def __getattr__(self, name): # Called when an attribute is not found through normal attribute access - if name.startswith(("set_", "get_")): # Check if the requested attribute is a getter or setter method - prefix = name[:4] # Extract "set_" or "get_" from the method name - attr_name = name[4:] # Get the actual attribute name by removing the prefix - - if hasattr(self, attr_name): # Verify that the target attribute actually exists on the object - if prefix == "set_": # Handle setter method creation - def setter(value): # Create a dynamic setter function that takes a value parameter - setattr(self, attr_name, value) # Set the attribute value using type-safe setattr from Type_Safe - return self # Return self for method chaining - return setter # Return the setter function - else: # get_ # Handle getter method creation - def getter(): # Create a dynamic getter function with no parameters - return getattr(self, attr_name) # Return the attribute value using Python's built-in getattr - return getter # Return the getter function - - raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") # Raise error if attribute is not a valid getter/setter + # def __getattr__(self, name): # Called when an attribute is not found through normal attribute access + # if name.startswith(("set_", "get_")): # Check if the requested attribute is a getter or setter method + # prefix = name[:4] # Extract "set_" or "get_" from the method name + # attr_name = name[4:] # Get the actual attribute name by removing the prefix + # + # if hasattr(self, attr_name): # Verify that the target attribute actually exists on the object + # if prefix == "set_": # Handle setter method creation + # def setter(value): # Create a dynamic setter function that takes a value parameter + # setattr(self, attr_name, value) # Set the attribute value using type-safe setattr from Type_Safe + # return self # Return self for method chaining + # return setter # Return the setter function + # else: # get_ # Handle getter method creation + # def getter(): # Create a dynamic getter function with no parameters + # return getattr(self, attr_name) # Return the attribute value using Python's built-in getattr + # return getter # Return the getter function + # + # raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") # Raise error if attribute is not a valid getter/setter def __setattr__(self, name, value): from osbot_utils.utils.Objects import convert_dict_to_value_from_obj_annotation diff --git a/tests/unit/type_safe/test_Type_Safe.py b/tests/unit/type_safe/test_Type_Safe.py index e7e25e12..bec4711c 100644 --- a/tests/unit/type_safe/test_Type_Safe.py +++ b/tests/unit/type_safe/test_Type_Safe.py @@ -616,55 +616,56 @@ class Target_Class(Base_Class): merged_class.an_int= 456 # confirm that change in merged_class assert base_class.an_int == 456 # impacts base_class - def test___supports_automatic_getters_and_setters_for_attributes(self): - class An_Class(Type_Safe): - an_str : str - an_int : int - an_list : list - _private : str # Test private attribute behavior - - an_class = An_Class() - - # Test basic getter/setter functionality - assert an_class.set_an_str('abc') == an_class - assert an_class.get_an_str() == 'abc' - assert an_class.json() == {'an_int': 0, 'an_list': [], 'an_str': 'abc', '_private': ''} - - # Test method chaining - assert an_class.set_an_int(123).set_an_str('def').get_an_str() == 'def' - assert an_class.get_an_int() == 123 - - # Test list attribute - test_list = [1, 2, 3] - assert an_class.set_an_list(test_list) == an_class - assert an_class.get_an_list() == test_list - - # Test None assignments - with pytest.raises(ValueError, match="Can't set None, to a variable that is already set. Invalid type for attribute 'an_str'. Expected '' but got ''"): - assert an_class.set_an_str(None) == an_class - assert an_class.get_an_str() == 'def' # confirm value has not been changed - - # Test private attribute access - assert an_class.set__private("secret") == an_class - assert an_class.get__private() == "secret" - - # Test error cases - with pytest.raises(AttributeError, match="'An_Class' object has no attribute 'set_an_aaa'"): - an_class.set_an_aaa() - with pytest.raises(AttributeError, match="'An_Class' object has no attribute 'get_an_aaa'"): - an_class.get_an_aaa() - with pytest.raises(AttributeError, match="'An_Class' object has no attribute 'aaaaaaaaaa'"): - an_class.aaaaaaaaaa() - with pytest.raises(ValueError, match="Invalid type for attribute 'an_str'. Expected '' but got '"): - an_class.set_an_str(123) - with pytest.raises(ValueError, match="Invalid type for attribute 'an_int'. Expected '' but got '"): - an_class.set_an_int('abc') - - # Test edge cases - with pytest.raises(AttributeError): - an_class.get_() # Empty attribute name - with pytest.raises(AttributeError): - an_class.set_() # Empty attribute name + # not supported anymore (it was a good idea, but this is better done with set_as_property) + # def test___supports_automatic_getters_and_setters_for_attributes(self): + # class An_Class(Type_Safe): + # an_str : str + # an_int : int + # an_list : list + # _private : str # Test private attribute behavior + # + # an_class = An_Class() + # + # # Test basic getter/setter functionality + # assert an_class.set_an_str('abc') == an_class + # assert an_class.get_an_str() == 'abc' + # assert an_class.json() == {'an_int': 0, 'an_list': [], 'an_str': 'abc', '_private': ''} + # + # # Test method chaining + # assert an_class.set_an_int(123).set_an_str('def').get_an_str() == 'def' + # assert an_class.get_an_int() == 123 + # + # # Test list attribute + # test_list = [1, 2, 3] + # assert an_class.set_an_list(test_list) == an_class + # assert an_class.get_an_list() == test_list + # + # # Test None assignments + # with pytest.raises(ValueError, match="Can't set None, to a variable that is already set. Invalid type for attribute 'an_str'. Expected '' but got ''"): + # assert an_class.set_an_str(None) == an_class + # assert an_class.get_an_str() == 'def' # confirm value has not been changed + # + # # Test private attribute access + # assert an_class.set__private("secret") == an_class + # assert an_class.get__private() == "secret" + # + # # Test error cases + # with pytest.raises(AttributeError, match="'An_Class' object has no attribute 'set_an_aaa'"): + # an_class.set_an_aaa() + # with pytest.raises(AttributeError, match="'An_Class' object has no attribute 'get_an_aaa'"): + # an_class.get_an_aaa() + # with pytest.raises(AttributeError, match="'An_Class' object has no attribute 'aaaaaaaaaa'"): + # an_class.aaaaaaaaaa() + # with pytest.raises(ValueError, match="Invalid type for attribute 'an_str'. Expected '' but got '"): + # an_class.set_an_str(123) + # with pytest.raises(ValueError, match="Invalid type for attribute 'an_int'. Expected '' but got '"): + # an_class.set_an_int('abc') + # + # # Test edge cases + # with pytest.raises(AttributeError): + # an_class.get_() # Empty attribute name + # with pytest.raises(AttributeError): + # an_class.set_() # Empty attribute name def test__type_assignments_and_validation(self): # Test simple type assignment with 'type' annotation class Simple_Type(Type_Safe):