@@ -177,7 +177,7 @@ def deco(func):
177177
178178
179179@checker (".py" , rst_only = False )
180- def check_python_syntax (file , lines ):
180+ def check_python_syntax (file , lines , options = None ):
181181 """Search invalid syntax in Python examples."""
182182 code = "" .join (lines )
183183 if "\r " in code :
@@ -198,7 +198,7 @@ def is_in_a_table(error, line):
198198
199199
200200@checker (".rst" )
201- def check_missing_backtick_after_role (file , lines ):
201+ def check_missing_backtick_after_role (file , lines , options = None ):
202202 """Search for roles missing their closing backticks.
203203
204204 Bad: :fct:`foo
@@ -214,7 +214,7 @@ def check_missing_backtick_after_role(file, lines):
214214
215215
216216@checker (".rst" )
217- def check_missing_space_after_literal (file , lines ):
217+ def check_missing_space_after_literal (file , lines , options = None ):
218218 r"""Search for inline literals immediately followed by a character.
219219
220220 Bad: ``items``s
@@ -259,7 +259,7 @@ def paragraphs(lines):
259259
260260
261261@checker (".rst" , enabled = False )
262- def check_default_role (file , lines ):
262+ def check_default_role (file , lines , options = None ):
263263 """Search for default roles (but they are allowed in many projects).
264264
265265 Bad: `print`
@@ -271,7 +271,7 @@ def check_default_role(file, lines):
271271
272272
273273@checker (".rst" )
274- def check_directive_with_three_dots (file , lines ):
274+ def check_directive_with_three_dots (file , lines , options = None ):
275275 """Search for directives with three dots instead of two.
276276
277277 Bad: ... versionchanged:: 3.6
@@ -283,7 +283,7 @@ def check_directive_with_three_dots(file, lines):
283283
284284
285285@checker (".rst" )
286- def check_directive_missing_colons (file , lines ):
286+ def check_directive_missing_colons (file , lines , options = None ):
287287 """Search for directive wrongly typed as comments.
288288
289289 Bad: .. versionchanged 3.6.
@@ -295,7 +295,7 @@ def check_directive_missing_colons(file, lines):
295295
296296
297297@checker (".rst" )
298- def check_missing_space_after_role (file , lines ):
298+ def check_missing_space_after_role (file , lines , options = None ):
299299 r"""Search for roles immediately followed by a character.
300300
301301 Bad: :exc:`Exception`s.
@@ -313,7 +313,7 @@ def check_missing_space_after_role(file, lines):
313313
314314
315315@checker (".rst" )
316- def check_role_without_backticks (file , lines ):
316+ def check_role_without_backticks (file , lines , options = None ):
317317 """Search roles without backticks.
318318
319319 Bad: :func:pdb.main
@@ -326,7 +326,7 @@ def check_role_without_backticks(file, lines):
326326
327327
328328@checker (".rst" )
329- def check_backtick_before_role (file , lines ):
329+ def check_backtick_before_role (file , lines , options = None ):
330330 """Search for roles preceded by a backtick.
331331
332332 Bad: `:fct:`sum`
@@ -340,7 +340,7 @@ def check_backtick_before_role(file, lines):
340340
341341
342342@checker (".rst" )
343- def check_missing_space_in_hyperlink (file , lines ):
343+ def check_missing_space_in_hyperlink (file , lines , options = None ):
344344 """Search for hyperlinks missing a space.
345345
346346 Bad: `Link text<https://example.com>_`
@@ -355,7 +355,7 @@ def check_missing_space_in_hyperlink(file, lines):
355355
356356
357357@checker (".rst" )
358- def check_missing_underscore_after_hyperlink (file , lines ):
358+ def check_missing_underscore_after_hyperlink (file , lines , options = None ):
359359 """Search for hyperlinks missing underscore after their closing backtick.
360360
361361 Bad: `Link text <https://example.com>`
@@ -370,7 +370,7 @@ def check_missing_underscore_after_hyperlink(file, lines):
370370
371371
372372@checker (".rst" )
373- def check_role_with_double_backticks (file , lines ):
373+ def check_role_with_double_backticks (file , lines , options = None ):
374374 """Search for roles with double backticks.
375375
376376 Bad: :fct:``sum``
@@ -384,7 +384,7 @@ def check_role_with_double_backticks(file, lines):
384384
385385
386386@checker (".rst" )
387- def check_missing_space_before_role (file , lines ):
387+ def check_missing_space_before_role (file , lines , options = None ):
388388 """Search for missing spaces before roles.
389389
390390 Bad: the:fct:`sum`
@@ -398,7 +398,7 @@ def check_missing_space_before_role(file, lines):
398398
399399
400400@checker (".rst" )
401- def check_missing_colon_in_role (file , lines ):
401+ def check_missing_colon_in_role (file , lines , options = None ):
402402 """Search for missing colons in roles.
403403
404404 Bad: :issue`123`
@@ -410,23 +410,23 @@ def check_missing_colon_in_role(file, lines):
410410
411411
412412@checker (".py" , ".rst" , rst_only = False )
413- def check_carriage_return (file , lines ):
413+ def check_carriage_return (file , lines , options = None ):
414414 r"""Check for carriage returns (\r) in lines."""
415415 for lno , line in enumerate (lines ):
416416 if "\r " in line :
417417 yield lno + 1 , "\\ r in line"
418418
419419
420420@checker (".py" , ".rst" , rst_only = False )
421- def check_horizontal_tab (file , lines ):
421+ def check_horizontal_tab (file , lines , options = None ):
422422 r"""Check for horizontal tabs (\t) in lines."""
423423 for lno , line in enumerate (lines ):
424424 if "\t " in line :
425425 yield lno + 1 , "OMG TABS!!!1"
426426
427427
428428@checker (".py" , ".rst" , rst_only = False )
429- def check_trailing_whitespace (file , lines ):
429+ def check_trailing_whitespace (file , lines , options = None ):
430430 """Check for trailing whitespaces at end of lines."""
431431 for lno , line in enumerate (lines ):
432432 stripped_line = line .rstrip ("\n " )
@@ -435,30 +435,33 @@ def check_trailing_whitespace(file, lines):
435435
436436
437437@checker (".py" , ".rst" , rst_only = False )
438- def check_missing_final_newline (file , lines ):
438+ def check_missing_final_newline (file , lines , options = None ):
439439 """Check that the last line of the file ends with a newline."""
440440 if lines and not lines [- 1 ].endswith ("\n " ):
441441 yield len (lines ), "No newline at end of file."
442442
443443
444- @checker (".rst" , enabled = False , rst_only = False )
445- def check_line_too_long (file , lines ):
444+ @checker (".rst" , enabled = False , rst_only = True )
445+ def check_line_too_long (file , lines , options = None ):
446446 """Check for line length; this checker is not run by default."""
447447 for lno , line in enumerate (lines ):
448- if len (line ) > 81 :
449- # don't complain about tables, links and function signatures
450- if (
451- line .lstrip ()[0 ] not in "+|"
452- and "http://" not in line
453- and not line .lstrip ().startswith (
454- (".. function" , ".. method" , ".. cfunction" )
455- )
456- ):
457- yield lno + 1 , "line too long"
448+ # Beware, in `line` we have the trailing newline.
449+ if len (line ) - 1 > options .max_line_length :
450+ if line .lstrip ()[0 ] in "+|" :
451+ continue # ignore wide tables
452+ if re .match (r"^\s*\W*(:(\w+:)+)?`.*`\W*$" , line ):
453+ continue # ignore long interpreted text
454+ if re .match (r"^\s*\.\. " , line ):
455+ continue # ignore directives and hyperlink targets
456+ if re .match (r"^\s*__ " , line ):
457+ continue # ignore anonymous hyperlink targets
458+ if re .match (r"^\s*``[^`]+``$" , line ):
459+ continue # ignore a very long literal string
460+ yield lno + 1 , f"Line too long ({ len (line )- 1 } /{ options .max_line_length } )"
458461
459462
460463@checker (".html" , enabled = False , rst_only = False )
461- def check_leaked_markup (file , lines ):
464+ def check_leaked_markup (file , lines , options = None ):
462465 """Check HTML files for leaked reST markup.
463466
464467 This only works if the HTML files have been built.
@@ -533,7 +536,7 @@ def type_of_explicit_markup(line):
533536
534537
535538@checker (".rst" , enabled = False )
536- def check_triple_backticks (file , lines ):
539+ def check_triple_backticks (file , lines , options = None ):
537540 """Check for triple backticks, like ```Point``` (but it's a valid syntax).
538541
539542 Bad: ```Point```
@@ -550,7 +553,7 @@ def check_triple_backticks(file, lines):
550553
551554
552555@checker (".rst" , rst_only = False )
553- def check_bad_dedent (file , lines ):
556+ def check_bad_dedent (file , lines , options = None ):
554557 """Check for mis-alignment in indentation in code blocks.
555558
556559 |A 5 lines block::
@@ -634,6 +637,12 @@ def __call__(self, parser, namespace, values, option_string=None):
634637 "Can be used to see which checkers would be used with a given set of "
635638 "--enable and --disable options." ,
636639 )
640+ parser .add_argument (
641+ "--max-line-length" ,
642+ help = "Maximum number of characters on a single line." ,
643+ default = 80 ,
644+ type = int ,
645+ )
637646 parser .add_argument ("paths" , default = "." , nargs = "*" )
638647 args = parser .parse_args (argv [1 :])
639648 try :
@@ -665,7 +674,21 @@ def walk(path, ignore_list):
665674 yield file if file [:2 ] != "./" else file [2 :]
666675
667676
668- def check_text (filename , text , checkers ):
677+ class CheckersOptions :
678+ """Configuration options for checkers."""
679+
680+ max_line_length = 80
681+
682+ @classmethod
683+ def from_argparse (cls , namespace ):
684+ options = cls ()
685+ options .max_line_length = namespace .max_line_length
686+ return options
687+
688+
689+ def check_text (filename , text , checkers , options = None ):
690+ if options is None :
691+ options = CheckersOptions ()
669692 errors = Counter ()
670693 ext = splitext (filename )[1 ]
671694 checkers = {checker for checker in checkers if ext in checker .suffixes }
@@ -676,14 +699,14 @@ def check_text(filename, text, checkers):
676699 if ext not in check .suffixes :
677700 continue
678701 for lno , msg in check (
679- filename , lines_with_rst_only if check .rst_only else lines
702+ filename , lines_with_rst_only if check .rst_only else lines , options
680703 ):
681704 print (f"{ filename } :{ lno } : { msg } ({ check .name } )" )
682705 errors [check .name ] += 1
683706 return errors
684707
685708
686- def check_file (filename , checkers ):
709+ def check_file (filename , checkers , options : CheckersOptions = None ):
687710 ext = splitext (filename )[1 ]
688711 if not any (ext in checker .suffixes for checker in checkers ):
689712 return Counter ()
@@ -696,11 +719,12 @@ def check_file(filename, checkers):
696719 except UnicodeDecodeError as err :
697720 print (f"{ filename } : cannot decode as UTF-8: { err } " )
698721 return Counter ({4 : 1 })
699- return check_text (filename , text , checkers )
722+ return check_text (filename , text , checkers , options )
700723
701724
702725def main (argv = None ):
703726 enabled_checkers , args = parse_args (argv )
727+ options = CheckersOptions .from_argparse (args )
704728 if args .list :
705729 if not enabled_checkers :
706730 print ("No checkers selected." )
@@ -721,7 +745,7 @@ def main(argv=None):
721745 return 2
722746
723747 todo = [
724- (path , enabled_checkers )
748+ (path , enabled_checkers , options )
725749 for path in chain .from_iterable (walk (path , args .ignore ) for path in args .paths )
726750 ]
727751
0 commit comments