15
15
16
16
import os
17
17
import sys
18
+ import logging
18
19
import tokenize as tk
19
20
from itertools import takewhile , dropwhile , chain
20
21
from optparse import OptionParser
21
22
from re import compile as re
22
23
24
+ log = logging .getLogger ()
25
+ log .addHandler (logging .StreamHandler ())
26
+
23
27
24
28
try :
25
29
from StringIO import StringIO
@@ -60,8 +64,9 @@ class Value(object):
60
64
__eq__ = lambda self , other : other and vars (self ) == vars (other )
61
65
62
66
def __repr__ (self ):
63
- args = [vars (self )[field ] for field in self ._fields ]
64
- return '%s(%s)' % (self .__class__ .__name__ , ', ' .join (map (repr , args )))
67
+ format_arg = lambda arg : '{}={!r}' .format (arg , getattr (self , arg ))
68
+ kwargs = ', ' .join (format_arg (arg ) for arg in self ._fields )
69
+ return '{}({})' .format (self .__class__ .__name__ , kwargs )
65
70
66
71
67
72
class Definition (Value ):
@@ -131,10 +136,19 @@ class NestedClass(Class):
131
136
is_public = False
132
137
133
138
139
+ class TokenKind (int ):
140
+ def __repr__ (self ):
141
+ return "tk.{}" .format (tk .tok_name [self ])
142
+
143
+
134
144
class Token (Value ):
135
145
136
146
_fields = 'kind value start end source' .split ()
137
147
148
+ def __init__ (self , * args ):
149
+ super (Token , self ).__init__ (* args )
150
+ self .kind = TokenKind (self .kind )
151
+
138
152
139
153
class TokenStream (object ):
140
154
@@ -187,34 +201,53 @@ def consume(self, kind):
187
201
assert self .stream .move ().kind == kind
188
202
189
203
def leapfrog (self , kind , value = None ):
190
- for token in self .stream :
191
- if token .kind == kind and (value is None or token .value == value ):
204
+ """Skip tokens in the stream until a certain token kind is reached.
205
+
206
+ If `value` is specified, tokens whose values are different will also
207
+ be skipped.
208
+ """
209
+ while self .current is not None :
210
+ if (self .current .kind == kind and
211
+ (value is None or self .current .value == value )):
192
212
self .consume (kind )
193
213
return
214
+ self .stream .move ()
194
215
195
216
def parse_docstring (self ):
196
- for token in self .stream :
197
- if token .kind in [tk .COMMENT , tk .NEWLINE , tk .NL ]:
198
- continue
199
- elif token .kind == tk .STRING :
200
- return token .value
201
- else :
202
- return None
217
+ """Parse a single docstring and return its value."""
218
+ log .debug ("parsing docstring, token is %r (%s)" ,
219
+ self .current .kind , self .current .value )
220
+ while self .current .kind in (tk .COMMENT , tk .NEWLINE , tk .NL ):
221
+ self .stream .move ()
222
+ log .debug ("parsing docstring, token is %r (%s)" ,
223
+ self .current .kind , self .current .value )
224
+ if self .current .kind == tk .STRING :
225
+ docstring = self .current .value
226
+ self .stream .move ()
227
+ return docstring
228
+ return None
203
229
204
230
def parse_definitions (self , class_ , all = False ):
205
- for token in self .stream :
206
- if all and token .value == '__all__' :
231
+ """Parse multiple defintions and yield them."""
232
+ while self .current is not None :
233
+ log .debug ("parsing defintion list, current token is %r (%s)" ,
234
+ self .current .kind , self .current .value )
235
+ if all and self .current .value == '__all__' :
207
236
self .parse_all ()
208
- if token .value in ['def' , 'class' ]:
209
- yield self .parse_definition (class_ ._nest (token .value ))
210
- if token .kind == tk .INDENT :
237
+ elif self . current .value in ['def' , 'class' ]:
238
+ yield self .parse_definition (class_ ._nest (self . current .value ))
239
+ elif self . current .kind == tk .INDENT :
211
240
self .consume (tk .INDENT )
212
241
for definition in self .parse_definitions (class_ ):
213
242
yield definition
214
- if token .kind == tk .DEDENT :
243
+ elif self .current .kind == tk .DEDENT :
244
+ self .consume (tk .DEDENT )
215
245
return
246
+ else :
247
+ self .stream .move ()
216
248
217
249
def parse_all (self ):
250
+ """Parse the __all__ definition in a module."""
218
251
assert self .current .value == '__all__'
219
252
self .consume (tk .NAME )
220
253
if self .current .value != '=' :
@@ -249,28 +282,49 @@ def parse_all(self):
249
282
raise AllError ('Could not evaluate contents of __all__: %s. ' % s )
250
283
251
284
def parse_module (self ):
285
+ """Parse a module (and its children) and return a Module object."""
286
+ log .debug ("parsing module." )
252
287
start = self .line
253
288
docstring = self .parse_docstring ()
254
289
children = list (self .parse_definitions (Module , all = True ))
255
- assert self .current is None
290
+ assert self .current is None , self . current
256
291
end = self .line
257
292
module = Module (self .filename , self .source , start , end ,
258
293
docstring , children , None , self .all )
259
294
for child in module .children :
260
295
child .parent = module
296
+ log .debug ("finished parsing module." )
261
297
return module
262
298
263
299
def parse_definition (self , class_ ):
300
+ """Parse a defintion and return its value in a `class_` object."""
264
301
start = self .line
265
302
self .consume (tk .NAME )
266
303
name = self .current .value
267
- self .leapfrog (tk .OP , value = ":" )
304
+ log .debug ("parsing %s '%s'" , class_ .__name__ , name )
305
+ self .stream .move ()
306
+ if self .current .kind == tk .OP and self .current .value == '(' :
307
+ parenthesis_level = 0
308
+ while True :
309
+ if self .current .kind == tk .OP :
310
+ if self .current .value == '(' :
311
+ parenthesis_level += 1
312
+ elif self .current .value == ')' :
313
+ parenthesis_level -= 1
314
+ if parenthesis_level == 0 :
315
+ break
316
+ self .stream .move ()
317
+ if self .current .kind != tk .OP or self .current .value != ':' :
318
+ self .leapfrog (tk .OP , value = ":" )
319
+ else :
320
+ self .consume (tk .OP )
268
321
if self .current .kind in (tk .NEWLINE , tk .COMMENT ):
269
322
self .leapfrog (tk .INDENT )
270
323
assert self .current .kind != tk .INDENT
271
324
docstring = self .parse_docstring ()
325
+ log .debug ("parsing nested defintions." )
272
326
children = list (self .parse_definitions (class_ ))
273
- assert self . current . kind == tk . DEDENT
327
+ log . debug ( "finished parsing nested defintions for '%s'" , name )
274
328
end = self .line - 1
275
329
else : # one-liner definition
276
330
docstring = self .parse_docstring ()
@@ -281,6 +335,9 @@ def parse_definition(self, class_):
281
335
docstring , children , None )
282
336
for child in definition .children :
283
337
child .parent = definition
338
+ log .debug ("finished parsing %s '%s'. Next token is %r (%s)" ,
339
+ class_ .__name__ , name , self .current .kind ,
340
+ self .current .value )
284
341
return definition
285
342
286
343
@@ -359,6 +416,8 @@ def parse_options():
359
416
help = "search only dirs that exactly match <pattern> regular "
360
417
"expression; default is --match-dir='[^\.].*', which matches "
361
418
"all dirs that don't start with a dot" )
419
+ option ('-d' , '--debug' , action = 'store_true' ,
420
+ help = 'print debug information' )
362
421
return parser .parse_args ()
363
422
364
423
@@ -411,6 +470,9 @@ def check(filenames, ignore=()):
411
470
412
471
413
472
def main (options , arguments ):
473
+ if options .debug :
474
+ log .setLevel (logging .DEBUG )
475
+ log .debug ("starting pep257 in debug mode." )
414
476
Error .explain = options .explain
415
477
Error .source = options .source
416
478
collected = collect (arguments or ['.' ],
0 commit comments