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

Commit 83a6327

Browse files
committed
Address code review comments from @Nurdok.
1 parent 87f5007 commit 83a6327

File tree

2 files changed

+105
-134
lines changed

2 files changed

+105
-134
lines changed

pep257.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ def __repr__(self):
9494

9595
class Definition(Value):
9696

97-
_fields = ['name', '_source', 'start', 'end', 'decorators', 'docstring',
98-
'children', 'parent']
97+
_fields = ('name', '_source', 'start', 'end', 'decorators', 'docstring',
98+
'children', 'parent')
9999

100100
_human = property(lambda self: humanize(type(self).__name__))
101101
kind = property(lambda self: self._human.split()[-1])
@@ -152,7 +152,8 @@ class Method(Function):
152152
def is_public(self):
153153
# Check if we are a setter/deleter method, and mark as private if so.
154154
for decorator in self.decorators:
155-
if decorator.name.startswith(self.name):
155+
# Given 'foo', match 'foo.bar' but not 'foobar' or 'sfoo'
156+
if re(r"^{0}\.".format(self.name)).match(decorator.name):
156157
return False
157158
name_is_public = not self.name.startswith('_') or is_magic(self.name)
158159
return self.parent.is_public and name_is_public
@@ -232,7 +233,7 @@ def __call__(self, filelike, filename):
232233
self.stream = TokenStream(StringIO(src))
233234
self.filename = filename
234235
self.all = None
235-
self._decorators = []
236+
self._accumulated_decorators = []
236237
return self.parse_module()
237238

