def __getattr__(self, name): """ Any non-__pydyty_* attributes will be routed to here. There are methods and fields, which are distinguished by checking '__call__' attribute on the attribute being routed for.""" def __method_missing__(*args, **kwargs): """ This nested function is invoked when the attribute (method) is invoked. It records the types of the arguments, runs the original method, records the return type, and returns the actual result of the original method call.""" # Try to get the caller information loc = Location.create(traceback.extract_stack()[-2]) # Get types of the arguments arg_types = [] for arg in args: if hasattr(arg, "__pydyty__"): arg_types.append(arg.__pydyty_type__) else: arg_types.append(types.NominalType(arg, is_object=True, loc=loc)) # Get types of the dictionary arguments kwarg_types = {} for kwarg_name, kwarg_val in kwargs.iteritems(): if hasattr(kwarg_val, '__pydyty__'): kwarg_type = kwarg_val.__pydyty_type__ else: kwarg_type = types.NominalType(kwarg_val, is_object=True, loc=loc) kwarg_types[kwarg_name] = kwarg_type # Lastly, return type. Run the original method first. ret_val = attr(*args, **kwargs) ret_type = types.NominalType(ret_val, is_object=True, loc=loc) method_type = types.MethodType(arg_types, kwarg_types, ret_type, loc=loc) self.__pydyty_type__.add_attr(name, method_type) return ret_val # Two possibilities: method and field # If it's a method, return __method_missing__ which will be executed # at the method invocation. If it's a field, get the type and return # the value. attr = getattr(self.__pydyty_obj__, name) if hasattr(attr, '__call__'): return __method_missing__ # Try to get the caller information loc = Location.create(traceback.extract_stack()[-1]) self.__pydyty_type__.add_attr( name, types.NominalType(attr, is_object=True, loc=loc)) return attr
def __add__(self, other): """ Strip off the argument and apply the original operator. Unless __add__ is overriden, it is NOT possible to wrap the argument. So do not wrap it. We will just NOT support structural type for the argument for now. """ if hasattr(other, "__pydyty__"): other_obj = other.__pydyty_obj__ else: other_obj = other ret_val = self.__pydyty_obj__ + other_obj if not isinstance(self.__pydyty_type__, types.NominalType): # Try to get the caller information loc = Location.create(traceback.extract_stack()[-1]) arg_types = [types.NominalType(other, is_object=True, loc=loc)] ret_type = types.NominalType(ret_val, is_object=True, loc=loc) method_type = types.MethodType(arg_types, {}, ret_type, loc=loc) self.__pydyty_type__.add_attr("__add__", method_type) return ret_val
def __method_missing__(*args, **kwargs): """ This nested function is invoked when the attribute (method) is invoked. It records the types of the arguments, runs the original method, records the return type, and returns the actual result of the original method call.""" # Try to get the caller information loc = Location.create(traceback.extract_stack()[-2]) # Get types of the arguments arg_types = [] for arg in args: if hasattr(arg, "__pydyty__"): arg_types.append(arg.__pydyty_type__) else: arg_types.append(types.NominalType(arg, is_object=True, loc=loc)) # Get types of the dictionary arguments kwarg_types = {} for kwarg_name, kwarg_val in kwargs.iteritems(): if hasattr(kwarg_val, '__pydyty__'): kwarg_type = kwarg_val.__pydyty_type__ else: kwarg_type = types.NominalType(kwarg_val, is_object=True, loc=loc) kwarg_types[kwarg_name] = kwarg_type # Lastly, return type. Run the original method first. ret_val = attr(*args, **kwargs) ret_type = types.NominalType(ret_val, is_object=True, loc=loc) method_type = types.MethodType(arg_types, kwarg_types, ret_type, loc=loc) self.__pydyty_type__.add_attr(name, method_type) return ret_val