Adding new stuff
This commit is contained in:
parent
39ee792ad4
commit
a410da0e04
722 changed files with 331 additions and 189 deletions
|
|
@ -1,38 +0,0 @@
|
|||
"""Rope object analysis and inference package
|
||||
|
||||
Rope makes some simplifying assumptions about a python program. It
|
||||
assumes that a program only performs assignments and function calls.
|
||||
Tracking assignments is simple and `PyName` objects handle that. The
|
||||
main problem is function calls. Rope uses these two approaches for
|
||||
obtaining call information:
|
||||
|
||||
* Static object analysis: `rope.base.pycore.PyCore.analyze_module()`
|
||||
|
||||
It can analyze modules to obtain information about functions. This
|
||||
is done by analyzing function calls in a module or scope. Currently
|
||||
SOA analyzes the scopes that are changed while saving or when the
|
||||
user asks to analyze a module. That is mainly because static
|
||||
analysis is time-consuming.
|
||||
|
||||
* Dynamic object analysis: `rope.base.pycore.PyCore.run_module()`
|
||||
|
||||
When you run a module or your testsuite, when DOA is enabled, it
|
||||
collects information about parameters passed to and objects returned
|
||||
from functions. The main problem with this approach is that it is
|
||||
quite slow; Not when looking up the information but when collecting
|
||||
them.
|
||||
|
||||
An instance of `rope.base.oi.objectinfo.ObjectInfoManager` can be used
|
||||
for accessing these information. It saves the data in a
|
||||
`rope.base.oi.objectdb.ObjectDB` internally.
|
||||
|
||||
Now if our objectdb does not know anything about a function and we
|
||||
need the value returned by it, static object inference, SOI, comes
|
||||
into play. It analyzes function body and tries to infer the object
|
||||
that is returned from it (we usually need the returned value for the
|
||||
given parameter objects).
|
||||
|
||||
Rope might collect and store information for other `PyName`\s, too.
|
||||
For instance rope stores the object builtin containers hold.
|
||||
|
||||
"""
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,166 +0,0 @@
|
|||
try:
|
||||
import pickle
|
||||
except ImportError:
|
||||
import cPickle as pickle
|
||||
import marshal
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
|
||||
|
||||
class PythonFileRunner(object):
|
||||
"""A class for running python project files"""
|
||||
|
||||
def __init__(self, pycore, file_, args=None, stdin=None,
|
||||
stdout=None, analyze_data=None):
|
||||
self.pycore = pycore
|
||||
self.file = file_
|
||||
self.analyze_data = analyze_data
|
||||
self.observers = []
|
||||
self.args = args
|
||||
self.stdin = stdin
|
||||
self.stdout = stdout
|
||||
|
||||
def run(self):
|
||||
"""Execute the process"""
|
||||
env = dict(os.environ)
|
||||
file_path = self.file.real_path
|
||||
path_folders = self.pycore.project.get_source_folders() + \
|
||||
self.pycore.project.get_python_path_folders()
|
||||
env['PYTHONPATH'] = os.pathsep.join(folder.real_path
|
||||
for folder in path_folders)
|
||||
runmod_path = self.pycore.project.find_module('rope.base.oi.runmod').real_path
|
||||
self.receiver = None
|
||||
self._init_data_receiving()
|
||||
send_info = '-'
|
||||
if self.receiver:
|
||||
send_info = self.receiver.get_send_info()
|
||||
args = [sys.executable, runmod_path, send_info,
|
||||
self.pycore.project.address, self.file.real_path]
|
||||
if self.analyze_data is None:
|
||||
del args[1:4]
|
||||
if self.args is not None:
|
||||
args.extend(self.args)
|
||||
self.process = subprocess.Popen(
|
||||
executable=sys.executable, args=args, env=env,
|
||||
cwd=os.path.split(file_path)[0], stdin=self.stdin,
|
||||
stdout=self.stdout, stderr=self.stdout, close_fds=os.name != 'nt')
|
||||
|
||||
def _init_data_receiving(self):
|
||||
if self.analyze_data is None:
|
||||
return
|
||||
# Disabling FIFO data transfer due to blocking when running
|
||||
# unittests in the GUI.
|
||||
# XXX: Handle FIFO data transfer for `rope.ui.testview`
|
||||
if True or os.name == 'nt':
|
||||
self.receiver = _SocketReceiver()
|
||||
else:
|
||||
self.receiver = _FIFOReceiver()
|
||||
self.receiving_thread = threading.Thread(
|
||||
target=self._receive_information)
|
||||
self.receiving_thread.setDaemon(True)
|
||||
self.receiving_thread.start()
|
||||
|
||||
def _receive_information(self):
|
||||
#temp = open('/dev/shm/info', 'wb')
|
||||
for data in self.receiver.receive_data():
|
||||
self.analyze_data(data)
|
||||
#temp.write(str(data) + '\n')
|
||||
#temp.close()
|
||||
for observer in self.observers:
|
||||
observer()
|
||||
|
||||
def wait_process(self):
|
||||
"""Wait for the process to finish"""
|
||||
self.process.wait()
|
||||
if self.analyze_data:
|
||||
self.receiving_thread.join()
|
||||
|
||||
def kill_process(self):
|
||||
"""Stop the process"""
|
||||
if self.process.poll() is not None:
|
||||
return
|
||||
try:
|
||||
if hasattr(self.process, 'terminate'):
|
||||
self.process.terminate()
|
||||
elif os.name != 'nt':
|
||||
os.kill(self.process.pid, 9)
|
||||
else:
|
||||
import ctypes
|
||||
handle = int(self.process._handle)
|
||||
ctypes.windll.kernel32.TerminateProcess(handle, -1)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def add_finishing_observer(self, observer):
|
||||
"""Notify this observer when execution finishes"""
|
||||
self.observers.append(observer)
|
||||
|
||||
|
||||
class _MessageReceiver(object):
|
||||
|
||||
def receive_data(self):
|
||||
pass
|
||||
|
||||
def get_send_info(self):
|
||||
pass
|
||||
|
||||
|
||||
class _SocketReceiver(_MessageReceiver):
|
||||
|
||||
def __init__(self):
|
||||
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.data_port = 3037
|
||||
while self.data_port < 4000:
|
||||
try:
|
||||
self.server_socket.bind(('', self.data_port))
|
||||
break
|
||||
except socket.error:
|
||||
self.data_port += 1
|
||||
self.server_socket.listen(1)
|
||||
|
||||
def get_send_info(self):
|
||||
return str(self.data_port)
|
||||
|
||||
def receive_data(self):
|
||||
conn, addr = self.server_socket.accept()
|
||||
self.server_socket.close()
|
||||
my_file = conn.makefile('rb')
|
||||
while True:
|
||||
try:
|
||||
yield pickle.load(my_file)
|
||||
except EOFError:
|
||||
break
|
||||
my_file.close()
|
||||
conn.close()
|
||||
|
||||
|
||||
class _FIFOReceiver(_MessageReceiver):
|
||||
|
||||
def __init__(self):
|
||||
# XXX: this is insecure and might cause race conditions
|
||||
self.file_name = self._get_file_name()
|
||||
os.mkfifo(self.file_name)
|
||||
|
||||
def _get_file_name(self):
|
||||
prefix = tempfile.gettempdir() + '/__rope_'
|
||||
i = 0
|
||||
while os.path.exists(prefix + str(i).rjust(4, '0')):
|
||||
i += 1
|
||||
return prefix + str(i).rjust(4, '0')
|
||||
|
||||
def get_send_info(self):
|
||||
return self.file_name
|
||||
|
||||
def receive_data(self):
|
||||
my_file = open(self.file_name, 'rb')
|
||||
while True:
|
||||
try:
|
||||
yield marshal.load(my_file)
|
||||
except EOFError:
|
||||
break
|
||||
my_file.close()
|
||||
os.remove(self.file_name)
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
"""
|
||||
Hinting the type using docstring of class/function.
|
||||
|
||||
It's an irreplaceable thing if you are using Dependency Injection with passive class:
|
||||
http://www.martinfowler.com/articles/injection.html
|
||||
|
||||
Some code extracted (or based on code) from:
|
||||
https://github.com/davidhalter/jedi/blob/b489019f5bd5750051122b94cc767df47751ecb7/jedi/evaluate/docstrings.py
|
||||
Thanks to @davidhalter for this utils under MIT License.
|
||||
|
||||
Similar solutions:
|
||||
|
||||
- https://www.jetbrains.com/pycharm/help/type-hinting-in-pycharm.html
|
||||
- https://www.python.org/dev/peps/pep-0484/#type-comments
|
||||
- http://www.pydev.org/manual_adv_type_hints.html
|
||||
- https://jedi.readthedocs.org/en/latest/docs/features.html#type-hinting
|
||||
|
||||
Discussions:
|
||||
|
||||
- https://groups.google.com/d/topic/rope-dev/JlAzmZ83K1M/discussion
|
||||
- https://groups.google.com/d/topic/rope-dev/LCFNN98vckI/discussion
|
||||
|
||||
"""
|
||||
import re
|
||||
from ast import literal_eval
|
||||
|
||||
from rope.base.exceptions import AttributeNotFoundError
|
||||
from rope.base.evaluate import ScopeNameFinder
|
||||
from rope.base.pyobjects import PyClass, PyFunction
|
||||
|
||||
PEP0484_PATTERNS = [
|
||||
re.compile(r'type:\s*([^\n, ]+)'),
|
||||
]
|
||||
|
||||
DOCSTRING_PARAM_PATTERNS = [
|
||||
r'\s*:type\s+%s:\s*([^\n, ]+)', # Sphinx
|
||||
r'\s*:param\s+(\w+)\s+%s:[^\n]+', # Sphinx param with type
|
||||
r'\s*@type\s+%s:\s*([^\n, ]+)', # Epydoc
|
||||
]
|
||||
|
||||
DOCSTRING_RETURN_PATTERNS = [
|
||||
re.compile(r'\s*:rtype:\s*([^\n, ]+)', re.M), # Sphinx
|
||||
re.compile(r'\s*@rtype:\s*([^\n, ]+)', re.M), # Epydoc
|
||||
]
|
||||
|
||||
REST_ROLE_PATTERN = re.compile(r':[^`]+:`([^`]+)`')
|
||||
|
||||
try:
|
||||
from numpydoc.docscrape import NumpyDocString
|
||||
except ImportError:
|
||||
def _search_param_in_numpydocstr(docstr, param_str):
|
||||
return []
|
||||
else:
|
||||
def _search_param_in_numpydocstr(docstr, param_str):
|
||||
"""Search `docstr` (in numpydoc format) for type(-s) of `param_str`."""
|
||||
params = NumpyDocString(docstr)._parsed_data['Parameters']
|
||||
for p_name, p_type, p_descr in params:
|
||||
if p_name == param_str:
|
||||
m = re.match('([^,]+(,[^,]+)*?)(,[ ]*optional)?$', p_type)
|
||||
if m:
|
||||
p_type = m.group(1)
|
||||
|
||||
if p_type.startswith('{'):
|
||||
types = set(type(x).__name__ for x in literal_eval(p_type))
|
||||
return list(types)
|
||||
else:
|
||||
return [p_type]
|
||||
return []
|
||||
|
||||
|
||||
def hint_pep0484(pyname):
|
||||
from rope.base.oi.soi import _get_lineno_for_node
|
||||
lineno = _get_lineno_for_node(pyname.assignments[0].ast_node)
|
||||
holding_scope = pyname.module.get_scope().get_inner_scope_for_line(lineno)
|
||||
line = holding_scope._get_global_scope()._scope_finder.lines.get_line(lineno)
|
||||
if '#' in line:
|
||||
type_strs = _search_type_in_pep0484(line.split('#', 1)[1])
|
||||
if type_strs:
|
||||
return _resolve_type(type_strs[0], holding_scope.pyobject)
|
||||
|
||||
|
||||
def _search_type_in_pep0484(code):
|
||||
""" For more info see:
|
||||
https://www.python.org/dev/peps/pep-0484/#type-comments
|
||||
|
||||
>>> _search_type_in_pep0484('type: int')
|
||||
['int']
|
||||
"""
|
||||
for p in PEP0484_PATTERNS:
|
||||
match = p.search(code)
|
||||
if match:
|
||||
return [match.group(1)]
|
||||
|
||||
|
||||
def hint_param(pyfunc, param_name):
|
||||
type_strs = None
|
||||
func = pyfunc
|
||||
while not type_strs and func:
|
||||
if func.get_doc():
|
||||
type_strs = _search_param_in_docstr(func.get_doc(), param_name)
|
||||
func = _get_superfunc(func)
|
||||
|
||||
if type_strs:
|
||||
return _resolve_type(type_strs[0], pyfunc)
|
||||
|
||||
|
||||
def _get_superfunc(pyfunc):
|
||||
|
||||
if not isinstance(pyfunc.parent, PyClass):
|
||||
return
|
||||
|
||||
for cls in _get_mro(pyfunc.parent)[1:]:
|
||||
try:
|
||||
superfunc = cls.get_attribute(pyfunc.get_name()).get_object()
|
||||
except AttributeNotFoundError:
|
||||
pass
|
||||
else:
|
||||
if isinstance(superfunc, PyFunction):
|
||||
return superfunc
|
||||
|
||||
|
||||
def _get_mro(pyclass):
|
||||
# FIXME: to use real mro() result
|
||||
l = [pyclass]
|
||||
for cls in l:
|
||||
for super_cls in cls.get_superclasses():
|
||||
if isinstance(super_cls, PyClass) and super_cls not in l:
|
||||
l.append(super_cls)
|
||||
return l
|
||||
|
||||
|
||||
def _resolve_type(type_name, pyobj):
|
||||
type_ = None
|
||||
if '.' not in type_name:
|
||||
try:
|
||||
type_ = pyobj.get_module().get_scope().get_name(type_name).get_object()
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
mod_name, attr_name = type_name.rsplit('.', 1)
|
||||
try:
|
||||
mod_finder = ScopeNameFinder(pyobj.get_module())
|
||||
mod = mod_finder._find_module(mod_name).get_object()
|
||||
type_ = mod.get_attribute(attr_name).get_object()
|
||||
except Exception:
|
||||
pass
|
||||
return type_
|
||||
|
||||
|
||||
def _search_param_in_docstr(docstr, param_str):
|
||||
"""
|
||||
Search `docstr` for type(-s) of `param_str`.
|
||||
|
||||
>>> _search_param_in_docstr(':type param: int', 'param')
|
||||
['int']
|
||||
>>> _search_param_in_docstr('@type param: int', 'param')
|
||||
['int']
|
||||
>>> _search_param_in_docstr(
|
||||
... ':type param: :class:`threading.Thread`', 'param')
|
||||
['threading.Thread']
|
||||
>>> bool(_search_param_in_docstr('no document', 'param'))
|
||||
False
|
||||
>>> _search_param_in_docstr(':param int param: some description', 'param')
|
||||
['int']
|
||||
|
||||
"""
|
||||
patterns = [re.compile(p % re.escape(param_str))
|
||||
for p in DOCSTRING_PARAM_PATTERNS]
|
||||
for pattern in patterns:
|
||||
match = pattern.search(docstr)
|
||||
if match:
|
||||
return [_strip_rst_role(match.group(1))]
|
||||
|
||||
return (_search_param_in_numpydocstr(docstr, param_str) or
|
||||
[])
|
||||
|
||||
|
||||
def _strip_rst_role(type_str):
|
||||
"""
|
||||
Strip off the part looks like a ReST role in `type_str`.
|
||||
|
||||
>>> _strip_rst_role(':class:`ClassName`') # strip off :class:
|
||||
'ClassName'
|
||||
>>> _strip_rst_role(':py:obj:`module.Object`') # works with domain
|
||||
'module.Object'
|
||||
>>> _strip_rst_role('ClassName') # do nothing when not ReST role
|
||||
'ClassName'
|
||||
|
||||
See also:
|
||||
http://sphinx-doc.org/domains.html#cross-referencing-python-objects
|
||||
|
||||
"""
|
||||
match = REST_ROLE_PATTERN.match(type_str)
|
||||
if match:
|
||||
return match.group(1)
|
||||
else:
|
||||
return type_str
|
||||
|
||||
|
||||
def hint_return(pyfunc):
|
||||
type_str = None
|
||||
func = pyfunc
|
||||
while not type_str and func:
|
||||
if func.get_doc():
|
||||
type_str = _search_return_in_docstr(func.get_doc())
|
||||
func = _get_superfunc(func)
|
||||
if type_str:
|
||||
return _resolve_type(type_str, pyfunc)
|
||||
|
||||
|
||||
def _search_return_in_docstr(code):
|
||||
for p in DOCSTRING_RETURN_PATTERNS:
|
||||
match = p.search(code)
|
||||
if match:
|
||||
return _strip_rst_role(match.group(1))
|
||||
|
||||
|
||||
def hint_attr(pyclass, attr_name):
|
||||
type_strs = None
|
||||
for cls in _get_mro(pyclass):
|
||||
if cls.get_doc():
|
||||
type_strs = _search_param_in_docstr(cls.get_doc(), attr_name)
|
||||
if type_strs:
|
||||
break
|
||||
if type_strs:
|
||||
return _resolve_type(type_strs[0], pyclass)
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
from rope.base.oi import objectdb
|
||||
|
||||
|
||||
class MemoryDB(objectdb.FileDict):
|
||||
|
||||
def __init__(self, project, persist=None):
|
||||
self.project = project
|
||||
self._persist = persist
|
||||
self.files = self
|
||||
self._load_files()
|
||||
self.project.data_files.add_write_hook(self.write)
|
||||
|
||||
def _load_files(self):
|
||||
self._files = {}
|
||||
if self.persist:
|
||||
result = self.project.data_files.read_data(
|
||||
'objectdb', compress=self.compress, import_=True)
|
||||
if result is not None:
|
||||
self._files = result
|
||||
|
||||
def keys(self):
|
||||
return self._files.keys()
|
||||
|
||||
def __iter__(self):
|
||||
for f in self._files:
|
||||
yield f
|
||||
|
||||
def __len__(self):
|
||||
return len(self._files)
|
||||
|
||||
def __setitem__(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._files
|
||||
|
||||
def __getitem__(self, key):
|
||||
return FileInfo(self._files[key])
|
||||
|
||||
def create(self, path):
|
||||
self._files[path] = {}
|
||||
|
||||
def rename(self, file, newfile):
|
||||
if file not in self._files:
|
||||
return
|
||||
self._files[newfile] = self._files[file]
|
||||
del self[file]
|
||||
|
||||
def __delitem__(self, file):
|
||||
del self._files[file]
|
||||
|
||||
def write(self):
|
||||
if self.persist:
|
||||
self.project.data_files.write_data('objectdb', self._files,
|
||||
self.compress)
|
||||
|
||||
@property
|
||||
def compress(self):
|
||||
return self.project.prefs.get('compress_objectdb', False)
|
||||
|
||||
@property
|
||||
def persist(self):
|
||||
if self._persist is not None:
|
||||
return self._persist
|
||||
else:
|
||||
return self.project.prefs.get('save_objectdb', False)
|
||||
|
||||
|
||||
class FileInfo(objectdb.FileInfo):
|
||||
|
||||
def __init__(self, scopes):
|
||||
self.scopes = scopes
|
||||
|
||||
def create_scope(self, key):
|
||||
self.scopes[key] = ScopeInfo()
|
||||
|
||||
def keys(self):
|
||||
return self.scopes.keys()
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.scopes
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.scopes[key]
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self.scopes[key]
|
||||
|
||||
def __iter__(self):
|
||||
for s in self.scopes:
|
||||
yield s
|
||||
|
||||
def __len__(self):
|
||||
return len(self.scopes)
|
||||
|
||||
def __setitem__(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
|
||||
class ScopeInfo(objectdb.ScopeInfo):
|
||||
|
||||
def __init__(self):
|
||||
self.call_info = {}
|
||||
self.per_name = {}
|
||||
|
||||
def get_per_name(self, name):
|
||||
return self.per_name.get(name, None)
|
||||
|
||||
def save_per_name(self, name, value):
|
||||
self.per_name[name] = value
|
||||
|
||||
def get_returned(self, parameters):
|
||||
return self.call_info.get(parameters, None)
|
||||
|
||||
def get_call_infos(self):
|
||||
for args, returned in self.call_info.items():
|
||||
yield objectdb.CallInfo(args, returned)
|
||||
|
||||
def add_call(self, parameters, returned):
|
||||
self.call_info[parameters] = returned
|
||||
|
||||
def __getstate__(self):
|
||||
return (self.call_info, self.per_name)
|
||||
|
||||
def __setstate__(self, data):
|
||||
self.call_info, self.per_name = data
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
from __future__ import print_function
|
||||
try:
|
||||
from collections import MutableMapping
|
||||
except ImportError:
|
||||
from UserDict import DictMixin as MutableMapping
|
||||
|
||||
|
||||
class ObjectDB(object):
|
||||
|
||||
def __init__(self, db, validation):
|
||||
self.db = db
|
||||
self.validation = validation
|
||||
self.observers = []
|
||||
self.files = db.files
|
||||
|
||||
def validate_files(self):
|
||||
for file in list(self.files):
|
||||
if not self.validation.is_file_valid(file):
|
||||
del self.files[file]
|
||||
self._file_removed(file)
|
||||
|
||||
def validate_file(self, file):
|
||||
if file not in self.files:
|
||||
return
|
||||
for key in list(self.files[file]):
|
||||
if not self.validation.is_scope_valid(file, key):
|
||||
del self.files[file][key]
|
||||
|
||||
def file_moved(self, file, newfile):
|
||||
if file not in self.files:
|
||||
return
|
||||
self.files.rename(file, newfile)
|
||||
self._file_removed(file)
|
||||
self._file_added(newfile)
|
||||
|
||||
def get_files(self):
|
||||
return self.files.keys()
|
||||
|
||||
def get_returned(self, path, key, args):
|
||||
scope_info = self._get_scope_info(path, key, readonly=True)
|
||||
result = scope_info.get_returned(args)
|
||||
if self.validation.is_value_valid(result):
|
||||
return result
|
||||
|
||||
def get_pername(self, path, key, name):
|
||||
scope_info = self._get_scope_info(path, key, readonly=True)
|
||||
result = scope_info.get_per_name(name)
|
||||
if self.validation.is_value_valid(result):
|
||||
return result
|
||||
|
||||
def get_callinfos(self, path, key):
|
||||
scope_info = self._get_scope_info(path, key, readonly=True)
|
||||
return scope_info.get_call_infos()
|
||||
|
||||
def add_callinfo(self, path, key, args, returned):
|
||||
scope_info = self._get_scope_info(path, key, readonly=False)
|
||||
old_returned = scope_info.get_returned(args)
|
||||
if self.validation.is_more_valid(returned, old_returned):
|
||||
scope_info.add_call(args, returned)
|
||||
|
||||
def add_pername(self, path, key, name, value):
|
||||
scope_info = self._get_scope_info(path, key, readonly=False)
|
||||
old_value = scope_info.get_per_name(name)
|
||||
if self.validation.is_more_valid(value, old_value):
|
||||
scope_info.save_per_name(name, value)
|
||||
|
||||
def add_file_list_observer(self, observer):
|
||||
self.observers.append(observer)
|
||||
|
||||
def write(self):
|
||||
self.db.write()
|
||||
|
||||
def _get_scope_info(self, path, key, readonly=True):
|
||||
if path not in self.files:
|
||||
if readonly:
|
||||
return _NullScopeInfo()
|
||||
self.files.create(path)
|
||||
self._file_added(path)
|
||||
if key not in self.files[path]:
|
||||
if readonly:
|
||||
return _NullScopeInfo()
|
||||
self.files[path].create_scope(key)
|
||||
result = self.files[path][key]
|
||||
if isinstance(result, dict):
|
||||
print(self.files, self.files[path], self.files[path][key])
|
||||
return result
|
||||
|
||||
def _file_removed(self, path):
|
||||
for observer in self.observers:
|
||||
observer.removed(path)
|
||||
|
||||
def _file_added(self, path):
|
||||
for observer in self.observers:
|
||||
observer.added(path)
|
||||
|
||||
def __str__(self):
|
||||
scope_count = 0
|
||||
for file_dict in self.files.values():
|
||||
scope_count += len(file_dict)
|
||||
return 'ObjectDB holds %s file and %s scope infos' % \
|
||||
(len(self.files), scope_count)
|
||||
|
||||
|
||||
class _NullScopeInfo(object):
|
||||
|
||||
def __init__(self, error_on_write=True):
|
||||
self.error_on_write = error_on_write
|
||||
|
||||
def get_per_name(self, name):
|
||||
pass
|
||||
|
||||
def save_per_name(self, name, value):
|
||||
if self.error_on_write:
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_returned(self, parameters):
|
||||
pass
|
||||
|
||||
def get_call_infos(self):
|
||||
return []
|
||||
|
||||
def add_call(self, parameters, returned):
|
||||
if self.error_on_write:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class FileInfo(MutableMapping):
|
||||
|
||||
def create_scope(self, key):
|
||||
pass
|
||||
|
||||
|
||||
class FileDict(MutableMapping):
|
||||
|
||||
def create(self, key):
|
||||
pass
|
||||
|
||||
def rename(self, key, new_key):
|
||||
pass
|
||||
|
||||
|
||||
class ScopeInfo(object):
|
||||
|
||||
def get_per_name(self, name):
|
||||
pass
|
||||
|
||||
def save_per_name(self, name, value):
|
||||
pass
|
||||
|
||||
def get_returned(self, parameters):
|
||||
pass
|
||||
|
||||
def get_call_infos(self):
|
||||
pass
|
||||
|
||||
def add_call(self, parameters, returned):
|
||||
pass
|
||||
|
||||
|
||||
class CallInfo(object):
|
||||
|
||||
def __init__(self, args, returned):
|
||||
self.args = args
|
||||
self.returned = returned
|
||||
|
||||
def get_parameters(self):
|
||||
return self.args
|
||||
|
||||
def get_returned(self):
|
||||
return self.returned
|
||||
|
||||
|
||||
class FileListObserver(object):
|
||||
|
||||
def added(self, path):
|
||||
pass
|
||||
|
||||
def removed(self, path):
|
||||
pass
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
import warnings
|
||||
|
||||
from rope.base import exceptions, resourceobserver
|
||||
from rope.base.oi import objectdb, memorydb, transform
|
||||
|
||||
|
||||
class ObjectInfoManager(object):
|
||||
"""Stores object information
|
||||
|
||||
It uses an instance of `objectdb.ObjectDB` for storing
|
||||
information.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
self.to_textual = transform.PyObjectToTextual(project)
|
||||
self.to_pyobject = transform.TextualToPyObject(project)
|
||||
self.doi_to_pyobject = transform.DOITextualToPyObject(project)
|
||||
self._init_objectdb()
|
||||
if project.prefs.get('validate_objectdb', False):
|
||||
self._init_validation()
|
||||
|
||||
def _init_objectdb(self):
|
||||
dbtype = self.project.get_prefs().get('objectdb_type', None)
|
||||
persist = None
|
||||
if dbtype is not None:
|
||||
warnings.warn(
|
||||
'"objectdb_type" project config is deprecated;\n'
|
||||
'Use "save_objectdb" instead in your project '
|
||||
'config file.\n(".ropeproject/config.py" by default)\n',
|
||||
DeprecationWarning)
|
||||
if dbtype != 'memory' and self.project.ropefolder is not None:
|
||||
persist = True
|
||||
self.validation = TextualValidation(self.to_pyobject)
|
||||
db = memorydb.MemoryDB(self.project, persist=persist)
|
||||
self.objectdb = objectdb.ObjectDB(db, self.validation)
|
||||
|
||||
def _init_validation(self):
|
||||
self.objectdb.validate_files()
|
||||
observer = resourceobserver.ResourceObserver(
|
||||
changed=self._resource_changed, moved=self._resource_moved,
|
||||
removed=self._resource_moved)
|
||||
files = []
|
||||
for path in self.objectdb.get_files():
|
||||
resource = self.to_pyobject.path_to_resource(path)
|
||||
if resource is not None and resource.project == self.project:
|
||||
files.append(resource)
|
||||
self.observer = resourceobserver.FilteredResourceObserver(observer,
|
||||
files)
|
||||
self.objectdb.add_file_list_observer(_FileListObserver(self))
|
||||
self.project.add_observer(self.observer)
|
||||
|
||||
def _resource_changed(self, resource):
|
||||
try:
|
||||
self.objectdb.validate_file(
|
||||
self.to_textual.resource_to_path(resource))
|
||||
except exceptions.ModuleSyntaxError:
|
||||
pass
|
||||
|
||||
def _resource_moved(self, resource, new_resource=None):
|
||||
self.observer.remove_resource(resource)
|
||||
if new_resource is not None:
|
||||
old = self.to_textual.resource_to_path(resource)
|
||||
new = self.to_textual.resource_to_path(new_resource)
|
||||
self.objectdb.file_moved(old, new)
|
||||
self.observer.add_resource(new_resource)
|
||||
|
||||
def get_returned(self, pyobject, args):
|
||||
result = self.get_exact_returned(pyobject, args)
|
||||
if result is not None:
|
||||
return result
|
||||
path, key = self._get_scope(pyobject)
|
||||
if path is None:
|
||||
return None
|
||||
for call_info in self.objectdb.get_callinfos(path, key):
|
||||
returned = call_info.get_returned()
|
||||
if returned and returned[0] not in ('unknown', 'none'):
|
||||
result = returned
|
||||
break
|
||||
if result is None:
|
||||
result = returned
|
||||
if result is not None:
|
||||
return self.to_pyobject(result)
|
||||
|
||||
def get_exact_returned(self, pyobject, args):
|
||||
path, key = self._get_scope(pyobject)
|
||||
if path is not None:
|
||||
returned = self.objectdb.get_returned(
|
||||
path, key, self._args_to_textual(pyobject, args))
|
||||
if returned is not None:
|
||||
return self.to_pyobject(returned)
|
||||
|
||||
def _args_to_textual(self, pyfunction, args):
|
||||
parameters = list(pyfunction.get_param_names(special_args=False))
|
||||
arguments = args.get_arguments(parameters)[:len(parameters)]
|
||||
textual_args = tuple([self.to_textual(arg)
|
||||
for arg in arguments])
|
||||
return textual_args
|
||||
|
||||
def get_parameter_objects(self, pyobject):
|
||||
path, key = self._get_scope(pyobject)
|
||||
if path is None:
|
||||
return None
|
||||
arg_count = len(pyobject.get_param_names(special_args=False))
|
||||
unknowns = arg_count
|
||||
parameters = [None] * arg_count
|
||||
for call_info in self.objectdb.get_callinfos(path, key):
|
||||
args = call_info.get_parameters()
|
||||
for index, arg in enumerate(args[:arg_count]):
|
||||
old = parameters[index]
|
||||
if self.validation.is_more_valid(arg, old):
|
||||
parameters[index] = arg
|
||||
if self.validation.is_value_valid(arg):
|
||||
unknowns -= 1
|
||||
if unknowns == 0:
|
||||
break
|
||||
if unknowns < arg_count:
|
||||
return [self.to_pyobject(parameter)
|
||||
for parameter in parameters]
|
||||
|
||||
def get_passed_objects(self, pyfunction, parameter_index):
|
||||
path, key = self._get_scope(pyfunction)
|
||||
if path is None:
|
||||
return []
|
||||
result = []
|
||||
for call_info in self.objectdb.get_callinfos(path, key):
|
||||
args = call_info.get_parameters()
|
||||
if len(args) > parameter_index:
|
||||
parameter = self.to_pyobject(args[parameter_index])
|
||||
if parameter is not None:
|
||||
result.append(parameter)
|
||||
return result
|
||||
|
||||
def doa_data_received(self, data):
|
||||
def doi_to_normal(textual):
|
||||
pyobject = self.doi_to_pyobject(textual)
|
||||
return self.to_textual(pyobject)
|
||||
function = doi_to_normal(data[0])
|
||||
args = tuple([doi_to_normal(textual) for textual in data[1]])
|
||||
returned = doi_to_normal(data[2])
|
||||
if function[0] == 'defined' and len(function) == 3:
|
||||
self._save_data(function, args, returned)
|
||||
|
||||
def function_called(self, pyfunction, params, returned=None):
|
||||
function_text = self.to_textual(pyfunction)
|
||||
params_text = tuple([self.to_textual(param)
|
||||
for param in params])
|
||||
returned_text = ('unknown',)
|
||||
if returned is not None:
|
||||
returned_text = self.to_textual(returned)
|
||||
self._save_data(function_text, params_text, returned_text)
|
||||
|
||||
def save_per_name(self, scope, name, data):
|
||||
path, key = self._get_scope(scope.pyobject)
|
||||
if path is not None:
|
||||
self.objectdb.add_pername(path, key, name, self.to_textual(data))
|
||||
|
||||
def get_per_name(self, scope, name):
|
||||
path, key = self._get_scope(scope.pyobject)
|
||||
if path is not None:
|
||||
result = self.objectdb.get_pername(path, key, name)
|
||||
if result is not None:
|
||||
return self.to_pyobject(result)
|
||||
|
||||
def _save_data(self, function, args, returned=('unknown',)):
|
||||
self.objectdb.add_callinfo(function[1], function[2], args, returned)
|
||||
|
||||
def _get_scope(self, pyobject):
|
||||
resource = pyobject.get_module().get_resource()
|
||||
if resource is None:
|
||||
return None, None
|
||||
textual = self.to_textual(pyobject)
|
||||
if textual[0] == 'defined':
|
||||
path = textual[1]
|
||||
if len(textual) == 3:
|
||||
key = textual[2]
|
||||
else:
|
||||
key = ''
|
||||
return path, key
|
||||
return None, None
|
||||
|
||||
def sync(self):
|
||||
self.objectdb.sync()
|
||||
|
||||
def __str__(self):
|
||||
return str(self.objectdb)
|
||||
|
||||
|
||||
class TextualValidation(object):
|
||||
|
||||
def __init__(self, to_pyobject):
|
||||
self.to_pyobject = to_pyobject
|
||||
|
||||
def is_value_valid(self, value):
|
||||
# ???: Should none and unknown be considered valid?
|
||||
if value is None or value[0] in ('none', 'unknown'):
|
||||
return False
|
||||
return self.to_pyobject(value) is not None
|
||||
|
||||
def is_more_valid(self, new, old):
|
||||
if old is None:
|
||||
return True
|
||||
return new[0] not in ('unknown', 'none')
|
||||
|
||||
def is_file_valid(self, path):
|
||||
return self.to_pyobject.path_to_resource(path) is not None
|
||||
|
||||
def is_scope_valid(self, path, key):
|
||||
if key == '':
|
||||
textual = ('defined', path)
|
||||
else:
|
||||
textual = ('defined', path, key)
|
||||
return self.to_pyobject(textual) is not None
|
||||
|
||||
|
||||
class _FileListObserver(object):
|
||||
|
||||
def __init__(self, object_info):
|
||||
self.object_info = object_info
|
||||
self.observer = self.object_info.observer
|
||||
self.to_pyobject = self.object_info.to_pyobject
|
||||
|
||||
def removed(self, path):
|
||||
resource = self.to_pyobject.path_to_resource(path)
|
||||
if resource is not None:
|
||||
self.observer.remove_resource(resource)
|
||||
|
||||
def added(self, path):
|
||||
resource = self.to_pyobject.path_to_resource(path)
|
||||
if resource is not None:
|
||||
self.observer.add_resource(resource)
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
def __rope_start_everything():
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
try:
|
||||
import pickle
|
||||
except ImportError:
|
||||
import cPickle as pickle
|
||||
import marshal
|
||||
import inspect
|
||||
import types
|
||||
import threading
|
||||
import rope.base.utils.pycompat as pycompat
|
||||
|
||||
class _MessageSender(object):
|
||||
|
||||
def send_data(self, data):
|
||||
pass
|
||||
|
||||
class _SocketSender(_MessageSender):
|
||||
|
||||
def __init__(self, port):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect(('127.0.0.1', port))
|
||||
self.my_file = s.makefile('wb')
|
||||
|
||||
def send_data(self, data):
|
||||
if not self.my_file.closed:
|
||||
pickle.dump(data, self.my_file)
|
||||
|
||||
def close(self):
|
||||
self.my_file.close()
|
||||
|
||||
class _FileSender(_MessageSender):
|
||||
|
||||
def __init__(self, file_name):
|
||||
self.my_file = open(file_name, 'wb')
|
||||
|
||||
def send_data(self, data):
|
||||
if not self.my_file.closed:
|
||||
marshal.dump(data, self.my_file)
|
||||
|
||||
def close(self):
|
||||
self.my_file.close()
|
||||
|
||||
def _cached(func):
|
||||
cache = {}
|
||||
|
||||
def newfunc(self, arg):
|
||||
if arg in cache:
|
||||
return cache[arg]
|
||||
result = func(self, arg)
|
||||
cache[arg] = result
|
||||
return result
|
||||
return newfunc
|
||||
|
||||
class _FunctionCallDataSender(object):
|
||||
|
||||
def __init__(self, send_info, project_root):
|
||||
self.project_root = project_root
|
||||
if send_info.isdigit():
|
||||
self.sender = _SocketSender(int(send_info))
|
||||
else:
|
||||
self.sender = _FileSender(send_info)
|
||||
|
||||
def global_trace(frame, event, arg):
|
||||
# HACK: Ignoring out->in calls
|
||||
# This might lose some information
|
||||
if self._is_an_interesting_call(frame):
|
||||
return self.on_function_call
|
||||
sys.settrace(global_trace)
|
||||
threading.settrace(global_trace)
|
||||
|
||||
def on_function_call(self, frame, event, arg):
|
||||
if event != 'return':
|
||||
return
|
||||
args = []
|
||||
returned = ('unknown',)
|
||||
code = frame.f_code
|
||||
for argname in code.co_varnames[:code.co_argcount]:
|
||||
try:
|
||||
argvalue = self._object_to_persisted_form(
|
||||
frame.f_locals[argname])
|
||||
args.append(argvalue)
|
||||
except (TypeError, AttributeError):
|
||||
args.append(('unknown',))
|
||||
try:
|
||||
returned = self._object_to_persisted_form(arg)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
try:
|
||||
data = (self._object_to_persisted_form(frame.f_code),
|
||||
tuple(args), returned)
|
||||
self.sender.send_data(data)
|
||||
except (TypeError):
|
||||
pass
|
||||
return self.on_function_call
|
||||
|
||||
def _is_an_interesting_call(self, frame):
|
||||
#if frame.f_code.co_name in ['?', '<module>']:
|
||||
# return False
|
||||
#return not frame.f_back or
|
||||
# not self._is_code_inside_project(frame.f_back.f_code)
|
||||
if not self._is_code_inside_project(frame.f_code) and \
|
||||
(not frame.f_back or
|
||||
not self._is_code_inside_project(frame.f_back.f_code)):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _is_code_inside_project(self, code):
|
||||
source = self._path(code.co_filename)
|
||||
return source is not None and os.path.exists(source) and \
|
||||
_realpath(source).startswith(self.project_root)
|
||||
|
||||
@_cached
|
||||
def _get_persisted_code(self, object_):
|
||||
source = self._path(object_.co_filename)
|
||||
if not os.path.exists(source):
|
||||
raise TypeError('no source')
|
||||
return ('defined', _realpath(source), str(object_.co_firstlineno))
|
||||
|
||||
@_cached
|
||||
def _get_persisted_class(self, object_):
|
||||
try:
|
||||
return ('defined', _realpath(inspect.getsourcefile(object_)),
|
||||
object_.__name__)
|
||||
except (TypeError, AttributeError):
|
||||
return ('unknown',)
|
||||
|
||||
def _get_persisted_builtin(self, object_):
|
||||
if isinstance(object_, pycompat.string_types):
|
||||
return ('builtin', 'str')
|
||||
if isinstance(object_, list):
|
||||
holding = None
|
||||
if len(object_) > 0:
|
||||
holding = object_[0]
|
||||
return ('builtin', 'list',
|
||||
self._object_to_persisted_form(holding))
|
||||
if isinstance(object_, dict):
|
||||
keys = None
|
||||
values = None
|
||||
if len(object_) > 0:
|
||||
# @todo - fix it properly, why is __locals__ being
|
||||
# duplicated ?
|
||||
keys = [key for key in object_.keys() if key != '__locals__'][0]
|
||||
values = object_[keys]
|
||||
return ('builtin', 'dict',
|
||||
self._object_to_persisted_form(keys),
|
||||
self._object_to_persisted_form(values))
|
||||
if isinstance(object_, tuple):
|
||||
objects = []
|
||||
if len(object_) < 3:
|
||||
for holding in object_:
|
||||
objects.append(self._object_to_persisted_form(holding))
|
||||
else:
|
||||
objects.append(self._object_to_persisted_form(object_[0]))
|
||||
return tuple(['builtin', 'tuple'] + objects)
|
||||
if isinstance(object_, set):
|
||||
holding = None
|
||||
if len(object_) > 0:
|
||||
for o in object_:
|
||||
holding = o
|
||||
break
|
||||
return ('builtin', 'set',
|
||||
self._object_to_persisted_form(holding))
|
||||
return ('unknown',)
|
||||
|
||||
def _object_to_persisted_form(self, object_):
|
||||
if object_ is None:
|
||||
return ('none',)
|
||||
if isinstance(object_, types.CodeType):
|
||||
return self._get_persisted_code(object_)
|
||||
if isinstance(object_, types.FunctionType):
|
||||
return self._get_persisted_code(object_.__code__)
|
||||
if isinstance(object_, types.MethodType):
|
||||
return self._get_persisted_code(object_.__func__.__code__)
|
||||
if isinstance(object_, types.ModuleType):
|
||||
return self._get_persisted_module(object_)
|
||||
if isinstance(object_, pycompat.string_types + (list, dict, tuple, set)):
|
||||
return self._get_persisted_builtin(object_)
|
||||
if isinstance(object_, type):
|
||||
return self._get_persisted_class(object_)
|
||||
return ('instance', self._get_persisted_class(type(object_)))
|
||||
|
||||
@_cached
|
||||
def _get_persisted_module(self, object_):
|
||||
path = self._path(object_.__file__)
|
||||
if path and os.path.exists(path):
|
||||
return ('defined', _realpath(path))
|
||||
return ('unknown',)
|
||||
|
||||
def _path(self, path):
|
||||
if path.endswith('.pyc'):
|
||||
path = path[:-1]
|
||||
if path.endswith('.py'):
|
||||
return path
|
||||
|
||||
def close(self):
|
||||
self.sender.close()
|
||||
sys.settrace(None)
|
||||
|
||||
def _realpath(path):
|
||||
return os.path.realpath(os.path.abspath(os.path.expanduser(path)))
|
||||
|
||||
send_info = sys.argv[1]
|
||||
project_root = sys.argv[2]
|
||||
file_to_run = sys.argv[3]
|
||||
run_globals = globals()
|
||||
run_globals.update({'__name__': '__main__',
|
||||
'__builtins__': __builtins__,
|
||||
'__file__': file_to_run})
|
||||
|
||||
if send_info != '-':
|
||||
data_sender = _FunctionCallDataSender(send_info, project_root)
|
||||
del sys.argv[1:4]
|
||||
pycompat.execfile(file_to_run, run_globals)
|
||||
if send_info != '-':
|
||||
data_sender.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
__rope_start_everything()
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
import rope.base.ast
|
||||
import rope.base.oi.soi
|
||||
import rope.base.pynames
|
||||
from rope.base import pyobjects, evaluate, astutils, arguments
|
||||
|
||||
|
||||
def analyze_module(pycore, pymodule, should_analyze,
|
||||
search_subscopes, followed_calls):
|
||||
"""Analyze `pymodule` for static object inference
|
||||
|
||||
Analyzes scopes for collecting object information. The analysis
|
||||
starts from inner scopes.
|
||||
|
||||
"""
|
||||
_analyze_node(pycore, pymodule, should_analyze,
|
||||
search_subscopes, followed_calls)
|
||||
|
||||
|
||||
def _analyze_node(pycore, pydefined, should_analyze,
|
||||
search_subscopes, followed_calls):
|
||||
if search_subscopes(pydefined):
|
||||
for scope in pydefined.get_scope().get_scopes():
|
||||
_analyze_node(pycore, scope.pyobject, should_analyze,
|
||||
search_subscopes, followed_calls)
|
||||
if should_analyze(pydefined):
|
||||
new_followed_calls = max(0, followed_calls - 1)
|
||||
return_true = lambda pydefined: True
|
||||
return_false = lambda pydefined: False
|
||||
|
||||
def _follow(pyfunction):
|
||||
_analyze_node(pycore, pyfunction, return_true,
|
||||
return_false, new_followed_calls)
|
||||
|
||||
if not followed_calls:
|
||||
_follow = None
|
||||
visitor = SOAVisitor(pycore, pydefined, _follow)
|
||||
for child in rope.base.ast.get_child_nodes(pydefined.get_ast()):
|
||||
rope.base.ast.walk(child, visitor)
|
||||
|
||||
|
||||
class SOAVisitor(object):
|
||||
|
||||
def __init__(self, pycore, pydefined, follow_callback=None):
|
||||
self.pycore = pycore
|
||||
self.pymodule = pydefined.get_module()
|
||||
self.scope = pydefined.get_scope()
|
||||
self.follow = follow_callback
|
||||
|
||||
def _FunctionDef(self, node):
|
||||
pass
|
||||
|
||||
def _ClassDef(self, node):
|
||||
pass
|
||||
|
||||
def _Call(self, node):
|
||||
for child in rope.base.ast.get_child_nodes(node):
|
||||
rope.base.ast.walk(child, self)
|
||||
primary, pyname = evaluate.eval_node2(self.scope, node.func)
|
||||
if pyname is None:
|
||||
return
|
||||
pyfunction = pyname.get_object()
|
||||
if isinstance(pyfunction, pyobjects.AbstractFunction):
|
||||
args = arguments.create_arguments(primary, pyfunction,
|
||||
node, self.scope)
|
||||
elif isinstance(pyfunction, pyobjects.PyClass):
|
||||
pyclass = pyfunction
|
||||
if '__init__' in pyfunction:
|
||||
pyfunction = pyfunction['__init__'].get_object()
|
||||
pyname = rope.base.pynames.UnboundName(pyobjects.PyObject(pyclass))
|
||||
args = self._args_with_self(primary, pyname, pyfunction, node)
|
||||
elif '__call__' in pyfunction:
|
||||
pyfunction = pyfunction['__call__'].get_object()
|
||||
args = self._args_with_self(primary, pyname, pyfunction, node)
|
||||
else:
|
||||
return
|
||||
self._call(pyfunction, args)
|
||||
|
||||
def _args_with_self(self, primary, self_pyname, pyfunction, node):
|
||||
base_args = arguments.create_arguments(primary, pyfunction,
|
||||
node, self.scope)
|
||||
return arguments.MixedArguments(self_pyname, base_args, self.scope)
|
||||
|
||||
def _call(self, pyfunction, args):
|
||||
if isinstance(pyfunction, pyobjects.PyFunction):
|
||||
if self.follow is not None:
|
||||
before = self._parameter_objects(pyfunction)
|
||||
self.pycore.object_info.function_called(
|
||||
pyfunction, args.get_arguments(pyfunction.get_param_names()))
|
||||
pyfunction._set_parameter_pyobjects(None)
|
||||
if self.follow is not None:
|
||||
after = self._parameter_objects(pyfunction)
|
||||
if after != before:
|
||||
self.follow(pyfunction)
|
||||
# XXX: Maybe we should not call every builtin function
|
||||
if isinstance(pyfunction, rope.base.builtins.BuiltinFunction):
|
||||
pyfunction.get_returned_object(args)
|
||||
|
||||
def _parameter_objects(self, pyfunction):
|
||||
result = []
|
||||
for i in range(len(pyfunction.get_param_names(False))):
|
||||
result.append(pyfunction.get_parameter(i))
|
||||
return result
|
||||
|
||||
def _Assign(self, node):
|
||||
for child in rope.base.ast.get_child_nodes(node):
|
||||
rope.base.ast.walk(child, self)
|
||||
visitor = _SOAAssignVisitor()
|
||||
nodes = []
|
||||
for child in node.targets:
|
||||
rope.base.ast.walk(child, visitor)
|
||||
nodes.extend(visitor.nodes)
|
||||
for subscript, levels in nodes:
|
||||
instance = evaluate.eval_node(self.scope, subscript.value)
|
||||
args_pynames = []
|
||||
args_pynames.append(evaluate.eval_node(self.scope,
|
||||
subscript.slice.value))
|
||||
value = rope.base.oi.soi._infer_assignment(
|
||||
rope.base.pynames.AssignmentValue(node.value, levels),
|
||||
self.pymodule)
|
||||
args_pynames.append(rope.base.pynames.UnboundName(value))
|
||||
if instance is not None and value is not None:
|
||||
pyobject = instance.get_object()
|
||||
if '__setitem__' in pyobject:
|
||||
pyfunction = pyobject['__setitem__'].get_object()
|
||||
args = arguments.ObjectArguments([instance] + args_pynames)
|
||||
self._call(pyfunction, args)
|
||||
# IDEA: handle `__setslice__`, too
|
||||
|
||||
|
||||
class _SOAAssignVisitor(astutils._NodeNameCollector):
|
||||
|
||||
def __init__(self):
|
||||
super(_SOAAssignVisitor, self).__init__()
|
||||
self.nodes = []
|
||||
|
||||
def _added(self, node, levels):
|
||||
if isinstance(node, rope.base.ast.Subscript) and \
|
||||
isinstance(node.slice, rope.base.ast.Index):
|
||||
self.nodes.append((node, levels))
|
||||
|
|
@ -1,243 +0,0 @@
|
|||
"""A module for inferring objects
|
||||
|
||||
For more information see the documentation in `rope.base.oi`
|
||||
package.
|
||||
|
||||
"""
|
||||
import rope.base.builtins
|
||||
import rope.base.pynames
|
||||
import rope.base.pyobjects
|
||||
from rope.base import evaluate, utils, arguments
|
||||
from rope.base.oi.docstrings import hint_return, hint_param, hint_attr, hint_pep0484
|
||||
|
||||
|
||||
_ignore_inferred = utils.ignore_exception(
|
||||
rope.base.pyobjects.IsBeingInferredError)
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def infer_returned_object(pyfunction, args):
|
||||
"""Infer the `PyObject` this `PyFunction` returns after calling"""
|
||||
object_info = pyfunction.pycore.object_info
|
||||
result = object_info.get_exact_returned(pyfunction, args)
|
||||
if result is not None:
|
||||
return result
|
||||
result = _infer_returned(pyfunction, args)
|
||||
if result is not None:
|
||||
if args and pyfunction.get_module().get_resource() is not None:
|
||||
params = args.get_arguments(
|
||||
pyfunction.get_param_names(special_args=False))
|
||||
object_info.function_called(pyfunction, params, result)
|
||||
return result
|
||||
result = object_info.get_returned(pyfunction, args)
|
||||
if result is not None:
|
||||
return result
|
||||
type_ = hint_return(pyfunction)
|
||||
if type_ is not None:
|
||||
return rope.base.pyobjects.PyObject(type_)
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def infer_parameter_objects(pyfunction):
|
||||
"""Infer the `PyObject`\s of parameters of this `PyFunction`"""
|
||||
object_info = pyfunction.pycore.object_info
|
||||
result = object_info.get_parameter_objects(pyfunction)
|
||||
if result is None:
|
||||
result = _parameter_objects(pyfunction)
|
||||
_handle_first_parameter(pyfunction, result)
|
||||
return result
|
||||
|
||||
|
||||
def _handle_first_parameter(pyobject, parameters):
|
||||
kind = pyobject.get_kind()
|
||||
if parameters is None or kind not in ['method', 'classmethod']:
|
||||
pass
|
||||
if not parameters:
|
||||
if not pyobject.get_param_names(special_args=False):
|
||||
return
|
||||
parameters.append(rope.base.pyobjects.get_unknown())
|
||||
if kind == 'method':
|
||||
parameters[0] = rope.base.pyobjects.PyObject(pyobject.parent)
|
||||
if kind == 'classmethod':
|
||||
parameters[0] = pyobject.parent
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def infer_assigned_object(pyname):
|
||||
if not pyname.assignments:
|
||||
return
|
||||
for assignment in reversed(pyname.assignments):
|
||||
result = _infer_assignment(assignment, pyname.module)
|
||||
if isinstance(result, rope.base.builtins.BuiltinUnknown) and result.get_name() == 'NotImplementedType':
|
||||
break
|
||||
elif result == rope.base.pyobjects.get_unknown():
|
||||
break
|
||||
elif result is not None:
|
||||
return result
|
||||
|
||||
hinting_result = hint_pep0484(pyname)
|
||||
if hinting_result is not None:
|
||||
return hinting_result
|
||||
|
||||
hinting_result = _infer_assigned_object_by_hint(pyname)
|
||||
if hinting_result is not None:
|
||||
return hinting_result
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _infer_assigned_object_by_hint(pyname):
|
||||
lineno = _get_lineno_for_node(pyname.assignments[0].ast_node)
|
||||
holding_scope = pyname.module.get_scope().get_inner_scope_for_line(lineno)
|
||||
pyobject = holding_scope.pyobject
|
||||
if isinstance(pyobject, rope.base.pyobjects.PyClass):
|
||||
pyclass = pyobject
|
||||
elif (isinstance(pyobject, rope.base.pyobjectsdef.PyFunction) and
|
||||
isinstance(pyobject.parent, rope.base.pyobjects.PyClass)):
|
||||
pyclass = pyobject.parent
|
||||
else:
|
||||
return
|
||||
for name, attr in pyclass.get_attributes().items():
|
||||
if attr is pyname:
|
||||
type_ = hint_attr(pyclass, name)
|
||||
if type_ is not None:
|
||||
return rope.base.pyobjects.PyObject(type_)
|
||||
break
|
||||
|
||||
|
||||
def get_passed_objects(pyfunction, parameter_index):
|
||||
object_info = pyfunction.pycore.object_info
|
||||
result = object_info.get_passed_objects(pyfunction,
|
||||
parameter_index)
|
||||
if not result:
|
||||
statically_inferred = _parameter_objects(pyfunction)
|
||||
if len(statically_inferred) > parameter_index:
|
||||
result.append(statically_inferred[parameter_index])
|
||||
return result
|
||||
|
||||
|
||||
def _infer_returned(pyobject, args):
|
||||
if args:
|
||||
# HACK: Setting parameter objects manually
|
||||
# This is not thread safe and might cause problems if `args`
|
||||
# does not come from a good call site
|
||||
pyobject.get_scope().invalidate_data()
|
||||
pyobject._set_parameter_pyobjects(
|
||||
args.get_arguments(pyobject.get_param_names(special_args=False)))
|
||||
scope = pyobject.get_scope()
|
||||
if not scope._get_returned_asts():
|
||||
return
|
||||
maxtries = 3
|
||||
for returned_node in reversed(scope._get_returned_asts()[-maxtries:]):
|
||||
try:
|
||||
resulting_pyname = evaluate.eval_node(scope, returned_node)
|
||||
if resulting_pyname is None:
|
||||
continue
|
||||
pyobject = resulting_pyname.get_object()
|
||||
if pyobject == rope.base.pyobjects.get_unknown():
|
||||
continue
|
||||
if not scope._is_generator():
|
||||
return pyobject
|
||||
else:
|
||||
return rope.base.builtins.get_generator(pyobject)
|
||||
except rope.base.pyobjects.IsBeingInferredError:
|
||||
pass
|
||||
|
||||
|
||||
def _parameter_objects(pyobject):
|
||||
result = []
|
||||
params = pyobject.get_param_names(special_args=False)
|
||||
for name in params:
|
||||
type_ = hint_param(pyobject, name)
|
||||
if type_ is not None:
|
||||
result.append(rope.base.pyobjects.PyObject(type_))
|
||||
else:
|
||||
result.append(rope.base.pyobjects.get_unknown())
|
||||
return result
|
||||
|
||||
# handling `rope.base.pynames.AssignmentValue`
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def _infer_assignment(assignment, pymodule):
|
||||
result = _follow_pyname(assignment, pymodule)
|
||||
if result is None:
|
||||
return None
|
||||
pyname, pyobject = result
|
||||
pyobject = _follow_evaluations(assignment, pyname, pyobject)
|
||||
if pyobject is None:
|
||||
return None
|
||||
return _follow_levels(assignment, pyobject)
|
||||
|
||||
|
||||
def _follow_levels(assignment, pyobject):
|
||||
for index in assignment.levels:
|
||||
if isinstance(pyobject.get_type(), rope.base.builtins.Tuple):
|
||||
holdings = pyobject.get_type().get_holding_objects()
|
||||
if holdings:
|
||||
pyobject = holdings[min(len(holdings) - 1, index)]
|
||||
else:
|
||||
pyobject = None
|
||||
elif isinstance(pyobject.get_type(), rope.base.builtins.List):
|
||||
pyobject = pyobject.get_type().holding
|
||||
else:
|
||||
pyobject = None
|
||||
if pyobject is None:
|
||||
break
|
||||
return pyobject
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def _follow_pyname(assignment, pymodule, lineno=None):
|
||||
assign_node = assignment.ast_node
|
||||
if lineno is None:
|
||||
lineno = _get_lineno_for_node(assign_node)
|
||||
holding_scope = pymodule.get_scope().get_inner_scope_for_line(lineno)
|
||||
pyname = evaluate.eval_node(holding_scope, assign_node)
|
||||
if pyname is not None:
|
||||
result = pyname.get_object()
|
||||
if isinstance(result.get_type(), rope.base.builtins.Property) and \
|
||||
holding_scope.get_kind() == 'Class':
|
||||
arg = rope.base.pynames.UnboundName(
|
||||
rope.base.pyobjects.PyObject(holding_scope.pyobject))
|
||||
return pyname, result.get_type().get_property_object(
|
||||
arguments.ObjectArguments([arg]))
|
||||
return pyname, result
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def _follow_evaluations(assignment, pyname, pyobject):
|
||||
new_pyname = pyname
|
||||
tokens = assignment.evaluation.split('.')
|
||||
for token in tokens:
|
||||
call = token.endswith('()')
|
||||
if call:
|
||||
token = token[:-2]
|
||||
if token:
|
||||
pyname = new_pyname
|
||||
new_pyname = _get_attribute(pyobject, token)
|
||||
if new_pyname is not None:
|
||||
pyobject = new_pyname.get_object()
|
||||
if pyobject is not None and call:
|
||||
if isinstance(pyobject, rope.base.pyobjects.AbstractFunction):
|
||||
args = arguments.ObjectArguments([pyname])
|
||||
pyobject = pyobject.get_returned_object(args)
|
||||
else:
|
||||
pyobject = None
|
||||
if pyobject is None:
|
||||
break
|
||||
if pyobject is not None and assignment.assign_type:
|
||||
return rope.base.pyobjects.PyObject(pyobject)
|
||||
return pyobject
|
||||
|
||||
|
||||
def _get_lineno_for_node(assign_node):
|
||||
if hasattr(assign_node, 'lineno') and \
|
||||
assign_node.lineno is not None:
|
||||
return assign_node.lineno
|
||||
return 1
|
||||
|
||||
|
||||
def _get_attribute(pyobject, name):
|
||||
if pyobject is not None and name in pyobject:
|
||||
return pyobject[name]
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
"""Provides classes for persisting `PyObject`\s"""
|
||||
import os
|
||||
import re
|
||||
|
||||
import rope.base.builtins
|
||||
from rope.base import exceptions
|
||||
|
||||
|
||||
class PyObjectToTextual(object):
|
||||
"""For transforming `PyObject` to textual form
|
||||
|
||||
This can be used for storing `PyObjects` in files. Use
|
||||
`TextualToPyObject` for converting back.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
|
||||
def transform(self, pyobject):
|
||||
"""Transform a `PyObject` to textual form"""
|
||||
if pyobject is None:
|
||||
return ('none',)
|
||||
object_type = type(pyobject)
|
||||
try:
|
||||
method = getattr(self, object_type.__name__ + '_to_textual')
|
||||
return method(pyobject)
|
||||
except AttributeError:
|
||||
return ('unknown',)
|
||||
|
||||
def __call__(self, pyobject):
|
||||
return self.transform(pyobject)
|
||||
|
||||
def PyObject_to_textual(self, pyobject):
|
||||
if isinstance(pyobject.get_type(), rope.base.pyobjects.AbstractClass):
|
||||
result = self.transform(pyobject.get_type())
|
||||
if result[0] == 'defined':
|
||||
return ('instance', result)
|
||||
return result
|
||||
return ('unknown',)
|
||||
|
||||
def PyFunction_to_textual(self, pyobject):
|
||||
return self._defined_to_textual(pyobject)
|
||||
|
||||
def PyClass_to_textual(self, pyobject):
|
||||
return self._defined_to_textual(pyobject)
|
||||
|
||||
def _defined_to_textual(self, pyobject):
|
||||
address = []
|
||||
while pyobject.parent is not None:
|
||||
address.insert(0, pyobject.get_name())
|
||||
pyobject = pyobject.parent
|
||||
return ('defined', self._get_pymodule_path(pyobject.get_module()),
|
||||
'.'.join(address))
|
||||
|
||||
def PyModule_to_textual(self, pyobject):
|
||||
return ('defined', self._get_pymodule_path(pyobject))
|
||||
|
||||
def PyPackage_to_textual(self, pyobject):
|
||||
return ('defined', self._get_pymodule_path(pyobject))
|
||||
|
||||
def List_to_textual(self, pyobject):
|
||||
return ('builtin', 'list', self.transform(pyobject.holding))
|
||||
|
||||
def Dict_to_textual(self, pyobject):
|
||||
return ('builtin', 'dict', self.transform(pyobject.keys),
|
||||
self.transform(pyobject.values))
|
||||
|
||||
def Tuple_to_textual(self, pyobject):
|
||||
objects = [self.transform(holding)
|
||||
for holding in pyobject.get_holding_objects()]
|
||||
return tuple(['builtin', 'tuple'] + objects)
|
||||
|
||||
def Set_to_textual(self, pyobject):
|
||||
return ('builtin', 'set', self.transform(pyobject.holding))
|
||||
|
||||
def Iterator_to_textual(self, pyobject):
|
||||
return ('builtin', 'iter', self.transform(pyobject.holding))
|
||||
|
||||
def Generator_to_textual(self, pyobject):
|
||||
return ('builtin', 'generator', self.transform(pyobject.holding))
|
||||
|
||||
def Str_to_textual(self, pyobject):
|
||||
return ('builtin', 'str')
|
||||
|
||||
def File_to_textual(self, pyobject):
|
||||
return ('builtin', 'file')
|
||||
|
||||
def BuiltinFunction_to_textual(self, pyobject):
|
||||
return ('builtin', 'function', pyobject.get_name())
|
||||
|
||||
def _get_pymodule_path(self, pymodule):
|
||||
return self.resource_to_path(pymodule.get_resource())
|
||||
|
||||
def resource_to_path(self, resource):
|
||||
if resource.project == self.project:
|
||||
return resource.path
|
||||
else:
|
||||
return resource.real_path
|
||||
|
||||
|
||||
class TextualToPyObject(object):
|
||||
"""For transforming textual form to `PyObject`"""
|
||||
|
||||
def __init__(self, project, allow_in_project_absolutes=False):
|
||||
self.project = project
|
||||
|
||||
def __call__(self, textual):
|
||||
return self.transform(textual)
|
||||
|
||||
def transform(self, textual):
|
||||
"""Transform an object from textual form to `PyObject`"""
|
||||
if textual is None:
|
||||
return None
|
||||
type = textual[0]
|
||||
try:
|
||||
method = getattr(self, type + '_to_pyobject')
|
||||
return method(textual)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def builtin_to_pyobject(self, textual):
|
||||
method = getattr(self, 'builtin_%s_to_pyobject' % textual[1], None)
|
||||
if method is not None:
|
||||
return method(textual)
|
||||
|
||||
def builtin_str_to_pyobject(self, textual):
|
||||
return rope.base.builtins.get_str()
|
||||
|
||||
def builtin_list_to_pyobject(self, textual):
|
||||
holding = self.transform(textual[2])
|
||||
return rope.base.builtins.get_list(holding)
|
||||
|
||||
def builtin_dict_to_pyobject(self, textual):
|
||||
keys = self.transform(textual[2])
|
||||
values = self.transform(textual[3])
|
||||
return rope.base.builtins.get_dict(keys, values)
|
||||
|
||||
def builtin_tuple_to_pyobject(self, textual):
|
||||
objects = []
|
||||
for holding in textual[2:]:
|
||||
objects.append(self.transform(holding))
|
||||
return rope.base.builtins.get_tuple(*objects)
|
||||
|
||||
def builtin_set_to_pyobject(self, textual):
|
||||
holding = self.transform(textual[2])
|
||||
return rope.base.builtins.get_set(holding)
|
||||
|
||||
def builtin_iter_to_pyobject(self, textual):
|
||||
holding = self.transform(textual[2])
|
||||
return rope.base.builtins.get_iterator(holding)
|
||||
|
||||
def builtin_generator_to_pyobject(self, textual):
|
||||
holding = self.transform(textual[2])
|
||||
return rope.base.builtins.get_generator(holding)
|
||||
|
||||
def builtin_file_to_pyobject(self, textual):
|
||||
return rope.base.builtins.get_file()
|
||||
|
||||
def builtin_function_to_pyobject(self, textual):
|
||||
if textual[2] in rope.base.builtins.builtins:
|
||||
return rope.base.builtins.builtins[textual[2]].get_object()
|
||||
|
||||
def unknown_to_pyobject(self, textual):
|
||||
return None
|
||||
|
||||
def none_to_pyobject(self, textual):
|
||||
return None
|
||||
|
||||
def _module_to_pyobject(self, textual):
|
||||
path = textual[1]
|
||||
return self._get_pymodule(path)
|
||||
|
||||
def _hierarchical_defined_to_pyobject(self, textual):
|
||||
path = textual[1]
|
||||
names = textual[2].split('.')
|
||||
pymodule = self._get_pymodule(path)
|
||||
pyobject = pymodule
|
||||
for name in names:
|
||||
if pyobject is None:
|
||||
return None
|
||||
if isinstance(pyobject, rope.base.pyobjects.PyDefinedObject):
|
||||
try:
|
||||
pyobject = pyobject.get_scope()[name].get_object()
|
||||
except exceptions.NameNotFoundError:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
return pyobject
|
||||
|
||||
def defined_to_pyobject(self, textual):
|
||||
if len(textual) == 2 or textual[2] == '':
|
||||
return self._module_to_pyobject(textual)
|
||||
else:
|
||||
return self._hierarchical_defined_to_pyobject(textual)
|
||||
|
||||
def instance_to_pyobject(self, textual):
|
||||
type = self.transform(textual[1])
|
||||
if type is not None:
|
||||
return rope.base.pyobjects.PyObject(type)
|
||||
|
||||
def _get_pymodule(self, path):
|
||||
resource = self.path_to_resource(path)
|
||||
if resource is not None:
|
||||
return self.project.get_pymodule(resource)
|
||||
|
||||
def path_to_resource(self, path):
|
||||
try:
|
||||
root = self.project.address
|
||||
if not os.path.isabs(path):
|
||||
return self.project.get_resource(path)
|
||||
if path == root or path.startswith(root + os.sep):
|
||||
# INFO: This is a project file; should not be absolute
|
||||
return None
|
||||
import rope.base.project
|
||||
return rope.base.project.get_no_project().get_resource(path)
|
||||
except exceptions.ResourceNotFoundError:
|
||||
return None
|
||||
|
||||
|
||||
class DOITextualToPyObject(TextualToPyObject):
|
||||
"""For transforming textual form to `PyObject`
|
||||
|
||||
The textual form DOI uses is different from rope's standard
|
||||
textual form. The reason is that we cannot find the needed
|
||||
information by analyzing live objects. This class can be
|
||||
used to transform DOI textual form to `PyObject` and later
|
||||
we can convert it to standard textual form using
|
||||
`TextualToPyObject` class.
|
||||
|
||||
"""
|
||||
|
||||
def _function_to_pyobject(self, textual):
|
||||
path = textual[1]
|
||||
lineno = int(textual[2])
|
||||
pymodule = self._get_pymodule(path)
|
||||
if pymodule is not None:
|
||||
scope = pymodule.get_scope()
|
||||
inner_scope = scope.get_inner_scope_for_line(lineno)
|
||||
return inner_scope.pyobject
|
||||
|
||||
def _class_to_pyobject(self, textual):
|
||||
path, name = textual[1:]
|
||||
pymodule = self._get_pymodule(path)
|
||||
if pymodule is None:
|
||||
return None
|
||||
module_scope = pymodule.get_scope()
|
||||
suspected = None
|
||||
if name in module_scope.get_names():
|
||||
suspected = module_scope[name].get_object()
|
||||
if suspected is not None and \
|
||||
isinstance(suspected, rope.base.pyobjects.PyClass):
|
||||
return suspected
|
||||
else:
|
||||
lineno = self._find_occurrence(name,
|
||||
pymodule.get_resource().read())
|
||||
if lineno is not None:
|
||||
inner_scope = module_scope.get_inner_scope_for_line(lineno)
|
||||
return inner_scope.pyobject
|
||||
|
||||
def defined_to_pyobject(self, textual):
|
||||
if len(textual) == 2:
|
||||
return self._module_to_pyobject(textual)
|
||||
else:
|
||||
if textual[2].isdigit():
|
||||
result = self._function_to_pyobject(textual)
|
||||
else:
|
||||
result = self._class_to_pyobject(textual)
|
||||
if not isinstance(result, rope.base.pyobjects.PyModule):
|
||||
return result
|
||||
|
||||
def _find_occurrence(self, name, source):
|
||||
pattern = re.compile(r'^\s*class\s*' + name + r'\b')
|
||||
lines = source.split('\n')
|
||||
for i in range(len(lines)):
|
||||
if pattern.match(lines[i]):
|
||||
return i + 1
|
||||
|
||||
def path_to_resource(self, path):
|
||||
import rope.base.libutils
|
||||
relpath = rope.base.libutils.path_relative_to_project_root(
|
||||
self.project, path)
|
||||
if relpath is not None:
|
||||
path = relpath
|
||||
return super(DOITextualToPyObject, self).path_to_resource(path)
|
||||
Loading…
Add table
Add a link
Reference in a new issue