# HG changeset patch # User Paul Boddie # Date 1481323907 -3600 # Node ID 209dc7a270fd15b8c27e2e99f2bab706122f387e # Parent 65ff81f922d309b5995114e5951a4c37312f5714 Added support for dynamic attribute access using getattr and hasattr, employing a special attribute on strings to hold the object table code and position for any attribute having the same name as the represented string. diff -r 65ff81f922d3 -r 209dc7a270fd generator.py --- a/generator.py Fri Dec 09 23:30:41 2016 +0100 +++ b/generator.py Fri Dec 09 23:51:47 2016 +0100 @@ -56,6 +56,7 @@ # NOTE: These must be synchronised with the library. function_type = "__builtins__.core.function" + string_type = "__builtins__.str.string" type_type = "__builtins__.core.type" predefined_constant_members = ( @@ -491,6 +492,14 @@ if data is not None: attrs["__data__"] = data + # Also set a key for dynamic attribute lookup, if a string. + + if cls == self.string_type: + if data in self.optimiser.all_attrnames: + attrs["__key__"] = data + else: + attrs["__key__"] = None + # Define the structure details. An object is created for the constant, # but an attribute is provided, referring to the object, for access to # the constant in the program. @@ -871,6 +880,13 @@ encode_literal_constant_value(attr))) continue + # Special internal key member. + + elif attrname == "__key__": + structure.append("{.code=%s, .pos=%s}" % (attr and encode_symbol("code", attr) or "0", + attr and encode_symbol("pos", attr) or "0")) + continue + # Special cases. elif attrname in ("__file__", "__fname__", "__mname__", "__name__"): diff -r 65ff81f922d3 -r 209dc7a270fd lib/__builtins__/attribute.py --- a/lib/__builtins__/attribute.py Fri Dec 09 23:30:41 2016 +0100 +++ b/lib/__builtins__/attribute.py Fri Dec 09 23:51:47 2016 +0100 @@ -19,19 +19,11 @@ this program. If not, see . """ +from __builtins__.types import check_string from native import object_getattr _default=object() # a unique placeholder for a missing value -def _getattr(obj, name, default=_default): - - """ - Return for 'obj' the attribute having the given 'name', returning the given - 'default' if the attribute is not defined for 'obj'. - """ - - return object_getattr(obj, name, default) - def getattr(obj, name, default=_default): """ @@ -40,13 +32,27 @@ 'default' is not indicated and the attribute is not defined. """ - result = _getattr(obj, name, default) + check_string(name) + + # Attempt to obtain the attribute. If the name is not recognised as an + # attribute name, the default will be returned. Otherwise, an access + # operation will be attempted. + + try: + result = object_getattr(obj, name, default) + + # Handle exceptions when the access operation fails. + + except TypeError: + result = _default + + # Check the result and, if it is the placeholder value, raise an exception. if result is _default: - if default is _default: - raise AttributeError(name) - else: - return default + raise AttributeError(name) + + # Otherwise, return the obtained value or supplied default. + else: return result @@ -54,8 +60,15 @@ "Return whether 'obj' has an attribute called 'name'." - result = _getattr(obj, name) - return result is not _default + try: + getattr(obj, name) + except AttributeError: + return False + else: + return True + +# NOTE: setattr would probably only be supported on instances due to deductions +# NOTE: applying to static objects being undermined by dynamic modifications. def setattr(obj, name, value): pass diff -r 65ff81f922d3 -r 209dc7a270fd lib/__builtins__/str.py --- a/lib/__builtins__/str.py Fri Dec 09 23:30:41 2016 +0100 +++ b/lib/__builtins__/str.py Fri Dec 09 23:51:47 2016 +0100 @@ -42,6 +42,12 @@ self.__data__ = None + # Note the __key__ member. This is also initialised statically. Where + # a string is the same as an attribute name, the __key__ member contains + # attribute position and code details. + + self.__key__ = None + def __hash__(self): "Return a value for hashing purposes." diff -r 65ff81f922d3 -r 209dc7a270fd templates/native/common.c --- a/templates/native/common.c Fri Dec 09 23:30:41 2016 +0100 +++ b/templates/native/common.c Fri Dec 09 23:51:47 2016 +0100 @@ -36,9 +36,10 @@ __attr __new_str(char *s) { - /* Create a new string and mutate the __data__ attribute. */ + /* Create a new string and mutate the __data__ and __key__ attributes. */ __attr attr = __new(&__InstanceTable___builtins___str_string, &__builtins___str_string, sizeof(__obj___builtins___str_string)); attr.value->attrs[__pos___data__].strvalue = s; + attr.value->attrs[__pos___key__] = (__attr) {0, 0}; return attr; } diff -r 65ff81f922d3 -r 209dc7a270fd templates/native/introspection.c --- a/templates/native/introspection.c Fri Dec 09 23:30:41 2016 +0100 +++ b/templates/native/introspection.c Fri Dec 09 23:51:47 2016 +0100 @@ -31,9 +31,13 @@ __attr * const obj = &__args[1]; __attr * const name = &__args[2]; __attr * const _default = &__args[3]; + /* name.__data__ interpreted as string */ + __attr key = __load_via_object(name->value, __pos___key__); - /* NOTE: To be written. */ - return __builtins___none_None; + if ((key.code == 0) && (key.pos == 0)) + return *_default; + else + return __check_and_load_via_any(obj->value, key.pos, key.code); } static int __issubclass(__ref obj, __attr cls) diff -r 65ff81f922d3 -r 209dc7a270fd templates/types.h --- a/templates/types.h Fri Dec 09 23:30:41 2016 +0100 +++ b/templates/types.h Fri Dec 09 23:51:47 2016 +0100 @@ -57,6 +57,7 @@ __obj * context; /* attribute context */ unsigned int min; /* minimum number of parameters */ __obj * b; /* bound callable object */ + unsigned int code; /* parameter table code for key */ }; /* One of... */ @@ -64,6 +65,7 @@ { __obj * value; /* attribute value */ const __ptable * ptable;/* parameter table */ + unsigned int pos; /* parameter table position for key */ struct __attr (*fn)(); /* callable details */ int intvalue; /* integer value */ diff -r 65ff81f922d3 -r 209dc7a270fd tests/getattr.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/getattr.py Fri Dec 09 23:51:47 2016 +0100 @@ -0,0 +1,38 @@ +class C: + def __init__(self): + self.x = 1 + self.y = 2 + self.z = 3 + +class D: + def __init__(self): + self.x = 4 + self.y = 5 + self.z = 6 + +c = C() +d = D() + +attrnames = ["a", "b", "c", "x", "y", "z"] + +print ". c d" + +for attrname in attrnames: + print attrname, hasattr(c, attrname) and "1" or "0", hasattr(d, attrname) and "1" or "0" + +print +print ". c d" + +for attrname in attrnames: + print attrname, + try: + v = getattr(c, attrname) + print "", v, + except AttributeError: + print " ?", + + try: + v = getattr(d, attrname) + print "", v + except AttributeError: + print " ?"