Skip to content

Commit 6ed33db

Browse files
committed
Initial commit
0 parents  commit 6ed33db

File tree

10 files changed

+449
-0
lines changed

10 files changed

+449
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# https://github.com/marketplace/actions/changelog-reader#example-workflow---create-a-release-from-changelog
2+
on:
3+
push:
4+
tags:
5+
- "v*"
6+
7+
name: Create GitHub Release
8+
9+
jobs:
10+
build:
11+
name: Parse release notes from changelog and create a GitHub release
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: write
15+
steps:
16+
- name: Get version from tag
17+
id: tag_name
18+
run: |
19+
echo ::set-output name=current_version::${GITHUB_REF#refs/tags/v}
20+
shell: bash
21+
- name: Checkout code
22+
uses: actions/checkout@v3
23+
- name: Get Changelog Entry
24+
id: changelog_reader
25+
uses: mindsers/changelog-reader-action@v2
26+
with:
27+
version: ${{ steps.tag_name.outputs.current_version }}
28+
path: ./CHANGELOG.md
29+
- name: Create/update release
30+
uses: ncipollo/release-action@v1
31+
with:
32+
name: Release v${{ steps.changelog_reader.outputs.version }}
33+
body: ${{ steps.changelog_reader.outputs.changes }}
34+
allowUpdates: true
35+
token: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# Distribution / packaging
7+
.Python
8+
env/
9+
build/
10+
develop-eggs/
11+
dist/
12+
downloads/
13+
eggs/
14+
.eggs/
15+
lib/
16+
lib64/
17+
parts/
18+
sdist/
19+
var/
20+
wheels/
21+
*.egg-info/
22+
.installed.cfg
23+
*.egg
24+
25+
# Unit test / coverage reports
26+
htmlcov/
27+
.tox/
28+
.coverage
29+
.coverage.*
30+
.cache
31+
nosetests.xml
32+
coverage.xml
33+
*.cover
34+
.pytest_cache/
35+
36+
# Sphinx documentation
37+
docs/_build/
38+
39+
# virtualenv
40+
.venv
41+
venv/
42+
ENV/
43+
44+
# IDE settings
45+
.vscode/
46+
.idea/

.pre-commit-config.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
exclude: '^docs/|\.tox|\.git|venv|^dist'
2+
3+
repos:
4+
- repo: https://github.com/pre-commit/pre-commit-hooks
5+
rev: v4.4.0
6+
hooks:
7+
- id: trailing-whitespace
8+
- id: end-of-file-fixer
9+
- id: check-yaml
10+
11+
- repo: https://github.com/Zac-HD/shed
12+
rev: 0.10.9
13+
hooks:
14+
- id: shed
15+
additional_dependencies: [ 'black~=23.1' ]
16+
types_or: [python, pyi, markdown, rst]

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6+
7+
## [UNRELEASED]
8+
### Added
9+
10+
- Customize error messages emitted by flake8 plugins

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2023 Ghazi Abbassi
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# flake8-custom-error-messages
2+
3+
A [flake8](https://github.com/pycqa/flake8) plugin to customize the error messages emitted by other flake8 plugins
4+
5+
## Installation
6+
7+
Install with `pip`
8+
9+
```shell
10+
pip install flake8-custom-error-messages
11+
```
12+
13+
## Configuration Options
14+
15+
The package has one configuration option `--custom-error-messages "<code> [<new_msg>" "<code> <new_msg>", ]` that
16+
can have multiple values. Each one of them should start with the error code followed by a space and after that
17+
comes the new message to be displayed. You can also interpolate the original message as well.
18+
19+
```shell
20+
flake8 some_file.py --format=custom_error_messages --custom-error-messages "TST000 {original_message} For more info,\
21+
check the styleguide at https://some-domain.com" "DTZ005 Use django.utils.timezone.now() instead of datetime.now()"
22+
```
23+
24+
Given that you might be overriding multiple error messages, adding that to a configuration file is better than
25+
specifying them as part of the flake8 command. Each message should be in its own line (messages cannot span
26+
multiple lines).
27+
```ini
28+
# setup.cfg or tox.ini or .flake8
29+
[flake8]
30+
format = custom_error_messages
31+
custom-error-messages =
32+
TST000 {original_message} For more info, check the styleguide at https://some-domain.com
33+
DTZ005 Use django.utils.timezone.now() instead of datetime.now()
34+
```
35+
36+
## Motivation
37+
38+
As a project grows and based on the libraries or framework it uses, it makes sense to change the default error
39+
messages emitted by flake8 plugins to recommend best practices followed by the project in question rather than
40+
a generic message. For example, adding a link to a style guide or best practices document adopted by the project.
41+
Another example is when a framework provides helpers to better deal with the issue in question: instead of the
42+
default messages emitted by [flake8-datetimez](https://github.com/pjknkda/flake8-datetimez#list-of-warnings)
43+
and when the project already relies on Django, it's better to recommend using helpers from the `django.utils.timezone`
44+
module.
45+
46+
## Usage with pre-commit
47+
48+
```yaml
49+
repos:
50+
- repo: https://github.com/pycqa/flake8
51+
rev: '6.0.0'
52+
hooks:
53+
- id: flake8
54+
args: [
55+
--format,
56+
'custom_error_messages',
57+
--custom-error-messages,
58+
'DTZ005 Use django.utils.timezone.now() instead of datetime.now()',
59+
'TST000 {original_message} For more info, check the styleguide at https://some-domain.com'
60+
]
61+
additional_dependencies: [ "flake8-custom-error-messages==0.0.1" ]
62+
```
63+
64+
## License
65+
66+
This project is [MIT licensed](LICENSE).
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""A flake8 plugin to customize the error messages emitted by other flake8 plugins"""
2+
3+
__version__ = "0.0.1"
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
from dataclasses import dataclass
2+
3+
from flake8.formatting.default import Default
4+
from flake8.options.manager import OptionManager
5+
from flake8.violation import Violation
6+
7+
8+
class Formatter(Default):
9+
def format(self, error: Violation):
10+
custom_error = self.get_custom_error(error.code)
11+
if custom_error:
12+
# interpolate the original error message in case it is specified in
13+
# the custom message.
14+
custom_text = custom_error.text.format(original_message=error.text)
15+
new_error = Violation(
16+
error.code,
17+
error.filename,
18+
error.line_number,
19+
error.column_number,
20+
custom_text,
21+
error.physical_line,
22+
)
23+
return super().format(new_error)
24+
else:
25+
return super().format(error)
26+
27+
def get_custom_error(self, code: str) -> "CustomError":
28+
for custom_error in self.options.error_messages:
29+
if code.startswith(custom_error.code):
30+
return custom_error
31+
32+
def after_init(self):
33+
pass
34+
35+
36+
class Plugin:
37+
name = "flake8-custom-error-messages"
38+
version = "0.0.1"
39+
40+
def __init__(self, tree):
41+
self.tree = tree
42+
43+
def run(self):
44+
return # because the package doesn't add any rules
45+
# yield added so python considers the run method as returning a generator
46+
# even though it's unreachable
47+
yield # noqa
48+
49+
@classmethod
50+
def add_options(cls, options_manager: OptionManager):
51+
options_manager.add_option(
52+
"--custom-error-messages",
53+
dest="error_messages",
54+
type=str,
55+
parse_from_config=True,
56+
help=(
57+
"Specify a custom error message for an error code. The message "
58+
"should start with the error code in question followed by a "
59+
"space and then the custom message."
60+
),
61+
metavar="MSG",
62+
nargs="+",
63+
)
64+
65+
@classmethod
66+
def parse_options(cls, options):
67+
if not options.error_messages:
68+
msgs = []
69+
elif isinstance(options.error_messages, str):
70+
# parsed from the config file, so we need to convert it to a list
71+
# flake8 does not support parsing values containing spaces from config
72+
# files
73+
msgs = [
74+
line.strip()
75+
for line in options.error_messages.splitlines()
76+
if line.strip()
77+
]
78+
else:
79+
# when parsed from the command line, the value is already a list
80+
msgs = options.error_messages
81+
82+
# split the error code from the custom error message
83+
errors = []
84+
for msg in msgs:
85+
code = msg.split()[0]
86+
text = msg.replace(code, "", 1).strip()
87+
errors.append(CustomError(code, text))
88+
cls.error_messages = errors
89+
90+
91+
@dataclass
92+
class CustomError:
93+
code: str
94+
text: str

pyproject.toml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
[build-system]
2+
requires = ["flit_core >=3.2,<4"]
3+
build-backend = "flit_core.buildapi"
4+
5+
[project]
6+
name = "flake8-custom-error-messages"
7+
keywords = [
8+
"flake8",
9+
"flake8 plugin",
10+
"custom error messages",
11+
]
12+
authors = [{ name = "Ghazi Abbassi" }]
13+
license = { file = "LICENSE" }
14+
readme = "README.md"
15+
classifiers = [
16+
"Intended Audience :: Developers",
17+
"License :: OSI Approved :: MIT License",
18+
"Programming Language :: Python :: 3",
19+
"Programming Language :: Python :: 3.8",
20+
"Programming Language :: Python :: 3.9",
21+
"Programming Language :: Python :: 3.10",
22+
"Programming Language :: Python :: 3.11",
23+
]
24+
dynamic = ["version", "description"]
25+
requires-python = ">=3.8"
26+
dependencies = ["flake8"]
27+
28+
[project.optional-dependencies]
29+
dev = ["pre-commit"]
30+
release = ["flit", "keyring", "tbump"]
31+
32+
[project.urls]
33+
Code = "https://github.com/ghazi-git/flake8-custom-error-messages"
34+
Issues = "https://github.com/ghazi-git/flake8-custom-error-messages/issues"
35+
Changelog = "https://github.com/ghazi-git/flake8-custom-error-messages/releases"
36+
37+
[project.entry-points."flake8.extension"]
38+
CEM = "flake8_custom_error_messages.plugin:Plugin"
39+
40+
[project.entry-points."flake8.report"]
41+
custom_error_messages = "flake8_custom_error_messages.plugin:Formatter"
42+
43+
[tool.flit.module]
44+
name = "flake8_custom_error_messages"
45+
46+
[tool.tbump]
47+
48+
[tool.tbump.version]
49+
current = "0.0.1"
50+
regex = '''
51+
(?P<major>\d+)
52+
\.
53+
(?P<minor>\d+)
54+
\.
55+
(?P<patch>\d+)
56+
'''
57+
58+
[tool.tbump.git]
59+
message_template = "Bump to {new_version}"
60+
tag_template = "v{new_version}"
61+
62+
[[tool.tbump.file]]
63+
src = "flake8_custom_error_messages/__init__.py"
64+
search = '__version__ = "{current_version}"'
65+
66+
[[tool.tbump.file]]
67+
src = "flake8_custom_error_messages/plugin.py"
68+
search = 'version = "{current_version}"'
69+
70+
[[tool.tbump.file]]
71+
src = "README.md"
72+
search = 'flake8-custom-error-messages=={current_version}'
73+
74+
[[tool.tbump.before_commit]]
75+
name = "Update the changelog"
76+
cmd = "python release/update_changelog.py --new-version {new_version}"
77+
78+
[[tool.tbump.after_push]]
79+
name = "Publish to PyPI"
80+
cmd = "flit publish"

0 commit comments

Comments
 (0)