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

Commit 59081e7

Browse files
committed
Added matrix multiplication operator test and fix.
1 parent 876d752 commit 59081e7

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

src/pydocstyle/parser.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import tokenize as tk
77
from itertools import chain, dropwhile
88
from re import compile as re
9+
from .utils import log
910

1011
try:
1112
from StringIO import StringIO
@@ -227,13 +228,15 @@ def __init__(self, filelike):
227228
self._generator = tk.generate_tokens(filelike.readline)
228229
self.current = Token(*next(self._generator, None))
229230
self.line = self.current.start[0]
230-
self.log = logging.getLogger()
231+
self.log = log
232+
self.got_newline = True
231233

232234
def move(self):
233235
previous = self.current
234236
current = self._next_from_generator()
235237
self.current = None if current is None else Token(*current)
236238
self.line = self.current.start[0] if self.current else self.line
239+
self.got_newline = (previous.kind == tk.NEWLINE)
237240
return previous
238241

239242
def _next_from_generator(self):
@@ -271,7 +274,7 @@ class Parser(object):
271274
def parse(self, filelike, filename):
272275
"""Parse the given file-like object and return its Module object."""
273276
# TODO: fix log
274-
self.log = logging.getLogger()
277+
self.log = log
275278
self.source = filelike.readlines()
276279
src = ''.join(self.source)
277280
try:
@@ -336,6 +339,8 @@ def parse_decorators(self):
336339
at_arguments = False
337340

338341
while self.current is not None:
342+
self.log.debug("parsing decorators, current token is %r (%s)",
343+
self.current.kind, self.current.value)
339344
if (self.current.kind == tk.NAME and
340345
self.current.value in ['def', 'class']):
341346
# Done with decorators - found function or class proper
@@ -373,9 +378,12 @@ def parse_definitions(self, class_, all=False):
373378
while self.current is not None:
374379
self.log.debug("parsing definition list, current token is %r (%s)",
375380
self.current.kind, self.current.value)
381+
self.log.debug('got_newline: %s', self.stream.got_newline)
376382
if all and self.current.value == '__all__':
377383
self.parse_all()
378-
elif self.current.kind == tk.OP and self.current.value == '@':
384+
elif (self.current.kind == tk.OP and
385+
self.current.value == '@' and
386+
self.stream.got_newline):
379387
self.consume(tk.OP)
380388
self.parse_decorators()
381389
elif self.current.value in ['def', 'class']:
@@ -392,6 +400,7 @@ def parse_definitions(self, class_, all=False):
392400
else:
393401
self.stream.move()
394402

403+
395404
def parse_all(self):
396405
"""Parse the __all__ definition in a module."""
397406
assert self.current.value == '__all__'
@@ -471,6 +480,7 @@ def parse_definition(self, class_):
471480
assert self.current.kind != tk.INDENT
472481
docstring = self.parse_docstring()
473482
decorators = self._accumulated_decorators
483+
self.log.debug("current accumulated decorators: %s", decorators)
474484
self._accumulated_decorators = []
475485
self.log.debug("parsing nested definitions.")
476486
children = list(self.parse_definitions(class_))

src/tests/parser_test.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""Parser tests."""
22

33
import six
4+
import sys
45
import pytest
56
import textwrap
6-
from pydocstyle.parser import Parser, Decorator, ParseError
7+
from pydocstyle.parser import Parser, ParseError
78

89

910
class CodeSnippet(six.StringIO):
@@ -416,6 +417,46 @@ def test_raise_from():
416417
parser.parse(code, 'file_path')
417418

418419

420+
@pytest.mark.skipif(six.PY2, reason='Matrix multiplication operator is '
421+
'invalid in Python 2.x')
422+
def test_simple_matrix_multiplication():
423+
"""Make sure 'a @ b' doesn't trip the parser."""
424+
if sys.version_info.minor < 5:
425+
return
426+
parser = Parser()
427+
code = CodeSnippet("""
428+
def foo():
429+
a @ b
430+
""")
431+
parser.parse(code, 'file_path')
432+
433+
434+
@pytest.mark.skipif(six.PY2, reason='Matrix multiplication operator is '
435+
'invalid in Python 2.x')
436+
def test_matrix_multiplication_with_decorators():
437+
"""Make sure 'a @ b' doesn't trip the parser."""
438+
if sys.version_info.minor < 5:
439+
return
440+
parser = Parser()
441+
code = CodeSnippet("""
442+
def foo():
443+
a @ b
444+
(a
445+
@b)
446+
@a
447+
def b():
448+
pass
449+
""")
450+
module = parser.parse(code, 'file_path')
451+
452+
outer_function, = module.children
453+
assert outer_function.name == 'foo'
454+
455+
inner_function, = outer_function.children
456+
assert len(inner_function.decorators) == 1
457+
assert inner_function.decorators[0].name == 'a'
458+
459+
419460
def test_module_publicity():
420461
"""Test that a module that has a single leading underscore is private."""
421462
parser = Parser()

0 commit comments

Comments
 (0)