Skip to content

Commit 5ccb9cd

Browse files
author
Mikhail Arkhipov
authored
Jedi 0.11 parser + test fixes (#777)
* Basic tokenizer * Fixed property names * Tests, round I * Tests, round II * tokenizer test * Remove temorary change * Fix merge issue * Merge conflict * Merge conflict * Completion test * Fix last line * Fix javascript math * Make test await for results * Add license headers * Rename definitions to types * License headers * Fix typo in completion details (typo) * Fix hover test * Russian translations * Update to better translation * Fix typo * #70 How to get all parameter info when filling in a function param list * Fix #70 How to get all parameter info when filling in a function param list * Clean up * Clean imports * CR feedback * Trim whitespace for test stability * More tests * Better handle no-parameters documentation * Better handle ellipsis and Python3 * #385 Auto-Indentation doesn't work after comment * #141 Auto indentation broken when return keyword involved * Undo changes * #627 Docstrings for builtin methods are not parsed correctly * reStructuredText converter * Fix: period is not an operator * Minor fixes * Restructure * Tests * Tests * Code heuristics * Baselines * HTML handling * Lists * State machine * Baselines * Squash * no message * Whitespace difference * Update Jedi to 0.11.1 * Enable Travis * Test fixes * Undo change * Jedi 0.11 with parser * Undo changes * Undo changes * Test fixes * More tests * Tests
1 parent 1c8b1a5 commit 5ccb9cd

File tree

168 files changed

+6662
-18207
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+6662
-18207
lines changed

.vscode/settings.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,5 @@
1818
"python.linting.enabled": false,
1919
"python.unitTest.promptToConfigure": false,
2020
"python.workspaceSymbols.enabled": false,
21-
"python.formatting.provider": "none",
22-
"files.insertFinalNewline": true
23-
}
21+
"python.formatting.provider": "none"
22+
}

pythonFiles/completion.py

Lines changed: 31 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def __init__(self, new_stdout=None):
1515

1616
def __enter__(self):
1717
sys.stdout.flush()
18-
oldstdout_fno = self.oldstdout_fno = os.dup(sys.stdout.fileno())
18+
self.oldstdout_fno = os.dup(sys.stdout.fileno())
1919
os.dup2(self._new_stdout.fileno(), 1)
2020

2121
def __exit__(self, exc_type, exc_value, traceback):
@@ -47,7 +47,6 @@ def __init__(self):
4747
self.drive_mount = ''
4848

4949
def _get_definition_type(self, definition):
50-
is_built_in = definition.in_builtin_module
5150
# if definition.type not in ['import', 'keyword'] and is_built_in():
5251
# return 'builtin'
5352
try:
@@ -89,7 +88,7 @@ def _generate_signature(self, completion):
8988
return ''
9089
return '%s(%s)' % (
9190
completion.name,
92-
', '.join(p.description for p in completion.params if p))
91+
', '.join(p.description[6:] for p in completion.params if p))
9392

