"""This module trys to support builtin types and functions.""" import inspect import io try: raw_input except NameError: raw_input = input import rope.base.evaluate from rope.base.utils import pycompat from rope.base import pynames, pyobjects, arguments, utils class BuiltinModule(pyobjects.AbstractModule): def __init__(self, name, pycore=None, initial={}): super(BuiltinModule, self).__init__() self.name = name self.pycore = pycore self.initial = initial parent = None def get_attributes(self): return self.attributes def get_doc(self): if self.module: return self.module.__doc__ def get_name(self): return self.name.split(".")[-1] @property @utils.saveit def attributes(self): result = _object_attributes(self.module, self) result.update(self.initial) if self.pycore is not None: submodules = self.pycore._builtin_submodules(self.name) for name, module in submodules.items(): result[name] = rope.base.builtins.BuiltinName(module) return result @property @utils.saveit def module(self): try: result = __import__(self.name) for token in self.name.split(".")[1:]: result = getattr(result, token, None) return result except ImportError: return class _BuiltinElement(object): def __init__(self, builtin, parent=None): self.builtin = builtin self._parent = parent def get_doc(self): if self.builtin: return getattr(self.builtin, "__doc__", None) def get_name(self): if self.builtin: return getattr(self.builtin, "__name__", None) @property def parent(self): if self._parent is None: return builtins return self._parent class BuiltinClass(_BuiltinElement, pyobjects.AbstractClass): def __init__(self, builtin, attributes, parent=None): _BuiltinElement.__init__(self, builtin, parent) pyobjects.AbstractClass.__init__(self) self.initial = attributes @utils.saveit def get_attributes(self): result = _object_attributes(self.builtin, self) result.update(self.initial) return result def get_module(self): return builtins class BuiltinFunction(_BuiltinElement, pyobjects.AbstractFunction): def __init__( self, returned=None, function=None, builtin=None, argnames=[], parent=None ): _BuiltinElement.__init__(self, builtin, parent) pyobjects.AbstractFunction.__init__(self) self.argnames = argnames self.returned = returned self.function = function def get_returned_object(self, args): if self.function is not None: return self.function(_CallContext(self.argnames, args)) else: return self.returned def get_param_names(self, special_args=True): return self.argnames class BuiltinUnknown(_BuiltinElement, pyobjects.PyObject): def __init__(self, builtin): super(BuiltinUnknown, self).__init__(pyobjects.get_unknown()) self.builtin = builtin self.type = pyobjects.get_unknown() def get_name(self): return getattr(type(self.builtin), "__name__", None) @utils.saveit def get_attributes(self): return _object_attributes(self.builtin, self) def _object_attributes(obj, parent): attributes = {} for name in dir(obj): if name == "None": continue try: child = getattr(obj, name) except AttributeError: # descriptors are allowed to raise AttributeError # even if they are in dir() continue pyobject = None if inspect.isclass(child): pyobject = BuiltinClass(child, {}, parent=parent) elif inspect.isroutine(child): pyobject = BuiltinFunction(builtin=child, parent=parent) else: pyobject = BuiltinUnknown(builtin=child) attributes[name] = BuiltinName(pyobject) return attributes def _create_builtin_type_getter(cls): def _get_builtin(*args): if not hasattr(cls, "_generated"): cls._generated = {} if args not in cls._generated: cls._generated[args] = cls(*args) return cls._generated[args] return _get_builtin def _create_builtin_getter(cls): type_getter = _create_builtin_type_getter(cls) def _get_builtin(*args): return pyobjects.PyObject(type_getter(*args)) return _get_builtin class _CallContext(object): def __init__(self, argnames, args): self.argnames = argnames self.args = args def _get_scope_and_pyname(self, pyname): if pyname is not None and isinstance(pyname, pynames.AssignedName): pymodule, lineno = pyname.get_definition_location() if pymodule is None: return None, None if lineno is None: lineno = 1 scope = pymodule.get_scope().get_inner_scope_for_line(lineno) name = None while name is None and scope is not None: for current in scope.get_names(): if scope[current] is pyname: name = current break else: scope = scope.parent return scope, name return None, None def get_argument(self, name): if self.args: args = self.args.get_arguments(self.argnames) return args[self.argnames.index(name)] def get_pyname(self, name): if self.args: args = self.args.get_pynames(self.argnames) if name in self.argnames: return args[self.argnames.index(name)] def get_arguments(self, argnames): if self.args: return self.args.get_arguments(argnames) def get_pynames(self, argnames): if self.args: return self.args.get_pynames(argnames) def get_per_name(self): if self.args is None: return None pyname = self.args.get_instance_pyname() scope, name = self._get_scope_and_pyname(pyname) if name is not None: pymodule = pyname.get_definition_location()[0] return pymodule.pycore.object_info.get_per_name(scope, name) return None def save_per_name(self, value): if self.args is None: return None pyname = self.args.get_instance_pyname() scope, name = self._get_scope_and_pyname(pyname) if name is not None: pymodule = pyname.get_definition_location()[0] pymodule.pycore.object_info.save_per_name(scope, name, value) class _AttributeCollector(object): def __init__(self, type): self.attributes = {} self.type = type def __call__( self, name, returned=None, function=None, argnames=["self"], check_existence=True, parent=None, ): try: builtin = getattr(self.type, name) except AttributeError: if check_existence: raise builtin = None self.attributes[name] = BuiltinName( BuiltinFunction( returned=returned, function=function, argnames=argnames, builtin=builtin, parent=parent, ) ) def __setitem__(self, name, value): self.attributes[name] = value class List(BuiltinClass): def __init__(self, holding=None): self.holding = holding collector = _AttributeCollector(list) collector("__iter__", function=self._iterator_get, parent=self) collector("__new__", function=self._new_list, parent=self) # Adding methods collector( "append", function=self._list_add, argnames=["self", "value"], parent=self ) collector( "__setitem__", function=self._list_add, argnames=["self", "index", "value"], parent=self, ) collector( "insert", function=self._list_add, argnames=["self", "index", "value"], parent=self, ) collector( "extend", function=self._self_set, argnames=["self", "iterable"], parent=self, ) # Getting methods collector("__getitem__", function=self._list_get, parent=self) collector("pop", function=self._list_get, parent=self) try: collector("__getslice__", function=self._list_get) except AttributeError: pass super(List, self).__init__(list, collector.attributes) def _new_list(self, args): return _create_builtin(args, get_list) def _list_add(self, context): if self.holding is not None: return holding = context.get_argument("value") if holding is not None and holding != pyobjects.get_unknown(): context.save_per_name(holding) def _self_set(self, context): if self.holding is not None: return iterable = context.get_pyname("iterable") holding = _infer_sequence_for_pyname(iterable) if holding is not None and holding != pyobjects.get_unknown(): context.save_per_name(holding) def _list_get(self, context): if self.holding is not None: args = context.get_arguments(["self", "key"]) if ( len(args) > 1 and args[1] is not None and args[1].get_type() == builtins["slice"].get_object() ): return get_list(self.holding) return self.holding return context.get_per_name() def _iterator_get(self, context): return get_iterator(self._list_get(context)) def _self_get(self, context): return get_list(self._list_get(context)) get_list = _create_builtin_getter(List) get_list_type = _create_builtin_type_getter(List) class Dict(BuiltinClass): def __init__(self, keys=None, values=None): self.keys = keys self.values = values collector = _AttributeCollector(dict) collector("__new__", function=self._new_dict, parent=self) collector("__setitem__", function=self._dict_add, parent=self) collector("popitem", function=self._item_get, parent=self) collector("pop", function=self._value_get, parent=self) collector("get", function=self._key_get, parent=self) collector("keys", function=self._key_list, parent=self) collector("values", function=self._value_list, parent=self) collector("items", function=self._item_list, parent=self) collector("copy", function=self._self_get, parent=self) collector("__getitem__", function=self._value_get, parent=self) collector("__iter__", function=self._key_iter, parent=self) collector("update", function=self._self_set, parent=self) super(Dict, self).__init__(dict, collector.attributes) def _new_dict(self, args): def do_create(holding=None): if holding is None: return get_dict() type = holding.get_type() if isinstance(type, Tuple) and len(type.get_holding_objects()) == 2: return get_dict(*type.get_holding_objects()) return _create_builtin(args, do_create) def _dict_add(self, context): if self.keys is not None: return key, value = context.get_arguments(["self", "key", "value"])[1:] if key is not None and key != pyobjects.get_unknown(): context.save_per_name(get_tuple(key, value)) def _item_get(self, context): if self.keys is not None: return get_tuple(self.keys, self.values) item = context.get_per_name() if item is None or not isinstance(item.get_type(), Tuple): return get_tuple(self.keys, self.values) return item def _value_get(self, context): item = self._item_get(context).get_type() return item.get_holding_objects()[1] def _key_get(self, context): item = self._item_get(context).get_type() return item.get_holding_objects()[0] def _value_list(self, context): return get_list(self._value_get(context)) def _key_list(self, context): return get_list(self._key_get(context)) def _item_list(self, context): return get_list(self._item_get(context)) def _value_iter(self, context): return get_iterator(self._value_get(context)) def _key_iter(self, context): return get_iterator(self._key_get(context)) def _item_iter(self, context): return get_iterator(self._item_get(context)) def _self_get(self, context): item = self._item_get(context).get_type() key, value = item.get_holding_objects()[:2] return get_dict(key, value) def _self_set(self, context): if self.keys is not None: return new_dict = context.get_pynames(["self", "d"])[1] if new_dict and isinstance(new_dict.get_object().get_type(), Dict): args = arguments.ObjectArguments([new_dict]) items = ( new_dict.get_object()["popitem"].get_object().get_returned_object(args) ) context.save_per_name(items) else: holding = _infer_sequence_for_pyname(new_dict) if holding is not None and isinstance(holding.get_type(), Tuple): context.save_per_name(holding) get_dict = _create_builtin_getter(Dict) get_dict_type = _create_builtin_type_getter(Dict) class Tuple(BuiltinClass): def __init__(self, *objects): self.objects = objects first = None if objects: first = objects[0] attributes = { "__getitem__": BuiltinName( BuiltinFunction(first) ), # TODO: add slice support "__getslice__": BuiltinName(BuiltinFunction(pyobjects.PyObject(self))), "__new__": BuiltinName(BuiltinFunction(function=self._new_tuple)), "__iter__": BuiltinName(BuiltinFunction(get_iterator(first))), } super(Tuple, self).__init__(tuple, attributes) def get_holding_objects(self): return self.objects def _new_tuple(self, args): return _create_builtin(args, get_tuple) get_tuple = _create_builtin_getter(Tuple) get_tuple_type = _create_builtin_type_getter(Tuple) class Set(BuiltinClass): def __init__(self, holding=None): self.holding = holding collector = _AttributeCollector(set) collector("__new__", function=self._new_set) self_methods = [ "copy", "difference", "intersection", "symmetric_difference", "union", ] for method in self_methods: collector(method, function=self._self_get, parent=self) collector("add", function=self._set_add, parent=self) collector("update", function=self._self_set, parent=self) collector("update", function=self._self_set, parent=self) collector("symmetric_difference_update", function=self._self_set, parent=self) collector("difference_update", function=self._self_set, parent=self) collector("pop", function=self._set_get, parent=self) collector("__iter__", function=self._iterator_get, parent=self) super(Set, self).__init__(set, collector.attributes) def _new_set(self, args): return _create_builtin(args, get_set) def _set_add(self, context): if self.holding is not None: return holding = context.get_arguments(["self", "value"])[1] if holding is not None and holding != pyobjects.get_unknown(): context.save_per_name(holding) def _self_set(self, context): if self.holding is not None: return iterable = context.get_pyname("iterable") holding = _infer_sequence_for_pyname(iterable) if holding is not None and holding != pyobjects.get_unknown(): context.save_per_name(holding) def _set_get(self, context): if self.holding is not None: return self.holding return context.get_per_name() def _iterator_get(self, context): return get_iterator(self._set_get(context)) def _self_get(self, context): return get_list(self._set_get(context)) get_set = _create_builtin_getter(Set) get_set_type = _create_builtin_type_getter(Set) class Str(BuiltinClass): def __init__(self): self_object = pyobjects.PyObject(self) collector = _AttributeCollector(str) collector("__iter__", get_iterator(self_object), check_existence=False) self_methods = [ "__getitem__", "capitalize", "center", "encode", "expandtabs", "join", "ljust", "lower", "lstrip", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "translate", "upper", "zfill", ] for method in self_methods: collector(method, self_object, parent=self) py2_self_methods = ["__getslice__", "decode"] for method in py2_self_methods: try: collector(method, self_object) except AttributeError: pass for method in ["rsplit", "split", "splitlines"]: collector(method, get_list(self_object), parent=self) super(Str, self).__init__(str, collector.attributes) def get_doc(self): return str.__doc__ get_str = _create_builtin_getter(Str) get_str_type = _create_builtin_type_getter(Str) class BuiltinName(pynames.PyName): def __init__(self, pyobject): self.pyobject = pyobject def get_object(self): return self.pyobject def get_definition_location(self): return (None, None) class Iterator(pyobjects.AbstractClass): def __init__(self, holding=None): super(Iterator, self).__init__() self.holding = holding self.attributes = { "next": BuiltinName(BuiltinFunction(self.holding)), "__iter__": BuiltinName(BuiltinFunction(self)), } def get_attributes(self): return self.attributes def get_returned_object(self, args): return self.holding get_iterator = _create_builtin_getter(Iterator) class Generator(pyobjects.AbstractClass): def __init__(self, holding=None): super(Generator, self).__init__() self.holding = holding self.attributes = { "next": BuiltinName(BuiltinFunction(self.holding)), "__iter__": BuiltinName(BuiltinFunction(get_iterator(self.holding))), "close": BuiltinName(BuiltinFunction()), "send": BuiltinName(BuiltinFunction()), "throw": BuiltinName(BuiltinFunction()), } def get_attributes(self): return self.attributes def get_returned_object(self, args): return self.holding get_generator = _create_builtin_getter(Generator) class File(BuiltinClass): def __init__(self, filename=None, mode="r", *args): self.filename = filename self.mode = mode self.args = args str_object = get_str() str_list = get_list(get_str()) attributes = {} def add(name, returned=None, function=None): builtin = getattr(io.TextIOBase, name, None) attributes[name] = BuiltinName( BuiltinFunction(returned=returned, function=function, builtin=builtin) ) add("__iter__", get_iterator(str_object)) add("__enter__", returned=pyobjects.PyObject(self)) for method in ["next", "read", "readline", "readlines"]: add(method, str_list) for method in [ "close", "flush", "lineno", "isatty", "seek", "tell", "truncate", "write", "writelines", ]: add(method) super(File, self).__init__(open, attributes) get_file = _create_builtin_getter(File) get_file_type = _create_builtin_type_getter(File) class Property(BuiltinClass): def __init__(self, fget=None, fset=None, fdel=None, fdoc=None): self._fget = fget self._fdoc = fdoc attributes = { "fget": BuiltinName(BuiltinFunction()), "fset": BuiltinName(pynames.UnboundName()), "fdel": BuiltinName(pynames.UnboundName()), "__new__": BuiltinName(BuiltinFunction(function=_property_function)), } super(Property, self).__init__(property, attributes) def get_property_object(self, args): if isinstance(self._fget, pyobjects.AbstractFunction): return self._fget.get_returned_object(args) def _property_function(args): parameters = args.get_arguments(["fget", "fset", "fdel", "fdoc"]) return pyobjects.PyObject(Property(parameters[0])) class Lambda(pyobjects.AbstractFunction): def __init__(self, node, scope): super(Lambda, self).__init__() self.node = node self.arguments = node.args self.scope = scope def get_returned_object(self, args): result = rope.base.evaluate.eval_node(self.scope, self.node.body) if result is not None: return result.get_object() else: return pyobjects.get_unknown() def get_module(self): return self.parent.get_module() def get_scope(self): return self.scope def get_kind(self): return "lambda" def get_ast(self): return self.node def get_attributes(self): return {} def get_name(self): return "lambda" def get_param_names(self, special_args=True): result = [ pycompat.get_ast_arg_arg(node) for node in self.arguments.args if isinstance(node, pycompat.ast_arg_type) ] if self.arguments.vararg: result.append("*" + pycompat.get_ast_arg_arg(self.arguments.vararg)) if self.arguments.kwarg: result.append("**" + pycompat.get_ast_arg_arg(self.arguments.kwarg)) return result @property def parent(self): return self.scope.pyobject class BuiltinObject(BuiltinClass): def __init__(self): super(BuiltinObject, self).__init__(object, {}) class BuiltinType(BuiltinClass): def __init__(self): super(BuiltinType, self).__init__(type, {}) def _infer_sequence_for_pyname(pyname): if pyname is None: return None seq = pyname.get_object() args = arguments.ObjectArguments([pyname]) if "__iter__" in seq: obj = seq["__iter__"].get_object() if not isinstance(obj, pyobjects.AbstractFunction): return None iter = obj.get_returned_object(args) if iter is not None and "next" in iter: holding = iter["next"].get_object().get_returned_object(args) return holding def _create_builtin(args, creator): passed = args.get_pynames(["sequence"])[0] if passed is None: holding = None else: holding = _infer_sequence_for_pyname(passed) if holding is not None: return creator(holding) else: return creator() def _open_function(args): return _create_builtin(args, get_file) def _range_function(args): return get_list() def _reversed_function(args): return _create_builtin(args, get_iterator) def _sorted_function(args): return _create_builtin(args, get_list) def _super_function(args): passed_class, passed_self = args.get_arguments(["type", "self"]) if passed_self is None: return passed_class else: # pyclass = passed_self.get_type() pyclass = passed_class if isinstance(pyclass, pyobjects.AbstractClass): supers = pyclass.get_superclasses() if supers: return pyobjects.PyObject(supers[0]) return passed_self def _zip_function(args): args = args.get_pynames(["sequence"]) objects = [] for seq in args: if seq is None: holding = None else: holding = _infer_sequence_for_pyname(seq) objects.append(holding) tuple = get_tuple(*objects) return get_list(tuple) def _enumerate_function(args): passed = args.get_pynames(["sequence"])[0] if passed is None: holding = None else: holding = _infer_sequence_for_pyname(passed) tuple = get_tuple(None, holding) return get_iterator(tuple) def _iter_function(args): passed = args.get_pynames(["sequence"])[0] if passed is None: holding = None else: holding = _infer_sequence_for_pyname(passed) return get_iterator(holding) def _input_function(args): return get_str() _initial_builtins = { "list": BuiltinName(get_list_type()), "dict": BuiltinName(get_dict_type()), "tuple": BuiltinName(get_tuple_type()), "set": BuiltinName(get_set_type()), "str": BuiltinName(get_str_type()), "file": BuiltinName(get_file_type()), "open": BuiltinName(BuiltinFunction(function=_open_function, builtin=open)), "unicode": BuiltinName(get_str_type()), "range": BuiltinName(BuiltinFunction(function=_range_function, builtin=range)), "reversed": BuiltinName( BuiltinFunction(function=_reversed_function, builtin=reversed) ), "sorted": BuiltinName(BuiltinFunction(function=_sorted_function, builtin=sorted)), "super": BuiltinName(BuiltinFunction(function=_super_function, builtin=super)), "property": BuiltinName( BuiltinFunction(function=_property_function, builtin=property) ), "zip": BuiltinName(BuiltinFunction(function=_zip_function, builtin=zip)), "enumerate": BuiltinName( BuiltinFunction(function=_enumerate_function, builtin=enumerate) ), "object": BuiltinName(BuiltinObject()), "type": BuiltinName(BuiltinType()), "iter": BuiltinName(BuiltinFunction(function=_iter_function, builtin=iter)), "raw_input": BuiltinName( BuiltinFunction(function=_input_function, builtin=raw_input) ), } builtins = BuiltinModule(pycompat.builtins.__name__, initial=_initial_builtins)