Compare commits

...

105 Commits
v0.5.0 ... main

Author SHA1 Message Date
5f3e154a36
Merge pull request #66 from JulienPalard/mdk-no-default-personal-dict
Don't force everyone to use French dictionary.
2021-10-13 21:22:22 +02:00
74a7c581c3
Don't force everyone to use French dictionary. 2021-10-13 15:01:29 +02:00
429ef81277
Merge pull request #59 from vpoulailleau/glossary_update 2021-02-02 14:47:07 +01:00
46e6eec6c5 poetry update 2021-02-02 14:43:31 +01:00
3fd8697927 fix #58 2021-02-02 14:42:36 +01:00
a9082a759f check tuple 2021-02-02 14:42:10 +01:00
97bde75feb hide unknown words if present in the glossary 2021-02-02 14:30:21 +01:00
76b77e502b prepare version v0.11.0 2021-02-02 14:26:00 +01:00
a628ad26b9 fix #58 2021-02-02 14:23:14 +01:00
cb7dd30057 partial implementation of #58 2021-02-02 14:07:48 +01:00
d39bb55588
Merge pull request #57 from vpoulailleau/cc
udpate CC information (#54)
2020-12-11 18:32:32 +01:00
259d02e8b2 udpate CC information (#54) 2020-12-11 18:30:01 +01:00
a4a3b5e4ad update links to point to the new repository 2020-12-04 10:18:30 +01:00
e6bd2be862
Merge pull request #56 from vpoulailleau/badge_test
tests badge
2020-12-04 10:11:07 +01:00
10640d7f62 tests badge 2020-12-04 10:07:14 +01:00
b1d9ac385e update changelog 2020-12-04 09:35:51 +01:00
8cbcc30f1e upgrade pip 2020-12-04 09:29:05 +01:00
2b7a621696 poetry update 2020-12-04 09:25:42 +01:00
8e9b46f4a9 another try 2020-12-04 09:22:40 +01:00
b88364aa9e remove tox usage 2020-12-04 09:19:52 +01:00
f2613f1a7b add Python 3.9 for tox 2020-12-04 09:14:08 +01:00
814df8c700 another try… 2020-12-04 09:11:05 +01:00
27e96ecc85 another try… 2020-12-04 09:04:16 +01:00
9211708771 install missing tools 2020-12-04 08:58:21 +01:00
93502bdbc7 start using GitHub Actions 2020-12-04 08:46:06 +01:00
3ebc35d96a remove badges 2020-12-04 08:21:18 +01:00
87e1bb3fe7 remove badges 2020-12-04 08:20:20 +01:00
bf703e33f8 remove warning 2020-12-04 08:18:05 +01:00
ea243cc9a3 use pygrammalecte 1.2.0 2020-12-04 08:17:34 +01:00
b0bc19af25
Merge pull request #52 from Seluj78/patch-1 2020-10-14 15:01:17 +02:00
Jules Lasne (jlasne)
532957ae03
Apply suggestions from code review
Co-authored-by: Vincent Poulailleau <vpoulailleau@gmail.com>
2020-10-14 13:52:22 +02:00
Jules Lasne (jlasne)
497bd0a1ec
Added poutils section to README 2020-10-14 13:44:28 +02:00
c8acf3101c poetry update 2020-09-08 08:04:55 +02:00
c5c9e1e0e3 use pygrammalecte v1.1+ 2020-09-07 09:11:45 +02:00
dca2b9188e poetry update 2020-09-07 09:11:45 +02:00
24e06451ed fix version parsing 2020-09-07 09:11:45 +02:00
c04e675166 prepare v0.9.0 2020-09-07 09:11:45 +02:00
de046c601d test from project root dir 2020-09-07 09:11:45 +02:00
5949619621 use provided message for spelling errors 2020-09-07 09:11:45 +02:00
3410a0a5e0 use python-dev-tools as dev dependency 2020-09-07 09:11:45 +02:00
9f886ed10b
Merge pull request #47 from vpoulailleau/dependabot/pip/simplelogging-0.11.0
Bump simplelogging from 0.10.0 to 0.11.0
2020-09-01 07:42:15 +02:00
dependabot-preview[bot]
10d5773dc9
Bump simplelogging from 0.10.0 to 0.11.0
Bumps [simplelogging](https://github.com/vpoulailleau/simplelogging) from 0.10.0 to 0.11.0.
- [Release notes](https://github.com/vpoulailleau/simplelogging/releases)
- [Changelog](https://github.com/vpoulailleau/simplelogging/blob/master/docs/history.md)
- [Commits](https://github.com/vpoulailleau/simplelogging/compare/v0.10.0...v0.11.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-01 04:37:37 +00:00
d5fc1bfa34 update tox.ini syntax 2020-08-31 09:04:26 +02:00
e98ed008a6 add 2020 year 2020-08-31 09:04:00 +02:00
2cda64a5d4 v0.8.0 2020-08-25 17:52:27 +02:00
3c95882ce8 GFM 2020-08-25 17:49:55 +02:00
89cb7c9f35 GFM 2020-08-25 17:48:19 +02:00
154933c783 prepare delivery of v0.8.0 2020-08-25 17:46:35 +02:00
9b374d2718 fix #12 2020-08-25 17:41:10 +02:00
e2955ccb5c fix #13 2020-08-25 17:36:17 +02:00
2c11208321 fix #14 2020-08-25 17:33:35 +02:00
38cf9c4c2b fix #17 2020-08-25 17:30:04 +02:00
695a0b59b6 fix #18 2020-08-25 17:17:39 +02:00
3f45cd9c51 fix #20 2020-08-25 17:15:07 +02:00
ead58d2b5b fix #15 2020-08-25 17:11:45 +02:00
2ec343a32e better output (#15) 2020-08-25 17:05:23 +02:00
0a0af9fb52 colored output (#15) 2020-08-25 16:58:30 +02:00
4bb8fd7819 remove output coloring by default (#15) 2020-08-25 16:39:51 +02:00
db5ce5e226
Merge pull request #45 from vpoulailleau/dependabot/pip/tox-3.19.0
Bump tox from 3.18.1 to 3.19.0
2020-08-20 16:07:38 +02:00
dependabot-preview[bot]
cdb3069e2b
Bump tox from 3.18.1 to 3.19.0
Bumps [tox](https://github.com/tox-dev/tox) from 3.18.1 to 3.19.0.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/master/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/3.18.1...3.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-20 11:34:40 +00:00
8d1e18fdee
Merge pull request #46 from vpoulailleau/dependabot/pip/pytest-cov-2.10.1
Bump pytest-cov from 2.10.0 to 2.10.1
2020-08-20 11:54:21 +02:00
dependabot-preview[bot]
5563373256
Bump pytest-cov from 2.10.0 to 2.10.1
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.10.0 to 2.10.1.
- [Release notes](https://github.com/pytest-dev/pytest-cov/releases)
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.10.0...v2.10.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-17 04:46:43 +00:00
33eb488a4b add badges 2020-08-02 21:42:53 +02:00
ed95fe288a use travis and codeclimate 2020-08-02 21:37:35 +02:00
da55cc9729 remove cat 2020-08-02 21:35:03 +02:00
8d26f52f35 update changelog 2020-08-02 21:34:14 +02:00
bdee146b63 use pygrammalecte 2020-08-02 21:32:56 +02:00
6da1a8f74d fix manual installation 2020-08-02 21:29:52 +02:00
114068b0b1 update dependencies 2020-08-02 21:26:05 +02:00
3eee104422 update pyproject 2020-08-02 18:53:15 +02:00
f1fda75ebc
Merge pull request #23 from vpoulailleau/dependabot/pip/wheel-0.34.2
Bump wheel from 0.33.6 to 0.34.2
2020-08-02 18:45:08 +02:00
dependabot-preview[bot]
8c1dad8356
Bump wheel from 0.33.6 to 0.34.2
Bumps [wheel](https://github.com/pypa/wheel) from 0.33.6 to 0.34.2.
- [Release notes](https://github.com/pypa/wheel/releases)
- [Changelog](https://github.com/pypa/wheel/blob/master/docs/news.rst)
- [Commits](https://github.com/pypa/wheel/compare/0.33.6...0.34.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-02 16:44:30 +00:00
19d3234840
Merge pull request #35 from vpoulailleau/dependabot/pip/requests-2.24.0
Bump requests from 2.22.0 to 2.24.0
2020-08-02 18:43:09 +02:00
dependabot-preview[bot]
c4da0e405d
Bump requests from 2.22.0 to 2.24.0
Bumps [requests](https://github.com/psf/requests) from 2.22.0 to 2.24.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/master/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.22.0...v2.24.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-02 16:42:58 +00:00
c6145d3cec
Merge pull request #34 from vpoulailleau/dependabot/pip/pytest-cov-2.10.0
Bump pytest-cov from 2.8.1 to 2.10.0
2020-08-02 18:41:31 +02:00
dependabot-preview[bot]
08651e7a19
Bump pytest-cov from 2.8.1 to 2.10.0
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.8.1 to 2.10.0.
- [Release notes](https://github.com/pytest-dev/pytest-cov/releases)
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.8.1...v2.10.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-02 16:39:44 +00:00
bf2b65ce3e
Merge pull request #36 from vpoulailleau/dependabot/pip/twine-3.2.0
Bump twine from 2.0.0 to 3.2.0
2020-08-02 18:38:11 +02:00
dependabot-preview[bot]
652517d06f
Bump twine from 2.0.0 to 3.2.0
Bumps [twine](https://github.com/pypa/twine) from 2.0.0 to 3.2.0.
- [Release notes](https://github.com/pypa/twine/releases)
- [Changelog](https://github.com/pypa/twine/blob/master/docs/changelog.rst)
- [Commits](https://github.com/pypa/twine/compare/2.0.0...3.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-02 16:37:35 +00:00
ba420b8da0
Merge pull request #43 from vpoulailleau/dependabot/pip/tox-3.18.1
Bump tox from 3.14.1 to 3.18.1
2020-08-02 18:36:02 +02:00
4e0e2464a6
Merge pull request #27 from vpoulailleau/dependabot/pip/bleach-3.1.4
[Security] Bump bleach from 3.1.0 to 3.1.4
2020-08-02 18:35:11 +02:00
dependabot-preview[bot]
b81bbf6969
Bump tox from 3.14.1 to 3.18.1
Bumps [tox](https://github.com/tox-dev/tox) from 3.14.1 to 3.18.1.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/master/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/3.14.1...3.18.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-02 16:34:54 +00:00
dependabot-preview[bot]
3f62583d2d
[Security] Bump bleach from 3.1.0 to 3.1.4
Bumps [bleach](https://github.com/mozilla/bleach) from 3.1.0 to 3.1.4. **This update includes security fixes.**
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/master/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v3.1.0...v3.1.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-02 16:34:39 +00:00
c34a81a6d7
Merge pull request #44 from vpoulailleau/dependabot/pip/pytest-6.0.1
Bump pytest from 5.3.1 to 6.0.1
2020-08-02 18:33:19 +02:00
dependabot-preview[bot]
4c24c7a38e
Bump pytest from 5.3.1 to 6.0.1
Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.3.1 to 6.0.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/5.3.1...6.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-31 04:46:55 +00:00
8ff690e066 fix behavior with empty list of files to be checked, fix #11 2019-12-11 16:31:00 +01:00
69969ed415 prepare v0.7.0, fix #11 2019-12-11 16:31:00 +01:00
1361bddfbb add --version CLI flag, fix #10 2019-12-11 16:31:00 +01:00
07ef2c1c09 poetry update 2019-12-11 16:31:00 +01:00
adce7af2aa accept multiple input paths 2019-12-11 16:31:00 +01:00
9ba93c8d65 prepare v0.6.0 2019-12-09 12:51:10 +01:00
dba235f406 poetry update 2019-12-09 12:51:10 +01:00
e307ed5945 linter 2019-12-09 12:51:10 +01:00
ea5f7a3f60 add glossary checker 2019-12-09 12:51:10 +01:00
83c291021f glossary 2019-12-09 12:51:10 +01:00
a6d4efc406 glossary 2019-12-09 12:51:10 +01:00
0da25ca8ad word boundary match 2019-12-09 12:51:10 +01:00
d3d6190d49 better matching 2019-12-09 12:51:10 +01:00
b38f71db4d glossary 2019-12-09 12:51:10 +01:00
0a1adac231 glossary 2019-12-09 12:51:10 +01:00
9604c93215 glossary 2019-12-09 12:51:10 +01:00
7505d29d0f glossary 2019-12-09 12:51:10 +01:00
0fc4d122c3 glossary 2019-12-09 12:51:10 +01:00
a7d6f2cb9c glossary 2019-12-09 12:51:10 +01:00
c758d3bb5c glossary 2019-12-09 12:51:10 +01:00
c32360560d begin glossary checker 2019-12-09 12:51:10 +01:00
24 changed files with 2197 additions and 493 deletions

10
.codeclimate.yml Normal file
View File

@ -0,0 +1,10 @@
plugins:
bandit:
enabled: true
radon:
enabled: true
sonar-python:
enabled: true
config:
tests_patterns:
- tests/**

35
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,35 @@
---
name: Tests
on: [push, pull_request]
jobs:
tests:
name: 'Tests (pytest)'
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9']
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: Install poetry
run: |
python -m pip install --upgrade pip
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
source $HOME/.poetry/env
poetry --version
- name: Run tests
run: |
source $HOME/.poetry/env
poetry run python --version
poetry install -v
poetry run pytest -s -vv --cov=padpo

30
.travis.yml Normal file
View File

@ -0,0 +1,30 @@
env:
global:
- CC_TEST_REPORTER_ID=4f307949bf2b8fece540e528b27c5304ef1df0dfbfbd0e2e7e85892e4d5a9190
language: python
matrix:
include:
- python: 3.8
dist: bionic
sudo: true
- python: 3.7
dist: xenial
sudo: true
before_install:
- pip install poetry
install:
- poetry install
before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
script:
- tox
after_script:
- echo "$TRAVIS_PULL_REQUEST"
- echo "$TRAVIS_PYTHON_VERSION"
- find . -name ".coverage"
- if [[ "$TRAVIS_PULL_REQUEST" == "false" && "$TRAVIS_PYTHON_VERSION" == "3.7" ]]; then ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT tests/coverage.xml; fi

View File

@ -1,6 +1,6 @@
BSD 3-Clause License
Copyright (c) 2019, Vincent Poulailleau
Copyright (c) 2019-2020, Vincent Poulailleau
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,23 +1,34 @@
# padpo
Linter for gettext files (*.po)
[![PyPI](https://img.shields.io/pypi/v/padpo.svg)](https://pypi.python.org/pypi/padpo)
[![PyPI](https://img.shields.io/pypi/l/padpo.svg)](https://github.com/vpoulailleau/padpo/blob/master/LICENSE)
[![Code style: Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
[![Downloads](https://pepy.tech/badge/padpo)](https://pepy.tech/project/padpo)
[![Tests](https://github.com/AFPy/padpo/workflows/Tests/badge.svg)](https://github.com/AFPy/padpo/actions?query=workflow%3ATests)
[![Maintainability](https://api.codeclimate.com/v1/badges/bbd3044291527d667778/maintainability)](https://codeclimate.com/github/AFPy/padpo/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/bbd3044291527d667778/test_coverage)](https://codeclimate.com/github/AFPy/padpo/test_coverage)
Linter for gettext files (\*.po)
Created to help the translation of official Python docs in French: https://github.com/python/python-docs-fr
Il faut demander aux traducteurs s'ils n'ont pas de pot quand ils traduisent, maintenant ils ont `padpo`
:smile: :laughing: :stuck_out_tongue_winking_eye: :joy: (note : il était tard le soir quand j'ai trouvé le nom).
**WORK IN PROGRESS**
## License
BSD 3-clause
Pull request are welcome.
## Padpo is part of poutils!
[Poutils](https://pypi.org/project/poutils) (`.po` utils) is a metapackage to easily install useful Python tools to use with po files
and `padpo` is a part of it! Go check out [Poutils](https://pypi.org/project/poutils) to discover the other tools!
## Usage
Using the *activated virtual environment* created during the installation:
Using the _activated virtual environment_ created during the installation:
For a local input file:
@ -45,6 +56,11 @@ padpo --github python/python-docs-fr/pull/978
![Screenshot](screenshot.png)
### Color
By default, the output is colorless, and formatted like GCC messages. You can use `-c`
or `--color` option to get a colored output.
## Installation
### Automatic installation
@ -55,19 +71,7 @@ pip install padpo
### Manual installation
1. Create a virtual environment with Python 3.7 and above
```bash
python3.7 -m venv venv
```
2. Activate the virtual environment
```bash
source venv/bin/activate
```
3. Install dependencies
1. Install dependencies
```bash
poetry install
@ -75,19 +79,56 @@ pip install padpo
Note: this uses `poetry` that you can get here: https://poetry.eustace.io/docs/
2. Use virtual environment$
```bash
poetry shell
```
## Update on PyPI
`./deliver.sh`
## Changelog
### v0.11.0 (2021-02-02)
- update glossary (fix #58)
### v0.10.0 (2020-12-04)
- use `pygrammalecte` v1.3.0
- use GitHub Actions
### v0.9.0 (2020-09-07)
- use `pygrammalecte` default message for spelling errors
### v0.8.0 (2020-08-25)
- use [`pygrammalecte`](https://github.com/vpoulailleau/pygrammalecte)
- add continuous integration
- fix #12, #13, #14, #15, #17, #18, #20
- add `--color` CLI option to get a colored output (default is colorless)
### v0.7.0 (2019-12-11)
- add `--version` CLI option to display the current version of `padpo`
- `--input-path` CLI option now accepts several paths as in
`padpo --input-path file1.po file2.po directory1 directory2` or
`padpo -i file1.po file2.po directory1 directory2`
### v0.6.0 (2019-12-9)
- check errors against defined glossaries
### v0.5.0 (2019-12-3)
* check spelling errors with grammalecte
* tag releases!
- check spelling errors with grammalecte
- tag releases!
### v0.4.0 (2019-12-2)
* use poetry: https://poetry.eustace.io/docs/
* add some tests with tox and pytests
* fix some false positive issues with grammalecte
- use poetry: https://poetry.eustace.io/docs/
- add some tests with tox and pytests
- fix some false positive issues with grammalecte

View File

@ -7,7 +7,7 @@ git rebase
git status
# version management
VERSION=`grep "version =" pyproject.toml | sed -e 's/.*= "//' -e 's/"//'`
VERSION=`egrep "^version =" pyproject.toml | sed -e 's/.*= "//' -e 's/"//'`
VERSION="v${VERSION}"
echo "Version to be delivered: ${VERSION}"
echo -n "Is it OK? (y/n) [y]: "

View File

@ -1,16 +1,16 @@
"""Checkers list."""
from padpo.checkers.doublespace import DoubleSpaceChecker
from padpo.checkers.empty import EmptyChecker
from padpo.checkers.fuzzy import FuzzyChecker
from padpo.checkers.glossary import GlossaryChecker
from padpo.checkers.grammalecte import GrammalecteChecker
from padpo.checkers.linelength import LineLengthChecker
from padpo.checkers.nbsp import NonBreakableSpaceChecker
checkers = [
DoubleSpaceChecker(),
EmptyChecker(),
FuzzyChecker(),
GrammalecteChecker(),
GlossaryChecker(),
LineLengthChecker(),
NonBreakableSpaceChecker(),
]

View File

@ -26,6 +26,12 @@ class Checker(ABC):
"""Check an item in a `*.po` file."""
return NotImplementedError
def add_arguments(self, parser):
"""Let any checker register argparse arguments."""
def configure(self, args):
"""Store the result of parse_args, to get back arguments from self.add_arguments."""
def replace_quotes(match):
"""Replace match with « xxxxxxx »."""

View File

@ -1,23 +0,0 @@
"""Checker for double spaces."""
import re
from padpo.checkers.baseclass import Checker
from padpo.pofile import PoItem
class DoubleSpaceChecker(Checker):
"""Checker for double spaces."""
name = "Double space"
def check_item(self, item: PoItem):
"""Check an item in a `*.po` file."""
for match in re.finditer(
r"(.{0,30})\s\s(.{0,30})", item.msgstr_full_content
):
item.add_warning(
self.name,
f"Double spaces detected between ###{match.group(1)}### "
f"and ###{match.group(2)}###",
)

366
padpo/checkers/glossary.py Normal file
View File

@ -0,0 +1,366 @@
"""Checker for glossary usage."""
import re
from padpo.checkers.baseclass import Checker
from padpo.pofile import PoItem
class GlossaryChecker(Checker):
"""Checker for glossary usage."""
name = "Glossary"
def check_item(self, item: PoItem):
"""Check an item in a `*.po` file."""
if not item.msgstr_full_content:
return # no warning
original_content = item.msgid_rst2txt.lower()
original_content = re.sub(r"« .*? »", "", original_content)
translated_content = item.msgstr_full_content.lower()
for word, translations in glossary.items():
if re.match(fr"\b{word.lower()}\b", original_content):
for translated_word in translations:
if translated_word.lower() in translated_content:
break
else:
possibilities = '"'
possibilities += '", "'.join(translations[:-1])
if len(translations) > 1:
possibilities += '" or "'
possibilities += translations[-1]
possibilities += '"'
item.add_warning(
self.name,
f'Found "{word}" that is not translated in '
f"{possibilities} in ###{item.msgstr_full_content}"
"###.",
)
# https://github.com/python/python-docs-fr/blob/
# 662b4ec48b27daa4fbef05cddc43da0d894b29e7/CONTRIBUTING.rst
glossary = {
"-like": ["-compatible"],
"abstract data type": ["type abstrait"],
"abstract data types": ["types abstraits"],
"argument": ["argument"],
"arguments": ["arguments"],
"backport": ["rétroporter"],
"backslash": ["antislash", "*backslash*"],
"backslashes": ["antislashs", "*backslashes*"],
"backtrace": ["trace d'appels", "trace de pile"],
"backtraces": ["traces d'appels", "traces de pile"],
"big-endian": ["gros-boutiste"],
"bound": ["lié"],
"bug": ["bogue"],
"bugs": ["bogues"],
"built-in": ["native", "natif"],
"built-ins": ["fonctions natives"],
"bytecode": ["code intermédiaire"],
"callback": ["fonction de rappel"],
"callbacks": ["fonctions de rappel"],
"call stack": ["pile d'appels"],
"call stacks": ["piles d'appels"],
"caught": ["interceptée", "interceptées"], # exception
"debugging": ["débogage"],
"deep copy": ["copie récursive", "copie profonde"],
"double quote": ["guillemet"],
"double quotes": ["guillemets"],
"deprecated": ["obsolète"],
"e.g.": ["p. ex.", "par exemple"],
"et al.": ["et autre", "et autres", "et ailleurs"],
"export": ["exporter", "exportation"],
"exports": ["exportations"],
"expression": ["expression"],
"expressions": ["expressions"],
"framework": ["cadriciel"],
"frozen package": ["paquet figé"],
"frozen packages": ["paquets figés"],
"frozen set": ["ensemble figé"],
"frozen sets": ["ensembles figés"],
"garbage collector": ["ramasse-miettes"],
"getter": ["accesseur"],
"getters": ["accesseurs"],
"i.e": ["c.-à-d.", "c'est-à-dire"],
"identifier": ["identifiant"],
"identifiers": ["identifiants"],
"index": ["indice"], # chaînes de caractères
"indexes": ["indices"], # chaînes de caractères
"immutable": ["immuable"],
"import": ["importer", "importation"],
"imports": ["importations"],
"installer": ["installateur"],
"installers": ["installateurs"],
"interpreter": ["interpréteur"],
"interpreters": ["interpréteurs"],
"keyword(?! argument)": ["mot clé"],
"keywords": ["mots clés"],
"keyword argument": ["argument nommé"],
"keyword arguments": ["arguments nommés"],
"library": ["bibliothèque"],
"libraries": ["bibliothèques"],
"list comprehension": ["liste en compréhension"],
"list comprehensions": ["listes en compréhension"],
"little-endian": ["petit-boutiste"],
"mixin type": ["type de mélange"],
"mixin types": ["types de mélange"],
"mutable": ["muable"],
"namespace": ["espace de nommage", "espace de noms"],
"namespaces": ["espaces de nommage", "espaces de noms"],
"parameter": ["paramètre"],
"parameters": ["paramètres"],
"pickle": ["sérialiser"],
"prompt": ["invite"],
"raise": ["lever"],
"raised": ["levé"],
"regular expression": ["expression rationnelle", "expression régulière"],
"regular expressions": [
"expressions rationnelles",
"expressions régulières",
],
"return": ["renvoie"],
"returns": ["renvoie"],
"returned": ["renvoyé", "renvoyée", "renvoyés", "renvoyées"],
"roughly": ["approximativement", "à peu près"],
"setter": ["mutateur"],
"setters": ["mutateurs"],
"simple quote": ["guillemet simple"],
"simple quotes": ["guillemets simples"],
"socket": ["connecteur", "interface de connexion"],
"sockets": ["connecteurs", "interfaces de connexion"],
"specify": ["définir", "préciser"],
"statement": ["instruction"],
"statements": ["instructions"],
"subprocess": ["sous-processus"],
"subprocesses": ["sous-processus"],
"support": [
"prendre en charge",
"prend en charge",
"prennent en charge",
"implémenter",
"implémente",
"implémentent",
],
"thread": ["fil d'exécution"],
"threads": ["fils d'exécution"],
"traceback": ["trace d'appels", "trace de pile"],
"tracebacks": ["traces d'appels", "traces de pile"],
"(?<![-])tuple": ["*n*-uplet"],
"(?<![-])tuples": ["*n*-uplets"],
"2-tuple": ["paire", "couple"],
"3-tuple": ["triplet"],
"4-tuple": ["quadruplet"],
"5-tuple": ["quintuplet"],
"6-tuple": ["sextuplet"],
"7-tuple": ["heptuplet"], # …
"typically": ["normalement", "habituellement", "comme d'habitude"],
"underscore": ["tiret bas", "*underscore*", "sous-tiret"],
"underscores": ["tirets bas", "*underscores*", "sous-tiret"],
"whitespace": ["caractère d'espacement"],
"whitespaces": ["caractères d'espacement"],
}
# https://github.com/python/python-docs-fr/blob/
# 25e6bb0dc12c0c22c1053e5c0861a163a84b9c02/glossary.po
glossary.update(
{
"abstract base class": ["classe de base abstraite"],
"abstract base classes": ["classes de base abstraites"],
"annotation": ["annotation"],
"annotations": ["annotations"],
"asynchronous context manager": ["gestionnaire de contexte asynchrone"],
"asynchronous context managers": ["gestionnaires de contexte asynchrone"],
"asynchronous generator": ["générateur asynchrone"],
"asynchronous generators": ["générateurs asynchrones"],
"asynchronous iterable": ["itérable asynchrone"],
"asynchronous iterables": ["itérables asynchrones"],
"asynchronous": ["asynchrone"],
"attribute": ["attribut"],
"attributes": ["attributs"],
"awaitable": ["*awaitable*"],
"BDFL": ["*BDFL*"],
"binary file": ["fichier binaire"],
"binary files": ["fichiers binaires"],
"bytes-like object": ["objet octet-compatible"],
"bytes-like objects": ["objets octet-compatible"],
"bytecode": ["code intermédiaire", "*bytecode*"],
"class": ["classe"],
"classes": ["classes"],
"class variable": ["variable de classe"],
"class variables": ["variables de classe"],
"coercion": ["coercition"],
"coercions": ["coercitions"],
"complex number": ["nombre complexe"],
"complex numbers": ["nombres complexes"],
"context manager": ["gestionnaire de contexte"],
"context managers": ["gestionnaires de contexte"],
"context variable": ["variable de contexte"],
"context variables": ["variables de contexte"],
"contiguous": ["contigu"],
"coroutine": ["coroutine"],
"coroutines": ["coroutines"],
"CPython": ["CPython"],
"decorator": ["décorateur"],
"decorators": ["décorateurs"],
"descriptor": ["descripteur"],
"descriptors": ["descripteurs"],
"dictionary": ["dictionnaire"],
"dictionaries": ["dictionnaires"],
"dictionary comprehension": [
"dictionnaire en compréhension",
"dictionnaire en intention",
],
"dictionary view": ["vue de dictionnaire"],
"dictionary views": ["vues de dictionnaire"],
"docstring": ["*docstring*", "chaîne de documentation"],
"docstrings": ["*docstrings*", "chaînes de documentation"],
"duck-typing": ["*duck-typing*"],
"extension module": ["module d'extension"],
"extension modules": ["modules d'extension"],
"f-string": ["f-string"],
"f-strings": ["f-strings"],
"file object": ["objet fichier"],
"file objects": ["objets fichier"],
"file-like object": ["objet fichier-compatible"],
"file-like objects": ["objets fichier-compatible"],
"finder": ["chercheur"],
"finders": ["chercheurs"],
"floor division": ["division entière"],
"floor divisions": ["divisions entières"],
"function": ["fonction"],
"functions": ["fonctions"],
"function annotation": ["annotation de fonction"],
"function annotations": ["annotations de fonction"],
"__future__": ["__future__"],
"garbage collection": ["ramasse-miettes"],
"generator": ["générateur", "génératrice"],
"generators": ["générateurs"],
"generator iterator": ["itérateur de générateur"],
"generator iterators": ["iterateurs de générateur"],
"generator expression": ["expression génératrice"],
"generator expressions": ["expressions génératrices"],
"generic function": ["fonction générique"],
"generic functions": ["fonctions génériques"],
"generic type": ["type générique"],
"generic types": ["types génériques"],
"GIL": ["GIL"],
"global interpreter lock": ["verrou global de l'interpréteur"],
"hash-based pyc": ["*pyc* utilisant le hachage"],
"hashable": ["hachable"],
# "IDLE": ["IDLE"], # confusion with "idle"
"immutable": ["immuable"],
"import path": ["chemin des importations"],
"import paths": ["chemins des importations"],
"importing": ["importer", "important", "importation"],
"importer": ["importateur"],
"importers": ["importateurs"],
"interactive": ["interactif", "interactive"],
"interpreted": ["interprété"],
"interpreter shutdown": ["arrêt de l'interpréteur"],
"iterable": ["itérable"],
"iterables": ["itérables"],
"iterator": ["itérateur"],
"iterators": ["itérateurs"],
"key function": ["fonction clé"],
"key functions": ["fonctions clé"],
"keyword argument": ["argument nommé"],
"keyword arguments": ["arguments nommés"],
"lambda": ["lambda"],
"list": ["*list*", "liste"],
"lists": ["listes"],
"list comprehension": ["liste en compréhension", "liste en intention"],
"loader": ["chargeur"],
"loaders": ["chargeurs"],
"magic method": ["méthode magique"],
"magic methods": ["méthodes magiques"],
"mapping": ["tableau de correspondance"],
"mappings": ["tableaux de correspondance"],
"meta path finder": ["chercheur dans les méta-chemins"],
"meta path finders": ["chercheurs dans les méta-chemins"],
"metaclass": ["métaclasse"],
"metaclasses": ["métaclasses"],
"method": ["méthode"],
"methods": ["méthodes"],
"method resolution order": ["ordre de résolution des méthodes"],
"module": ["module"],
"modules": ["modules"],
"module spec": ["spécificateur de module"],
"module specs": ["spécificateurs de module"],
"MRO": ["MRO"],
"mutable": ["muable"],
"named tuple": ["*n*-uplet nommé"],
"named tuples": ["*n*-uplets nommés"],
# "tuple": already in glossary
# "tuples": already in glossary
"namespace": ["espace de nommage", "espace de noms"],
"namespaces": ["espaces de nommage", "espaces de noms"],
"namespace package": ["paquet-espace de nommage"],
"namespace packages": ["paquets-espace de nommage"],
"nested scope": ["portée imbriquée"],
"nested scopes": ["portées imbriquées"],
"new-style class": ["nouvelle classe"],
"new-style classes": ["nouvelles classes"],
"object": ["objet"],
"objects": ["objets"],
"package": ["paquet"],
"packages": ["paquets"],
"parameter": ["paramètre"],
"parameters": ["paramètres"],
"path entry": ["entrée de chemin"],
"path entries": ["entrées de chemin"],
"path entry finder": ["chercheur de chemins"],
"path entry finders": ["chercheurs de chemins"],
"path entry hook": ["point d'entrée pour la recherche dans *path*"],
"path entry hooks": ["points d'entrée pour la recherche dans *path*"],
"path based finder": ["chercheur basé sur les chemins"],
"path based finders": ["chercheurs basés sur les chemins"],
"path-like object": ["objet simili-chemin"],
"path-like objects": ["objets simili-chemin"],
"PEP": ["PEP"],
"portion": ["portion"],
"portions": ["portions"],
"positional argument": ["argument positionnel"],
"positional arguments": ["arguments positionnels"],
"provisional API": ["API provisoire"],
"provisional package": ["paquet provisoire"],
"provisional packages": ["paquets provisoires"],
"pythonic": ["*pythonique*", "*pythoniques*"],
"qualified name": ["nom qualifié"],
"qualified names": ["noms qualifiés"],
"reference count": ["nombre de références"],
"regular package": ["paquet classique"],
"regular packages": ["paquets classiques"],
"__slots__": ["``__slots__``"],
"sequence": ["séquence"],
"sequences": ["séquences"],
"set comprehension": ["ensemble en compréhension", "ensemble en intention"],
"single dispatch": ["distribution simple"],
"slice": ["tranche"],
"slices": ["tranches"],
"special method": ["méthode spéciale"],
"special methods": ["méthodes spéciales"],
"statement": ["instruction"],
"statements": ["instructions"],
"text encoding": ["encodage de texte"],
"text encodings": ["encodages de texte"],
"text file": ["fichier texte"],
"text files": ["fichiers texte"],
"triple quoted string": ["chaîne entre triple guillemets"],
"triple quoted strings": ["chaîne entre triple guillemets"],
"type": ["type"],
"types": ["types"],
"type alias": ["alias de type"],
"type aliases": ["alias de type"],
"type hint": ["indication de type"],
"type hints": ["indications de type"],
"universal newlines": ["retours à la ligne universels"],
"variable annotation": ["annotation de variable"],
"variable annotations": ["annotations de variables"],
"virtual environment": ["environnement virtuel"],
"virtual environments": ["environnements virtuels"],
"virtual machine": ["machine virtuelle"],
"virtual machines": ["machines virtuelles"],
"zen of Python": ["le zen de Python"],
}
)

View File

@ -1,18 +1,21 @@
"""Checker for grammar errors."""
import json
import re
import subprocess
import tempfile
from pathlib import Path
from typing import Set
from zipfile import ZipFile
from typing import Set, Optional
import requests
import simplelogging
from pygrammalecte import (
GrammalecteGrammarMessage,
GrammalecteMessage,
GrammalecteSpellingMessage,
grammalecte_text,
)
from padpo.checkers.baseclass import Checker, replace_quotes
from padpo.pofile import PoItem, PoFile
from padpo.checkers.glossary import glossary
from padpo.pofile import PoFile, PoItem
log = simplelogging.get_logger()
@ -25,151 +28,100 @@ class GrammalecteChecker(Checker):
def __init__(self):
"""Initialiser."""
super().__init__()
self.dir = None
self.personal_dict: Set[str] = set()
self.get_personal_dict()
@staticmethod
def run_grammalecte(filename: str) -> subprocess.CompletedProcess:
return subprocess.run(
[
"grammalecte-cli.py",
"-f",
filename,
"-off",
"apos",
"--json",
"--only_when_errors",
],
capture_output=True,
text=True,
)
def check_file(self, pofile: PoFile):
"""Check a `*.po` file."""
if not isinstance(pofile, PoFile):
log.error("%s is not an instance of PoFile", str(pofile))
_, filename = tempfile.mkstemp(
suffix=".txt", prefix="padpo_", text=True
)
with open(filename, "w", encoding="utf8") as f:
text = pofile.rst2txt()
text = re.sub(r"«\s(.*?)\", replace_quotes, text)
f.write(text)
try:
result = self.run_grammalecte(filename)
except FileNotFoundError as e:
if e.filename == "grammalecte-cli.py":
install_grammalecte()
result = self.run_grammalecte(filename)
if result.stdout:
warnings = json.loads(result.stdout)
self.manage_grammar_errors(warnings, pofile)
self.manage_spelling_errors(warnings, pofile)
Path(filename).unlink()
text = pofile.rst2txt()
text = re.sub(r"«\s(.*?)\", replace_quotes, text)
warnings = grammalecte_text(text)
self.manage_warnings(warnings, pofile)
def check_item(self, item: PoItem):
"""Check an item in a `*.po` file (does nothing)."""
pass
def manage_grammar_errors(self, warnings, pofile: PoFile):
"""Manage grammar errors returned by grammalecte."""
for warning in warnings["data"]:
for error in warning["lGrammarErrors"]:
if self.filter_out_grammar_error(error):
continue
item_index = int(warning["iParagraph"]) // 2
item = pofile.content[item_index]
start = max(0, int(error["nStart"]) - 40)
end = max(0, int(error["nEnd"]) + 10)
item.add_warning(
self.name,
f'{error["sMessage"]} => '
f"###{item.msgstr_rst2txt[start:end]}###",
)
def manage_warnings(self, warnings: GrammalecteMessage, pofile: PoFile) -> None:
"""Manage warnings returned by grammalecte."""
for warning in warnings:
if self.filter_out_grammar_error(warning) or self.filter_out_spelling_error(
warning
):
continue
item_index = warning.line // 2
item = pofile.content[item_index]
start = max(0, warning.start - 40)
end = warning.end + 10
item.add_warning(
self.name,
f"{warning.message} => " f"###{item.msgstr_rst2txt[start:end]}###",
)
def filter_out_grammar_error(self, error):
def filter_out_grammar_error(self, warning: GrammalecteMessage) -> bool:
"""Return True when grammalecte error should be ignored."""
msg = error["sRuleId"]
if msg in (
if not isinstance(warning, GrammalecteGrammarMessage):
return False
if warning.rule in (
"esp_milieu_ligne", # double space
"nbsp_avant_deux_points", # NBSP
"nbsp_avant_double_ponctuation", # NBSP
):
return True
if "typo_guillemets_typographiques_simples" in msg:
if "typo_guillemets_typographiques_simples" in warning.rule:
return True # ignore ' quotes
msg_text = error["sMessage"]
if msg_text in (
if warning.message in (
"Accord de genre erroné : « ABC » est masculin.",
"Accord de genre erroné : « PEP » est masculin.",
"Accord de nombre erroné : « PEP » devrait être au pluriel.",
"Accord de genre erroné : « une entrée » est féminin, « utilisateur » est masculin.",
):
return True
if "Sil sagit dun impératif" in msg_text:
if error["nStart"] == 0:
if "Sil sagit dun impératif" in warning.message:
if warning.start == 0:
# ignore imperative conjugation at begining of 1st sentence
return True
return False
def manage_spelling_errors(self, warnings, pofile: PoFile):
"""Manage spelling errors returned by grammalecte."""
for warning in warnings["data"]:
for error in warning["lSpellingErrors"]:
if self.filter_out_spelling_error(error):
continue
item_index = int(warning["iParagraph"]) // 2
item = pofile.content[item_index]
start = max(0, int(error["nStart"]) - 40)
end = max(0, int(error["nEnd"]) + 10)
word = error["sValue"]
item.add_warning(
self.name,
f'Unknown word "{word}" in '
f"###{item.msgstr_rst2txt[start:end]}###",
)
def filter_out_spelling_error(self, error):
def filter_out_spelling_error(self, warning: GrammalecteMessage) -> bool:
"""Return True when grammalecte error should be ignored."""
word = error["sValue"]
if set(word) == {"x"}:
if not isinstance(warning, GrammalecteSpellingMessage):
return False
if set(warning.word) == {"x"}:
return True # word is xxxxx or xxxxxxxx…
if word.strip() in self.personal_dict:
if warning.word.strip() in self.personal_dict:
return True # white list
if warning.word.endswith("_"):
return True
if warning.word.lower() in glossary:
return True
if warning.word.lower() == "uplet": # partially italic word in glossary
return True
return False
def get_personal_dict(self):
"""
Add spelling white list.
Based on
https://raw.githubusercontent.com/python/python-docs-fr/3.8/dict
"""
download_request = requests.get(
"https://raw.githubusercontent.com/python/python-docs-fr/3.8/dict"
)
download_request.raise_for_status()
for line in download_request.text.splitlines():
def _get_personal_dict(self, dict_path: str) -> None:
if "://" in dict_path:
download_request = requests.get(dict_path)
download_request.raise_for_status()
lines = download_request.text
else:
lines = Path(dict_path).read_text(encoding="UTF-8")
for line in lines.splitlines():
word = line.strip()
self.personal_dict.add(word)
self.personal_dict.add(word.title())
def add_arguments(self, parser):
parser.add_argument(
"--dict",
nargs="*",
dest="dicts",
help="Personal dict files or URLs. Should contain onw word per line.",
)
def install_grammalecte():
"""Install grammalecte CLI."""
log.warning("Missing grammalecte, trying to install it")
tmpdirname = tempfile.mkdtemp(prefix="padpo_grammalecte_")
tmpdirname = Path(tmpdirname)
tmpdirname.mkdir(exist_ok=True)
download_request = requests.get(
"https://grammalecte.net/grammalecte/zip/Grammalecte-fr-v1.5.0.zip"
)
download_request.raise_for_status()
zip_file = tmpdirname / "Grammalecte-fr-v1.5.0.zip"
zip_file.write_bytes(download_request.content)
with ZipFile(zip_file, "r") as zip_obj:
zip_obj.extractall(tmpdirname / "Grammalecte-fr-v1.5.0")
subprocess.run(
["pip", "install", str(tmpdirname / "Grammalecte-fr-v1.5.0")]
)
def configure(self, args):
"""Store the result of parse_args, to get back arguments from self.add_arguments."""
if args.dicts:
for dict_path in args.dicts:
self._get_personal_dict(dict_path)

View File

@ -27,7 +27,8 @@ class NonBreakableSpaceChecker(Checker):
prefix = item.msgstr_rst2txt[match.start(1) : match.end(1)]
suffix = item.msgstr_rst2txt[match.start(3) : match.end(3)]
match = item.msgstr_rst2txt[match.start(2) : match.end(2)]
self.__add_message_space_before(item, prefix, match, suffix)
if prefix[-1] not in ":?!.":
self.__add_message_space_before(item, prefix, match, suffix)
def __add_message(self, item, prefix, match, suffix):
item.add_error(

View File

@ -4,6 +4,9 @@ import tempfile
from pathlib import Path
import requests
import simplelogging
log = simplelogging.get_logger()
class PullRequestInfo:
@ -40,9 +43,7 @@ class PullRequestInfo:
def pull_request_files(pull_request: str):
"""Return pull request information."""
pull_request = pull_request.replace("/pull/", "/pulls/")
request = requests.get(
f"https://api.github.com/repos/{pull_request}/files"
)
request = requests.get(f"https://api.github.com/repos/{pull_request}/files")
request.raise_for_status()
# TODO remove directory at end of execution
temp_dir = tempfile.mkdtemp(prefix="padpo_")
@ -56,5 +57,7 @@ def pull_request_files(pull_request: str):
temp_file_dir = temp_file.parent
temp_file_dir.mkdir(parents=True, exist_ok=True)
temp_file.write_bytes(content_request.content)
pr.add_file(filename, temp_file, fileinfo["patch"])
if "patch" in fileinfo:
# if a patch is provided (patch is small enough)
pr.add_file(filename, temp_file, fileinfo["patch"])
return pr

View File

@ -1,6 +1,7 @@
"""Entry point of padpo."""
import argparse
import pkg_resources
import sys
from pathlib import Path
@ -45,10 +46,20 @@ def check_path(path, pull_request_info=None):
return check_file(path, pull_request_info)
def check_paths(paths, pull_request_info=None):
"""Check a list of paths (`*.po` file or directory)."""
result_errors = []
result_warnings = []
for path in paths:
errors, warnings = check_path(path, pull_request_info)
result_errors.extend(errors)
result_warnings.extend(warnings)
return result_errors, result_warnings
def main():
"""Entry point."""
global log
log = simplelogging.get_logger("__main__")
parser = argparse.ArgumentParser(description="Linter for *.po files.")
parser.add_argument("-v", "--verbose", action="count", default=0)
@ -59,7 +70,10 @@ def main():
metavar="PATH",
type=str,
help="path of the file or directory to check",
default="",
default=[],
# allow the user to provide no path at all,
# this helps writing scripts
nargs="*",
)
files.add_argument(
"-g",
@ -77,7 +91,28 @@ def main():
help="ID of pull request in python-docs-fr repository",
default=0,
)
files.add_argument("--version", action="store_true", help="Return version")
parser.add_argument("-c", "--color", action="store_true", help="color output")
for checker in checkers:
checker.add_arguments(parser)
args = parser.parse_args()
if args.version:
print(pkg_resources.get_distribution("padpo").version)
sys.exit(0)
if args.color:
console_format = (
"%(log_color)s[%(levelname)-8s]%(reset)s "
"%(green)s%(pofile)s:%(poline)s: "
"%(cyan)s[%(checker)s] %(message)s%(reset)s"
)
else:
console_format = "%(pofile)s:%(poline)s: %(leveldesc)s: %(message)s"
log = simplelogging.get_logger("__main__", console_format=console_format)
if args.verbose < 1:
log.reduced_logging()
elif args.verbose < 2:
@ -85,18 +120,21 @@ def main():
else:
log.full_logging()
if args.input_path:
path = args.input_path
pull_request_info = None
else:
if args.github or args.python_docs_fr:
pull_request = ""
if args.github:
pull_request = args.github
if args.python_docs_fr:
pull_request = f"python/python-docs-fr/pull/{args.python_docs_fr}"
pull_request_info = pull_request_files(pull_request)
path = pull_request_info.download_directory
path = [pull_request_info.download_directory]
else:
path = args.input_path
pull_request_info = None
errors, warnings = check_path(path, pull_request_info=pull_request_info)
for checker in checkers:
checker.configure(args)
errors, warnings = check_paths(path, pull_request_info=pull_request_info)
if errors:
sys.exit(1)

View File

@ -82,15 +82,11 @@ class PoItem:
text = re.sub(r"::", r":", text)
text = re.sub(r"``(.*?)``", r"« \1 »", text)
text = re.sub(r"\"(.*?)\"", r"« \1 »", text)
text = re.sub(r":pep:`(.*?)`", r"PEP \1", text)
text = re.sub(r":[a-z:]+:`(.+?)`", r"« \1 »", text)
text = re.sub(r":[Pp][Ee][Pp]:`(.*?)`", r"PEP \1", text)
text = re.sub(r":[a-zA-Z:]+:`(.+?)`", r"« \1 »", text)
text = re.sub(r"\*\*(.*?)\*\*", r"« \1 »", text)
text = re.sub(
r"\*(.*?)\*", r"« \1 »", text
) # TODO sauf si déjà entre «»
text = re.sub(
r"`(.*?)\s*<((?:http|https|ftp)://.*?)>`_", r"\1 (« \2 »)", text
)
text = re.sub(r"\*(.*?)\*", r"« \1 »", text) # TODO sauf si déjà entre «»
text = re.sub(r"`(.*?)\s*<((?:http|https|ftp)://.*?)>`_", r"\1 (« \2 »)", text)
text = re.sub(r"<((?:http|https|ftp)://.*?)>", r"« \1 »", text)
return text
@ -146,14 +142,28 @@ class PoFile:
for item in self.content:
if not item.inside_pull_request:
continue
prefix = f"{self.path}:{item.lineno_start:-4} %s"
log.debug(prefix, "")
for message in item.warnings:
if isinstance(message, Error):
log.error(prefix, message)
log.error(
message.text,
extra={
"pofile": self.path,
"poline": item.lineno_start,
"checker": message.checker_name,
"leveldesc": "error",
},
)
errors.append(message)
elif isinstance(message, Warning):
log.warning(prefix, message)
log.warning(
message.text,
extra={
"pofile": self.path,
"poline": item.lineno_start,
"checker": message.checker_name,
"leveldesc": "warning",
},
)
warnings.append(message)
return errors, warnings

1712
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +1,60 @@
[tool.poetry]
name = "padpo"
version = "0.5.0"
version = "0.11.0"
description = "Linter for gettext files"
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Operating System :: OS Independent"
# TODO: Add more
]
authors = ["Vincent Poulailleau <vpoulailleau@gmail.com>"]
readme = "README.md"
repository = "https://github.com/vpoulailleau/padpo"
homepage = "https://github.com/vpoulailleau/padpo"
documentation = "https://github.com/vpoulailleau/padpo"
keywords = ["gettext", "linter"]
repository = "https://github.com/AFPy/padpo"
homepage = "https://github.com/AFPy/padpo"
documentation = "https://github.com/AFPy/padpo"
keywords = ["gettext", "linter", "grammalecte"]
license = "BSD-3-Clause"
include = ["padpo/**/*.py"]
[tool.poetry.dependencies]
python = "^3.7"
requests = "2.22.0"
simplelogging = "0.10.0"
wheel = "0.33.6" # for grammalecte
setuptools = "42.0.2" # for grammalecte
pygrammalecte = "^1.3.0"
requests = "^2.20.0"
simplelogging = ">=0.10,<0.12"
[tool.poetry.dev-dependencies]
pytest = "5.3.1"
pytest-cov = "2.8.1"
tox = "3.14.1"
twine = "2.0.0"
python-dev-tools = {version = ">=2020.9.4", python = ">=3.7,<4.0"}
[tool.poetry.scripts]
padpo = "padpo.padpo:main"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
[tool.tox]
legacy_tox_ini = """
[tox]
isolated_build = True
envlist = py37, py38
envlist = py37, py38, py39
[testenv]
whitelist_externals = poetry
whitelist_externals =
poetry
echo
sed
cp
changedir = {toxinidir}/tests
commands =
poetry install -v
# poetry run pytest -s -vv --cov={envsitepackagesdir}/padpo
poetry run pytest -s -vv --cov=padpo
"""
poetry run coverage xml
echo 'fix travis bug'
sed --in-place -e 's#//home#/home#g' coverage.xml
echo 'fix codeclimate bug, use relative path'
sed --in-place -e 's#/home.*vpoulailleau/padpo/##g' coverage.xml
cp coverage.xml ../coverage.xml
"""
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 663 KiB

After

Width:  |  Height:  |  Size: 645 KiB

View File

@ -0,0 +1,5 @@
#: ../Doc/library/typing.rst:19
msgid ""
"tuple"
msgstr ""
"n-uplet"

View File

@ -0,0 +1,5 @@
#: ../Doc/library/typing.rst:19
msgid ""
"internal_link_"
msgstr ""
"cemotnexistepas_"

View File

@ -0,0 +1,9 @@
#: ../Doc/library/typing.rst:19
msgid ""
"Non breakable space.::"
msgstr ""
"Espace insécable. ::"
" Espace insécable ! ::"
" Espace insécable ? ::"
" Oui !"
" Non ?"

View File

@ -0,0 +1,5 @@
#: ../Doc/library/typing.rst:19
msgid ""
"tuple"
msgstr ""
"*n*-uplet"

35
tests/test_po_warnings.py Normal file
View File

@ -0,0 +1,35 @@
"""Test known to be good files."""
from pathlib import Path
from padpo.padpo import check_file
def pytest_generate_tests(metafunc):
"""Parametrize tests according to po_without_warnings directory content."""
if "known_good_po_file" in metafunc.fixturenames:
# assume using tox (that cd into tests directory)
files = list(Path("./po_without_warnings").rglob("*.po"))
files.extend(Path("./tests/po_without_warnings").rglob("*.po"))
metafunc.parametrize(
"known_good_po_file", [str(file_path) for file_path in files]
)
if "known_bad_po_file" in metafunc.fixturenames:
# assume using tox (that cd into tests directory)
files = list(Path("./po_with_warnings").rglob("*.po"))
files.extend(Path("./tests/po_with_warnings").rglob("*.po"))
metafunc.parametrize(
"known_bad_po_file", [str(file_path) for file_path in files]
)
def test_no_error_no_warning(known_good_po_file):
"""Test known to be good files."""
errors, warnings = check_file(known_good_po_file)
assert not errors
assert not warnings
def test_error_or_warning(known_bad_po_file):
"""Test known to be bad files."""
errors, warnings = check_file(known_bad_po_file)
assert errors or warnings

View File

@ -1,22 +0,0 @@
"""Test known to be good files."""
from pathlib import Path
from padpo.padpo import check_file
def pytest_generate_tests(metafunc):
"""Parametrize tests according to po_without_warnings directory content."""
if "known_good_po_file" in metafunc.fixturenames:
# assume using tox (that cd into tests directory)
directory = Path("./po_without_warnings")
files = directory.rglob("*.po")
metafunc.parametrize(
"known_good_po_file", [str(file) for file in files]
)
def test_no_error_no_warning(known_good_po_file):
"""Test known to be good files."""
errors, warnings = check_file(known_good_po_file)
assert not errors
assert not warnings