238239
current = property(lambda self: self.stream.current)
@@ -271,8 +272,8 @@ def parse_docstring(self):
271272
def parse_decorators(self):
272273
"""Called after first @ is found.
273274
274-
Build list of current decorators and return last parsed token,
275-
which is def or class start token.
275+
Parse decorators into self._accumulated_decorators.
276+
Continue to do so until encountering the 'def' or 'class' start token.
276277
"""
277278
name = []
278279
arguments = []
@@ -281,11 +282,13 @@ def parse_decorators(self):
281282
while self.current is not None:
282283
if (self.current.kind == tk.NAME and
283284
self.current.value in ['def', 'class']):
285+
# Done with decorators - found function or class proper
284286
break
285287
elif self.current.kind == tk.OP and self.current.value == '@':
286-
# New decorator found.
287-
self._decorators.append(
288+
# New decorator found. Store the decorator accumulated so far:
289+
self._accumulated_decorators.append(
288290
Decorator(''.join(name), ''.join(arguments)))
291+
# Now reset to begin accumulating the new decorator:
289292
name = []
290293
arguments = []
291294
at_arguments = False
@@ -295,18 +298,18 @@ def parse_decorators(self):
295298
# Ignore close parenthesis
296299
pass
297300
elif self.current.kind == tk.NEWLINE or self.current.kind == tk.NL:
298-
# Ignoe newlines
301+
# Ignore newlines
299302
pass
300303
else:
301-
# Keep accumulating decorator's name or argument.
304+
# Keep accumulating current decorator's name or argument.
302305
if not at_arguments:
303306
name.append(self.current.value)
304307
else:
305308
arguments.append(self.current.value)
306309
self.stream.move()
307310

308311
# Add decorator accumulated so far
309-
self._decorators.append(
312+
self._accumulated_decorators.append(
310313
Decorator(''.join(name), ''.join(arguments)))
311314

312315
def parse_definitions(self, class_, all=False):
@@ -414,8 +417,8 @@ def parse_definition(self, class_):
414417
self.leapfrog(tk.INDENT)
415418
assert self.current.kind != tk.INDENT
416419
docstring = self.parse_docstring()
417-
decorators = self._decorators
418-
self._decorators = []
420+
decorators = self._accumulated_decorators
421+
self._accumulated_decorators = []
419422
log.debug("parsing nested defintions.")
420423
children = list(self.parse_definitions(class_))
421424
log.debug("finished parsing nested defintions for '%s'", name)

test_decorators.py

Lines changed: 89 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
"""
2-
Unit test for pep257 module decorator handling.
1+
"""Unit test for pep257 module decorator handling.
32
43
Use tox or py.test to run the test suite.
54
"""
@@ -9,23 +8,21 @@
98
except ImportError:
109
from io import StringIO
1110

11+
import textwrap
12+
1213
import pep257
1314

1415

1516
class TestParser:
16-
"""
17-
Check parsing of Python source code.
18-
"""
17+
"""Check parsing of Python source code."""
1918

2019
def test_parse_class_single_decorator(self):
21-
"""
22-
Class decorator is recorded in class instance.
23-
"""
24-
code = """
25-
@first_decorator
26-
class Foo:
27-
pass
28-
"""
20+
"""Class decorator is recorded in class instance."""
21+
code = textwrap.dedent("""\
22+
@first_decorator
23+
class Foo:
24+
pass
25+
""")
2926
module = pep257.parse(StringIO(code), 'dummy.py')
3027
decorators = module.children[0].decorators
3128

@@ -34,19 +31,17 @@ class Foo:
3431
assert '' == decorators[0].arguments
3532

3633
def test_parse_class_decorators(self):
37-
"""
38-
Class decorators are accumulated, together with they arguments.
39-
"""
40-
code = """
41-
@first_decorator
42-
@second.decorator(argument)
43-
@third.multi.line(
44-
decorator,
45-
key=value,
46-
)
47-
class Foo:
48-
pass
49-
"""
34+
"""Class decorators are accumulated together with their arguments."""
35+
code = textwrap.dedent("""\
36+
@first_decorator
37+
@second.decorator(argument)
38+
@third.multi.line(
39+
decorator,
40+
key=value,
41+
)
42+
class Foo:
43+
pass
44+
""")
5045

5146
module = pep257.parse(StringIO(code), 'dummy.py')
5247
defined_class = module.children[0]
@@ -61,17 +56,15 @@ class Foo:
6156
assert 'decorator,key=value,' == decorators[2].arguments
6257

6358
def test_parse_class_nested_decorator(self):
64-
"""
65-
Class decorator is recorded even for nested classes.
66-
"""
67-
code = """
68-
@parent_decorator
69-
class Foo:
70-
pass
71-
@first_decorator
72-
class NestedClass:
73-
pass
74-
"""
59+
"""Class decorator is recorded even for nested classes."""
60+
code = textwrap.dedent("""\
61+
@parent_decorator
62+
class Foo:
63+
pass
64+
@first_decorator
65+
class NestedClass:
66+
pass
67+
""")
7568
module = pep257.parse(StringIO(code), 'dummy.py')
7669
nested_class = module.children[0].children[0]
7770
decorators = nested_class.decorators
@@ -81,15 +74,13 @@ class NestedClass:
8174
assert '' == decorators[0].arguments
8275

8376
def test_parse_method_single_decorator(self):
84-
"""
85-
Method decorators are accumulated.
86-
"""
87-
code = """
88-
class Foo:
89-
@first_decorator
90-
def method(self):
91-
pass
92-
"""
77+
"""Method decorators are accumulated."""
78+
code = textwrap.dedent("""\
79+
class Foo:
80+
@first_decorator
81+
def method(self):
82+
pass
83+
""")
9384

9485
module = pep257.parse(StringIO(code), 'dummy.py')
9586
defined_class = module.children[0]
@@ -100,20 +91,18 @@ def method(self):
10091
assert '' == decorators[0].arguments
10192

10293
def test_parse_method_decorators(self):
103-
"""
104-
Method decorators are accumulated.
105-
"""
106-
code = """
107-
class Foo:
108-
@first_decorator
109-
@second.decorator(argument)
110-
@third.multi.line(
111-
decorator,
112-
key=value,
113-
)
114-
def method(self):
115-
pass
116-
"""
94+
"""Multiple method decorators are accumulated along with their args."""
95+
code = textwrap.dedent("""\
96+
class Foo:
97+
@first_decorator
98+
@second.decorator(argument)
99+
@third.multi.line(
100+
decorator,
101+
key=value,
102+
)
103+
def method(self):
104+
pass
105+
""")
117106

118107
module = pep257.parse(StringIO(code), 'dummy.py')
119108
defined_class = module.children[0]
@@ -128,13 +117,12 @@ def method(self):
128117
assert 'decorator,key=value,' == decorators[2].arguments
129118

130119
def test_parse_function_decorator(self):
131-
"""
132-
It accumulates decorators for functions.
133-
"""
134-
code = """@first_decorator
135-
def some_method(self):
136-
pass
137-
"""
120+
"""A function decorator is also accumulated."""
121+
code = textwrap.dedent("""\
122+
@first_decorator
123+
def some_method(self):
124+
pass
125+
""")
138126

139127
module = pep257.parse(StringIO(code), 'dummy.py')
140128
decorators = module.children[0].decorators
@@ -144,17 +132,15 @@ def some_method(self):
144132
assert '' == decorators[0].arguments
145133

146134
def test_parse_method_nested_decorator(self):
147-
"""
148-
Method decorators are accumulated for nested methods.
149-
"""
150-
code = """
151-
class Foo:
152-
@parent_decorator
153-
def method(self):
154-
@first_decorator
155-
def nested_method(arg):
156-
pass
157-
"""
135+
"""Method decorators are accumulated for nested methods."""
136+
code = textwrap.dedent("""\
137+
class Foo:
138+
@parent_decorator
139+
def method(self):
140+
@first_decorator
141+
def nested_method(arg):
142+
pass
143+
""")
158144

159145
module = pep257.parse(StringIO(code), 'dummy.py')
160146
defined_class = module.children[0]
@@ -166,53 +152,25 @@ def nested_method(arg):
166152

167153

168154
class TestMethod:
169-
"""
170-
Unit test for Method class.
171-
"""
155+
"""Unit test for Method class."""
172156

173157
def makeMethod(self, name='someMethodName'):
174-
"""
175-
Return a simple method instance.
176-
"""
158+
"""Return a simple method instance."""
177159
children = []
178160
all = ['ClassName']
179-
source = 'class ClassName:\n def %s(self):\n' % (name)
180-
181-
module = pep257.Module(
182-
'module_name',
183-
source,
184-
0,
185-
1,
186-
[],
187-
'Docstring for module',
188-
[],
189-
None,
190-
all,
191-
)
192-
193-
parent = pep257.Class(
194-
'ClassName',
195-
source,
196-
0,
197-
1,
198-
[],
199-
'Docstring for class',
200-
children,
201-
module,
202-
all,
203-
)
204-
205-
return pep257.Method(
206-
name,
207-
source,
208-
0,
209-
1,
210-
[],
211-
'Docstring for method',
212-
children,
213-
parent,
214-
all,
215-
)
161+
source = textwrap.dedent("""\
162+
class ClassName:
163+
def %s(self):
164+
""" % (name))
165+
166+
module = pep257.Module('module_name', source, 0, 1, [],
167+
'Docstring for module', [], None, all)
168+
169+
cls = pep257.Class('ClassName', source, 0, 1, [],
170+
'Docstring for class', children, module, all)
171+
172+
return pep257.Method(name, source, 0, 1, [],
173+
'Docstring for method', children, cls, all)
216174

217175
def test_is_public_normal(self):
218176
"""Methods are normally public, even if decorated."""
@@ -240,3 +198,13 @@ def test_is_public_deleter(self):
240198
]
241199

242200
assert not method.is_public
201+
202+
def test_is_public_trick(self):
203+
"""Common prefix does not necessarily indicate private."""
204+
method = self.makeMethod("foo")
205+
method.decorators = [
206+
pep257.Decorator('foobar', []),
207+
pep257.Decorator('foobar.baz', []),
208+
]
209+
210+
assert method.is_public

0 commit comments

Comments
 (0)