forked from VimPlug/jedi
Merge branch 'dev' of git://github.com/davidhalter/jedi
This commit is contained in:
@@ -42,8 +42,8 @@ import sys
|
||||
# imports and circular imports... Just avoid it:
|
||||
sys.path.insert(0, __path__[0])
|
||||
|
||||
from .api import Script, NotFoundError, set_debug_function, _quick_complete, \
|
||||
preload_module
|
||||
from .api import Script, Interpreter, NotFoundError, set_debug_function, \
|
||||
preload_module, defined_names
|
||||
from . import settings
|
||||
|
||||
sys.path.pop(0)
|
||||
|
||||
2
jedi/__main__.py
Normal file
2
jedi/__main__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from os import path
|
||||
print(path.join(path.dirname(path.abspath(__file__)), 'replstartup.py'))
|
||||
83
jedi/api.py
83
jedi/api.py
@@ -20,6 +20,7 @@ from jedi import helpers
|
||||
from jedi import common
|
||||
from jedi import cache
|
||||
from jedi import modules
|
||||
from jedi import interpret
|
||||
from jedi._compatibility import next, unicode
|
||||
import evaluate
|
||||
import keywords
|
||||
@@ -53,14 +54,18 @@ class Script(object):
|
||||
``unicode`` object (default ``'utf-8'``).
|
||||
:type source_encoding: str
|
||||
"""
|
||||
def __init__(self, source, line, column, source_path,
|
||||
source_encoding='utf-8'):
|
||||
def __init__(self, source, line=None, column=None, source_path=None,
|
||||
source_encoding='utf-8'):
|
||||
lines = source.splitlines()
|
||||
line = len(lines) if line is None else line
|
||||
column = len(lines[-1]) if column is None else column
|
||||
|
||||
api_classes._clear_caches()
|
||||
debug.reset_time()
|
||||
self.source = modules.source_to_unicode(source, source_encoding)
|
||||
self.pos = line, column
|
||||
self._module = modules.ModuleWithCursor(source_path,
|
||||
source=self.source, position=self.pos)
|
||||
self._module = modules.ModuleWithCursor(
|
||||
source_path, source=self.source, position=self.pos)
|
||||
self._source_path = source_path
|
||||
self.source_path = None if source_path is None \
|
||||
else os.path.abspath(source_path)
|
||||
@@ -337,7 +342,8 @@ class Script(object):
|
||||
|
||||
:rtype: list of :class:`api_classes.Definition`
|
||||
"""
|
||||
d = [api_classes.Definition(d) for d in set(self._goto()[0])]
|
||||
d = [api_classes.Definition(d) for d in set(self._goto()[0])
|
||||
if not isinstance(d, imports.ImportPath._GlobalNamespace)]
|
||||
return self._sorted_defs(d)
|
||||
|
||||
def _goto(self, add_import_name=False):
|
||||
@@ -385,7 +391,9 @@ class Script(object):
|
||||
defs, search_name = evaluate.goto(stmt)
|
||||
definitions = follow_inexistent_imports(defs)
|
||||
if isinstance(user_stmt, pr.Statement):
|
||||
if user_stmt.get_commands()[0].start_pos > self.pos:
|
||||
call = user_stmt.get_commands()[0]
|
||||
if not isinstance(call, (str, unicode)) and \
|
||||
call.start_pos > self.pos:
|
||||
# The cursor must be after the start, otherwise the
|
||||
# statement is just an assignee.
|
||||
definitions = [user_stmt]
|
||||
@@ -503,6 +511,45 @@ class Script(object):
|
||||
return sorted(d, key=lambda x: (x.module_path or '', x.start_pos))
|
||||
|
||||
|
||||
class Interpreter(Script):
|
||||
|
||||
"""
|
||||
Jedi API for Python REPLs.
|
||||
|
||||
In addition to completion of simple attribute access, Jedi
|
||||
supports code completion based on static code analysis.
|
||||
Jedi can complete attributes of object which is not initialized
|
||||
yet.
|
||||
|
||||
>>> from os.path import join
|
||||
>>> namespace = locals()
|
||||
>>> script = Interpreter('join().up', [namespace])
|
||||
>>> print(script.complete()[0].word)
|
||||
upper
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, source, namespaces=[], **kwds):
|
||||
"""
|
||||
Parse `source` and mixin interpreted Python objects from `namespaces`.
|
||||
|
||||
:type source: str
|
||||
:arg source: Code to parse.
|
||||
:type namespaces: list of dict
|
||||
:arg namespaces: a list of namespace dictionaries such as the one
|
||||
returned by :func:`locals`.
|
||||
|
||||
Other optional arguments are same as the ones for :class:`Script`.
|
||||
If `line` and `column` are None, they are assumed be at the end of
|
||||
`source`.
|
||||
"""
|
||||
super(Interpreter, self).__init__(source, **kwds)
|
||||
|
||||
importer = interpret.ObjectImporter(self._parser.user_scope)
|
||||
for ns in namespaces:
|
||||
importer.import_raw_namespace(ns)
|
||||
|
||||
|
||||
def defined_names(source, source_path=None, source_encoding='utf-8'):
|
||||
"""
|
||||
Get all definitions in `source` sorted by its position.
|
||||
@@ -519,7 +566,7 @@ def defined_names(source, source_path=None, source_encoding='utf-8'):
|
||||
modules.source_to_unicode(source, source_encoding),
|
||||
module_path=source_path,
|
||||
)
|
||||
return api_classes._defined_names(parser.scope)
|
||||
return api_classes._defined_names(parser.module)
|
||||
|
||||
|
||||
def preload_module(*modules):
|
||||
@@ -545,25 +592,3 @@ def set_debug_function(func_cb=debug.print_to_stdout, warnings=True,
|
||||
debug.enable_warning = warnings
|
||||
debug.enable_notice = notices
|
||||
debug.enable_speed = speed
|
||||
|
||||
|
||||
def _quick_complete(source):
|
||||
"""
|
||||
Convenience function to complete a source string at the end.
|
||||
|
||||
Example:
|
||||
|
||||
>>> _quick_complete('''
|
||||
... import datetime
|
||||
... datetime.da''') #doctest: +ELLIPSIS
|
||||
[<Completion: date>, <Completion: datetime>, ...]
|
||||
|
||||
:param source: The source code to be completed.
|
||||
:type source: string
|
||||
:return: Completion objects as returned by :meth:`complete`.
|
||||
:rtype: list of :class:`api_classes.Completion`
|
||||
"""
|
||||
lines = re.sub(r'[\n\r\s]*$', '', source).splitlines()
|
||||
pos = len(lines), len(lines[-1])
|
||||
script = Script(source, pos[0], pos[1], '')
|
||||
return script.completions()
|
||||
|
||||
@@ -142,9 +142,19 @@ class BaseDefinition(object):
|
||||
def path(self):
|
||||
"""The module path."""
|
||||
path = []
|
||||
|
||||
def insert_nonnone(x):
|
||||
if x:
|
||||
path.insert(0, x)
|
||||
|
||||
if not isinstance(self._definition, keywords.Keyword):
|
||||
par = self._definition
|
||||
while par is not None:
|
||||
if isinstance(par, pr.Import):
|
||||
insert_nonnone(par.namespace)
|
||||
insert_nonnone(par.from_ns)
|
||||
if par.relative_count == 0:
|
||||
break
|
||||
with common.ignored(AttributeError):
|
||||
path.insert(0, par.name)
|
||||
par = par.parent
|
||||
|
||||
@@ -318,7 +318,9 @@ def find_name(scope, name_str, position=None, search_global=False,
|
||||
exc = pr.Class, pr.Function
|
||||
until = lambda: par.parent.parent.get_parent_until(exc)
|
||||
|
||||
if par.isinstance(pr.Flow):
|
||||
if par is None:
|
||||
pass
|
||||
elif par.isinstance(pr.Flow):
|
||||
if par.command == 'for':
|
||||
result += handle_for_loops(par)
|
||||
else:
|
||||
@@ -554,16 +556,33 @@ def assign_tuples(tup, results, seek_name):
|
||||
else:
|
||||
r = eval_results(i)
|
||||
|
||||
# are there still tuples or is it just a Call.
|
||||
if isinstance(command, pr.Array):
|
||||
# These are "sub"-tuples.
|
||||
result += assign_tuples(command, r, seek_name)
|
||||
else:
|
||||
if command.name.names[-1] == seek_name:
|
||||
result += r
|
||||
# LHS of tuples can be nested, so resolve it recursively
|
||||
result += find_assignments(command, r, seek_name)
|
||||
return result
|
||||
|
||||
|
||||
def find_assignments(lhs, results, seek_name):
|
||||
"""
|
||||
Check if `seek_name` is in the left hand side `lhs` of assignment.
|
||||
|
||||
`lhs` can simply be a variable (`pr.Call`) or a tuple/list (`pr.Array`)
|
||||
representing the following cases::
|
||||
|
||||
a = 1 # lhs is pr.Call
|
||||
(a, b) = 2 # lhs is pr.Array
|
||||
|
||||
:type lhs: pr.Call
|
||||
:type results: list
|
||||
:type seek_name: str
|
||||
"""
|
||||
if isinstance(lhs, pr.Array):
|
||||
return assign_tuples(lhs, results, seek_name)
|
||||
elif lhs.name.names[-1] == seek_name:
|
||||
return results
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
@recursion.RecursionDecorator
|
||||
@cache.memoize_default(default=())
|
||||
def follow_statement(stmt, seek_name=None):
|
||||
@@ -587,7 +606,7 @@ def follow_statement(stmt, seek_name=None):
|
||||
if len(stmt.get_set_vars()) > 1 and seek_name and stmt.assignment_details:
|
||||
new_result = []
|
||||
for ass_commands, op in stmt.assignment_details:
|
||||
new_result += assign_tuples(ass_commands[0], result, seek_name)
|
||||
new_result += find_assignments(ass_commands[0], result, seek_name)
|
||||
result = new_result
|
||||
return set(result)
|
||||
|
||||
@@ -775,7 +794,10 @@ def goto(stmt, call_path=None):
|
||||
commands = stmt.get_commands()
|
||||
assert len(commands) == 1
|
||||
call = commands[0]
|
||||
call_path = list(call.generate_call_path())
|
||||
if isinstance(call, (str, unicode)):
|
||||
call_path = [call]
|
||||
else:
|
||||
call_path = list(call.generate_call_path())
|
||||
|
||||
scope = stmt.get_parent_until(pr.IsScope)
|
||||
pos = stmt.start_pos
|
||||
|
||||
@@ -162,9 +162,9 @@ class ImportPath(pr.Base):
|
||||
# If you edit e.g. gunicorn, there will be imports like this:
|
||||
# `from gunicorn import something`. But gunicorn is not in the
|
||||
# sys.path. Therefore look if gunicorn is a parent directory, #56.
|
||||
parts = self.file_path.split(os.path.sep)
|
||||
in_path = []
|
||||
if self.import_path:
|
||||
parts = self.file_path.split(os.path.sep)
|
||||
for i, p in enumerate(parts):
|
||||
if p == self.import_path[0]:
|
||||
new = os.path.sep.join(parts[:i])
|
||||
|
||||
171
jedi/interpret.py
Normal file
171
jedi/interpret.py
Normal file
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
Module to handle interpreted Python objects.
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import tokenize
|
||||
|
||||
from jedi import parsing_representation as pr
|
||||
|
||||
|
||||
class ObjectImporter(object):
|
||||
|
||||
"""
|
||||
Import objects in "raw" namespace such as :func:`locals`.
|
||||
"""
|
||||
|
||||
def __init__(self, scope):
|
||||
self.scope = scope
|
||||
|
||||
count = itertools.count()
|
||||
self._genname = lambda: '*jedi-%s*' % next(count)
|
||||
"""
|
||||
Generate unique variable names to avoid name collision.
|
||||
To avoid name collision to already defined names, generated
|
||||
names are invalid as Python identifier.
|
||||
"""
|
||||
|
||||
def import_raw_namespace(self, raw_namespace):
|
||||
"""
|
||||
Import interpreted Python objects in a namespace.
|
||||
|
||||
Three kinds of objects are treated here.
|
||||
|
||||
1. Functions and classes. The objects imported like this::
|
||||
|
||||
from os.path import join
|
||||
|
||||
2. Modules. The objects imported like this::
|
||||
|
||||
import os
|
||||
|
||||
3. Instances. The objects created like this::
|
||||
|
||||
from datetime import datetime
|
||||
dt = datetime(2013, 1, 1)
|
||||
|
||||
:type raw_namespace: dict
|
||||
:arg raw_namespace: e.g., the dict given by `locals`
|
||||
"""
|
||||
scope = self.scope
|
||||
for (variable, obj) in raw_namespace.items():
|
||||
objname = getattr(obj, '__name__', None)
|
||||
|
||||
# Import functions and classes
|
||||
module = getattr(obj, '__module__', None)
|
||||
if module and objname:
|
||||
fakeimport = self.make_fakeimport(module, objname, variable)
|
||||
scope.add_import(fakeimport)
|
||||
continue
|
||||
|
||||
# Import modules
|
||||
if getattr(obj, '__file__', None) and objname:
|
||||
fakeimport = self.make_fakeimport(objname)
|
||||
scope.add_import(fakeimport)
|
||||
continue
|
||||
|
||||
# Import instances
|
||||
objclass = getattr(obj, '__class__', None)
|
||||
module = getattr(objclass, '__module__', None)
|
||||
if objclass and module:
|
||||
alias = self._genname()
|
||||
fakeimport = self.make_fakeimport(module, objclass.__name__,
|
||||
alias)
|
||||
fakestmt = self.make_fakestatement(variable, alias, call=True)
|
||||
scope.add_import(fakeimport)
|
||||
scope.add_statement(fakestmt)
|
||||
continue
|
||||
|
||||
def make_fakeimport(self, module, variable=None, alias=None):
|
||||
"""
|
||||
Make a fake import object.
|
||||
|
||||
The following statements are created depending on what parameters
|
||||
are given:
|
||||
|
||||
- only `module`: ``import <module>``
|
||||
- `module` and `variable`: ``from <module> import <variable>``
|
||||
- all: ``from <module> import <variable> as <alias>``
|
||||
|
||||
:type module: str
|
||||
:arg module: ``<module>`` part in ``from <module> import ...``
|
||||
:type variable: str
|
||||
:arg variable: ``<variable>`` part in ``from ... import <variable>``
|
||||
:type alias: str
|
||||
:arg alias: ``<alias>`` part in ``... import ... as <alias>``.
|
||||
|
||||
:rtype: :class:`parsing_representation.Import`
|
||||
"""
|
||||
submodule = self.scope._sub_module
|
||||
if variable:
|
||||
varname = pr.Name(
|
||||
module=submodule,
|
||||
names=[(variable, (-1, 0))],
|
||||
start_pos=(-1, 0),
|
||||
end_pos=(None, None))
|
||||
else:
|
||||
varname = None
|
||||
modname = pr.Name(
|
||||
module=submodule,
|
||||
names=[(module, (-1, 0))],
|
||||
start_pos=(-1, 0),
|
||||
end_pos=(None, None))
|
||||
if alias:
|
||||
aliasname = pr.Name(
|
||||
module=submodule,
|
||||
names=[(alias, (-1, 0))],
|
||||
start_pos=(-1, 0),
|
||||
end_pos=(None, None))
|
||||
else:
|
||||
aliasname = None
|
||||
if varname:
|
||||
fakeimport = pr.Import(
|
||||
module=submodule,
|
||||
namespace=varname,
|
||||
from_ns=modname,
|
||||
alias=aliasname,
|
||||
start_pos=(-1, 0),
|
||||
end_pos=(None, None))
|
||||
else:
|
||||
fakeimport = pr.Import(
|
||||
module=submodule,
|
||||
namespace=modname,
|
||||
alias=aliasname,
|
||||
start_pos=(-1, 0),
|
||||
end_pos=(None, None))
|
||||
return fakeimport
|
||||
|
||||
def make_fakestatement(self, lhs, rhs, call=False):
|
||||
"""
|
||||
Make a fake statement object that represents ``lhs = rhs``.
|
||||
|
||||
:type call: bool
|
||||
:arg call: When `call` is true, make a fake statement that represents
|
||||
``lhs = rhs()``.
|
||||
|
||||
:rtype: :class:`parsing_representation.Statement`
|
||||
"""
|
||||
submodule = self.scope._sub_module
|
||||
lhsname = pr.Name(
|
||||
module=submodule,
|
||||
names=[(lhs, (0, 0))],
|
||||
start_pos=(0, 0),
|
||||
end_pos=(None, None))
|
||||
rhsname = pr.Name(
|
||||
module=submodule,
|
||||
names=[(rhs, (0, 0))],
|
||||
start_pos=(0, 0),
|
||||
end_pos=(None, None))
|
||||
token_list = [lhsname, (tokenize.OP, '=', (0, 0)), rhsname]
|
||||
if call:
|
||||
token_list.extend([
|
||||
(tokenize.OP, '(', (0, 0)),
|
||||
(tokenize.OP, ')', (0, 0)),
|
||||
])
|
||||
return pr.Statement(
|
||||
module=submodule,
|
||||
set_vars=[lhsname],
|
||||
used_vars=[rhsname],
|
||||
token_list=token_list,
|
||||
start_pos=(0, 0),
|
||||
end_pos=(None, None))
|
||||
@@ -141,6 +141,7 @@ class ModuleWithCursor(Module):
|
||||
last_line = self.get_line(self._line_temp)
|
||||
if last_line and last_line[-1] == '\\':
|
||||
line = last_line[:-1] + ' ' + line
|
||||
self._line_length = len(last_line)
|
||||
else:
|
||||
break
|
||||
return line[::-1]
|
||||
@@ -187,6 +188,7 @@ class ModuleWithCursor(Module):
|
||||
elif token_type == tokenize.NUMBER:
|
||||
pass
|
||||
else:
|
||||
self._column_temp = self._line_length - end[1]
|
||||
break
|
||||
|
||||
self._column_temp = self._line_length - end[1]
|
||||
|
||||
@@ -56,7 +56,7 @@ class Parser(object):
|
||||
self.start_pos = self.end_pos = 1 + offset[0], offset[1]
|
||||
# initialize global Scope
|
||||
self.module = pr.SubModule(module_path, self.start_pos, top_module)
|
||||
self.scope = self.module
|
||||
self._scope = self.module
|
||||
self.current = (None, None)
|
||||
|
||||
source = source + '\n' # end with \n, because the parser needs it
|
||||
@@ -390,7 +390,7 @@ class Parser(object):
|
||||
#print 'new_stat', set_vars, used_vars
|
||||
if self.freshscope and not self.no_docstr and len(tok_list) == 1 \
|
||||
and self.last_token[0] == tokenize.STRING:
|
||||
self.scope.add_docstr(self.last_token[1])
|
||||
self._scope.add_docstr(self.last_token[1])
|
||||
return None, tok
|
||||
else:
|
||||
stmt = stmt_class(self.module, set_vars, used_vars, tok_list,
|
||||
@@ -408,7 +408,7 @@ class Parser(object):
|
||||
and len(stmt.token_list) == 1
|
||||
and first_tok[0] == tokenize.STRING):
|
||||
# ... then set it as a docstring
|
||||
self.scope.statements[-1].add_docstr(first_tok[1])
|
||||
self._scope.statements[-1].add_docstr(first_tok[1])
|
||||
|
||||
if tok in always_break + not_first_break:
|
||||
self._gen.push_last_back()
|
||||
@@ -429,7 +429,7 @@ class Parser(object):
|
||||
self.start_pos, self.end_pos = start_pos, end_pos
|
||||
except (StopIteration, common.MultiLevelStopIteration):
|
||||
# on finish, set end_pos correctly
|
||||
s = self.scope
|
||||
s = self._scope
|
||||
while s is not None:
|
||||
if isinstance(s, pr.Module) \
|
||||
and not isinstance(s, pr.SubModule):
|
||||
@@ -443,8 +443,8 @@ class Parser(object):
|
||||
or self.user_scope is None
|
||||
and self.start_pos[0] >= self.user_position[0]):
|
||||
debug.dbg('user scope found [%s] = %s' %
|
||||
(self.parserline.replace('\n', ''), repr(self.scope)))
|
||||
self.user_scope = self.scope
|
||||
(self.parserline.replace('\n', ''), repr(self._scope)))
|
||||
self.user_scope = self._scope
|
||||
self.last_token = self.current
|
||||
self.current = (typ, tok)
|
||||
return self.current
|
||||
@@ -472,29 +472,29 @@ class Parser(object):
|
||||
#debug.dbg('main: tok=[%s] type=[%s] indent=[%s]'\
|
||||
# % (tok, tokenize.tok_name[token_type], start_position[0]))
|
||||
|
||||
while token_type == tokenize.DEDENT and self.scope != self.module:
|
||||
while token_type == tokenize.DEDENT and self._scope != self.module:
|
||||
token_type, tok = self.next()
|
||||
if self.start_pos[1] <= self.scope.start_pos[1]:
|
||||
self.scope.end_pos = self.start_pos
|
||||
self.scope = self.scope.parent
|
||||
if isinstance(self.scope, pr.Module) \
|
||||
and not isinstance(self.scope, pr.SubModule):
|
||||
self.scope = self.module
|
||||
if self.start_pos[1] <= self._scope.start_pos[1]:
|
||||
self._scope.end_pos = self.start_pos
|
||||
self._scope = self._scope.parent
|
||||
if isinstance(self._scope, pr.Module) \
|
||||
and not isinstance(self._scope, pr.SubModule):
|
||||
self._scope = self.module
|
||||
|
||||
# check again for unindented stuff. this is true for syntax
|
||||
# errors. only check for names, because thats relevant here. If
|
||||
# some docstrings are not indented, I don't care.
|
||||
while self.start_pos[1] <= self.scope.start_pos[1] \
|
||||
while self.start_pos[1] <= self._scope.start_pos[1] \
|
||||
and (token_type == tokenize.NAME or tok in ['(', '['])\
|
||||
and self.scope != self.module:
|
||||
self.scope.end_pos = self.start_pos
|
||||
self.scope = self.scope.parent
|
||||
if isinstance(self.scope, pr.Module) \
|
||||
and not isinstance(self.scope, pr.SubModule):
|
||||
self.scope = self.module
|
||||
and self._scope != self.module:
|
||||
self._scope.end_pos = self.start_pos
|
||||
self._scope = self._scope.parent
|
||||
if isinstance(self._scope, pr.Module) \
|
||||
and not isinstance(self._scope, pr.SubModule):
|
||||
self._scope = self.module
|
||||
|
||||
use_as_parent_scope = self.top_module if isinstance(self.scope,
|
||||
pr.SubModule) else self.scope
|
||||
use_as_parent_scope = self.top_module if isinstance(self._scope,
|
||||
pr.SubModule) else self._scope
|
||||
first_pos = self.start_pos
|
||||
if tok == 'def':
|
||||
func = self._parse_function()
|
||||
@@ -503,7 +503,7 @@ class Parser(object):
|
||||
self.start_pos[0])
|
||||
continue
|
||||
self.freshscope = True
|
||||
self.scope = self.scope.add_scope(func, self._decorators)
|
||||
self._scope = self._scope.add_scope(func, self._decorators)
|
||||
self._decorators = []
|
||||
elif tok == 'class':
|
||||
cls = self._parse_class()
|
||||
@@ -511,7 +511,7 @@ class Parser(object):
|
||||
debug.warning("class: syntax error@%s" % self.start_pos[0])
|
||||
continue
|
||||
self.freshscope = True
|
||||
self.scope = self.scope.add_scope(cls, self._decorators)
|
||||
self._scope = self._scope.add_scope(cls, self._decorators)
|
||||
self._decorators = []
|
||||
# import stuff
|
||||
elif tok == 'import':
|
||||
@@ -522,7 +522,7 @@ class Parser(object):
|
||||
i = pr.Import(self.module, first_pos, end_pos, m,
|
||||
alias, defunct=defunct)
|
||||
self._check_user_stmt(i)
|
||||
self.scope.add_import(i)
|
||||
self._scope.add_import(i)
|
||||
if not imports:
|
||||
i = pr.Import(self.module, first_pos, self.end_pos, None,
|
||||
defunct=True)
|
||||
@@ -559,7 +559,7 @@ class Parser(object):
|
||||
alias, mod, star, relative_count,
|
||||
defunct=defunct or defunct2)
|
||||
self._check_user_stmt(i)
|
||||
self.scope.add_import(i)
|
||||
self._scope.add_import(i)
|
||||
self.freshscope = False
|
||||
#loops
|
||||
elif tok == 'for':
|
||||
@@ -569,7 +569,7 @@ class Parser(object):
|
||||
if tok == ':':
|
||||
s = [] if statement is None else [statement]
|
||||
f = pr.ForFlow(self.module, s, first_pos, set_stmt)
|
||||
self.scope = self.scope.add_statement(f)
|
||||
self._scope = self._scope.add_statement(f)
|
||||
else:
|
||||
debug.warning('syntax err, for flow started @%s',
|
||||
self.start_pos[0])
|
||||
@@ -612,13 +612,13 @@ class Parser(object):
|
||||
# the flow statement, because a dedent releases the
|
||||
# main scope, so just take the last statement.
|
||||
try:
|
||||
s = self.scope.statements[-1].set_next(f)
|
||||
s = self._scope.statements[-1].set_next(f)
|
||||
except (AttributeError, IndexError):
|
||||
# If set_next doesn't exist, just add it.
|
||||
s = self.scope.add_statement(f)
|
||||
s = self._scope.add_statement(f)
|
||||
else:
|
||||
s = self.scope.add_statement(f)
|
||||
self.scope = s
|
||||
s = self._scope.add_statement(f)
|
||||
self._scope = s
|
||||
else:
|
||||
for i in inputs:
|
||||
i.parent = use_as_parent_scope
|
||||
@@ -629,7 +629,7 @@ class Parser(object):
|
||||
s = self.start_pos
|
||||
self.freshscope = False
|
||||
# add returns to the scope
|
||||
func = self.scope.get_parent_until(pr.Function)
|
||||
func = self._scope.get_parent_until(pr.Function)
|
||||
if tok == 'yield':
|
||||
func.is_generator = True
|
||||
|
||||
@@ -646,7 +646,7 @@ class Parser(object):
|
||||
elif tok == 'global':
|
||||
stmt, tok = self._parse_statement(self.current)
|
||||
if stmt:
|
||||
self.scope.add_statement(stmt)
|
||||
self._scope.add_statement(stmt)
|
||||
for name in stmt.used_vars:
|
||||
# add the global to the top, because there it is
|
||||
# important.
|
||||
@@ -659,8 +659,9 @@ class Parser(object):
|
||||
continue
|
||||
elif tok == 'assert':
|
||||
stmt, tok = self._parse_statement()
|
||||
stmt.parent = use_as_parent_scope
|
||||
self.scope.asserts.append(stmt)
|
||||
if stmt is not None:
|
||||
stmt.parent = use_as_parent_scope
|
||||
self._scope.asserts.append(stmt)
|
||||
# default
|
||||
elif token_type in [tokenize.NAME, tokenize.STRING,
|
||||
tokenize.NUMBER] \
|
||||
@@ -670,7 +671,7 @@ class Parser(object):
|
||||
# by the statement parser.
|
||||
stmt, tok = self._parse_statement(self.current)
|
||||
if stmt:
|
||||
self.scope.add_statement(stmt)
|
||||
self._scope.add_statement(stmt)
|
||||
self.freshscope = False
|
||||
else:
|
||||
if token_type not in [tokenize.COMMENT, tokenize.INDENT,
|
||||
|
||||
@@ -16,11 +16,11 @@ is the easiest way to write a parser. The same behaviour applies to ``Param``,
|
||||
which is being used in a function definition.
|
||||
|
||||
The easiest way to play with this module is to use :class:`parsing.Parser`.
|
||||
:attr:`parsing.Parser.scope` holds an instance of :class:`SubModule`:
|
||||
:attr:`parsing.Parser.module` holds an instance of :class:`SubModule`:
|
||||
|
||||
>>> from jedi.parsing import Parser
|
||||
>>> parser = Parser('import os', 'example.py')
|
||||
>>> submodule = parser.scope
|
||||
>>> submodule = parser.module
|
||||
>>> submodule
|
||||
<SubModule: example.py@1-1>
|
||||
|
||||
@@ -248,14 +248,14 @@ class Scope(Simple, IsScope):
|
||||
... b = y
|
||||
... b.c = z
|
||||
... ''')
|
||||
>>> parser.scope.get_defined_names()
|
||||
>>> parser.module.get_defined_names()
|
||||
[<Name: a@2,0>, <Name: b@3,0>]
|
||||
|
||||
Note that unlike :meth:`get_set_vars`, assignment to object
|
||||
attribute does not change the result because it does not change
|
||||
the defined names in this scope.
|
||||
|
||||
>>> parser.scope.get_set_vars()
|
||||
>>> parser.module.get_set_vars()
|
||||
[<Name: a@2,0>, <Name: b@3,0>, <Name: b.c@4,0>]
|
||||
|
||||
"""
|
||||
|
||||
25
jedi/replstartup.py
Normal file
25
jedi/replstartup.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
``PYTHONSTARTUP`` to use Jedi in your Python interpreter.
|
||||
|
||||
To use Jedi completion in Python interpreter, add the following in your shell
|
||||
setup (e.g., ``.bashrc``)::
|
||||
|
||||
export PYTHONSTARTUP="$(python -m jedi)"
|
||||
|
||||
Then you will be able to use Jedi completer in your Python interpreter::
|
||||
|
||||
$ python
|
||||
Python 2.7.2+ (default, Jul 20 2012, 22:15:08)
|
||||
[GCC 4.6.1] on linux2
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> import os
|
||||
>>> os.path.join().split().in<TAB> # doctest: +SKIP
|
||||
os.path.join().split().index os.path.join().split().insert
|
||||
|
||||
"""
|
||||
|
||||
import jedi.utils
|
||||
jedi.utils.setup_readline()
|
||||
del jedi
|
||||
# Note: try not to do many things here, as it will contaminate global
|
||||
# namespace of the interpreter.
|
||||
@@ -127,8 +127,10 @@ cache_directory = os.path.expanduser(_cache_directory)
|
||||
"""
|
||||
The path where all the caches can be found.
|
||||
|
||||
On Linux, this defaults to ``~/.cache/jedi/``, on OS X to ``~/.jedi/`` and on
|
||||
Windows to ``%APPDATA%\\Jedi\\Jedi\\``.
|
||||
On Linux, this defaults to ``~/.cache/jedi/``, on OS X to
|
||||
``~/Library/Caches/Jedi/`` and on Windows to ``%APPDATA%\\Jedi\\Jedi\\``.
|
||||
On Linux, if environment variable ``$XDG_CACHE_HOME`` is set,
|
||||
``$XDG_CACHE_HOME/jedi`` is used instead of the default one.
|
||||
"""
|
||||
|
||||
# ----------------
|
||||
|
||||
110
jedi/utils.py
Normal file
110
jedi/utils.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
Utilities for end-users.
|
||||
"""
|
||||
|
||||
from rlcompleter import Completer
|
||||
|
||||
from jedi import Interpreter
|
||||
|
||||
|
||||
_NON_DELIMS = ' \t\n()'
|
||||
"""
|
||||
:class:`rcompleter.Completer` assumes these characters to be delimiter
|
||||
(i.e., :meth:`rcompleter.Completer.complete` does not expect these
|
||||
characters) but :class:`JediRLCompleter` can handle them.
|
||||
"""
|
||||
|
||||
try:
|
||||
import readline
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
_READLINE_DEFAULT_DELIMS = readline.get_completer_delims()
|
||||
_READLINE_JEDI_DELIMS = ''.join(
|
||||
set(_READLINE_DEFAULT_DELIMS) - set(_NON_DELIMS))
|
||||
|
||||
|
||||
class JediRLCompleter(Completer):
|
||||
|
||||
"""
|
||||
:class:`rlcompleter.Completer` enhanced by Jedi.
|
||||
|
||||
This class tries matchers defined in :class:`.Completer` first.
|
||||
If they fail, :class:`jedi.Interpreter` is used.
|
||||
|
||||
>>> import os
|
||||
>>> completer = JediRLCompleter(locals())
|
||||
>>> completer.complete('os.path.joi', 0) # completion w/o Jedi
|
||||
'os.path.join('
|
||||
>>> completer.complete('os.path.join().s', 0) # completion with Jedi
|
||||
'os.path.join().split'
|
||||
|
||||
"""
|
||||
|
||||
def _jedi_matches(self, text):
|
||||
completions = Interpreter(text, [self.namespace]).completions()
|
||||
return [text + c.complete for c in completions]
|
||||
|
||||
@staticmethod
|
||||
def _split_for_default_matcher(text, delims=_NON_DELIMS):
|
||||
"""
|
||||
Split `text` before passing it to :meth:`Completer.attr_matches` etc.
|
||||
|
||||
>>> JediRLCompleter._split_for_default_matcher('f(')
|
||||
('f(', '')
|
||||
>>> JediRLCompleter._split_for_default_matcher('f().g')
|
||||
('f()', '.g')
|
||||
|
||||
"""
|
||||
import re
|
||||
m = re.match(r"(.*[{0}])([^{0}]*)".format(re.escape(delims)), text)
|
||||
if not m:
|
||||
return ('', text)
|
||||
return m.groups()
|
||||
|
||||
def _find_matches(self, default_matcher, text):
|
||||
"""
|
||||
Common part for :meth:`attr_matches` and :meth:`global_matches`.
|
||||
|
||||
Try `default_matcher` first and return what it returns if
|
||||
it is not empty. Otherwise, try :meth:`_jedi_matches`.
|
||||
|
||||
:arg default_matcher: :meth:`.Completer.attr_matches` or
|
||||
:meth:`.Completer.global_matches`.
|
||||
:arg str text: code to complete
|
||||
"""
|
||||
(pre, body) = self._split_for_default_matcher(text)
|
||||
matches = default_matcher(self, body)
|
||||
if matches:
|
||||
return [pre + m for m in matches]
|
||||
return self._jedi_matches(text)
|
||||
|
||||
def attr_matches(self, text):
|
||||
# NOTE: Completer is old type class so `super` cannot be used here
|
||||
return self._find_matches(Completer.attr_matches, text)
|
||||
|
||||
def global_matches(self, text):
|
||||
# NOTE: Completer is old type class so `super` cannot be used here
|
||||
return self._find_matches(Completer.global_matches, text)
|
||||
|
||||
|
||||
def setup_readline():
|
||||
"""
|
||||
Install Jedi completer to :mod:`readline`.
|
||||
|
||||
This function setups :mod:`readline` to use Jedi in Python interactive
|
||||
shell. If you want to use custom ``PYTHONSTARTUP`` file, you can call
|
||||
this function like this:
|
||||
|
||||
>>> from jedi.utils import setup_readline
|
||||
>>> setup_readline()
|
||||
|
||||
"""
|
||||
try:
|
||||
import readline
|
||||
except ImportError:
|
||||
print("Module readline not available.")
|
||||
else:
|
||||
readline.set_completer(JediRLCompleter().complete)
|
||||
readline.parse_and_bind("tab: complete")
|
||||
readline.set_completer_delims(_READLINE_JEDI_DELIMS)
|
||||
Reference in New Issue
Block a user