1313__version__ = "0.6.6"
1414
1515import argparse
16+ import io
1617import multiprocessing
1718import os
1819import sys
2122from itertools import chain , starmap
2223from os .path import exists , isfile , join , splitext
2324
25+ from polib import pofile , POFile
2426import regex as re
2527
26-
2728# The following chars groups are from docutils:
2829CLOSING_DELIMITERS = "\\ \\ .,;!?"
2930DELIMITERS = (
@@ -193,7 +194,7 @@ def check_python_syntax(file, lines, options=None):
193194role_missing_closing_backtick = re .compile (rf"({ ROLE_HEAD } `[^`]+?)[^`]*$" )
194195
195196
196- @checker (".rst" )
197+ @checker (".rst" , ".po" )
197198def check_missing_backtick_after_role (file , lines , options = None ):
198199 """Search for roles missing their closing backticks.
199200
@@ -246,7 +247,7 @@ def clean_paragraph(paragraph):
246247 return paragraph .replace ("\x00 " , "\\ " )
247248
248249
249- @checker (".rst" )
250+ @checker (".rst" , ".po" )
250251def check_missing_space_after_literal (file , lines , options = None ):
251252 r"""Search for inline literals immediately followed by a character.
252253
@@ -267,7 +268,7 @@ def check_missing_space_after_literal(file, lines, options=None):
267268 )
268269
269270
270- @checker (".rst" )
271+ @checker (".rst" , ".po" )
271272def check_unbalanced_inline_literals_delimiters (file , lines , options = None ):
272273 r"""Search for unbalanced inline literals delimiters.
273274
@@ -445,7 +446,7 @@ def inline_markup_gen(start_string, end_string, extra_allowed_before=""):
445446)
446447
447448
448- @checker (".rst" , enabled = False )
449+ @checker (".rst" , ".po" , enabled = False )
449450def check_default_role (file , lines , options = None ):
450451 """Search for default roles (but they are allowed in many projects).
451452
@@ -471,7 +472,7 @@ def check_default_role(file, lines, options=None):
471472 yield lno , "default role used (hint: for inline literals, use double backticks)"
472473
473474
474- @checker (".rst" )
475+ @checker (".rst" , ".po" )
475476def check_directive_with_three_dots (file , lines , options = None ):
476477 """Search for directives with three dots instead of two.
477478
@@ -483,7 +484,7 @@ def check_directive_with_three_dots(file, lines, options=None):
483484 yield lno , "directive should start with two dots, not three."
484485
485486
486- @checker (".rst" )
487+ @checker (".rst" , ".po" )
487488def check_directive_missing_colons (file , lines , options = None ):
488489 """Search for directive wrongly typed as comments.
489490
@@ -495,7 +496,7 @@ def check_directive_missing_colons(file, lines, options=None):
495496 yield lno , "comment seems to be intended as a directive"
496497
497498
498- @checker (".rst" )
499+ @checker (".rst" , ".po" )
499500def check_missing_space_after_role (file , lines , options = None ):
500501 r"""Search for roles immediately followed by a character.
501502
@@ -515,7 +516,7 @@ def check_missing_space_after_role(file, lines, options=None):
515516 yield lno , f"role missing (escaped) space after role: { role .group (0 )!r} "
516517
517518
518- @checker (".rst" )
519+ @checker (".rst" , ".po" )
519520def check_role_without_backticks (file , lines , options = None ):
520521 """Search roles without backticks.
521522
@@ -528,7 +529,7 @@ def check_role_without_backticks(file, lines, options=None):
528529 yield lno , f"role with no backticks: { no_backticks .group (0 )!r} "
529530
530531
531- @checker (".rst" )
532+ @checker (".rst" , ".po" )
532533def check_backtick_before_role (file , lines , options = None ):
533534 """Search for roles preceded by a backtick.
534535
@@ -542,7 +543,7 @@ def check_backtick_before_role(file, lines, options=None):
542543 yield lno , "superfluous backtick in front of role"
543544
544545
545- @checker (".rst" )
546+ @checker (".rst" , ".po" )
546547def check_missing_space_in_hyperlink (file , lines , options = None ):
547548 """Search for hyperlinks missing a space.
548549
@@ -557,7 +558,7 @@ def check_missing_space_in_hyperlink(file, lines, options=None):
557558 yield lno , "missing space before < in hyperlink"
558559
559560
560- @checker (".rst" )
561+ @checker (".rst" , ".po" )
561562def check_missing_underscore_after_hyperlink (file , lines , options = None ):
562563 """Search for hyperlinks missing underscore after their closing backtick.
563564
@@ -572,7 +573,7 @@ def check_missing_underscore_after_hyperlink(file, lines, options=None):
572573 yield lno , "missing underscore after closing backtick in hyperlink"
573574
574575
575- @checker (".rst" )
576+ @checker (".rst" , ".po" )
576577def check_role_with_double_backticks (file , lines , options = None ):
577578 """Search for roles with double backticks.
578579
@@ -638,7 +639,7 @@ def looks_like_glued(match):
638639 return True
639640
640641
641- @checker (".rst" )
642+ @checker (".rst" , ".po" )
642643def check_missing_space_before_role (file , lines , options = None ):
643644 """Search for missing spaces before roles.
644645
@@ -658,7 +659,7 @@ def check_missing_space_before_role(file, lines, options=None):
658659 yield paragraph_lno + error_offset , f"role missing opening tag colon ({ match .group (0 )} )."
659660
660661
661- @checker (".rst" )
662+ @checker (".rst" , ".po" )
662663def check_missing_space_before_default_role (file , lines , options = None ):
663664 """Search for missing spaces before default role.
664665
@@ -681,7 +682,7 @@ def check_missing_space_before_default_role(file, lines, options=None):
681682 )
682683
683684
684- @checker (".rst" )
685+ @checker (".rst" , ".po" )
685686def check_hyperlink_reference_missing_backtick (file , lines , options = None ):
686687 """Search for missing backticks in front of hyperlink references.
687688
@@ -702,7 +703,7 @@ def check_hyperlink_reference_missing_backtick(file, lines, options=None):
702703 )
703704
704705
705- @checker (".rst" )
706+ @checker (".rst" , ".po" )
706707def check_missing_colon_in_role (file , lines , options = None ):
707708 """Search for missing colons in roles.
708709
@@ -715,23 +716,23 @@ def check_missing_colon_in_role(file, lines, options=None):
715716 yield lno , f"role missing colon before first backtick ({ match .group (0 )} )."
716717
717718
718- @checker (".py" , ".rst" , rst_only = False )
719+ @checker (".py" , ".rst" , ".po" , rst_only = False )
719720def check_carriage_return (file , lines , options = None ):
720721 r"""Check for carriage returns (\r) in lines."""
721722 for lno , line in enumerate (lines ):
722723 if "\r " in line :
723724 yield lno + 1 , "\\ r in line"
724725
725726
726- @checker (".py" , ".rst" , rst_only = False )
727+ @checker (".py" , ".rst" , ".po" , rst_only = False )
727728def check_horizontal_tab (file , lines , options = None ):
728729 r"""Check for horizontal tabs (\t) in lines."""
729730 for lno , line in enumerate (lines ):
730731 if "\t " in line :
731732 yield lno + 1 , "OMG TABS!!!1"
732733
733734
734- @checker (".py" , ".rst" , rst_only = False )
735+ @checker (".py" , ".rst" , ".po" , rst_only = False )
735736def check_trailing_whitespace (file , lines , options = None ):
736737 """Check for trailing whitespaces at end of lines."""
737738 for lno , line in enumerate (lines ):
@@ -740,14 +741,14 @@ def check_trailing_whitespace(file, lines, options=None):
740741 yield lno + 1 , "trailing whitespace"
741742
742743
743- @checker (".py" , ".rst" , rst_only = False )
744+ @checker (".py" , ".rst" , ".po" , rst_only = False )
744745def check_missing_final_newline (file , lines , options = None ):
745746 """Check that the last line of the file ends with a newline."""
746747 if lines and not lines [- 1 ].endswith ("\n " ):
747748 yield len (lines ), "No newline at end of file."
748749
749750
750- @checker (".rst" , enabled = False , rst_only = True )
751+ @checker (".rst" , ".po" , enabled = False , rst_only = True )
751752def check_line_too_long (file , lines , options = None ):
752753 """Check for line length; this checker is not run by default."""
753754 for lno , line in enumerate (lines ):
@@ -849,7 +850,7 @@ def type_of_explicit_markup(line):
849850)
850851
851852
852- @checker (".rst" , enabled = False )
853+ @checker (".rst" , ".po" , enabled = False )
853854def check_triple_backticks (file , lines , options = None ):
854855 """Check for triple backticks, like ```Point``` (but it's a valid syntax).
855856
@@ -866,7 +867,7 @@ def check_triple_backticks(file, lines, options=None):
866867 yield lno + 1 , "There's no rst syntax using triple backticks"
867868
868869
869- @checker (".rst" , rst_only = False )
870+ @checker (".rst" , ".po" , rst_only = False )
870871def check_bad_dedent (file , lines , options = None ):
871872 """Check for mis-alignment in indentation in code blocks.
872873
@@ -1023,13 +1024,28 @@ def check_text(filename, text, checkers, options=None):
10231024 return errors
10241025
10251026
1027+ def po2rst (text ):
1028+ """Extract msgstr entries from a po content, keeping linenos."""
1029+ output = []
1030+ po = pofile (text , encoding = "UTF-8" )
1031+ for entry in po .translated_entries ():
1032+ # Don't check original msgid, assume it's checked directly.
1033+ while len (output ) + 1 < entry .linenum :
1034+ output .append ("\n " )
1035+ for line in entry .msgstr .splitlines ():
1036+ output .append (line + "\n " )
1037+ return "" .join (output )
1038+
1039+
10261040def check_file (filename , checkers , options : CheckersOptions = None ):
10271041 ext = splitext (filename )[1 ]
10281042 if not any (ext in checker .suffixes for checker in checkers ):
10291043 return Counter ()
10301044 try :
10311045 with open (filename , encoding = "utf-8" ) as f :
10321046 text = f .read ()
1047+ if filename .endswith (".po" ):
1048+ text = po2rst (text )
10331049 except OSError as err :
10341050 print (f"{ filename } : cannot open: { err } " )
10351051 return Counter ({4 : 1 })
0 commit comments