9493
def _get_call_signatures(self, script):
9594
"""Extract call signatures from jedi.api.Script object in failsafe way.
@@ -108,18 +107,28 @@ def _get_call_signatures(self, script):
108107
for pos, param in enumerate(signature.params):
109108
if not param.name:
110109
continue
110+
111+
name = self._get_param_name(param)
111112
if param.name == 'self' and pos == 0:
112113
continue
113-
try:
114-
name, value = param.description.split('=')
115-
except ValueError:
116-
name = param.description
117-
value = None
118114
if name.startswith('*'):
119115
continue
116+
117+
value = self._get_param_value(param)
120118
_signatures.append((signature, name, value))
121119
return _signatures
122120

121+
def _get_param_name(self, p):
122+
if(p.name.startswith('param ')):
123+
return p.name[6:] # drop leading 'param '
124+
return p.name
125+
126+
def _get_param_value(self, p):
127+
pair = p.description.split('=')
128+
if(len(pair) > 1):
129+
return pair[1]
130+
return None
131+
123132
def _get_call_signatures_with_args(self, script):
124133
"""Extract call signatures from jedi.api.Script object in failsafe way.
125134
@@ -150,16 +159,12 @@ def _get_call_signatures_with_args(self, script):
150159
for pos, param in enumerate(signature.params):
151160
if not param.name:
152161
continue
162+
163+
name = self._get_param_name(param)
153164
if param.name == 'self' and pos == 0:
154165
continue
155-
try:
156-
name, value = param.description.split('=')
157-
except ValueError:
158-
name = param.description
159-
value = None
160-
# if name.startswith('*'):
161-
# continue
162-
#_signatures.append((signature, name, value))
166+
167+
value = self._get_param_value(param)
163168
paramDocstring = ''
164169
try:
165170
paramDocstring = param.docstring()
@@ -251,8 +256,7 @@ def _serialize_methods(self, script, identifier=None, prefix=''):
251256
for completion in completions:
252257
params = []
253258
if hasattr(completion, 'params'):
254-
params = [p.description for p in completion.params
255-
if ARGUMENT_RE.match(p.description)]
259+
params = [p.description for p in completion.params if p]
256260
if completion.parent().type == 'class':
257261
_methods.append({
258262
'parent': completion.parent().name,
@@ -288,50 +292,8 @@ def _top_definition(self, definition):
288292
return d
289293
return definition
290294

291-
def _extract_range_jedi_0_9_0(self, definition):
292-
from jedi import common
293-
from jedi.parser.utils import load_parser
294-
# get the scope range
295-
try:
296-
if definition.type in ['class', 'function'] and hasattr(definition, '_definition'):
297-
scope = definition._definition
298-
start_line = scope.start_pos[0] - 1
299-
start_column = scope.start_pos[1]
300-
end_line = scope.end_pos[0] - 1
301-
end_column = scope.end_pos[1]
302-
# get the lines
303-
path = definition._definition.get_parent_until().path
304-
parser = load_parser(path)
305-
lines = common.splitlines(parser.source)
306-
lines[end_line] = lines[end_line][:end_column]
307-
# trim the lines
308-
lines = lines[start_line:end_line + 1]
309-
lines = '\n'.join(lines).rstrip().split('\n')
310-
end_line = start_line + len(lines) - 1
311-
end_column = len(lines[-1]) - 1
312-
else:
313-
symbol = definition._name
314-
start_line = symbol.start_pos[0] - 1
315-
start_column = symbol.start_pos[1]
316-
end_line = symbol.end_pos[0] - 1
317-
end_column = symbol.end_pos[1]
318-
return {
319-
'start_line': start_line,
320-
'start_column': start_column,
321-
'end_line': end_line,
322-
'end_column': end_column
323-
}
324-
except Exception as e:
325-
return {
326-
'start_line': definition.line - 1,
327-
'start_column': definition.column,
328-
'end_line': definition.line - 1,
329-
'end_column': definition.column
330-
}
331-
332-
def _extract_range_jedi_0_10_1(self, definition):
333-
from jedi import common
334-
from jedi.parser.python import parse
295+
def _extract_range_jedi_0_11_1(self, definition):
296+
from parso.utils import split_lines
335297
# get the scope range
336298
try:
337299
if definition.type in ['class', 'function']:
@@ -341,7 +303,7 @@ def _extract_range_jedi_0_10_1(self, definition):
341303
start_column = scope.start_pos[1]
342304
# get the lines
343305
code = scope.get_code(include_prefix=False)
344-
lines = common.splitlines(code)
306+
lines = split_lines(code)
345307
# trim the lines
346308
lines = '\n'.join(lines).rstrip().split('\n')
347309
end_line = start_line + len(lines) - 1
@@ -380,10 +342,7 @@ def _extract_range(self, definition):
380342
last character of actual code. That's why we extract the lines that
381343
make up our scope and trim the trailing whitespace.
382344
"""
383-
if jedi.__version__ in ('0.9.0', '0.10.0'):
384-
return self._extract_range_jedi_0_9_0(definition)
385-
else:
386-
return self._extract_range_jedi_0_10_1(definition)
345+
return self._extract_range_jedi_0_11_1(definition)
387346

388347
def _get_definitionsx(self, definitions, identifier=None, ignoreNoModulePath=False):
389348
"""Serialize response to be read from VSCode.
@@ -680,22 +639,17 @@ def watch(self):
680639
if __name__ == '__main__':
681640
cachePrefix = 'v'
682641
modulesToLoad = ''
683-
if len(sys.argv) > 0 and sys.argv[1] == 'preview':
684-
jediPath = os.path.join(os.path.dirname(__file__), 'preview')
685-
jediPreview = True
686-
if len(sys.argv) > 2:
687-
modulesToLoad = sys.argv[2]
688-
elif len(sys.argv) > 0 and sys.argv[1] == 'custom':
642+
if len(sys.argv) > 2 and sys.argv[1] == 'custom':
689643
jediPath = sys.argv[2]
690644
jediPreview = True
691645
cachePrefix = 'custom_v'
692646
if len(sys.argv) > 3:
693647
modulesToLoad = sys.argv[3]
694648
else:
695-
#std
696-
jediPath = os.path.join(os.path.dirname(__file__), 'release')
697-
if len(sys.argv) > 2:
698-
modulesToLoad = sys.argv[2]
649+
#release
650+
jediPath = os.path.dirname(__file__)
651+
if len(sys.argv) > 1:
652+
modulesToLoad = sys.argv[1]
699653

700654
sys.path.insert(0, jediPath)
701655
import jedi
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

pythonFiles/parso/__init__.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
r"""
2+
Parso is a Python parser that supports error recovery and round-trip parsing
3+
for different Python versions (in multiple Python versions). Parso is also able
4+
to list multiple syntax errors in your python file.
5+
6+
Parso has been battle-tested by jedi_. It was pulled out of jedi to be useful
7+
for other projects as well.
8+
9+
Parso consists of a small API to parse Python and analyse the syntax tree.
10+
11+
.. _jedi: https://github.com/davidhalter/jedi
12+
13+
A simple example:
14+
15+
>>> import parso
16+
>>> module = parso.parse('hello + 1', version="3.6")
17+
>>> expr = module.children[0]
18+
>>> expr
19+
PythonNode(arith_expr, [<Name: hello@1,0>, <Operator: +>, <Number: 1>])
20+
>>> print(expr.get_code())
21+
hello + 1
22+
>>> name = expr.children[0]
23+
>>> name
24+
<Name: hello@1,0>
25+
>>> name.end_pos
26+
(1, 5)
27+
>>> expr.end_pos
28+
(1, 9)
29+
30+
To list multiple issues:
31+
32+
>>> grammar = parso.load_grammar()
33+
>>> module = grammar.parse('foo +\nbar\ncontinue')
34+
>>> error1, error2 = grammar.iter_errors(module)
35+
>>> error1.message
36+
'SyntaxError: invalid syntax'
37+
>>> error2.message
38+
"SyntaxError: 'continue' not properly in loop"
39+
"""
40+
41+
from parso.parser import ParserSyntaxError
42+
from parso.grammar import Grammar, load_grammar
43+
from parso.utils import split_lines, python_bytes_to_unicode
44+
45+
46+
__version__ = '0.1.1'
47+
48+
49+
def parse(code=None, **kwargs):
50+
"""
51+
A utility function to avoid loading grammars.
52+
Params are documented in :py:meth:`parso.Grammar.parse`.
53+
54+
:param str version: The version used by :py:func:`parso.load_grammar`.
55+
"""
56+
version = kwargs.pop('version', None)
57+
grammar = load_grammar(version=version)
58+
return grammar.parse(code, **kwargs)

pythonFiles/parso/_compatibility.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""
2+
To ensure compatibility from Python ``2.6`` - ``3.3``, a module has been
3+
created. Clearly there is huge need to use conforming syntax.
4+
"""
5+
import sys
6+
import platform
7+
8+
# Cannot use sys.version.major and minor names, because in Python 2.6 it's not
9+
# a namedtuple.
10+
py_version = int(str(sys.version_info[0]) + str(sys.version_info[1]))
11+
12+
# unicode function
13+
try:
14+
unicode = unicode
15+
except NameError:
16+
unicode = str
17+
18+
is_pypy = platform.python_implementation() == 'PyPy'
19+
20+
21+
def use_metaclass(meta, *bases):
22+
""" Create a class with a metaclass. """
23+
if not bases:
24+
bases = (object,)
25+
return meta("HackClass", bases, {})
26+
27+
28+
try:
29+
encoding = sys.stdout.encoding
30+
if encoding is None:
31+
encoding = 'utf-8'
32+
except AttributeError:
33+
encoding = 'ascii'
34+
35+
36+
def u(string):
37+
"""Cast to unicode DAMMIT!
38+
Written because Python2 repr always implicitly casts to a string, so we
39+
have to cast back to a unicode (and we now that we always deal with valid
40+
unicode, because we check that in the beginning).
41+
"""
42+
if py_version >= 30:
43+
return str(string)
44+
45+
if not isinstance(string, unicode):
46+
return unicode(str(string), 'UTF-8')
47+
return string
48+
49+
50+
try:
51+
FileNotFoundError = FileNotFoundError
52+
except NameError:
53+
FileNotFoundError = IOError
54+
55+
56+
def utf8_repr(func):
57+
"""
58+
``__repr__`` methods in Python 2 don't allow unicode objects to be
59+
returned. Therefore cast them to utf-8 bytes in this decorator.
60+
"""
61+
def wrapper(self):
62+
result = func(self)
63+
if isinstance(result, unicode):
64+
return result.encode('utf-8')
65+
else:
66+
return result
67+
68+
if py_version >= 30:
69+
return func
70+
else:
71+
return wrapper
72+
73+
74+
try:
75+
from functools import total_ordering
76+
except ImportError:
77+
# Python 2.6
78+
def total_ordering(cls):
79+
"""Class decorator that fills in missing ordering methods"""
80+
convert = {
81+
'__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
82+
('__le__', lambda self, other: self < other or self == other),
83+
('__ge__', lambda self, other: not self < other)],
84+
'__le__': [('__ge__', lambda self, other: not self <= other or self == other),
85+
('__lt__', lambda self, other: self <= other and not self == other),
86+
('__gt__', lambda self, other: not self <= other)],
87+
'__gt__': [('__lt__', lambda self, other: not (self > other or self == other)),
88+
('__ge__', lambda self, other: self > other or self == other),
89+
('__le__', lambda self, other: not self > other)],
90+
'__ge__': [('__le__', lambda self, other: (not self >= other) or self == other),
91+
('__gt__', lambda self, other: self >= other and not self == other),
92+
('__lt__', lambda self, other: not self >= other)]
93+
}
94+
roots = set(dir(cls)) & set(convert)
95+
if not roots:
96+
raise ValueError('must define at least one ordering operation: < > <= >=')
97+
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
98+
for opname, opfunc in convert[root]:
99+
if opname not in roots:
100+
opfunc.__name__ = opname
101+
opfunc.__doc__ = getattr(int, opname).__doc__
102+
setattr(cls, opname, opfunc)
103+
return cls

0 commit comments

Comments
 (0)