Skip to content
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

Lint packaged workflows #5873

Merged
merged 6 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changes.d/5873.feat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Allow use of `#noqa: S001` comments to skip Cylc lint
checks for a single line.
- Stop Cylc lint objecting to `%include <file>` syntax.
1 change: 1 addition & 0 deletions cylc/flow/etc/tutorial/consolidation-tutorial/.validate
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@

set -eux

cylc lint .
cylc validate . --icp=2000
1 change: 1 addition & 0 deletions cylc/flow/etc/tutorial/cylc-forecasting-workflow/.validate
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
set -eux
APIKEY="$(head --lines 1 ../api-keys)"
FLOW_NAME="$(< /dev/urandom tr -dc A-Za-z | head -c6)"
cylc lint .
cylc install --workflow-name "$FLOW_NAME" --no-run-name
sed -i "s/DATAPOINT_API_KEY/$APIKEY/" "$HOME/cylc-run/$FLOW_NAME/flow.cylc"
cylc validate --check-circular --icp=2000 "$FLOW_NAME"
Expand Down
1 change: 1 addition & 0 deletions cylc/flow/etc/tutorial/retries-tutorial/.validate
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@

set -eux

cylc lint .
cylc validate --check-circular . --icp=2000
4 changes: 2 additions & 2 deletions cylc/flow/etc/tutorial/retries-tutorial/flow.cylc
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
DIE_2=$((RANDOM%6 + 1))
echo "Rolled $DIE_1 and $DIE_2..."
if (($DIE_1 == $DIE_2)); then
echo "doubles!"
echo "doubles!"
else
exit 1
exit 1
fi
"""
1 change: 1 addition & 0 deletions cylc/flow/etc/tutorial/runtime-introduction/.validate
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
set -eux
FLOW_NAME="$(< /dev/urandom tr -dc A-Za-z | head -c6)"
SRC=$(cylc get-resources tutorial 2>&1 | head -n1 | awk '{print $NF}')
cylc lint .
cylc install "${SRC}/runtime-introduction" --workflow-name "$FLOW_NAME" --no-run-name
cylc validate --check-circular --icp=2000 "$FLOW_NAME"
cylc play --no-detach --abort-if-any-task-fails "$FLOW_NAME"
Expand Down
54 changes: 48 additions & 6 deletions cylc/flow/scripts/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
This can be overridden by providing the "--exit-zero" flag.
"""

NOQA = """
Individual errors can be ignored using the ``# noqa`` line comment.
It is good practice to specify specific errors you wish to ignore using
``# noqa: S002 S007 U999``
"""

TOMLDOC = """
pyproject.toml configuration:{}
[cylc-lint] # any of {}
Expand Down Expand Up @@ -323,7 +329,7 @@ def check_indentation(line: str) -> bool:
'short': 'Item not indented.',
# Non-indented items should be sections:
'url': STYLE_GUIDE + 'indentation',
FUNCTION: re.compile(r'^[^\{\[|\s]').findall
FUNCTION: re.compile(r'^[^%\{\[|\s]').findall
},
"S003": {
'short': 'Top level sections should not be indented.',
Expand Down Expand Up @@ -960,6 +966,33 @@ def check_cylc_file(
pass


def no_qa(line: str, index: str):
"""This line has a no-qa comment.

Examples:
# No comment, no exception:
>>> no_qa('foo = bar', 'S001')
False

# Comment, no error codes, no checking:
>>> no_qa('foo = bar # noqa', 'S001')
True

# Comment, no relevent error codes, no checking:
>>> no_qa('foo = bar # noqa: S999, 997', 'S001')
False

# Comment, relevent error codes, checking:
>>> no_qa('foo = bar # noqa: S001 S003', 'S001')
True
"""
NOQA = re.compile(r'.*#\s*[Nn][Oo][Qq][Aa]:?(.*)')
noqa = NOQA.findall(line)
if noqa and (noqa[0] == '' or index in noqa[0]):
return True
return False


def lint(
file_rel: Path,
lines: Iterator[str],
Expand Down Expand Up @@ -999,9 +1032,13 @@ def lint(
# run lint checks against the current line
for index, check_meta in checks.items():
# Skip commented line unless check says not to.
index_str = get_index_str(check_meta, index)
if (
line.strip().startswith('#')
and not check_meta.get('evaluate commented lines', False)
(
line.strip().startswith('#')
and not check_meta.get('evaluate commented lines', False)
)
or no_qa(line, index_str)
):
continue

Expand Down Expand Up @@ -1033,15 +1070,15 @@ def lint(
url = get_url(check_meta)

yield (
f'# [{get_index_str(check_meta, index)}]: '
f'# [{index_str}]: '
f'{msg}\n'
f'# - see {url}\n'
)
else:
# write a message to inform the user
write(
Fore.YELLOW +
f'[{get_index_str(check_meta, index)}]'
f'[{index_str}]'
f' {file_rel}:{line_no}: {msg}'
)
if modify:
Expand Down Expand Up @@ -1171,7 +1208,11 @@ def target_version_check(

def get_option_parser() -> COP:
parser = COP(
COP_DOC + TOMLDOC.format('', str(LINT_SECTIONS)),
(
COP_DOC
+ NOQA.replace('``', '"')
+ TOMLDOC.format('', str(LINT_SECTIONS))
),
argdoc=[
COP.optional(WORKFLOW_ID_OR_PATH_ARG_DOC)
],
Expand Down Expand Up @@ -1296,4 +1337,5 @@ def main(parser: COP, options: 'Values', target=None) -> None:

# NOTE: use += so that this works with __import__
# (docstring needed for `cylc help all` output)
__doc__ += NOQA
__doc__ += get_reference('all', 'rst')
17 changes: 17 additions & 0 deletions tests/unit/scripts/test_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@
[[and_another_thing]]
[[[remote]]]
host = `rose host-select thingy`

%include foo.cylc
"""


Expand Down Expand Up @@ -634,3 +636,18 @@ def test_indents(spaces, expect):
assert expect in result
else:
assert not result


def test_noqa():
"""Comments turn of checks.

"""
output = lint_text(
'foo = bar#noqa\n'
'qux = baz # noqa: S002\n'
'buzz = food # noqa: S007\n'
'quixotic = foolish # noqa: S007, S992 S002\n',
['style']
)
assert len(output.messages) == 1
assert 'flow.cylc:3' in output.messages[0]
Loading