Skip to content
This repository was archived by the owner on Nov 3, 2023. It is now read-only.

Commit 0c11410

Browse files
committed
Wrote some tests and working on replacing the parser with astroid.
1 parent 190b0c2 commit 0c11410

File tree

5 files changed

+317
-2
lines changed

5 files changed

+317
-2
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
-r requirements/docs.txt
22
-r requirements/tests.txt
3+
-r requirements/runtime.txt

requirements/runtime.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
astroid

src/pydocstyle/parser.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from collections import defaultdict
66
from itertools import chain, dropwhile
77
from re import compile as re
8+
import astroid
89

910
try:
1011
from StringIO import StringIO
@@ -207,7 +208,7 @@ def __init__(self, *args):
207208

208209

209210
class Parser(object):
210-
def __call__(self, filelike, filename):
211+
def parse(self, filelike, filename):
211212
# TODO: fix log
212213
self.log = logging.getLogger()
213214
self.source = filelike.readlines()
@@ -219,6 +220,10 @@ def __call__(self, filelike, filename):
219220
self._accumulated_decorators = []
220221
return self.parse_module()
221222

223+
# TODO: remove
224+
def __call__(self, *args, **kwargs):
225+
return self.parse(*args, **kwargs)
226+
222227
current = property(lambda self: self.stream.current)
223228
line = property(lambda self: self.stream.line)
224229

@@ -500,3 +505,42 @@ def _parse_from_import_names(self, is_future_import):
500505
self.consume(tk.OP)
501506
self.log.debug("parsing import, token is %r (%s)",
502507
self.current.kind, self.current.value)
508+
509+
510+
class Parser(object):
511+
def parse(self, filelike, filename):
512+
# TODO: fix log
513+
self.log = logging.getLogger()
514+
module_node = astroid.parse(filelike.read(), path=filename)
515+
516+
module_children_handler = {
517+
astroid.FunctionDef: self.handle_function,
518+
#astroid.ClassDef: self.handle_class,
519+
#astroid.ImportFrom: self.handle_from_import,
520+
}
521+
522+
module_children = []
523+
for child in module_node.get_children():
524+
handler = module_children_handler[child.__class__]
525+
module_children.append(handler(module_node, child))
526+
527+
return Module(filename,
528+
module_node.source_code,
529+
module_node.fromlineno,
530+
module_node.tolineno,
531+
[],
532+
module_node.doc,
533+
module_children,
534+
None,
535+
None,
536+
None)
537+
538+
def handle_function(self, parent, node):
539+
return Function(node.name,
540+
parent.source_code[node.fromlineno:node.tolineno],
541+
node.fromlineno, node.tolineno, node.decorators or [], node.doc,
542+
[], parent)
543+
544+
# TODO: remove
545+
def __call__(self, *args, **kwargs):
546+
return self.parse(*args, **kwargs)

src/pydocstyle/pydocstyle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
log = logging.getLogger(__name__)
1313

1414

15-
__version__ = '1.0.1-rc1'
15+
__version__ = '1.1.0-rc1'
1616
__all__ = ()

