-
Notifications
You must be signed in to change notification settings - Fork 93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow CLI selection of a single rule to check. #6120
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Fix a bug causing ``cylc lint`` to exit with traceback if given an empty file. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Allow checking of individual ``cylc lint`` checks using ``--rule`` (or ``-R``) |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -78,7 +78,7 @@ | |||||||||||||
from cylc.flow import LOG | ||||||||||||||
from cylc.flow.exceptions import CylcError | ||||||||||||||
import cylc.flow.flags | ||||||||||||||
from cylc.flow.loggingutil import set_timestamps | ||||||||||||||
from cylc.flow.loggingutil import set_timestamps, bullet_list | ||||||||||||||
from cylc.flow.option_parsers import ( | ||||||||||||||
CylcOptionParser as COP, | ||||||||||||||
WORKFLOW_ID_OR_PATH_ARG_DOC | ||||||||||||||
|
@@ -1144,7 +1144,10 @@ | |||||||||||||
""" | ||||||||||||||
# get the first line | ||||||||||||||
line_no = 1 | ||||||||||||||
line = next(lines) | ||||||||||||||
try: | ||||||||||||||
line = next(lines) | ||||||||||||||
except StopIteration: | ||||||||||||||
return | ||||||||||||||
# check if it is a jinja2 shebang | ||||||||||||||
jinja_shebang = line.strip().lower() == JINJA2_SHEBANG | ||||||||||||||
|
||||||||||||||
|
@@ -1340,6 +1343,13 @@ | |||||||||||||
action='store_true', | ||||||||||||||
default=False, | ||||||||||||||
) | ||||||||||||||
parser.add_option( | ||||||||||||||
'--rule', '-R', | ||||||||||||||
help='Carry out only specific checks.', | ||||||||||||||
choices=list(parse_checks(['style', '728']).keys()), | ||||||||||||||
action='append', | ||||||||||||||
default=[], | ||||||||||||||
) | ||||||||||||||
parser.add_option( | ||||||||||||||
'--ruleset', '-r', | ||||||||||||||
help=( | ||||||||||||||
|
@@ -1390,6 +1400,13 @@ | |||||||||||||
print(get_reference(options.ruleset, 'text')) | ||||||||||||||
sys.exit(0) | ||||||||||||||
|
||||||||||||||
if options.rule and options.ruleset: | ||||||||||||||
LOG.error('arguments --rule and --ruleset are mutually exclusive') | ||||||||||||||
sys.exit(1) | ||||||||||||||
if options.rule and options.ignores: | ||||||||||||||
LOG.error('arguments --rule and --ignores are mutually exclusive') | ||||||||||||||
sys.exit(1) | ||||||||||||||
|
||||||||||||||
# If target not given assume we are looking at PWD: | ||||||||||||||
if target is None: | ||||||||||||||
target = str(Path.cwd()) | ||||||||||||||
|
@@ -1417,6 +1434,14 @@ | |||||||||||||
max_line_len=mergedopts[MAX_LINE_LENGTH] | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
if options.rule: | ||||||||||||||
checks = {k: v for k, v in checks.items() if k in options.rule} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can be simplified to: checks = {rule: checks[rule] for rule in options.rule} Which has the advantage of raising an error if a rule does not exist.
Suggested change
However, it looks like I feel we should implement this more like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rule can apply multiple rules: cylc lint /var/tmp/cylc-src/mean.marble -R S001 -R S002
A legit point - I'll have a poke and see if I can do that without having two CLI args. |
||||||||||||||
LOG.warning(bullet_list( | ||||||||||||||
[f'{k}: {v["short"]}' for k, v in checks.items()], | ||||||||||||||
header='Checking only:', | ||||||||||||||
singular_header='Checking only {}') | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
# Check each file matching a pattern: | ||||||||||||||
counter: Dict[str, int] = {} | ||||||||||||||
for file in get_cylc_files(target, mergedopts[EXCLUDE]): | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#!/usr/bin/env python3 | ||
# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE. | ||
# Copyright (C) NIWA & British Crown (Met Office) & Contributors. | ||
# | ||
# 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 <http://www.gnu.org/licenses/>. | ||
"""Tests `cylc lint` CLI Utility. | ||
|
||
TODO: Consider tests in unit test file for movement to here. | ||
""" | ||
|
||
import pytest | ||
|
||
from cylc.flow.scripts.lint import main, get_option_parser | ||
|
||
cylc_lint = main.__wrapped__ | ||
|
||
|
||
@pytest.fixture | ||
def setup(): | ||
parser = get_option_parser() | ||
options = parser.get_default_values() | ||
return parser, options | ||
|
||
|
||
def test_lint_empty_file(tmp_path, setup, caplog): | ||
"""Argument --rule is mutually exclusive of either --ignores | ||
and --ruleset. | ||
""" | ||
(tmp_path / 'flow.cylc').touch() | ||
parser, options = setup | ||
with pytest.raises(SystemExit, match="False"): | ||
main.__wrapped__(parser, options, str(tmp_path)) | ||
|
||
|
||
def test_mutually_exclusive_args(tmp_path, setup): | ||
"""Argument --rule is mutually exclusive of either --ignores | ||
and --ruleset. | ||
""" | ||
(tmp_path / 'flow.cylc').write_text('[hi]') | ||
parser, options = setup | ||
options.rule = ['S002'] | ||
|
||
# Rule + Ruleset | ||
options.ruleset = '728' | ||
with pytest.raises(SystemExit, match="1"): | ||
main.__wrapped__(parser, options, str(tmp_path)) | ||
|
||
# Rule + Ignores | ||
options.ignores = ['S005'] | ||
options.ruleset = '' | ||
with pytest.raises(SystemExit, match="1"): | ||
main.__wrapped__(parser, options, str(tmp_path)) | ||
|
||
|
||
def test_single_check_cli(setup, tmp_path, caplog): | ||
"""Demo that CLI --rule option works. | ||
""" | ||
(tmp_path / 'flow.cylc').write_text(' [meta]') | ||
parser, options = setup | ||
|
||
# This rule should cause a failure: | ||
options.rule = ['S003'] | ||
with pytest.raises(SystemExit, match="True"): | ||
main.__wrapped__(parser, options, str(tmp_path)) | ||
assert ( | ||
'Checking only S003: Top level sections should' | ||
' not be indented.' | ||
) in caplog.messages | ||
|
||
# This rule should NOT cause a failure: | ||
options.ruleset = '' | ||
options.rule = ['S001'] | ||
with pytest.raises(SystemExit, match="False"): | ||
main.__wrapped__(parser, options, str(tmp_path)) | ||
assert ( | ||
'Checking only S001: Use multiple spaces, not tabs' | ||
) in caplog.messages |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So
--rule
can be one ofstyle
or728
.AND
--ruleset
can be one ofstyle
,728
orall
.What's the point of
--rule
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
--select
/--ignore
interface ofruff
is a good gauge of how to go about implementing rule selection.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No - rulest can be any of the items in the parsed checks for both rulesets currently available because
Side effect of this is that the safety mechanism (try/except) is completely redundant.