From 16359b158281063c7e0a7325aeefb0660b1bc2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Thu, 30 Apr 2020 19:57:37 +0200 Subject: [PATCH 01/33] Run tests workflow on pull request --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 12349c7..0e9d601 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,6 @@ name: Tests -on: [push] +on: [push, pull_request] jobs: build: From 980cd040719c89f214ce37aa35a94c3ef0143be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 4 May 2020 23:19:38 +0200 Subject: [PATCH 02/33] Use actions checkout v2 --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0e9d601..217ae85 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: python-version: [2.7, 3.5, 3.6, 3.7] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: From 3b88c6bf3d13863c0af3877b9769262e95cc9dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Wed, 6 May 2020 23:00:45 +0200 Subject: [PATCH 03/33] Add tests with Python 3.8 --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 217ae85..3cfcec7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [2.7, 3.5, 3.6, 3.7] + python-version: [2.7, 3.5, 3.6, 3.7, 3.8] steps: - uses: actions/checkout@v2 From 1b61115d0fa622dc274bc3d1df82509f56f11f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sat, 23 May 2020 08:44:56 +0200 Subject: [PATCH 04/33] Rename GitHub Actions workflow to ci.yml --- .github/workflows/ci.yml | 47 +++++++++++++++++++++++++++++++++++++ .github/workflows/tests.yml | 40 ------------------------------- README.adoc | 2 +- 3 files changed, 48 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1cf2d1d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,47 @@ +name: CI + +on: [push, pull_request] + +jobs: + + build: + + name: ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + + steps: + + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + sudo apt-get install gettext aspell aspell-fr + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pytest + + - name: Lint with flake8 + run: | + pip install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings + flake8 . --count --exit-zero --max-complexity=10 --statistics + + - name: Lint with pylint + run: | + pip install pylint + pylint --disable=W0511,R0205 msgcheck + pylint --disable=W0511,R0205 tests + + - name: Test with pytest + run: | + pytest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 3cfcec7..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Tests - -on: [push, pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8] - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - sudo apt-get install gettext aspell aspell-fr - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pytest - - name: Lint with flake8 - run: | - pip install flake8 - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings - flake8 . --count --exit-zero --max-complexity=10 --statistics - - name: Lint with pylint - run: | - pip install pylint - pylint --disable=W0511,R0205 msgcheck - pylint --disable=W0511,R0205 tests - - name: Test with pytest - run: | - pytest diff --git a/README.adoc b/README.adoc index 4af84aa..ee8d33f 100644 --- a/README.adoc +++ b/README.adoc @@ -4,7 +4,7 @@ :lang: en image:https://img.shields.io/pypi/v/msgcheck.svg["PyPI", link="https://pypi.org/project/msgcheck/"] -image:https://github.com/flashcode/msgcheck/workflows/Tests/badge.svg["Tests", link="https://github.com/flashcode/msgcheck/actions"] +image:https://github.com/flashcode/msgcheck/workflows/CI/badge.svg["CI", link="https://github.com/flashcode/msgcheck/actions"] `msgcheck` performs various checks on gettext files (with extension ".po"): From fab8ce2b2def2e5d46a5705f732ba61a2e90a090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 30 Nov 2020 21:41:21 +0100 Subject: [PATCH 05/33] Rename CI job --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cf2d1d..7996a90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: build: - name: ${{ matrix.python-version }} + name: Test: Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: matrix: From fbb0b601a164d03bacbee5c9b0e66752a927af14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 30 Nov 2020 21:42:50 +0100 Subject: [PATCH 06/33] Fix typo in CI job name --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7996a90..713b1ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: build: - name: Test: Python ${{ matrix.python-version }} + name: Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: matrix: From 75960547126a4d6792a46492309feea062a34ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 30 Nov 2020 21:58:21 +0100 Subject: [PATCH 07/33] Version 3.2.0-dev Msgcheck is now following Semantic Versioning (https://semver.org/). --- msgcheck/msgcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msgcheck/msgcheck.py b/msgcheck/msgcheck.py index eb2a348..39762fb 100644 --- a/msgcheck/msgcheck.py +++ b/msgcheck/msgcheck.py @@ -39,7 +39,7 @@ from . po import PoCheck -__version__ = '3.1' +__version__ = '3.2.0-dev' def msgcheck_version(): From 421fd46ab6f7e23183909c6c3d2b5a58974cba5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 7 Dec 2020 23:12:47 +0100 Subject: [PATCH 08/33] Convert README and ChangeLog to markdown --- ChangeLog.adoc | 161 ------------------------------------------------- ChangeLog.md | 149 +++++++++++++++++++++++++++++++++++++++++++++ README.adoc | 123 ------------------------------------- README.md | 110 +++++++++++++++++++++++++++++++++ 4 files changed, 259 insertions(+), 284 deletions(-) delete mode 100644 ChangeLog.adoc create mode 100644 ChangeLog.md delete mode 100644 README.adoc create mode 100644 README.md diff --git a/ChangeLog.adoc b/ChangeLog.adoc deleted file mode 100644 index ee7c5c9..0000000 --- a/ChangeLog.adoc +++ /dev/null @@ -1,161 +0,0 @@ -= Msgcheck ChangeLog -:author: Sébastien Helleu -:email: flashcode@flashtux.org -:lang: en - - -== Version 3.1 (2020-03-07) - -* add option "-i" (or "--ignore-errors"): always return 0 even if errors are found -* fix parsing of "noqa" tag in comments (issue #11) -* use pytest for unit tests -* replace Travis CI by GitHub Actions - -== Version 3.0 (2018-12-14) - -* add support of multiple personal word list files (multiple options -P/--pwl) (issue #5) - -== Version 2.9 (2018-01-15) - -* add option "-n" (or "--skip-noqa") to not check "noqa"-commented lines (issue #2, issue #7) -* remove C and Python string formatters for spell checking (issue #3) - -== Version 2.8 (2014-12-07) - -* fix read of fuzzy flag -* display multiple misspelled words on same error line -* sort and keep unique misspelled words with option -m/--only-misspelled - -== Version 2.7 (2014-06-28) - -* add argument id/str for -s/--spelling to check messages or translations -* ensure pwl is not None before checking if file exists -* exit immediately if pwl file does not exist -* add pylint checks for Travis CI - -== Version 2.6 (2014-05-03) - -* add tests with Travis CI -* code refactoring, add setup.py and tests -* fix return code when there are more than 255 files with errors - -== Version 2.5 (2014-04-26) - -* code cleanup - -== Version 2.4 (2014-03-18) - -* add option "-W" (or "--no-whitespace-eol") to not check trailing whitespace - at end of lines inside strings - -== Version 2.3 (2014-01-20) - -* fix error with --spelling - -== Version 2.2 (2013-11-08) - -* add a main function - -== Version 2.1 (2013-11-02) - -* fix problem when latest string in file has a plural form (this last - translation was ignored) -* use codecs module to unescape strings (faster with python 3) -* display full exception in case of problem when reading file -* add short option synonym "-P" for "--pwl" -* add short option synonym "-e" for "--extract" -* rename some long names for command line options -* major code cleanup: add comments, move checking/error functions from - class PoMessage to class PoFile -* full PEP8 compliance - -== Version 2.0 (2013-09-23) - -* display number of files OK when there are multiple files checked and no - errors - -== Version 1.9 (2013-09-21) - -* add short option "-m" for "--onlymisspelled" - -== Version 1.8 (2013-09-21) - -* add option "-d" (or "--dicts") to use extra dictionaries for spell checking - -== Version 1.7 (2013-09-21) - -* add option "--onlymisspelled" to display only misspelled words instead of - errors with translations - -== Version 1.6 (2013-09-15) - -* add option "--extract" to extract translations - -== Version 1.5 (2013-09-15) - -* add option "-s" (or "--spelling") to check spelling and option "--pwl" to - use a personal list of words (with module "python-enchant") - -== Version 1.4 (2013-09-14) - -* Use argparse module to parse command line arguments, allow long name for - arguments -* Rename arguments: "-n" to "-l", "-s" to "-w" -* Display "(fuzzy)" after line number and colon in error messages -* Fix detection of fuzzy strings in gettext files - -== Version 1.3 (2013-08-23) - -* use absolute path for filenames displayed - -== Version 1.2 (2013-07-02) - -* remove some fancy chars in output so that output can be used as compilation - output in editors like Emacs - -== Version 1.1 (2013-07-01) - -* read environment variable "MSGCHECK_OPTIONS" - -== Version 1.0 (2013-07-01) - -* add option "-c" (do not check compilation) - -== Version 0.9 (2013-07-01) - -* use specific period for Japanese when checking punctuation - -== Version 0.8 (2013-06-30) - -* use own .po parser (about 200x faster!) -* add options "-f" (check fuzzy), "-q" (quiet) and "-v" (display version) - -== Version 0.7 (2013-06-29) - -* add options to disable some checks - -== Version 0.6 (2013-06-29) - -* check punctuation at end of string - -== Version 0.5 (2013-01-02) - -* replace os.system by subprocess -* display syntax when script is called without filename -* rename script to "msgcheck.py" - -== Version 0.4 (2012-09-21) - -* add check of compilation with "msgfmt -c" - -== Version 0.3 (2011-04-14) - -* allow multiple po filenames - -== Version 0.2 (2011-04-10) - -* add check of spaces at beginning/end of strings - -== Version 0.1 (2010-03-22) - -* first release diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 0000000..96f52c7 --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,149 @@ +# Msgcheck ChangeLog + +## Version 3.1 (2020-03-07) + +- Add option `-i` (or `--ignore-errors`): always return 0 even if errors are found. +- Fix parsing of "noqa" tag in comments (issue #11). +- Use pytest for unit tests. +- Replace Travis CI by GitHub Actions. + +## Version 3.0 (2018-12-14) + +- Add support of multiple personal word list files (multiple options `-P`/`--pwl`) (issue #5). + +## Version 2.9 (2018-01-15) + +- Add option `-n` (or `--skip-noqa`) to not check "noqa"-commented lines (issue #2, issue #7). +- Remove C and Python string formatters for spell checking (issue #3). + +## Version 2.8 (2014-12-07) + +- Fix read of fuzzy flag. +- Display multiple misspelled words on same error line. +- Sort and keep unique misspelled words with option `-m`/`--only-misspelled`. + +## Version 2.7 (2014-06-28) + +* Add argument id/str for `-s`/`--spelling` to check messages or translations. +* Ensure pwl is not None before checking if file exists. +* Exit immediately if pwl file does not exist. +* Add pylint checks for Travis CI. + +## Version 2.6 (2014-05-03) + +- Add tests with Travis CI +- Code refactoring, add setup.py and tests +- Fix return code when there are more than 255 files with errors + +## Version 2.5 (2014-04-26) + +- Code cleanup. + +## Version 2.4 (2014-03-18) + +- Add option `-W` (or `--no-whitespace-eol`) to not check trailing whitespace at end of lines inside strings. + +## Version 2.3 (2014-01-20) + +- Fix error with `--spelling`. + +## Version 2.2 (2013-11-08) + +- Add a main function. + +## Version 2.1 (2013-11-02) + +- Fix problem when latest string in file has a plural form (this last translation was ignored). +- Use codecs module to unescape strings (faster with python 3). +- Display full exception in case of problem when reading file. +- Add short option synonym `-P` for `--pwl`. +- Add short option synonym `-e` for `--extract`. +- Rename some long names for command line options. +- Major code cleanup: add comments, move checking/error functions from class PoMessage to class PoFile. +- Full PEP8 compliance. + +## Version 2.0 (2013-09-23) + +- Display number of files OK when there are multiple files checked and no errors. + +## Version 1.9 (2013-09-21) + +- Add short option `-m` for `--onlymisspelled`. + +## Version 1.8 (2013-09-21) + +- Add option `-d` (or `--dicts`) to use extra dictionaries for spell checking. + +## Version 1.7 (2013-09-21) + +* Add option `--onlymisspelled` to display only misspelled words instead of errors with translations. + +## Version 1.6 (2013-09-15) + +- Add option `--extract` to extract translations. + +## Version 1.5 (2013-09-15) + +- Add option `-s` (or `--spelling`) to check spelling and option `--pwl` to use a personal list of words (with module `python-enchant`). + +## Version 1.4 (2013-09-14) + +- Use argparse module to parse command line arguments, allow long name for arguments. +- Rename arguments: `-n` to `-l`, `-s` to `-w`. +- Display "(fuzzy)" after line number and colon in error messages. +- Fix detection of fuzzy strings in gettext files. + +## Version 1.3 (2013-08-23) + +- Use absolute path for filenames displayed. + +## Version 1.2 (2013-07-02) + +- Remove some fancy chars in output so that output can be used as compilation output in editors like Emacs. + +## Version 1.1 (2013-07-01) + +- Read environment variable `MSGCHECK_OPTIONS`. + +## Version 1.0 (2013-07-01) + +- Add option `-c` (do not check compilation). + +## Version 0.9 (2013-07-01) + +- Use specific period for Japanese when checking punctuation. + +## Version 0.8 (2013-06-30) + +- Use own .po parser (about 200x faster!). +- Add options `-f` (check fuzzy), `-q` (quiet) and `-v` (display version). + +## Version 0.7 (2013-06-29) + +- Add options to disable some checks. + +## Version 0.6 (2013-06-29) + +- Check punctuation at end of string. + +## Version 0.5 (2013-01-02) + +- Replace os.system by subprocess. +- Display syntax when script is called without filename. +- Rename script to `msgcheck.py`. + +## Version 0.4 (2012-09-21) + +- Add check of compilation with `msgfmt -c`. + +## Version 0.3 (2011-04-14) + +- Allow multiple po filenames. + +## Version 0.2 (2011-04-10) + +- Add check of spaces at beginning/end of strings. + +## Version 0.1 (2010-03-22) + +- First release. diff --git a/README.adoc b/README.adoc deleted file mode 100644 index ee8d33f..0000000 --- a/README.adoc +++ /dev/null @@ -1,123 +0,0 @@ -= msgcheck -:author: Sébastien Helleu -:email: flashcode@flashtux.org -:lang: en - -image:https://img.shields.io/pypi/v/msgcheck.svg["PyPI", link="https://pypi.org/project/msgcheck/"] -image:https://github.com/flashcode/msgcheck/workflows/CI/badge.svg["CI", link="https://github.com/flashcode/msgcheck/actions"] - -`msgcheck` performs various checks on gettext files (with extension ".po"): - -* compilation (with command `msgfmt -c`) -* for each translation: -** number of lines in translated strings -** whitespace at beginning/end of strings -** trailing whitespace at end of lines inside strings -** punctuation at end of strings -** spelling (messages and translations). - -The script requires: - -* Python ≥ 2.7 -* gettext (for the command `msgfmt`, used to compile PO files) -* the python module `pyenchant` if spelling is checked (with option `-s`). - -== Install - -Install a released version from the Python package index with pip: - ----- -$ pip install msgcheck ----- - -Or you can install via source distribution: - ----- -$ python setup.py install ----- - -== Usage - -Syntax: - ----- -$ msgcheck [options] file.po [file.po...] ----- - -Options: - -* `-h`, `--help`: display help message and exit -* `-c`, `--no-compile`: do not check compilation of file (with `msgfmt -c`) -* `-f`, `--fuzzy`: check fuzzy strings -* `-n`, `--skip-noqa`: do not check "noqa"-commented lines -* `-l`, `--no-lines`: do not check number of lines -* `-p`, `--no-punct`: do not check punctuation at end of strings -* `-s id|str`, `--spelling id|str`: check spelling (`id` = source messages, - `str` = translations) -* `-d `, `--dicts `: comma-separated list of extra dictionaries - to use (in addition to file language) -* `-P `, `--pwl `: file(s) with personal list of words used when - checking spelling (this option can be given multiple times) -* `-m`, `--only-misspelled`: display only misspelled words (no error, line - number and translation) -* `-w`, `--no-whitespace`: do not check whitespace at beginning/end of strings -* `-W`, `--no-whitespace-eol`: do not check whitespace at end of lines inside - strings -* `-e`, `--extract`: display all translations and exit (all checks except - compilation are disabled in this mode) -* `-i`, `--ignore-errors`: display but ignore errors (always return 0) -* `-q`, `--quiet`: quiet mode: only display number of errors -* `-v`, `--version`: display version and exit - -The environment variable `MSGCHECK_OPTIONS` can be set with some default -options. - -The script returns exit code *0* if all files checked are OK -(0 errors or option `--extract` given) or it returns *N*: number of files with -errors (1 ≤ N ≤ 255). - -== Example - ----- -$ msgcheck fr.po -====================================================================== -/path/to/fr.po:242: [punct] end punctuation: ":" in translation, ":" not in string: ---- -error ---- -erreur: -====================================================================== -/path/to/fr.po:262: [lines] number of lines: 1 in string, 2 in translation: ---- -Message filters: ---- -Filtres de -messages: -====================================================================== -/path/to/fr.po:336: [whitespace] spaces at beginning: 0 in string, 1 in translation: ---- -current value ---- - valeur courante -====================================================================== -/path/to/fr.po: 3 errors (almost good!) ----- - -== Copyright - -Copyright (C) 2009-2020 Sébastien Helleu - -This file is part of msgcheck. - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . diff --git a/README.md b/README.md new file mode 100644 index 0000000..6525b4e --- /dev/null +++ b/README.md @@ -0,0 +1,110 @@ +# Msgcheck + +[![PyPI](https://img.shields.io/pypi/v/msgcheck.svg)](https://pypi.org/project/msgcheck/) +[![Build Status](https://github.com/flashcode/msgcheck/workflows/CI/badge.svg)](https://github.com/flashcode/msgcheck/actions?query=workflow%3A%22CI%22) + +Msgcheck performs various checks on gettext files (with extension `.po`): + +- compilation (with command `msgfmt -c`) +- for each translation: + - number of lines in translated strings + - whitespace at beginning/end of strings + - trailing whitespace at end of lines inside strings + - punctuation at end of strings + - spelling (messages and translations). + +The script requires: + +- Python ≥ 2.7 +- gettext (for the command `msgfmt`, used to compile PO files) +- the python module `pyenchant` if spelling is checked (with option `-s`). + +## Install + +Install a released version from the Python package index with pip: + +``` +$ pip install msgcheck +``` + +Or you can install via source distribution: + +``` +$ python setup.py install +``` + +## Usage + +Syntax: + +``` +$ msgcheck [options] file.po [file.po...] +``` + +Options: + +- `-h`, `--help`: display help message and exit +- `-c`, `--no-compile`: do not check compilation of file (with `msgfmt -c`) +- `-f`, `--fuzzy`: check fuzzy strings +- `-n`, `--skip-noqa`: do not check "noqa"-commented lines +- `-l`, `--no-lines`: do not check number of lines +- `-p`, `--no-punct`: do not check punctuation at end of strings +- `-s id|str`, `--spelling id|str`: check spelling (`id` = source messages, `str` = translations) +- `-d `, `--dicts `: comma-separated list of extra dictionaries to use (in addition to file language) +- `-P `, `--pwl `: file(s) with personal list of words used when checking spelling (this option can be given multiple times) +- `-m`, `--only-misspelled`: display only misspelled words (no error, line number and translation) +- `-w`, `--no-whitespace`: do not check whitespace at beginning/end of strings +- `-W`, `--no-whitespace-eol`: do not check whitespace at end of lines inside strings +- `-e`, `--extract`: display all translations and exit (all checks except compilation are disabled in this mode) +- `-i`, `--ignore-errors`: display but ignore errors (always return 0) +- `-q`, `--quiet`: quiet mode: only display number of errors +- `-v`, `--version`: display version and exit + +The environment variable `MSGCHECK_OPTIONS` can be set with some default options. + +The script returns exit code **0** if all files checked are OK (0 errors or option +`--extract` given) or it returns **N**: number of files with errors (1 ≤ N ≤ 255). + +## Example + +``` +$ msgcheck fr.po +====================================================================== +/path/to/fr.po:242: [punct] end punctuation: ":" in translation, ":" not in string: +--- +error +--- +erreur: +====================================================================== +/path/to/fr.po:262: [lines] number of lines: 1 in string, 2 in translation: +--- +Message filters: +--- +Filtres de +messages: +====================================================================== +/path/to/fr.po:336: [whitespace] spaces at beginning: 0 in string, 1 in translation: +--- +current value +--- + valeur courante +====================================================================== +/path/to/fr.po: 3 errors (almost good!) +``` + +## Copyright + +Copyright © 2009-2020 [Sébastien Helleu](https://github.com/flashcode) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . From c48b5ae995246164483fc28d8f810764124ef4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Thu, 10 Dec 2020 19:05:31 +0100 Subject: [PATCH 09/33] Add GitHub issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 40 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 1 + .github/ISSUE_TEMPLATE/feature_request.md | 8 +++++ .github/ISSUE_TEMPLATE/question.md | 16 +++++++++ 4 files changed, 65 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/question.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..8a322f7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a bug report (please do not report security issues here) +labels: bug + +--- + +## Bug summary + + + +## Steps to reproduce + +1.  +2.  +3.  + +## Current behavior + + + +## Expected behavior + + + +## Suggested solutions + + + +## Additional information + + + +--- + + + +- Msgcheck version:  +- Python version:  +- OS, distribution and version:  diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..40b8d40 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,8 @@ +--- +name: Feature request +about: Request a new feature / enhancement +labels: feature + +--- + +## Feature description diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..b1f5a7a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,16 @@ +--- +name: Question +about: Ask a question +labels: question + +--- + +## Question + + + +--- + +- Msgcheck version:  +- Python version:  +- OS, distribution and version:  From 60e73b305004ec7f3519f5e787fa45a7006fa95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Fri, 11 Dec 2020 21:38:50 +0100 Subject: [PATCH 10/33] Add code coverage with pytest, increase verbosity --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 713b1ae..838e0a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,4 +44,4 @@ jobs: - name: Test with pytest run: | - pytest + pytest -vv --cov-report term-missing --cov=msgcheck tests From 22a5f9c937d2e883fbe51538a7b32b82ffde2814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Fri, 11 Dec 2020 21:41:10 +0100 Subject: [PATCH 11/33] Install coverage and pytest-cov in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 838e0a2..329103e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: sudo apt-get install gettext aspell aspell-fr python -m pip install --upgrade pip pip install -r requirements.txt - pip install pytest + pip install pytest coverage pytest-cov - name: Lint with flake8 run: | From 4bd096c59abd39554a8fda78501cc8069c72b7f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 14 Dec 2020 23:16:05 +0100 Subject: [PATCH 12/33] Add .coverage in .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c802041..b121236 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.coverage *.pyc *.pyo build/ From efcc789a911f6c69696887e495fe3cedcc012bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Wed, 16 Dec 2020 20:11:31 +0100 Subject: [PATCH 13/33] Add test of gettext file extract --- tests/test_msgcheck.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/test_msgcheck.py b/tests/test_msgcheck.py index 1e932ee..bfdc726 100644 --- a/tests/test_msgcheck.py +++ b/tests/test_msgcheck.py @@ -56,6 +56,46 @@ def test_read(): PoFile(local_path('fr_does_not_exist.po')).read() +def test_extract(): + """Test extract on a gettext file.""" + po_check = PoCheck() + po_check.set_check('extract', True) + result = po_check.check_files([local_path('fr.po')]) + assert len(result) == 1 + assert 'fr.po' in result[0][0] + assert len(result[0][1]) == 3 + + report = result[0][1][0] + assert report.message == 'Ceci est un test.\n' + assert report.idmsg == 'extract' + assert report.filename == '-' + assert report.line == 0 + assert report.mid == '' + assert report.mstr == '' + assert report.fuzzy is False + assert str(report) == 'Ceci est un test.\n\n---' + + report = result[0][1][1] + assert report.message == 'Test sur deux lignes.\nLigne 2.' + assert report.idmsg == 'extract' + assert report.filename == '-' + assert report.line == 0 + assert report.mid == '' + assert report.mstr == '' + assert report.fuzzy is False + assert str(report) == 'Test sur deux lignes.\nLigne 2.\n---' + + report = result[0][1][2] + assert report.message == ' erreur : %s' + assert report.idmsg == 'extract' + assert report.filename == '-' + assert report.line == 0 + assert report.mid == '' + assert report.mstr == '' + assert report.fuzzy is False + assert str(report) == ' erreur : %s\n---' + + def test_checks(): """Test checks on gettext files.""" po_check = PoCheck() From bbb3058b59c1413d7efb7bd713b82bb842666a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Tue, 5 Jan 2021 20:48:06 +0100 Subject: [PATCH 14/33] Update copyright dates --- README.md | 2 +- msgcheck/__init__.py | 2 +- msgcheck/msgcheck.py | 2 +- msgcheck/po.py | 2 +- msgcheck/utils.py | 2 +- setup.py | 2 +- tests/__init__.py | 2 +- tests/fr.po | 2 +- tests/fr_compile.po | 2 +- tests/fr_errors.po | 2 +- tests/fr_language.po | 2 +- tests/fr_spelling_id.po | 2 +- tests/fr_spelling_str.po | 2 +- tests/test_msgcheck.py | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 6525b4e..78ee5eb 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ current value ## Copyright -Copyright © 2009-2020 [Sébastien Helleu](https://github.com/flashcode) +Copyright © 2009-2021 [Sébastien Helleu](https://github.com/flashcode) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/msgcheck/__init__.py b/msgcheck/__init__.py index ae353d0..35466d1 100644 --- a/msgcheck/__init__.py +++ b/msgcheck/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/msgcheck/msgcheck.py b/msgcheck/msgcheck.py index 39762fb..9efb6f2 100644 --- a/msgcheck/msgcheck.py +++ b/msgcheck/msgcheck.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/msgcheck/po.py b/msgcheck/po.py index be2f6ef..cea2a50 100644 --- a/msgcheck/po.py +++ b/msgcheck/po.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/msgcheck/utils.py b/msgcheck/utils.py index a3a2598..d575f9b 100644 --- a/msgcheck/utils.py +++ b/msgcheck/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/setup.py b/setup.py index 8a24740..2a44ea6 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/tests/__init__.py b/tests/__init__.py index 7e8b709..5f44550 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/tests/fr.po b/tests/fr.po index 6538e68..807075e 100644 --- a/tests/fr.po +++ b/tests/fr.po @@ -1,5 +1,5 @@ # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/tests/fr_compile.po b/tests/fr_compile.po index 38a9424..a0019ab 100644 --- a/tests/fr_compile.po +++ b/tests/fr_compile.po @@ -1,5 +1,5 @@ # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/tests/fr_errors.po b/tests/fr_errors.po index 322b211..6066860 100644 --- a/tests/fr_errors.po +++ b/tests/fr_errors.po @@ -1,5 +1,5 @@ # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/tests/fr_language.po b/tests/fr_language.po index 16693b1..1e7d335 100644 --- a/tests/fr_language.po +++ b/tests/fr_language.po @@ -1,5 +1,5 @@ # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/tests/fr_spelling_id.po b/tests/fr_spelling_id.po index 2f01c79..dd1ef87 100644 --- a/tests/fr_spelling_id.po +++ b/tests/fr_spelling_id.po @@ -1,5 +1,5 @@ # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/tests/fr_spelling_str.po b/tests/fr_spelling_str.po index 86d2cbb..63ed255 100644 --- a/tests/fr_spelling_str.po +++ b/tests/fr_spelling_str.po @@ -1,5 +1,5 @@ # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # diff --git a/tests/test_msgcheck.py b/tests/test_msgcheck.py index bfdc726..7df84f6 100644 --- a/tests/test_msgcheck.py +++ b/tests/test_msgcheck.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2020 Sébastien Helleu +# Copyright (C) 2009-2021 Sébastien Helleu # # This file is part of msgcheck. # From 2c6f6534f5c6041190c193f0c68af7a73822f823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Tue, 5 Jan 2021 20:48:50 +0100 Subject: [PATCH 15/33] Add some tests, rename variables in tests --- tests/test_msgcheck.py | 100 +++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/tests/test_msgcheck.py b/tests/test_msgcheck.py index 7df84f6..0ccde01 100644 --- a/tests/test_msgcheck.py +++ b/tests/test_msgcheck.py @@ -18,13 +18,12 @@ # along with msgcheck. If not, see . # -""" -Tests on msgcheck. -""" +"""Tests on msgcheck.""" import os import pytest +from msgcheck.msgcheck import msgcheck_version from msgcheck.po import PoFile, PoCheck from msgcheck.utils import replace_formatters @@ -34,6 +33,12 @@ def local_path(filename): return os.path.join(os.path.dirname(os.path.abspath(__file__)), filename) +def test_version(): + """Test msgcheck version.""" + version = msgcheck_version() + assert len(version.split('.')) > 1 + + def test_compilation(): """Test compilation of gettext files.""" # valid file @@ -110,6 +115,31 @@ def test_checks(): # second file has 10 errors assert len(result[1][1]) == 10 + + # check first error + report = result[1][1][0] + assert report.message == 'number of lines: 2 in string, 1 in translation' + assert report.idmsg == 'lines' + assert 'fr_errors.po' in report.filename + assert report.line == 42 + assert report.mid == 'Test 1 on two lines.\nLine 2.' + assert report.mstr == 'Test 1 sur deux lignes.' + assert report.fuzzy is False + assert ('fr_errors.po:42: [lines] number of lines: ' + '2 in string, 1 in translation' in str(report)) + + # check last error + report = result[1][1][9] + assert report.message == \ + 'different whitespace at end of a line: 1 in string, 0 in translation' + assert report.idmsg == 'whitespace_eol' + assert 'fr_errors.po' in report.filename + assert report.line == 74 + assert report.mid == 'Line 1. \nLine 2.' + assert report.mstr == 'Ligne 1.\nLigne 2.' + assert report.fuzzy is False + + # check number of errors by type errors = {} for report in result[1][1]: errors[report.idmsg] = errors.get(report.idmsg, 0) + 1 @@ -207,13 +237,14 @@ def test_spelling_id(): assert len(result) == 1 # the file has 2 spelling errors: "Thsi" and "errro" - errors = result[0][1] - assert len(errors) == 3 + report = result[0][1] + assert len(report) == 3 for i, word in enumerate(('Thsi', 'testtwo', 'errro')): - assert errors[i].idmsg == 'spelling-id' - assert isinstance(errors[i].message, list) - assert len(errors[i].message) == 1 - assert errors[i].message[0] == word + assert report[i].idmsg == 'spelling-id' + assert isinstance(report[i].message, list) + assert len(report[i].message) == 1 + assert report[i].message[0] == word + assert report[i].get_misspelled_words() == [word] def test_spelling_id_multilpe_pwl(): @@ -233,13 +264,14 @@ def test_spelling_id_multilpe_pwl(): assert len(result) == 1 # the file has 2 spelling errors: "Thsi" and "errro" - errors = result[0][1] - assert len(errors) == 2 + report = result[0][1] + assert len(report) == 2 for i, word in enumerate(('Thsi', 'errro')): - assert errors[i].idmsg == 'spelling-id' - assert isinstance(errors[i].message, list) - assert len(errors[i].message) == 1 - assert errors[i].message[0] == word + assert report[i].idmsg == 'spelling-id' + assert isinstance(report[i].message, list) + assert len(report[i].message) == 1 + assert report[i].message[0] == word + assert report[i].get_misspelled_words() == [word] def test_spelling_str(): @@ -254,18 +286,19 @@ def test_spelling_str(): assert len(result) == 2 # first file has 3 spelling errors: "CecX", "aabbcc" and "xxyyzz" - errors = result[0][1] - assert len(errors) == 4 + report = result[0][1] + assert len(report) == 4 for i, word in enumerate(('testtwo', 'CecX', 'aabbcc', 'xxyyzz')): - assert errors[i].idmsg == 'spelling-str' - assert isinstance(errors[i].message, list) - assert len(errors[i].message) == 1 - assert errors[i].message[0] == word + assert report[i].idmsg == 'spelling-str' + assert isinstance(report[i].message, list) + assert len(report[i].message) == 1 + assert report[i].message[0] == word + assert report[i].get_misspelled_words() == [word] # second file has 1 error: dict/language "xyz" not found - errors = result[1][1] - assert len(errors) == 1 - assert errors[0].idmsg == 'dict' + report = result[1][1] + assert len(report) == 1 + assert report[0].idmsg == 'dict' def test_spelling_str_multiple_pwl(): @@ -286,18 +319,19 @@ def test_spelling_str_multiple_pwl(): assert len(result) == 2 # first file has 3 spelling errors: "CecX", "aabbcc" and "xxyyzz" - errors = result[0][1] - assert len(errors) == 3 + report = result[0][1] + assert len(report) == 3 for i, word in enumerate(('CecX', 'aabbcc', 'xxyyzz')): - assert errors[i].idmsg == 'spelling-str' - assert isinstance(errors[i].message, list) - assert len(errors[i].message) == 1 - assert errors[i].message[0] == word + assert report[i].idmsg == 'spelling-str' + assert isinstance(report[i].message, list) + assert len(report[i].message) == 1 + assert report[i].message[0] == word + assert report[i].get_misspelled_words() == [word] # second file has 1 error: dict/language "xyz" not found - errors = result[1][1] - assert len(errors) == 1 - assert errors[0].idmsg == 'dict' + report = result[1][1] + assert len(report) == 1 + assert report[0].idmsg == 'dict' def test_spelling_bad_dict(): From c9881764dafd45d9ae64e4fad6b65a5a2c0ba05c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Wed, 13 Jan 2021 19:55:02 +0100 Subject: [PATCH 16/33] Add Makefile --- .github/workflows/ci.yml | 17 ++++------------- Makefile | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 Makefile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 329103e..a8f019b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,22 +26,13 @@ jobs: sudo apt-get install gettext aspell aspell-fr python -m pip install --upgrade pip pip install -r requirements.txt - pip install pytest coverage pytest-cov + pip install flake8 pylint pytest coverage pytest-cov - name: Lint with flake8 - run: | - pip install flake8 - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings - flake8 . --count --exit-zero --max-complexity=10 --statistics + run: make flake8 - name: Lint with pylint - run: | - pip install pylint - pylint --disable=W0511,R0205 msgcheck - pylint --disable=W0511,R0205 tests + run: make pylint - name: Test with pytest - run: | - pytest -vv --cov-report term-missing --cov=msgcheck tests + run: make test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5f3dfa4 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +# +# Copyright (C) 2021 Sébastien Helleu +# +# This file is part of msgcheck. +# +# Msgcheck is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Msgcheck is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with msgcheck. If not, see . +# + +all: check + +check: lint test + +lint: flake8 pylint + +flake8: + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 . --count --exit-zero --max-complexity=10 --statistics + +pylint: + pylint --disable=W0511,R0205 msgcheck + pylint --disable=W0511,R0205 tests + +test: + pytest -vv --cov-report term-missing --cov=msgcheck tests From 8e504de6dd13165a253a433987b3ef395fe33f73 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Tue, 5 Jan 2021 18:47:36 +0100 Subject: [PATCH 17/33] =?UTF-8?q?Ref=20#12=20--=20Add=20support=20for=20Ch?= =?UTF-8?q?inese=20full-stop=20(=E5=8F=A5=E8=99=9F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- msgcheck/po.py | 12 ++++++------ tests/test_msgcheck.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/msgcheck/po.py b/msgcheck/po.py index cea2a50..ba84b7d 100644 --- a/msgcheck/po.py +++ b/msgcheck/po.py @@ -180,8 +180,8 @@ def check_punct(self, language): continue puncts = [(':', ':'), (';', ';'), (',', ','), ('...', '...')] # special symbols in some languages - if language.startswith('ja'): - puncts.append(('.', '。')) + if language[:2] in ['ja', 'zh']: + puncts.append(('.', u'。')) else: puncts.append(('.', '.')) for punctid, punctstr in puncts: @@ -195,15 +195,15 @@ def check_punct(self, language): break if match_id and not match_str: errors.append( - PoReport('end punctuation: "{0}" in string, ' - '"{1}" not in translation' + PoReport(u'end punctuation: "{0}" in string, ' + u'"{1}" not in translation' ''.format(punctid, punctstr), 'punct', self.filename, self.line, mid, mstr)) break if not match_id and match_str: errors.append( - PoReport('end punctuation: "{0}" in translation, ' - '"{1}" not in string' + PoReport(u'end punctuation: "{0}" in translation, ' + u'"{1}" not in string' ''.format(punctstr, punctid), 'punct', self.filename, self.line, mid, mstr)) break diff --git a/tests/test_msgcheck.py b/tests/test_msgcheck.py index 0ccde01..05c3bef 100644 --- a/tests/test_msgcheck.py +++ b/tests/test_msgcheck.py @@ -24,7 +24,7 @@ import pytest from msgcheck.msgcheck import msgcheck_version -from msgcheck.po import PoFile, PoCheck +from msgcheck.po import PoFile, PoCheck, PoMessage from msgcheck.utils import replace_formatters @@ -347,3 +347,30 @@ def test_spelling_bad_pwl(): with pytest.raises(IOError): pwl_files = [local_path('pwl_does_not_exist.txt')] po_check.set_spelling_options('str', None, pwl_files) + + +@pytest.mark.parametrize('language, msgid, msgstr, error_message', [ + ('ja', 'Should not raise an error.', u'エラーが発生しないようにしてください。', ''), + ('ja', 'Should raise an error', u'エラーを発生させる必要があります。', + u'end punctuation: "。" in translation, "." not in string'), + ('ja', 'Should raise an error.', u'エラーを発生させる必要があります', + u'end punctuation: "." in string, "。" not in translation'), + ('ja', 'Should raise an error.', u'エラーを発生させる必要があります.', + u'end punctuation: "." in string, "。" not in translation'), + ('zh-Hans', 'Should not raise an error.', u'不应引起错误。', ''), + ('zh-Hans', 'Should raise an error', u'应该会出现一个错误。', + u'end punctuation: "。" in translation, "." not in string'), + ('zh-Hans', 'Should raise an error.', u'应该会出现一个错误', + u'end punctuation: "." in string, "。" not in translation'), + ('zh-Hans', 'Should raise an error.', u'应该会出现一个错误.', + u'end punctuation: "." in string, "。" not in translation'), +]) +def test_check_punct__full_stop__ja_zh(language, msgid, msgstr, error_message): + """Test punctuation with non-latin full-stops.""" + msg = PoMessage('translation.po', 42, {}, 'utf-8', False, False, False) + msg.messages = [(msgid, msgstr)] + errors = PoMessage.check_punct(msg, language) + if error_message: + assert error_message in errors[0].message + else: + assert not errors From 82ad5a90637050e19ed2641097e894c724f29b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Wed, 31 Mar 2021 23:05:58 +0200 Subject: [PATCH 18/33] Switch to Ubuntu 20.04 in CI, reformat workflow --- .github/workflows/ci.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8f019b..bdb2ff2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,23 @@ name: CI -on: [push, pull_request] +on: + - push + - pull_request jobs: build: name: Python ${{ matrix.python-version }} - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + python-version: + - "2.7" + - "3.5" + - "3.6" + - "3.7" + - "3.8" steps: From fb3a73d31997295cd020a1a22e3e486865c30d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Wed, 31 Mar 2021 23:15:45 +0200 Subject: [PATCH 19/33] Install enchant in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdb2ff2..5506a74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get install gettext aspell aspell-fr + sudo apt-get install gettext aspell aspell-fr enchant python -m pip install --upgrade pip pip install -r requirements.txt pip install flake8 pylint pytest coverage pytest-cov From 1ac05f349026c249f1f8baddd89b071af680f851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Fri, 7 May 2021 20:48:37 +0200 Subject: [PATCH 20/33] Switch to setup-python v2 in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5506a74..6af1ece 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} From 4a723d49feefd863404321cd6ed3c439fa907331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Fri, 7 May 2021 21:00:53 +0200 Subject: [PATCH 21/33] Use with on tempfile.NamedTemporaryFile() --- msgcheck/po.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/msgcheck/po.py b/msgcheck/po.py index cea2a50..f3cf463 100644 --- a/msgcheck/po.py +++ b/msgcheck/po.py @@ -47,13 +47,13 @@ def build_temp_file_concat_files(filenames): """Build a temporary file with concatenation of multiple files.""" if not filenames: return None - tmp_file = tempfile.NamedTemporaryFile() - for filename in filenames: - if not os.path.isfile(filename): - raise IOError('file "{0}" not found'.format(filename)) - tmp_file.write(open(filename, 'rb').read()) - tmp_file.flush() - return tmp_file + with tempfile.NamedTemporaryFile() as tmp_file: + for filename in filenames: + if not os.path.isfile(filename): + raise IOError('file "{0}" not found'.format(filename)) + tmp_file.write(open(filename, 'rb').read()) + tmp_file.flush() + return tmp_file class PoReport(object): # pylint: disable=too-few-public-methods From cb4f704c68a08055922ddf79525af805a7314e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Fri, 7 May 2021 21:24:54 +0200 Subject: [PATCH 22/33] Fix read of pwl files --- msgcheck/po.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/msgcheck/po.py b/msgcheck/po.py index f3cf463..33f99cc 100644 --- a/msgcheck/po.py +++ b/msgcheck/po.py @@ -43,17 +43,15 @@ from . utils import count_lines, replace_formatters -def build_temp_file_concat_files(filenames): - """Build a temporary file with concatenation of multiple files.""" +def get_concatenated_files(filenames): + """Return concatenated content of multiple files.""" if not filenames: return None - with tempfile.NamedTemporaryFile() as tmp_file: - for filename in filenames: - if not os.path.isfile(filename): - raise IOError('file "{0}" not found'.format(filename)) - tmp_file.write(open(filename, 'rb').read()) - tmp_file.flush() - return tmp_file + content = [] + for filename in filenames: + with open(filename, 'rb') as _file: + content.append(_file.read().decode('utf-8')) + return '\n'.join(content) class PoReport(object): # pylint: disable=too-few-public-methods @@ -457,11 +455,10 @@ def __init__(self): def __repr__(self): return ('checks: {0}, dicts: {1}, ' - 'extra_checkers: {2}, pwl: {3}'.format( + 'extra_checkers: {2}'.format( self.checks, self.dicts, - self.extra_checkers, - self.pwl)) + self.extra_checkers)) def set_check(self, check, state): """Enable/disable a specific check.""" @@ -472,7 +469,7 @@ def set_spelling_options(self, spelling, dicts, pwl_files): """Set spelling options.""" self.spelling = spelling self.dicts = dicts - self.pwl = build_temp_file_concat_files(pwl_files) + self.pwl = get_concatenated_files(pwl_files) # build extra checkers with dicts self.extra_checkers = [] @@ -498,8 +495,11 @@ def _get_language_checker(self, po_file, reports): lang = po_file.props['language'] \ if self.spelling == 'str' else 'en' try: - _dict = DictWithPWL(lang, self.pwl.name if self.pwl else None) - checker.append(SpellChecker(_dict)) + with tempfile.NamedTemporaryFile() as tmp_file: + tmp_file.write(self.pwl.encode('utf-8')) + tmp_file.flush() + _dict = DictWithPWL(lang, tmp_file.name) + checker.append(SpellChecker(_dict)) except DictNotFoundError: reports.append(PoReport( 'enchant dictionary not found for language "{0}"' From d0d30c57170b8ff695d24f7a89d974ccaeb59a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 10 May 2021 22:38:57 +0200 Subject: [PATCH 23/33] Add tests with Python 3.9 --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6af1ece..260a499 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ jobs: - "3.6" - "3.7" - "3.8" + - "3.9" steps: From b5aa8a4fb3e82abccb145dd0aa107f7f5808e154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sat, 10 Jul 2021 20:48:38 +0200 Subject: [PATCH 24/33] Fix name of test function --- tests/test_msgcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msgcheck.py b/tests/test_msgcheck.py index 05c3bef..8f0f3f4 100644 --- a/tests/test_msgcheck.py +++ b/tests/test_msgcheck.py @@ -365,7 +365,7 @@ def test_spelling_bad_pwl(): ('zh-Hans', 'Should raise an error.', u'应该会出现一个错误.', u'end punctuation: "." in string, "。" not in translation'), ]) -def test_check_punct__full_stop__ja_zh(language, msgid, msgstr, error_message): +def test_punct_full_stop_ja_zh(language, msgid, msgstr, error_message): """Test punctuation with non-latin full-stops.""" msg = PoMessage('translation.po', 42, {}, 'utf-8', False, False, False) msg.messages = [(msgid, msgstr)] From 28b03b8cf839fa800b817754b2e850e35d8a9a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sat, 10 Jul 2021 21:01:41 +0200 Subject: [PATCH 25/33] Update ChangeLog --- ChangeLog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 96f52c7..7b090d1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # Msgcheck ChangeLog +## Version 3.2.0 (under dev) + +- Add support for Chinese full-stop. + ## Version 3.1 (2020-03-07) - Add option `-i` (or `--ignore-errors`): always return 0 even if errors are found. From a5598b49818be2dcef4669219600e9901bb30d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Tue, 30 Nov 2021 19:10:31 +0100 Subject: [PATCH 26/33] Add tests with Python 3.10 --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 260a499..46a08ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ jobs: - "3.7" - "3.8" - "3.9" + - "3.10" steps: From 84378b20c5e1f2d4c3c27e0dc5fc949d32855b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Tue, 30 Nov 2021 19:10:50 +0100 Subject: [PATCH 27/33] Add tests on Ubuntu 18.04 --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46a08ba..d4a812c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,10 +8,11 @@ jobs: build: - name: Python ${{ matrix.python-version }} - runs-on: ubuntu-20.04 strategy: matrix: + os: + - ubuntu-20.04 + - ubuntu-18.04 python-version: - "2.7" - "3.5" @@ -21,6 +22,9 @@ jobs: - "3.9" - "3.10" + name: Python ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + steps: - uses: actions/checkout@v2 From 9d1a2d9d7acae7980e5783302e235a75be174029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Thu, 2 Dec 2021 08:35:30 +0100 Subject: [PATCH 28/33] Fix error when enabling spell checking without a file with personal list of words --- msgcheck/po.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/msgcheck/po.py b/msgcheck/po.py index dd3e84f..44a1c0e 100644 --- a/msgcheck/po.py +++ b/msgcheck/po.py @@ -495,11 +495,14 @@ def _get_language_checker(self, po_file, reports): lang = po_file.props['language'] \ if self.spelling == 'str' else 'en' try: - with tempfile.NamedTemporaryFile() as tmp_file: - tmp_file.write(self.pwl.encode('utf-8')) - tmp_file.flush() - _dict = DictWithPWL(lang, tmp_file.name) - checker.append(SpellChecker(_dict)) + if self.pwl: + with tempfile.NamedTemporaryFile() as tmp_file: + tmp_file.write(self.pwl.encode('utf-8')) + tmp_file.flush() + _dict = DictWithPWL(lang, tmp_file.name) + else: + _dict = DictWithPWL(lang, None) + checker.append(SpellChecker(_dict)) except DictNotFoundError: reports.append(PoReport( 'enchant dictionary not found for language "{0}"' From 5e5b00945483adbd8a9f28116d9bc61ef4a5fe04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Fri, 3 Dec 2021 09:43:32 +0100 Subject: [PATCH 29/33] Drop Python 2 support, Python 3.6 is now required --- .github/workflows/ci.yml | 2 - ChangeLog.md | 1 + README.md | 2 +- msgcheck/__init__.py | 6 +- msgcheck/msgcheck.py | 16 ++-- msgcheck/po.py | 171 ++++++++++++++++++++++++--------------- msgcheck/utils.py | 6 +- setup.py | 3 +- tests/__init__.py | 6 +- tests/test_msgcheck.py | 64 +++++++++++---- 10 files changed, 169 insertions(+), 108 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4a812c..8529e81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,8 +14,6 @@ jobs: - ubuntu-20.04 - ubuntu-18.04 python-version: - - "2.7" - - "3.5" - "3.6" - "3.7" - "3.8" diff --git a/ChangeLog.md b/ChangeLog.md index 7b090d1..0f93d05 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,6 +2,7 @@ ## Version 3.2.0 (under dev) +- Drop Python 2 support, Python 3.6 is now required. - Add support for Chinese full-stop. ## Version 3.1 (2020-03-07) diff --git a/README.md b/README.md index 78ee5eb..1534db4 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Msgcheck performs various checks on gettext files (with extension `.po`): The script requires: -- Python ≥ 2.7 +- Python ≥ 3.6 - gettext (for the command `msgfmt`, used to compile PO files) - the python module `pyenchant` if spelling is checked (with option `-s`). diff --git a/msgcheck/__init__.py b/msgcheck/__init__.py index 35466d1..355c470 100644 --- a/msgcheck/__init__.py +++ b/msgcheck/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # # Copyright (C) 2009-2021 Sébastien Helleu # @@ -18,9 +18,7 @@ # along with msgcheck. If not, see . # -""" -Gettext file checker. -""" +"""Gettext file checker.""" from . import msgcheck # noqa: F401 from . import po # noqa: F401 diff --git a/msgcheck/msgcheck.py b/msgcheck/msgcheck.py index 9efb6f2..146145c 100644 --- a/msgcheck/msgcheck.py +++ b/msgcheck/msgcheck.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # # Copyright (C) 2009-2021 Sébastien Helleu # @@ -174,21 +174,19 @@ def msgcheck_display_result(args, result): for filename, reports in result: errors = len(reports) if errors == 0: - print('{0}: OK'.format(filename)) + print(f'{filename}: OK') else: - print('{0}: {1} errors ({2})'.format( - filename, - errors, - 'almost good!' if errors <= 10 else 'uh oh... try again!')) + result = 'almost good!' if errors <= 10 else 'uh oh... try again!' + print(f'{filename}: {errors} errors ({result})') # display total (if many files processed) if len(args.file) > 1: print('---') if files_with_errors == 0: - print('TOTAL: {0} files OK'.format(files_ok)) + print(f'TOTAL: {files_ok} files OK') else: - print('TOTAL: {0} files OK, {1} files with {2} errors'.format( - files_ok, files_with_errors, total_errors)) + print(f'TOTAL: {files_ok} files OK, {files_with_errors} files ' + f'with {total_errors} errors') return files_with_errors diff --git a/msgcheck/po.py b/msgcheck/po.py index 44a1c0e..256a0db 100644 --- a/msgcheck/po.py +++ b/msgcheck/po.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # # Copyright (C) 2009-2021 Sébastien Helleu # @@ -18,9 +18,7 @@ # along with msgcheck. If not, see . # -""" -Classes to read and check PO (gettext) files. -""" +"""Classes to read and check PO (gettext) files.""" from __future__ import print_function @@ -28,7 +26,6 @@ import os import re import subprocess -import sys import tempfile # enchant module is optional, spelling is checked on demand @@ -72,17 +69,13 @@ def __repr__(self): if self.idmsg == 'extract': return self.message + '\n---' if self.idmsg == 'compile': - return '{0}\n{1}'.format('=' * 70, self.message) + return f'{"=" * 70}\n{self.message}' is_list = isinstance(self.message, list) - count = '(%d)' % len(self.message) if is_list else '' - msg = '{0}\n{1}:{2}: [{3}{4}] {5}{6}'.format( - '=' * 70, - self.filename, - self.line, - self.idmsg, - count, - '(fuzzy) ' if self.fuzzy else '', - ', '.join(self.message) if is_list else self.message) + count = f'({len(self.message)})' if is_list else '' + str_fuzzy = '(fuzzy) ' if self.fuzzy else '' + str_msg = ', '.join(self.message) if is_list else self.message + msg = (f'{"=" * 70}\n{self.filename}:{self.line}: ' + f'[{self.idmsg}{count}] {str_fuzzy}{str_msg}') if self.mid: msg += '\n---\n' + self.mid if self.mstr: @@ -126,19 +119,16 @@ def __init__(self, filename, line, msg, charset, fuzzy, fmt, noqa): self.filename = filename self.line = line # unescape strings - if sys.version_info < (3,): - # python 2.x - msg = {k: escape_decode(v)[0] for k, v in msg.items()} - else: - # python 3.x - msg = {k: escape_decode(v)[0]. decode(charset) - for k, v in msg.items()} + msg = { + k: escape_decode(v)[0].decode(charset) + for k, v in msg.items() + } # build messages as a list of tuples: (string, translation) self.messages = [] if 'msgid_plural' in msg: i = 0 while True: - key = 'msgstr[{0}]'.format(i) + key = f'msgstr[{i}]' if key not in msg: break self.messages.append((msg['msgid_plural'], msg[key])) @@ -162,9 +152,16 @@ def check_lines(self): nb_str = count_lines(mstr) if nb_id != nb_str: errors.append( - PoReport('number of lines: {0} in string, ' - '{1} in translation'.format(nb_id, nb_str), - 'lines', self.filename, self.line, mid, mstr)) + PoReport( + f'number of lines: {nb_id} in string, ' + f'{nb_str} in translation', + 'lines', + self.filename, + self.line, + mid, + mstr, + ) + ) return errors def check_punct(self, language): @@ -176,10 +173,15 @@ def check_punct(self, language): for mid, mstr in self.messages: if not mid or not mstr: continue - puncts = [(':', ':'), (';', ';'), (',', ','), ('...', '...')] + puncts = [ + (':', ':'), + (';', ';'), + (',', ','), + ('...', '...'), + ] # special symbols in some languages if language[:2] in ['ja', 'zh']: - puncts.append(('.', u'。')) + puncts.append(('.', '。')) else: puncts.append(('.', '.')) for punctid, punctstr in puncts: @@ -193,17 +195,29 @@ def check_punct(self, language): break if match_id and not match_str: errors.append( - PoReport(u'end punctuation: "{0}" in string, ' - u'"{1}" not in translation' - ''.format(punctid, punctstr), - 'punct', self.filename, self.line, mid, mstr)) + PoReport( + f'end punctuation: "{punctid}" in string, ' + f'"{punctstr}" not in translation', + 'punct', + self.filename, + self.line, + mid, + mstr, + ) + ) break if not match_id and match_str: errors.append( - PoReport(u'end punctuation: "{0}" in translation, ' - u'"{1}" not in string' - ''.format(punctstr, punctid), - 'punct', self.filename, self.line, mid, mstr)) + PoReport( + f'end punctuation: "{punctstr}" in ' + f'translation, "{punctid}" not in string', + 'punct', + self.filename, + self.line, + mid, + mstr, + ) + ) break return errors @@ -222,20 +236,31 @@ def check_whitespace(self): startout = len(mstr) - len(mstr.lstrip(' ')) if startin != startout: errors.append( - PoReport('whitespace at beginning: {0} in string, ' - '{1} in translation' - ''.format(startin, startout), - 'whitespace', self.filename, self.line, mid, - mstr)) + PoReport( + f'whitespace at beginning: {startin} in ' + f'string, {startout} in translation', + 'whitespace', + self.filename, + self.line, + mid, + mstr, + ) + ) # check whitespace at end of string endin = len(mid) - len(mid.rstrip(' ')) endout = len(mstr) - len(mstr.rstrip(' ')) if endin != endout: errors.append( - PoReport('whitespace at end: {0} in string, ' - '{1} in translation'.format(endin, endout), - 'whitespace', self.filename, self.line, mid, - mstr)) + PoReport( + f'whitespace at end: {endin} in string, ' + f'{endout} in translation', + 'whitespace', + self.filename, + self.line, + mid, + mstr, + ) + ) return errors def check_whitespace_eol(self): @@ -256,11 +281,16 @@ def check_whitespace_eol(self): endout = len(strlines[i]) - len(strlines[i].rstrip(' ')) if (endin > 0 or endout > 0) and endin != endout: errors.append( - PoReport('different whitespace at end of a line: {0} ' - 'in string, {1} in translation' - ''.format(endin, endout), - 'whitespace_eol', self.filename, self.line, - mid, mstr)) + PoReport( + f'different whitespace at end of a line: {endin} ' + f'in string, {endout} in translation', + 'whitespace_eol', + self.filename, + self.line, + mid, + mstr, + ) + ) break return errors @@ -289,8 +319,16 @@ def check_spelling(self, spelling, checkers): if misspelled_word: misspelled.append(err.word) if misspelled: - errors.append(PoReport(misspelled, 'spelling-' + spelling, - self.filename, self.line, mid, mstr)) + errors.append( + PoReport( + misspelled, + 'spelling-' + spelling, + self.filename, + self.line, + mid, + mstr, + ) + ) return errors @@ -407,7 +445,7 @@ def read(self): # pylint: disable=too-many-locals """ self.msgs = [] checker = Checker() - with open(self.filename, 'r') as po_file: + with open(self.filename, 'r', encoding='utf-8') as po_file: for line in po_file: message = checker.check_line(line.strip()) if message: @@ -454,11 +492,8 @@ def __init__(self): self.pwl = None def __repr__(self): - return ('checks: {0}, dicts: {1}, ' - 'extra_checkers: {2}'.format( - self.checks, - self.dicts, - self.extra_checkers)) + return (f'checks: {self.checks}, dicts: {self.dicts}, ' + f'extra_checkers: {self.extra_checkers}') def set_check(self, check, state): """Enable/disable a specific check.""" @@ -482,8 +517,8 @@ def set_spelling_options(self, spelling, dicts, pwl_files): _dict = Dict(lang) self.extra_checkers.append(SpellChecker(_dict)) except DictNotFoundError: - print('WARNING: enchant dictionary not found for ' - 'language "{0}"'.format(lang)) + print(f'WARNING: enchant dictionary not found for ' + f'language "{lang}"') def _get_language_checker(self, po_file, reports): """Get checker for PO file language.""" @@ -504,11 +539,14 @@ def _get_language_checker(self, po_file, reports): _dict = DictWithPWL(lang, None) checker.append(SpellChecker(_dict)) except DictNotFoundError: - reports.append(PoReport( - 'enchant dictionary not found for language "{0}"' - ''.format(lang), - 'dict', po_file.filename, - po_file.props['language_numline'])) + reports.append( + PoReport( + f'enchant dictionary not found for language "{lang}"', + 'dict', + po_file.filename, + po_file.props['language_numline'] + ) + ) checker = [] except IOError as exc: reports.append(PoReport( @@ -586,8 +624,7 @@ def check_files(self, files): result.append((po_file.filename, self.check_pofile(po_file))) else: # compilation failed - if sys.version_info >= (3,): - compile_output = bytes(compile_output).decode('utf-8') + compile_output = bytes(compile_output).decode('utf-8') result.append((po_file.filename, [PoReport(compile_output, 'compile', po_file.filename)])) diff --git a/msgcheck/utils.py b/msgcheck/utils.py index d575f9b..2aeaf37 100644 --- a/msgcheck/utils.py +++ b/msgcheck/utils.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # # Copyright (C) 2009-2021 Sébastien Helleu # @@ -18,9 +18,7 @@ # along with msgcheck. If not, see . # -""" -Some utility functions for msgcheck. -""" +"""Some utility functions for msgcheck.""" from __future__ import print_function diff --git a/setup.py b/setup.py index 2a44ea6..e887295 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # # Copyright (C) 2009-2021 Sébastien Helleu # @@ -54,7 +54,6 @@ 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Topic :: Software Development :: Localization', ], diff --git a/tests/__init__.py b/tests/__init__.py index 5f44550..ca754e4 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # # Copyright (C) 2009-2021 Sébastien Helleu # @@ -18,6 +18,4 @@ # along with msgcheck. If not, see . # -""" -Tests on msgcheck. -""" +"""Tests on msgcheck.""" diff --git a/tests/test_msgcheck.py b/tests/test_msgcheck.py index 8f0f3f4..4659a54 100644 --- a/tests/test_msgcheck.py +++ b/tests/test_msgcheck.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 # # Copyright (C) 2009-2021 Sébastien Helleu # @@ -350,20 +350,54 @@ def test_spelling_bad_pwl(): @pytest.mark.parametrize('language, msgid, msgstr, error_message', [ - ('ja', 'Should not raise an error.', u'エラーが発生しないようにしてください。', ''), - ('ja', 'Should raise an error', u'エラーを発生させる必要があります。', - u'end punctuation: "。" in translation, "." not in string'), - ('ja', 'Should raise an error.', u'エラーを発生させる必要があります', - u'end punctuation: "." in string, "。" not in translation'), - ('ja', 'Should raise an error.', u'エラーを発生させる必要があります.', - u'end punctuation: "." in string, "。" not in translation'), - ('zh-Hans', 'Should not raise an error.', u'不应引起错误。', ''), - ('zh-Hans', 'Should raise an error', u'应该会出现一个错误。', - u'end punctuation: "。" in translation, "." not in string'), - ('zh-Hans', 'Should raise an error.', u'应该会出现一个错误', - u'end punctuation: "." in string, "。" not in translation'), - ('zh-Hans', 'Should raise an error.', u'应该会出现一个错误.', - u'end punctuation: "." in string, "。" not in translation'), + ( + 'ja', + 'Should not raise an error.', + 'エラーが発生しないようにしてください。', + '', + ), + ( + 'ja', + 'Should raise an error', + 'エラーを発生させる必要があります。', + 'end punctuation: "。" in translation, "." not in string', + ), + ( + 'ja', + 'Should raise an error.', + 'エラーを発生させる必要があります', + 'end punctuation: "." in string, "。" not in translation', + ), + ( + 'ja', + 'Should raise an error.', + 'エラーを発生させる必要があります.', + 'end punctuation: "." in string, "。" not in translation', + ), + ( + 'zh-Hans', + 'Should not raise an error.', + '不应引起错误。', + '', + ), + ( + 'zh-Hans', + 'Should raise an error', + '应该会出现一个错误。', + 'end punctuation: "。" in translation, "." not in string', + ), + ( + 'zh-Hans', + 'Should raise an error.', + '应该会出现一个错误', + 'end punctuation: "." in string, "。" not in translation', + ), + ( + 'zh-Hans', + 'Should raise an error.', + '应该会出现一个错误.', + 'end punctuation: "." in string, "。" not in translation', + ), ]) def test_punct_full_stop_ja_zh(language, msgid, msgstr, error_message): """Test punctuation with non-latin full-stops.""" From 469787dee4518f8d28908a157d02deae6a4d6bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Fri, 3 Dec 2021 12:30:14 +0100 Subject: [PATCH 30/33] Add lint with bandit in CI --- .github/workflows/ci.yml | 5 ++++- ChangeLog.md | 1 + Makefile | 5 ++++- msgcheck/po.py | 4 ++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8529e81..9f47244 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: sudo apt-get install gettext aspell aspell-fr enchant python -m pip install --upgrade pip pip install -r requirements.txt - pip install flake8 pylint pytest coverage pytest-cov + pip install bandit coverage flake8 pylint pytest pytest-cov - name: Lint with flake8 run: make flake8 @@ -45,5 +45,8 @@ jobs: - name: Lint with pylint run: make pylint + - name: Lint with bandit + run: make bandit + - name: Test with pytest run: make test diff --git a/ChangeLog.md b/ChangeLog.md index 0f93d05..33c692c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -4,6 +4,7 @@ - Drop Python 2 support, Python 3.6 is now required. - Add support for Chinese full-stop. +- Add lint with bandit in CI. ## Version 3.1 (2020-03-07) diff --git a/Makefile b/Makefile index 5f3dfa4..6d8091b 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ all: check check: lint test -lint: flake8 pylint +lint: flake8 pylint bandit flake8: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics @@ -31,5 +31,8 @@ pylint: pylint --disable=W0511,R0205 msgcheck pylint --disable=W0511,R0205 tests +bandit: + bandit -r msgcheck + test: pytest -vv --cov-report term-missing --cov=msgcheck tests diff --git a/msgcheck/po.py b/msgcheck/po.py index 256a0db..071c54e 100644 --- a/msgcheck/po.py +++ b/msgcheck/po.py @@ -25,7 +25,7 @@ from codecs import escape_decode import os import re -import subprocess +import subprocess # nosec import tempfile # enchant module is optional, spelling is checked on demand @@ -461,7 +461,7 @@ def compile(self): """ output = '' try: - output = subprocess.check_output( + output = subprocess.check_output( # nosec ['msgfmt', '-c', '-o', '/dev/null', self.filename], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as exc: From 1aa08e261a10815324dafa9cd7e56dad5650460f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sun, 23 Jan 2022 09:51:35 +0100 Subject: [PATCH 31/33] Rename option "--skip-noqa" to "--check-noqa" and reverse behavior This is an incompatible change: without option, the "noqa"-commented lines are now skipped by default (this is the way most linters work). With the option `--check-noqa`, the "noqa"-commented lines are checked. --- ChangeLog.md | 1 + README.md | 2 +- msgcheck/msgcheck.py | 10 ++++++---- msgcheck/po.py | 6 +++--- tests/test_msgcheck.py | 16 ++++++++-------- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 33c692c..ca94e87 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,7 @@ ## Version 3.2.0 (under dev) - Drop Python 2 support, Python 3.6 is now required. +- Rename option `--skip-noqa` to `--check-noqa` and reverse behavior: without option, strings with `noqa` are now skipped by default. - Add support for Chinese full-stop. - Add lint with bandit in CI. diff --git a/README.md b/README.md index 1534db4..c5d26b9 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Options: - `-h`, `--help`: display help message and exit - `-c`, `--no-compile`: do not check compilation of file (with `msgfmt -c`) - `-f`, `--fuzzy`: check fuzzy strings -- `-n`, `--skip-noqa`: do not check "noqa"-commented lines +- `-n`, `--check-noqa`: check "noqa"-commented lines (they are skipped by default) - `-l`, `--no-lines`: do not check number of lines - `-p`, `--no-punct`: do not check punctuation at end of strings - `-s id|str`, `--spelling id|str`: check spelling (`id` = source messages, `str` = translations) diff --git a/msgcheck/msgcheck.py b/msgcheck/msgcheck.py index 146145c..dd75e68 100644 --- a/msgcheck/msgcheck.py +++ b/msgcheck/msgcheck.py @@ -66,8 +66,9 @@ def msgcheck_parser(): help='do not check compilation of file') parser.add_argument('-f', '--fuzzy', action='store_true', help='check fuzzy strings') - parser.add_argument('-n', '--skip-noqa', action='store_true', - help='do not check "noqa"-commented lines') + parser.add_argument('-n', '--check-noqa', action='store_true', + help='check "noqa"-commented lines (they are skipped ' + 'by default)') parser.add_argument('-l', '--no-lines', action='store_true', help='do not check number of lines') parser.add_argument('-p', '--no-punct', action='store_true', @@ -118,8 +119,9 @@ def msgcheck_check_files(args): """Check files.""" # create checker and set boolean options po_check = PoCheck() - for option in ('no_compile', 'fuzzy', 'skip_noqa', 'no_lines', 'no_punct', - 'no_whitespace', 'no_whitespace_eol', 'extract'): + for option in ('no_compile', 'fuzzy', 'check_noqa', 'no_lines', + 'no_punct', 'no_whitespace', 'no_whitespace_eol', + 'extract'): if args.__dict__[option]: po_check.set_check(option.lstrip('no_'), not option.startswith('no_')) diff --git a/msgcheck/po.py b/msgcheck/po.py index 071c54e..7786fc1 100644 --- a/msgcheck/po.py +++ b/msgcheck/po.py @@ -477,7 +477,7 @@ def __init__(self): self.checks = { 'compile': True, 'fuzzy': False, - 'skip_noqa': False, + 'check_noqa': False, 'lines': True, 'punct': True, 'whitespace': True, @@ -587,9 +587,9 @@ def check_pofile(self, po_file): # check all messages check_fuzzy = self.checks['fuzzy'] - skip_noqa = self.checks['skip_noqa'] + check_noqa = self.checks['check_noqa'] for msg in po_file.msgs: - if skip_noqa and msg.noqa: + if msg.noqa and not check_noqa: continue if msg.fuzzy and not check_fuzzy: continue diff --git a/tests/test_msgcheck.py b/tests/test_msgcheck.py index 4659a54..8365f64 100644 --- a/tests/test_msgcheck.py +++ b/tests/test_msgcheck.py @@ -114,7 +114,7 @@ def test_checks(): assert not result[0][1] # second file has 10 errors - assert len(result[1][1]) == 10 + assert len(result[1][1]) == 9 # check first error report = result[1][1][0] @@ -129,7 +129,7 @@ def test_checks(): '2 in string, 1 in translation' in str(report)) # check last error - report = result[1][1][9] + report = result[1][1][8] assert report.message == \ 'different whitespace at end of a line: 1 in string, 0 in translation' assert report.idmsg == 'whitespace_eol' @@ -144,7 +144,7 @@ def test_checks(): for report in result[1][1]: errors[report.idmsg] = errors.get(report.idmsg, 0) + 1 assert errors['lines'] == 2 - assert errors['punct'] == 2 + assert errors['punct'] == 1 assert errors['whitespace'] == 4 assert errors['whitespace_eol'] == 2 @@ -159,20 +159,20 @@ def test_checks_fuzzy(): assert len(result) == 1 # the file has 11 errors (with the fuzzy string) - assert len(result[0][1]) == 11 + assert len(result[0][1]) == 10 def test_checks_noqa(): - """Test checks on a gettext file ignoring `noqa`-commented lines.""" + """Test checks on a gettext file including `noqa`-commented lines.""" po_check = PoCheck() - po_check.set_check('skip_noqa', True) + po_check.set_check('check_noqa', True) result = po_check.check_files([local_path('fr_errors.po')]) # be sure we have one file in result assert len(result) == 1 - # the file has 9 errors (`noqa` was skipped) - assert len(result[0][1]) == 9 + # the file has 10 errors (including `noqa`-commented lines) + assert len(result[0][1]) == 10 def test_replace_fmt_c(): From 92bc32e19fc6b3b342c5d9c6de9dca30127cb2a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sun, 23 Jan 2022 09:57:26 +0100 Subject: [PATCH 32/33] Remove useless object inheritance --- Makefile | 4 ++-- msgcheck/po.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 6d8091b..068f566 100644 --- a/Makefile +++ b/Makefile @@ -28,8 +28,8 @@ flake8: flake8 . --count --exit-zero --max-complexity=10 --statistics pylint: - pylint --disable=W0511,R0205 msgcheck - pylint --disable=W0511,R0205 tests + pylint --disable=W0511 msgcheck + pylint --disable=W0511 tests bandit: bandit -r msgcheck diff --git a/msgcheck/po.py b/msgcheck/po.py index 7786fc1..af7a5ff 100644 --- a/msgcheck/po.py +++ b/msgcheck/po.py @@ -51,7 +51,7 @@ def get_concatenated_files(filenames): return '\n'.join(content) -class PoReport(object): # pylint: disable=too-few-public-methods +class PoReport: # pylint: disable=too-few-public-methods """A message in report (commonly an error in detected in gettext file).""" # pylint: disable=too-many-arguments @@ -87,7 +87,7 @@ def get_misspelled_words(self): return self.message if isinstance(self.message, list) else [] -class PoMessage(object): +class PoMessage: """ A message from a gettext file. It is stored as a list of tuples (string, translation). @@ -332,7 +332,7 @@ def check_spelling(self, spelling, checkers): return errors -class Checker(object): # pylint: disable=too-many-instance-attributes +class Checker: # pylint: disable=too-many-instance-attributes """Messages checker.""" def __init__(self): @@ -404,7 +404,7 @@ def last_check(self): return None -class PoFile(object): +class PoFile: """ A gettext file. It includes methods to read the file, and perform checks on the translations. @@ -469,7 +469,7 @@ def compile(self): return (output, 0) -class PoCheck(object): +class PoCheck: """Perform checks on a gettext file.""" def __init__(self): From ab02cd50d262ef4cb94736ead1844255e1d082e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sun, 23 Jan 2022 10:03:06 +0100 Subject: [PATCH 33/33] Version 4.0.0 --- ChangeLog.md | 2 +- msgcheck/msgcheck.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index ca94e87..f024f11 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,6 @@ # Msgcheck ChangeLog -## Version 3.2.0 (under dev) +## Version 4.0.0 (2022-01-23) - Drop Python 2 support, Python 3.6 is now required. - Rename option `--skip-noqa` to `--check-noqa` and reverse behavior: without option, strings with `noqa` are now skipped by default. diff --git a/msgcheck/msgcheck.py b/msgcheck/msgcheck.py index dd75e68..f72f381 100644 --- a/msgcheck/msgcheck.py +++ b/msgcheck/msgcheck.py @@ -39,7 +39,7 @@ from . po import PoCheck -__version__ = '3.2.0-dev' +__version__ = '4.0.0' def msgcheck_version():