src/pydocstyle/tests/parser_test.py

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
import six
2+
import textwrap
3+
from pydocstyle.parser import Parser
4+
5+
6+
class CodeSnippet(six.StringIO):
7+
def __init__(self, code_string):
8+
six.StringIO.__init__(self, textwrap.dedent(code_string))
9+
10+
11+
def test_function():
12+
"""Test parsing of a simple function."""
13+
parser = Parser()
14+
code = CodeSnippet("""\
15+
def do_something(pos_param0, pos_param1, kw_param0="default"):
16+
\"""Do something.\"""
17+
return None
18+
""")
19+
module = parser.parse(code, 'file_path')
20+
function, = module.children
21+
assert function.name == 'do_something'
22+
assert function.decorators == []
23+
assert function.children == []
24+
assert function.docstring == '"""Do something."""'
25+
assert function.kind == 'function'
26+
assert function.parent == module
27+
assert function.start == 1
28+
assert function.end == 3
29+
assert function.source == code.getvalue()
30+
assert function.is_public
31+
assert str(function) == 'in public function `do_something`'
32+
33+
34+
def test_nested_function():
35+
"""Test parsing of a nested function."""
36+
parser = Parser()
37+
code = CodeSnippet("""\
38+
def outer_function():
39+
\"""This is the outer function.\"""
40+
def inner_function():
41+
'''This is the inner function.'''
42+
return None
43+
return None
44+
""")
45+
module = parser.parse(code, 'file_path')
46+
47+
outer_function, = module.children
48+
assert outer_function.name == 'outer_function'
49+
assert outer_function.decorators == []
50+
assert outer_function.docstring == '"""This is the outer function."""'
51+
assert outer_function.kind == 'function'
52+
assert outer_function.parent == module
53+
assert outer_function.start == 1
54+
assert outer_function.end == 6
55+
assert outer_function.source == code.getvalue()
56+
assert outer_function.is_public
57+
assert str(outer_function) == 'in public function `outer_function`'
58+
59+
inner_function, = outer_function.children
60+
assert inner_function.name == 'inner_function'
61+
assert inner_function.decorators == []
62+
assert inner_function.docstring == "'''This is the inner function.'''"
63+
assert inner_function.kind == 'function'
64+
assert inner_function.parent == outer_function
65+
assert inner_function.start == 3
66+
assert inner_function.end == 5
67+
assert textwrap.dedent(inner_function.source) == textwrap.dedent("""\
68+
def inner_function():
69+
'''This is the inner function.'''
70+
return None
71+
""")
72+
assert not inner_function.is_public
73+
assert str(inner_function) == 'in private nested function `inner_function`'
74+
75+
76+
def test_class():
77+
"""Test parsing of a class."""
78+
parser = Parser()
79+
code = CodeSnippet("""\
80+
class TestedClass(object):
81+
82+
" an ugly docstring "
83+
""")
84+
module = parser.parse(code, 'file_path')
85+
86+
klass, = module.children
87+
assert klass.name == 'TestedClass'
88+
assert klass.decorators == []
89+
assert klass.children == []
90+
assert klass.docstring == '" an ugly docstring "'
91+
assert klass.kind == 'class'
92+
assert klass.parent == module
93+
assert klass.start == 1
94+
assert klass.end == 3
95+
assert klass.source == code.getvalue()
96+
assert klass.is_public
97+
assert str(klass) == 'in public class `TestedClass`'
98+
99+
100+
def test_public_method():
101+
"""Test parsing of a public method."""
102+
parser = Parser()
103+
code = CodeSnippet("""\
104+
class TestedClass(object):
105+
def do_it(param):
106+
\"""Do the 'it'\"""
107+
# do nothing
108+
return None
109+
""")
110+
module = parser.parse(code, 'file_path')
111+
112+
klass, = module.children
113+
assert klass.name == 'TestedClass'
114+
assert klass.decorators == []
115+
assert klass.docstring is None
116+
assert klass.kind == 'class'
117+
assert klass.parent == module
118+
assert klass.start == 1
119+
assert klass.end == 5
120+
assert klass.source == code.getvalue()
121+
assert klass.is_public
122+
assert str(klass) == 'in public class `TestedClass`'
123+
124+
method, = klass.children
125+
assert method.name == 'do_it'
126+
assert method.decorators == []
127+
assert method.docstring == '''"""Do the 'it'"""'''
128+
assert method.kind == 'method'
129+
assert method.parent == klass
130+
assert method.start == 2
131+
assert method.end == 5
132+
assert textwrap.dedent(method.source) == textwrap.dedent("""\
133+
def do_it(param):
134+
\"""Do the 'it'\"""
135+
# do nothing
136+
return None
137+
""")
138+
assert method.is_public
139+
assert not method.is_magic
140+
assert str(method) == 'in public method `do_it`'
141+
142+
143+
def test_private_method():
144+
"""Test parsing of a private method."""
145+
parser = Parser()
146+
code = CodeSnippet("""\
147+
class TestedClass(object):
148+
def _do_it(param):
149+
\"""Do the 'it'\"""
150+
# do nothing
151+
return None
152+
""")
153+
module = parser.parse(code, 'file_path')
154+
155+
klass, = module.children
156+
assert klass.name == 'TestedClass'
157+
assert klass.decorators == []
158+
assert klass.docstring is None
159+
assert klass.kind == 'class'
160+
assert klass.parent == module
161+
assert klass.start == 1
162+
assert klass.end == 5
163+
assert klass.source == code.getvalue()
164+
assert klass.is_public
165+
assert str(klass) == 'in public class `TestedClass`'
166+
167+
method, = klass.children
168+
assert method.name == '_do_it'
169+
assert method.decorators == []
170+
assert method.docstring == '''"""Do the 'it'"""'''
171+
assert method.kind == 'method'
172+
assert method.parent == klass
173+
assert method.start == 2
174+
assert method.end == 5
175+
assert textwrap.dedent(method.source) == textwrap.dedent("""\
176+
def _do_it(param):
177+
\"""Do the 'it'\"""
178+
# do nothing
179+
return None
180+
""")
181+
assert not method.is_public
182+
assert not method.is_magic
183+
assert str(method) == 'in private method `_do_it`'
184+
185+
186+
def test_magic_method():
187+
"""Test parsing of a magic method."""
188+
parser = Parser()
189+
code = CodeSnippet("""\
190+
class TestedClass(object):
191+
def __str__(self):
192+
return "me"
193+
""")
194+
module = parser.parse(code, 'file_path')
195+
196+
klass, = module.children
197+
assert klass.name == 'TestedClass'
198+
assert klass.decorators == []
199+
assert klass.docstring is None
200+
assert klass.kind == 'class'
201+
assert klass.parent == module
202+
assert klass.start == 1
203+
assert klass.end == 3
204+
assert klass.source == code.getvalue()
205+
assert klass.is_public
206+
assert str(klass) == 'in public class `TestedClass`'
207+
208+
method, = klass.children[0]
209+
assert method.name == '__str__'
210+
assert method.decorators == []
211+
assert method.docstring is None
212+
assert method.kind == 'method'
213+
assert method.parent == klass
214+
assert method.start == 2
215+
assert method.end == 3
216+
assert textwrap.dedent(method.source) == textwrap.dedent("""\
217+
def __str__(self):
218+
return "me"
219+
""")
220+
assert method.is_public
221+
assert method.is_magic
222+
assert str(method) == 'in public method `__str__`'
223+
224+
225+
def test_nested_class():
226+
"""Test parsing of a class."""
227+
parser = Parser()
228+
code = CodeSnippet("""\
229+
class OuterClass(object):
230+
' an outer docstring'
231+
class InnerClass(object):
232+
"An inner docstring."
233+
""")
234+
module = parser.parse(code, 'file_path')
235+
236+
outer_class, = module.children
237+
assert outer_class.name == 'OuterClass'
238+
assert outer_class.decorators == []
239+
assert outer_class.docstring == "' an outer docstring'"
240+
assert outer_class.kind == 'class'
241+
assert outer_class.parent == module
242+
assert outer_class.start == 1
243+
assert outer_class.end == 4
244+
assert outer_class.source == code.getvalue()
245+
assert outer_class.is_public
246+
assert str(outer_class) == 'in public class `OuterClass`'
247+
248+
inner_class, = outer_class.children
249+
assert inner_class.name == 'InnerClass'
250+
assert inner_class.decorators == []
251+
assert inner_class.children == []
252+
assert inner_class.docstring == '"An inner docstring."'
253+
assert inner_class.kind == 'class'
254+
assert inner_class.parent == outer_class
255+
assert inner_class.start == 3
256+
assert inner_class.end == 4
257+
assert textwrap.dedent(inner_class.source) == textwrap.dedent("""\
258+
class InnerClass(object):
259+
"An inner docstring."
260+
""")
261+
assert inner_class.is_public
262+
assert str(inner_class) == 'in public nested class `InnerClass`'
263+
264+
265+
def test_raise_from():
266+
"""Make sure 'raise x from y' doesn't trip the parser."""
267+
parser = Parser()
268+
code = CodeSnippet("raise ValueError from None")
269+
parser.parse(code, 'file_path')

0 commit comments

Comments
 (0)