Skip to content

Instantly share code, notes, and snippets.

@AlexRiina
Last active Jan 15, 2022
Embed
What would you like to do?
automatically ignore lines from flake8
"""
Add `# noqa: FXXX` comments to all lines with violations from flake8
"""
# MIT License
#
# Copyright (c) 2022 Alex Riina
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys
import argparse
import fileinput
from itertools import groupby
from collections import defaultdict
parser = argparse.ArgumentParser(
description=__doc__,
usage="flake8 $(git ls-files **/*.py) | noqaer.py - [--dry-run]",
epilog="""
Intended to be used once in lieu of manually fixing all flake8 violatios so
flake8 passes 100% and can be added to a build.""",
)
parser.add_argument("errors", type=argparse.FileType("r"), help="errors from flake8")
parser.add_argument("--dry-run", action="store_true")
args = parser.parse_args()
def parse_flake8_errors(error_lines):
for error_line in error_lines:
filename, line_number, col, message = error_line.strip().split(":", 3)
error_code, message = message.strip().split(" ", 1)
yield filename, int(line_number), error_code
for filename, error_tuples in groupby(
parse_flake8_errors(args.errors), key=lambda error_tuple: error_tuple[0]
):
errors = defaultdict(set)
for _, line_number, code in error_tuples:
errors[line_number].add(code)
with fileinput.input(files=[filename], inplace=not args.dry_run) as fp:
for line_number, line in enumerate(fp, start=1):
if line_number not in errors:
# line already has a newline, so don't add an extra
print(line, end="")
elif "noqa" in line or line.strip().endswith("\\"):
print("skipping {}:{}".format(filename, line_number), file=sys.stderr)
print(line, end="")
else:
# note the fileinput inplace captures output and writes back to the file
print(line.rstrip("\n") + " # noqa: " + ", ".join(errors[line_number]))
@sigmavirus24
Copy link

sigmavirus24 commented Jan 15, 2022

@AlexRiina have you considered suggesting folks use flake8-json so you have structured data that you can parse more easily?

@AlexRiina
Copy link
Author

AlexRiina commented Jan 15, 2022

@sigmavirus24 I hadn't heard of that project but my inclination is to avoid adding any external dependencies so this can be audited and run very easily. I do like the idea of reducing the split and strips though and think the script could be rewritten to use an explicit flake8 --format ....

With something like flake8 --format '%(path)s:%(row)d:%(col)d:%(code)s' some of the strips can go away and this could even use csv.reader (if someone is dealing with a gnarly error message that breaks this script and reads this: '%(path)s:%(row)d:%(col)d:%(code)s x' looks like be good enough).

I'm not planning to change this gist since I've run the code on my repo and we're unlikely to declare a "jubilee" for any other flake8 errors but please feel free to fork this repo and make any changes you like (just added a license).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment