Skip to content

Commit 5e5b009

Browse files
committed
Drop Python 2 support, Python 3.6 is now required
1 parent 9d1a2d9 commit 5e5b009

10 files changed

Lines changed: 169 additions & 108 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ jobs:
1414
- ubuntu-20.04
1515
- ubuntu-18.04
1616
python-version:
17-
- "2.7"
18-
- "3.5"
1917
- "3.6"
2018
- "3.7"
2119
- "3.8"

ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Version 3.2.0 (under dev)
44

5+
- Drop Python 2 support, Python 3.6 is now required.
56
- Add support for Chinese full-stop.
67

78
## Version 3.1 (2020-03-07)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Msgcheck performs various checks on gettext files (with extension `.po`):
1515

1616
The script requires:
1717

18-
- Python ≥ 2.7
18+
- Python ≥ 3.6
1919
- gettext (for the command `msgfmt`, used to compile PO files)
2020
- the python module `pyenchant` if spelling is checked (with option `-s`).
2121

msgcheck/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# -*- coding: utf-8 -*-
1+
#!/usr/bin/env python3
22
#
33
# Copyright (C) 2009-2021 Sébastien Helleu <flashcode@flashtux.org>
44
#
@@ -18,9 +18,7 @@
1818
# along with msgcheck. If not, see <https://www.gnu.org/licenses/>.
1919
#
2020

21-
"""
22-
Gettext file checker.
23-
"""
21+
"""Gettext file checker."""
2422

2523
from . import msgcheck # noqa: F401
2624
from . import po # noqa: F401

msgcheck/msgcheck.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# -*- coding: utf-8 -*-
1+
#!/usr/bin/env python3
22
#
33
# Copyright (C) 2009-2021 Sébastien Helleu <flashcode@flashtux.org>
44
#
@@ -174,21 +174,19 @@ def msgcheck_display_result(args, result):
174174
for filename, reports in result:
175175
errors = len(reports)
176176
if errors == 0:
177-
print('{0}: OK'.format(filename))
177+
print(f'{filename}: OK')
178178
else:
179-
print('{0}: {1} errors ({2})'.format(
180-
filename,
181-
errors,
182-
'almost good!' if errors <= 10 else 'uh oh... try again!'))
179+
result = 'almost good!' if errors <= 10 else 'uh oh... try again!'
180+
print(f'{filename}: {errors} errors ({result})')
183181

184182
# display total (if many files processed)
185183
if len(args.file) > 1:
186184
print('---')
187185
if files_with_errors == 0:
188-
print('TOTAL: {0} files OK'.format(files_ok))
186+
print(f'TOTAL: {files_ok} files OK')
189187
else:
190-
print('TOTAL: {0} files OK, {1} files with {2} errors'.format(
191-
files_ok, files_with_errors, total_errors))
188+
print(f'TOTAL: {files_ok} files OK, {files_with_errors} files '
189+
f'with {total_errors} errors')
192190

193191
return files_with_errors
194192

msgcheck/po.py

Lines changed: 104 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# -*- coding: utf-8 -*-
1+
#!/usr/bin/env python3
22
#
33
# Copyright (C) 2009-2021 Sébastien Helleu <flashcode@flashtux.org>
44
#
@@ -18,17 +18,14 @@
1818
# along with msgcheck. If not, see <https://www.gnu.org/licenses/>.
1919
#
2020

21-
"""
22-
Classes to read and check PO (gettext) files.
23-
"""
21+
"""Classes to read and check PO (gettext) files."""
2422

2523
from __future__ import print_function
2624

2725
from codecs import escape_decode
2826
import os
2927
import re
3028
import subprocess
31-
import sys
3229
import tempfile
3330

3431
# enchant module is optional, spelling is checked on demand
@@ -72,17 +69,13 @@ def __repr__(self):
7269
if self.idmsg == 'extract':
7370
return self.message + '\n---'
7471
if self.idmsg == 'compile':
75-
return '{0}\n{1}'.format('=' * 70, self.message)
72+
return f'{"=" * 70}\n{self.message}'
7673
is_list = isinstance(self.message, list)
77-
count = '(%d)' % len(self.message) if is_list else ''
78-
msg = '{0}\n{1}:{2}: [{3}{4}] {5}{6}'.format(
79-
'=' * 70,
80-
self.filename,
81-
self.line,
82-
self.idmsg,
83-
count,
84-
'(fuzzy) ' if self.fuzzy else '',
85-
', '.join(self.message) if is_list else self.message)
74+
count = f'({len(self.message)})' if is_list else ''
75+
str_fuzzy = '(fuzzy) ' if self.fuzzy else ''
76+
str_msg = ', '.join(self.message) if is_list else self.message
77+
msg = (f'{"=" * 70}\n{self.filename}:{self.line}: '
78+
f'[{self.idmsg}{count}] {str_fuzzy}{str_msg}')
8679
if self.mid:
8780
msg += '\n---\n' + self.mid
8881
if self.mstr:
@@ -126,19 +119,16 @@ def __init__(self, filename, line, msg, charset, fuzzy, fmt, noqa):
126119
self.filename = filename
127120
self.line = line
128121
# unescape strings
129-
if sys.version_info < (3,):
130-
# python 2.x
131-
msg = {k: escape_decode(v)[0] for k, v in msg.items()}
132-
else:
133-
# python 3.x
134-
msg = {k: escape_decode(v)[0]. decode(charset)
135-
for k, v in msg.items()}
122+
msg = {
123+
k: escape_decode(v)[0].decode(charset)
124+
for k, v in msg.items()
125+
}
136126
# build messages as a list of tuples: (string, translation)
137127
self.messages = []
138128
if 'msgid_plural' in msg:
139129
i = 0
140130
while True:
141-
key = 'msgstr[{0}]'.format(i)
131+
key = f'msgstr[{i}]'
142132
if key not in msg:
143133
break
144134
self.messages.append((msg['msgid_plural'], msg[key]))
@@ -162,9 +152,16 @@ def check_lines(self):
162152
nb_str = count_lines(mstr)
163153
if nb_id != nb_str:
164154
errors.append(
165-
PoReport('number of lines: {0} in string, '
166-
'{1} in translation'.format(nb_id, nb_str),
167-
'lines', self.filename, self.line, mid, mstr))
155+
PoReport(
156+
f'number of lines: {nb_id} in string, '
157+
f'{nb_str} in translation',
158+
'lines',
159+
self.filename,
160+
self.line,
161+
mid,
162+
mstr,
163+
)
164+
)
168165
return errors
169166

170167
def check_punct(self, language):
@@ -176,10 +173,15 @@ def check_punct(self, language):
176173
for mid, mstr in self.messages:
177174
if not mid or not mstr:
178175
continue
179-
puncts = [(':', ':'), (';', ';'), (',', ','), ('...', '...')]
176+
puncts = [
177+
(':', ':'),
178+
(';', ';'),
179+
(',', ','),
180+
('...', '...'),
181+
]
180182
# special symbols in some languages
181183
if language[:2] in ['ja', 'zh']:
182-
puncts.append(('.', u'。'))
184+
puncts.append(('.', '。'))
183185
else:
184186
puncts.append(('.', '.'))
185187
for punctid, punctstr in puncts:
@@ -193,17 +195,29 @@ def check_punct(self, language):
193195
break
194196
if match_id and not match_str:
195197
errors.append(
196-
PoReport(u'end punctuation: "{0}" in string, '
197-
u'"{1}" not in translation'
198-
''.format(punctid, punctstr),
199-
'punct', self.filename, self.line, mid, mstr))
198+
PoReport(
199+
f'end punctuation: "{punctid}" in string, '
200+
f'"{punctstr}" not in translation',
201+
'punct',
202+
self.filename,
203+
self.line,
204+
mid,
205+
mstr,
206+
)
207+
)
200208
break
201209
if not match_id and match_str:
202210
errors.append(
203-
PoReport(u'end punctuation: "{0}" in translation, '
204-
u'"{1}" not in string'
205-
''.format(punctstr, punctid),
206-
'punct', self.filename, self.line, mid, mstr))
211+
PoReport(
212+
f'end punctuation: "{punctstr}" in '
213+
f'translation, "{punctid}" not in string',
214+
'punct',
215+
self.filename,
216+
self.line,
217+
mid,
218+
mstr,
219+
)
220+
)
207221
break
208222
return errors
209223

@@ -222,20 +236,31 @@ def check_whitespace(self):
222236
startout = len(mstr) - len(mstr.lstrip(' '))
223237
if startin != startout:
224238
errors.append(
225-
PoReport('whitespace at beginning: {0} in string, '
226-
'{1} in translation'
227-
''.format(startin, startout),
228-
'whitespace', self.filename, self.line, mid,
229-
mstr))
239+
PoReport(
240+
f'whitespace at beginning: {startin} in '
241+
f'string, {startout} in translation',
242+
'whitespace',
243+
self.filename,
244+
self.line,
245+
mid,
246+
mstr,
247+
)
248+
)
230249
# check whitespace at end of string
231250
endin = len(mid) - len(mid.rstrip(' '))
232251
endout = len(mstr) - len(mstr.rstrip(' '))
233252
if endin != endout:
234253
errors.append(
235-
PoReport('whitespace at end: {0} in string, '
236-
'{1} in translation'.format(endin, endout),
237-
'whitespace', self.filename, self.line, mid,
238-
mstr))
254+
PoReport(
255+
f'whitespace at end: {endin} in string, '
256+
f'{endout} in translation',
257+
'whitespace',
258+
self.filename,
259+
self.line,
260+
mid,
261+
mstr,
262+
)
263+
)
239264
return errors
240265

241266
def check_whitespace_eol(self):
@@ -256,11 +281,16 @@ def check_whitespace_eol(self):
256281
endout = len(strlines[i]) - len(strlines[i].rstrip(' '))
257282
if (endin > 0 or endout > 0) and endin != endout:
258283
errors.append(
259-
PoReport('different whitespace at end of a line: {0} '
260-
'in string, {1} in translation'
261-
''.format(endin, endout),
262-
'whitespace_eol', self.filename, self.line,
263-
mid, mstr))
284+
PoReport(
285+
f'different whitespace at end of a line: {endin} '
286+
f'in string, {endout} in translation',
287+
'whitespace_eol',
288+
self.filename,
289+
self.line,
290+
mid,
291+
mstr,
292+
)
293+
)
264294
break
265295
return errors
266296

@@ -289,8 +319,16 @@ def check_spelling(self, spelling, checkers):
289319
if misspelled_word:
290320
misspelled.append(err.word)
291321
if misspelled:
292-
errors.append(PoReport(misspelled, 'spelling-' + spelling,
293-
self.filename, self.line, mid, mstr))
322+
errors.append(
323+
PoReport(
324+
misspelled,
325+
'spelling-' + spelling,
326+
self.filename,
327+
self.line,
328+
mid,
329+
mstr,
330+
)
331+
)
294332
return errors
295333

296334

@@ -407,7 +445,7 @@ def read(self): # pylint: disable=too-many-locals
407445
"""
408446
self.msgs = []
409447
checker = Checker()
410-
with open(self.filename, 'r') as po_file:
448+
with open(self.filename, 'r', encoding='utf-8') as po_file:
411449
for line in po_file:
412450
message = checker.check_line(line.strip())
413451
if message:
@@ -454,11 +492,8 @@ def __init__(self):
454492
self.pwl = None
455493

456494
def __repr__(self):
457-
return ('checks: {0}, dicts: {1}, '
458-
'extra_checkers: {2}'.format(
459-
self.checks,
460-
self.dicts,
461-
self.extra_checkers))
495+
return (f'checks: {self.checks}, dicts: {self.dicts}, '
496+
f'extra_checkers: {self.extra_checkers}')
462497

463498
def set_check(self, check, state):
464499
"""Enable/disable a specific check."""
@@ -482,8 +517,8 @@ def set_spelling_options(self, spelling, dicts, pwl_files):
482517
_dict = Dict(lang)
483518
self.extra_checkers.append(SpellChecker(_dict))
484519
except DictNotFoundError:
485-
print('WARNING: enchant dictionary not found for '
486-
'language "{0}"'.format(lang))
520+
print(f'WARNING: enchant dictionary not found for '
521+
f'language "{lang}"')
487522

488523
def _get_language_checker(self, po_file, reports):
489524
"""Get checker for PO file language."""
@@ -504,11 +539,14 @@ def _get_language_checker(self, po_file, reports):
504539
_dict = DictWithPWL(lang, None)
505540
checker.append(SpellChecker(_dict))
506541
except DictNotFoundError:
507-
reports.append(PoReport(
508-
'enchant dictionary not found for language "{0}"'
509-
''.format(lang),
510-
'dict', po_file.filename,
511-
po_file.props['language_numline']))
542+
reports.append(
543+
PoReport(
544+
f'enchant dictionary not found for language "{lang}"',
545+
'dict',
546+
po_file.filename,
547+
po_file.props['language_numline']
548+
)
549+
)
512550
checker = []
513551
except IOError as exc:
514552
reports.append(PoReport(
@@ -586,8 +624,7 @@ def check_files(self, files):
586624
result.append((po_file.filename, self.check_pofile(po_file)))
587625
else:
588626
# compilation failed
589-
if sys.version_info >= (3,):
590-
compile_output = bytes(compile_output).decode('utf-8')
627+
compile_output = bytes(compile_output).decode('utf-8')
591628
result.append((po_file.filename,
592629
[PoReport(compile_output, 'compile',
593630
po_file.filename)]))

msgcheck/utils.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# -*- coding: utf-8 -*-
1+
#!/usr/bin/env python3
22
#
33
# Copyright (C) 2009-2021 Sébastien Helleu <flashcode@flashtux.org>
44
#
@@ -18,9 +18,7 @@
1818
# along with msgcheck. If not, see <https://www.gnu.org/licenses/>.
1919
#
2020

21-
"""
22-
Some utility functions for msgcheck.
23-
"""
21+
"""Some utility functions for msgcheck."""
2422

2523
from __future__ import print_function
2624

0 commit comments

Comments
 (0)