15
15
16
16
import os
17
17
import sys
18
+ import ast
18
19
import copy
19
20
import logging
20
21
import tokenize as tk
@@ -118,7 +119,6 @@ class Definition(Value):
118
119
module = property (lambda self : self .parent .module )
119
120
all = property (lambda self : self .module .all )
120
121
_slice = property (lambda self : slice (self .start - 1 , self .end ))
121
- source = property (lambda self : '' .join (self ._source [self ._slice ]))
122
122
is_class = False
123
123
124
124
def __iter__ (self ):
@@ -128,6 +128,17 @@ def __iter__(self):
128
128
def _publicity (self ):
129
129
return {True : 'public' , False : 'private' }[self .is_public ]
130
130
131
+ @property
132
+ def source (self ):
133
+ """Return the source code for the definition."""
134
+ full_src = self ._source [self ._slice ]
135
+
136
+ def is_empty_or_comment (line ):
137
+ return line .strip () == '' or line .strip ().startswith ('#' )
138
+
139
+ filtered_src = dropwhile (is_empty_or_comment , reversed (full_src ))
140
+ return '' .join (reversed (list (filtered_src )))
141
+
131
142
def __str__ (self ):
132
143
return 'in %s %s `%s`' % (self ._publicity , self ._human , self .name )
133
144
@@ -429,7 +440,7 @@ def parse_module(self):
429
440
return module
430
441
431
442
def parse_definition (self , class_ ):
432
- """Parse a defintion and return its value in a `class_` object."""
443
+ """Parse a definition and return its value in a `class_` object."""
433
444
start = self .line
434
445
self .consume (tk .NAME )
435
446
name = self .current .value
@@ -456,9 +467,9 @@ def parse_definition(self, class_):
456
467
docstring = self .parse_docstring ()
457
468
decorators = self ._accumulated_decorators
458
469
self ._accumulated_decorators = []
459
- log .debug ("parsing nested defintions ." )
470
+ log .debug ("parsing nested definitions ." )
460
471
children = list (self .parse_definitions (class_ ))
461
- log .debug ("finished parsing nested defintions for '%s'" , name )
472
+ log .debug ("finished parsing nested definitions for '%s'" , name )
462
473
end = self .line - 1
463
474
else : # one-liner definition
464
475
docstring = self .parse_docstring ()
@@ -682,6 +693,8 @@ def to_rst(cls):
682
693
'%r, not %r' )
683
694
D402 = D4xx .create_error ('D402' , 'First line should not be the function\' s '
684
695
'"signature"' )
696
+ D403 = D4xx .create_error ('D403' , 'First word of the first line should be '
697
+ 'properly capitalized' , '%r, not %r' )
685
698
686
699
687
700
class AttrDict (dict ):
@@ -1361,7 +1374,7 @@ def check_docstring_missing(self, definition, docstring):
1361
1374
1362
1375
"""
1363
1376
if (not docstring and definition .is_public or
1364
- docstring and is_blank (eval (docstring ))):
1377
+ docstring and is_blank (ast . literal_eval (docstring ))):
1365
1378
codes = {Module : D100 , Class : D101 , NestedClass : D101 ,
1366
1379
Method : (lambda : D105 () if is_magic (definition .name )
1367
1380
else D102 ()),
@@ -1377,7 +1390,7 @@ def check_one_liners(self, definition, docstring):
1377
1390
1378
1391
"""
1379
1392
if docstring :
1380
- lines = eval (docstring ).split ('\n ' )
1393
+ lines = ast . literal_eval (docstring ).split ('\n ' )
1381
1394
if len (lines ) > 1 :
1382
1395
non_empty_lines = sum (1 for l in lines if not is_blank (l ))
1383
1396
if non_empty_lines == 1 :
@@ -1390,7 +1403,6 @@ def check_no_blank_before(self, function, docstring): # def
1390
1403
There's no blank line either before or after the docstring.
1391
1404
1392
1405
"""
1393
- # NOTE: This does not take comments into account.
1394
1406
# NOTE: This does not take into account functions with groups of code.
1395
1407
if docstring :
1396
1408
before , _ , after = function .source .partition (docstring )
@@ -1448,7 +1460,7 @@ def check_blank_after_summary(self, definition, docstring):
1448
1460
1449
1461
"""
1450
1462
if docstring :
1451
- lines = eval (docstring ).strip ().split ('\n ' )
1463
+ lines = ast . literal_eval (docstring ).strip ().split ('\n ' )
1452
1464
if len (lines ) > 1 :
1453
1465
post_summary_blanks = list (map (is_blank , lines [1 :]))
1454
1466
blanks_count = sum (takewhile (bool , post_summary_blanks ))
@@ -1487,7 +1499,8 @@ def check_newline_after_last_paragraph(self, definition, docstring):
1487
1499
1488
1500
"""
1489
1501
if docstring :
1490
- lines = [l for l in eval (docstring ).split ('\n ' ) if not is_blank (l )]
1502
+ lines = [l for l in ast .literal_eval (docstring ).split ('\n ' )
1503
+ if not is_blank (l )]
1491
1504
if len (lines ) > 1 :
1492
1505
if docstring .split ("\n " )[- 1 ].strip () not in ['"""' , "'''" ]:
1493
1506
return D209 ()
@@ -1496,7 +1509,7 @@ def check_newline_after_last_paragraph(self, definition, docstring):
1496
1509
def check_surrounding_whitespaces (self , definition , docstring ):
1497
1510
"""D210: No whitespaces allowed surrounding docstring text."""
1498
1511
if docstring :
1499
- lines = eval (docstring ).split ('\n ' )
1512
+ lines = ast . literal_eval (docstring ).split ('\n ' )
1500
1513
if lines [0 ].startswith (' ' ) or \
1501
1514
len (lines ) == 1 and lines [0 ].endswith (' ' ):
1502
1515
return D210 ()
@@ -1514,8 +1527,8 @@ def check_triple_double_quotes(self, definition, docstring):
1514
1527
""" quotes in its body.
1515
1528
1516
1529
'''
1517
- if docstring and '"""' in eval (docstring ) and docstring . startswith (
1518
- ( "'''" , "r'''" , "u'''" , "ur'''" )):
1530
+ if ( docstring and '"""' in ast . literal_eval (docstring ) and
1531
+ docstring . startswith (( "'''" , "r'''" , "u'''" , "ur'''" ) )):
1519
1532
# Allow ''' quotes if docstring contains """, because otherwise """
1520
1533
# quotes could not be expressed inside docstring. Not in PEP 257.
1521
1534
return
@@ -1563,7 +1576,7 @@ def check_ends_with_period(self, definition, docstring):
1563
1576
1564
1577
"""
1565
1578
if docstring :
1566
- summary_line = eval (docstring ).strip ().split ('\n ' )[0 ]
1579
+ summary_line = ast . literal_eval (docstring ).strip ().split ('\n ' )[0 ]
1567
1580
if not summary_line .endswith ('.' ):
1568
1581
return D400 (summary_line [- 1 ])
1569
1582
@@ -1577,7 +1590,7 @@ def check_imperative_mood(self, function, docstring): # def context
1577
1590
1578
1591
"""
1579
1592
if docstring :
1580
- stripped = eval (docstring ).strip ()
1593
+ stripped = ast . literal_eval (docstring ).strip ()
1581
1594
if stripped :
1582
1595
first_word = stripped .split ()[0 ]
1583
1596
if first_word .endswith ('s' ) and not first_word .endswith ('ss' ):
@@ -1592,10 +1605,22 @@ def check_no_signature(self, function, docstring): # def context
1592
1605
1593
1606
"""
1594
1607
if docstring :
1595
- first_line = eval (docstring ).strip ().split ('\n ' )[0 ]
1608
+ first_line = ast . literal_eval (docstring ).strip ().split ('\n ' )[0 ]
1596
1609
if function .name + '(' in first_line .replace (' ' , '' ):
1597
1610
return D402 ()
1598
1611
1612
+ @check_for (Function )
1613
+ def check_capitalized (self , function , docstring ):
1614
+ """D403: First word of the first line should be properly capitalized.
1615
+
1616
+ The [first line of a] docstring is a phrase ending in a period.
1617
+
1618
+ """
1619
+ if docstring :
1620
+ first_word = ast .literal_eval (docstring ).split ()[0 ]
1621
+ if first_word != first_word .capitalize ():
1622
+ return D403 (first_word .capitalize (), first_word )
1623
+
1599
1624
# Somewhat hard to determine if return value is mentioned.
1600
1625
# @check(Function)
1601
1626
def SKIP_check_return_type (self , function , docstring ):
0 commit comments