Skip to content

Commit 0dc9dc3

Browse files
amurekicodingjoe
andauthored
Fix #23 -- implement output message template format (#29)
Inspired by pylint: https://pylint.pycqa.org/en/latest/user_guide/usage/output.html#custom-message-formats Co-authored-by: Johannes Maron <[email protected]>
1 parent 3c5a78c commit 0dc9dc3

File tree

5 files changed

+61
-19
lines changed

5 files changed

+61
-19
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,29 @@ option `--diff [-d]` or `--git-diff [-g]`:
5454
git diff --unified=0 | relint my_file.py --diff
5555
```
5656

57+
### Custom message format
58+
59+
Customize the output message format with the `--msg-template=<format string>` option.
60+
[Python format syntax](https://docs.python.org/3/library/string.html#formatstrings)
61+
is suported for the message template and the following fields are available:
62+
63+
* filename
64+
65+
The name of the file being linted.
66+
67+
* line_no
68+
69+
The line number of the match.
70+
71+
* match
72+
73+
The matched text.
74+
75+
* test.*
76+
77+
Any attribute of the test rule, e.g. `test.name` or `test.hint`.
78+
79+
5780
### pre-commit
5881

5982
You can automate the linting process by adding a

relint/__main__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ def parse_args(args):
4242
parser.add_argument(
4343
"-W", "--fail-warnings", action="store_true", help="Fail for warnings."
4444
)
45+
parser.add_argument(
46+
"--msg-template",
47+
metavar="MSG_TEMPLATE",
48+
type=str,
49+
default="{filename}:{line_no} {test.name}\nHint: {test.hint}\n{match}",
50+
help="Template used to display messages. "
51+
r"Default: {filename}:{line_no} {test.name}\nHint: {test.hint}\n{match}",
52+
)
4553
return parser.parse_args(args=args)
4654

4755

@@ -73,7 +81,7 @@ def main(args=sys.argv[1:]):
7381
changed_content = parse_diff(output)
7482
matches = match_with_diff_changes(changed_content, matches)
7583

76-
exit_code = print_culprits(matches)
84+
exit_code = print_culprits(matches, args.msg_template)
7785
exit(exit_code)
7886

7987

relint/parse.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def split_diff_content_by_filename(output: str) -> {str: str}:
8282
return content_by_filename
8383

8484

85-
def print_culprits(matches):
85+
def print_culprits(matches, msg_template):
8686
exit_code = 0
8787
_filename = ""
8888
lines = []
@@ -96,24 +96,23 @@ def print_culprits(matches):
9696

9797
start_line_no = match.string[: match.start()].count("\n")
9898
end_line_no = match.string[: match.end()].count("\n")
99-
output_format = "{filename}:{line_no} {test.name}"
100-
print(
101-
output_format.format(
102-
filename=filename,
103-
line_no=start_line_no + 1,
104-
test=test,
105-
)
106-
)
107-
if test.hint:
108-
print("Hint:", test.hint)
10999
match_lines = (
110100
"{line_no}> {code_line}".format(
111101
line_no=no + start_line_no + 1,
112-
code_line=line,
102+
code_line=line.lstrip(),
113103
)
114104
for no, line in enumerate(lines[start_line_no : end_line_no + 1])
115105
)
116-
print(*match_lines, sep="\n")
106+
# special characters from shell are escaped
107+
msg_template = msg_template.replace("\\n", "\n")
108+
print(
109+
msg_template.format(
110+
filename=filename,
111+
line_no=start_line_no + 1,
112+
test=test,
113+
match=chr(10).join(match_lines),
114+
)
115+
)
117116

118117
return exit_code
119118

tests/fixtures/.relint.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,3 @@
33
hint: Get it done right away!
44
filePattern: ^(?!.*test_).*\.(py|js)$
55
error: false
6-
7-
- name: Not Unicode
8-
pattern: '[tT][oO][dD][oO]'
9-
filePattern: .*
10-
error: false

tests/test_main.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,23 @@ def test_main_execution(self, filename, tmpdir, fixture_dir):
2828

2929
assert exc_info.value.code == 0
3030

31+
def test_main_execution_with_custom_template(self, capsys, tmpdir, fixture_dir):
32+
with (fixture_dir / ".relint.yml").open() as fs:
33+
config = fs.read()
34+
tmpdir.join(".relint.yml").write(config)
35+
tmpdir.join("dummy.py").write("# TODO do something")
36+
37+
with tmpdir.as_cwd():
38+
with pytest.raises(SystemExit) as exc_info:
39+
template = r"😵{filename}:{line_no} | {test.name} \n {match}"
40+
main(["relint.py", "dummy.py", "--msg-template", template])
41+
42+
expected_message = "😵dummy.py:1 | No ToDo \n" " 1> # TODO do something\n"
43+
44+
out, _ = capsys.readouterr()
45+
assert expected_message == out
46+
assert exc_info.value.code == 0
47+
3148
@pytest.mark.parametrize("filename", ["test_parse.py", "[a-b].py", "[b-a].py"])
3249
def test_raise_for_warnings(self, filename, tmpdir, fixture_dir):
3350
with (fixture_dir / ".relint.yml").open() as fs:

0 commit comments

Comments
 (0)