Skip to content

Commit

Permalink
Add Ruff linter pre-commit hook (#690)
Browse files Browse the repository at this point in the history
In place of isort, pyupgrade and flake8
Fix new checks
  • Loading branch information
sbrunner authored Oct 22, 2024
1 parent 25b9c6c commit a38e2ab
Show file tree
Hide file tree
Showing 33 changed files with 106 additions and 132 deletions.
37 changes: 9 additions & 28 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,24 @@ repos:
args: [--fix=lf]
- id: debug-statements
- id: check-added-large-files
- repo: https://github.com/asottile/pyupgrade
rev: v3.17.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.0
hooks:
- id: pyupgrade
args: [--py39-plus]
- repo: https://github.com/psf/black
rev: 24.8.0
hooks:
- id: black
- id: ruff-format
- id: ruff
args:
- --exclude=/(tests)/
- --line-length=120
- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort
args: ["--profile", "black", "--filter-files"]
- --fix
exclude: |
(?x)^(
|tests/.*
)$
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
args:
- --ignore-words-list=nin,astroid
- --skip=poetry.lock
- repo: https://github.com/PyCQA/flake8
rev: 7.1.1
hooks:
- id: flake8
exclude: |
(?x)^(
.*/testdata/.*
|.*/testpath/.*
|tests/tools/pylint/test_no_init_found/.*
|tests/test_message\.py
)$
args:
- --max-line-length=120
- repo: https://github.com/regebro/pyroma
rev: "4.2"
hooks:
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
html_title = "prospector documentation"

# A shorter title for the navigation bar. Default is the same as html_title.
html_short_title = "prospector-%s" % version
html_short_title = f"prospector-{version}"

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
Expand Down
2 changes: 1 addition & 1 deletion prospector/autodetect.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def find_from_path(path: Path) -> set[str]:
names |= find_from_imports(contents)
except encoding.CouldNotHandleEncoding as err:
# TODO: this output will break output formats such as JSON
warnings.warn(f"{err.path}: {err.__cause__}", ImportWarning)
warnings.warn(f"{err.path}: {err.__cause__}", ImportWarning, stacklevel=0)

if len(names) == len(POSSIBLE_LIBRARIES):
# don't continue on recursing, there's no point!
Expand Down
20 changes: 13 additions & 7 deletions prospector/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
except ImportError:
import sre_constants # pylint: disable=deprecated-module

import contextlib

from prospector import tools
from prospector.autodetect import autodetect_libraries
from prospector.compat import is_relative_to
Expand Down Expand Up @@ -174,7 +176,7 @@ def _get_profile(

# Use the strictness profile only if no profile has been given
if config.strictness is not None and config.strictness:
cmdline_implicit.append("strictness_%s" % config.strictness)
cmdline_implicit.append(f"strictness_{config.strictness}")
strictness = config.strictness

# the profile path is
Expand All @@ -196,8 +198,14 @@ def _get_profile(
profile = ProspectorProfile.load(profile_name, profile_path, forced_inherits=forced_inherits)
except CannotParseProfile as cpe:
sys.stderr.write(
"Failed to run:\nCould not parse profile %s as it is not valid YAML\n%s\n"
% (cpe.filepath, cpe.get_parse_message())
"\n".join(
[
"Failed to run:",
f"Could not parse profile {cpe.filepath} as it is not valid YAML",
f"{cpe.get_parse_message()}",
"",
]
)
)
sys.exit(1)
except ProfileNotFound as nfe:
Expand Down Expand Up @@ -273,10 +281,8 @@ def _determine_ignores(
# ignore-patterns:
# uses: django
continue
try:
with contextlib.suppress(sre_constants.error):
ignores.append(re.compile(pattern))
except sre_constants.error:
pass

# Convert ignore paths into patterns
boundary = r"(^|/|\\)%s(/|\\|$)"
Expand Down Expand Up @@ -318,7 +324,7 @@ def tool_options(self, tool_name: str) -> dict[str, str]:
return tool.get("options", {})

def external_config_location(self, tool_name: str) -> Optional[Path]:
return getattr(self.config, "%s_config_file" % tool_name, None)
return getattr(self.config, f"{tool_name}_config_file", None)

@property
def die_on_tool_error(self) -> bool:
Expand Down
16 changes: 8 additions & 8 deletions prospector/config/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,10 @@ def build_command_line_source(
},
"output_format": {
"flags": ["-o", "--output-format"],
"help": "The output format. Valid values are: %s. This will output to stdout by default, "
"however a target file can be used instead by adding :path-to-output-file, eg, -o json:output.json"
% (", ".join(sorted(FORMATTERS.keys())),),
"help": "The output format. Valid values are: {}. This will output to stdout by default, "
"however a target file can be used instead by adding :path-to-output-file, eg, -o json:output.json".format(
", ".join(sorted(FORMATTERS.keys()))
),
},
"absolute_paths": {
"help": "Whether to output absolute paths when referencing files "
Expand All @@ -199,9 +200,8 @@ def build_command_line_source(
"flags": ["-t", "--tool"],
"help": "A list of tools to run. This lets you set exactly which "
"tools to run. To add extra tools to the defaults, see "
"--with-tool. Possible values are: %s. By "
"default, the following tools will be run: %s"
% (
"--with-tool. Possible values are: {}. By "
"default, the following tools will be run: {}".format(
", ".join(sorted(TOOLS.keys())),
", ".join(sorted(DEFAULT_TOOLS)),
),
Expand All @@ -210,14 +210,14 @@ def build_command_line_source(
"flags": ["-w", "--with-tool"],
"help": "A list of tools to run in addition to the default tools. "
"To specify all tools explicitly, use the --tool argument. "
"Possible values are %s." % (", ".join(sorted(TOOLS.keys()))),
"Possible values are {}.".format(", ".join(sorted(TOOLS.keys()))),
},
"without_tools": {
"flags": ["-W", "--without-tool"],
"help": "A list of tools that should not be run. Useful to turn off "
"only a single tool from the defaults. "
"To specify all tools explicitly, use the --tool argument. "
"Possible values are %s." % (", ".join(sorted(TOOLS.keys()))),
"Possible values are {}.".format(", ".join(sorted(TOOLS.keys()))),
},
"profiles": {
"flags": ["-P", "--profile"],
Expand Down
6 changes: 1 addition & 5 deletions prospector/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ class FatalProspectorException(Exception):
Problems in prospector itself should raise this to notify
the user directly. Errors in dependent tools should be
caught and the user notified elegantly.
"""

# (see also the --die-on-tool-error flag)
Expand All @@ -27,10 +26,7 @@ def __init__(self, path: Path):
class PermissionMissing(Exception):
def __init__(self, path: Path):
docs_url = "https://prospector.landscape.io/en/master/profiles.html#ignoring-paths-and-patterns"
if os.path.isdir(path):
what = f"directory {path}"
else:
what = f"the file {path}"
what = f"directory {path}" if os.path.isdir(path) else f"the file {path}"
error_msg = (
f"The current user {os.getlogin()} does not have permission to open "
f"{what}. Either fix permissions or tell prospector to skip it "
Expand Down
2 changes: 1 addition & 1 deletion prospector/formatters/base_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class SummaryFormatter(Formatter):
summary_labels = (
("started", "Started", None),
("completed", "Finished", None),
("time_taken", "Time Taken", lambda x: "%s seconds" % x),
("time_taken", "Time Taken", lambda x: f"{x} seconds"),
("formatter", "Formatter", None),
("profiles", "Profiles", None),
("strictness", "Strictness", None),
Expand Down
5 changes: 2 additions & 3 deletions prospector/formatters/emacs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ def render_message(self, message: Message) -> str:
message.location.line,
(message.location.character or 0) + 1,
),
" L%s:%s %s: %s - %s"
% (
" L{}:{} {}: {} - {}".format(
message.location.line or "-",
message.location.character if message.location.line else "-",
message.location.function,
message.source,
message.code,
),
" %s" % message.message,
f" {message.message}",
]

return "\n".join(output)
7 changes: 3 additions & 4 deletions prospector/formatters/grouped.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,15 @@ def render_messages(self) -> str:
output.append(str(filename))

for line in sorted(groups[filename].keys(), key=lambda x: 0 if x is None else int(x)):
output.append(" Line: %s" % line)
output.append(f" Line: {line}")

for message in groups[filename][line]:
output.append(
" %s: %s / %s%s"
% (
" {}: {} / {}{}".format(
message.source,
message.code,
message.message,
(" (col %s)" % message.location.character) if message.location.character else "",
(f" (col {message.location.character})") if message.location.character else "",
)
)

Expand Down
2 changes: 1 addition & 1 deletion prospector/formatters/pylint.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def render(self, summary: bool = True, messages: bool = True, profile: bool = Fa
module_name = self._make_path(message.location.path).replace(os.path.sep, ".")
module_name = re.sub(r"(\.__init__)?\.py$", "", module_name)

header = "************* Module %s" % module_name
header = f"************* Module {module_name}"
output.append(header)

# ={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
Expand Down
8 changes: 3 additions & 5 deletions prospector/formatters/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,16 @@


class TextFormatter(SummaryFormatter):

def render_message(self, message: Message) -> str:
output = []

if message.location.module:
output.append(f"{message.location.module} ({self._make_path(message.location.path)}):")
else:
output.append("%s:" % self._make_path(message.location.path))
output.append(f"{self._make_path(message.location.path)}:")

output.append(
" L%s:%s %s: %s - %s"
% (
" L{}:{} {}: {} - {}".format(
message.location.line or "-",
message.location.character if message.location.character else "-",
message.location.function,
Expand All @@ -28,7 +26,7 @@ def render_message(self, message: Message) -> str:
)
)

output.append(" %s" % message.message)
output.append(f" {message.message}")

return "\n".join(output)

Expand Down
2 changes: 1 addition & 1 deletion prospector/formatters/vscode.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def render(self, summary: bool = True, messages: bool = True, profile: bool = Fa
module_name = self._make_path(message.location.path).replace(os.path.sep, ".")
module_name = re.sub(r"(\.__init__)?\.py$", "", module_name)

header = "************* Module %s" % module_name
header = f"************* Module {module_name}"
output.append(header)

template = "%(line)s,%(character)s,%(code)s,%(code)s:%(source)s %(message)s"
Expand Down
4 changes: 2 additions & 2 deletions prospector/formatters/xunit.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def render(self, summary: bool = True, messages: bool = True, profile: bool = Fa
testsuite_el = xml_doc.createElement("testsuite")
testsuite_el.setAttribute("errors", str(self.summary["message_count"]))
testsuite_el.setAttribute("failures", "0")
testsuite_el.setAttribute("name", "prospector-%s" % "-".join(self.summary["tools"]))
testsuite_el.setAttribute("name", "prospector-{}".format("-".join(self.summary["tools"])))
testsuite_el.setAttribute("tests", str(self.summary["message_count"]))
testsuite_el.setAttribute("time", str(self.summary["time_taken"]))
xml_doc.appendChild(testsuite_el)
Expand All @@ -38,7 +38,7 @@ def render(self, summary: bool = True, messages: bool = True, profile: bool = Fa

failure_el = xml_doc.createElement("error")
failure_el.setAttribute("message", message.message.strip())
failure_el.setAttribute("type", "%s Error" % message.source)
failure_el.setAttribute("type", f"{message.source} Error")
template = "%(path)s:%(line)s: [%(code)s(%(source)s), %(function)s] %(message)s"
cdata = template % {
"path": self._make_path(message.location.path),
Expand Down
13 changes: 3 additions & 10 deletions prospector/pathutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@ def is_python_module(path: Path) -> bool:


def is_virtualenv(path: Path) -> bool:
if os.name == "nt":
# Windows!
clues = ("Scripts", "lib", "include")
else:
clues = ("bin", "lib", "include")
clues = ("Scripts", "lib", "include") if os.name == "nt" else ("bin", "lib", "include")

try:
# just get the name, iterdir returns absolute paths by default
Expand All @@ -37,8 +33,5 @@ def is_virtualenv(path: Path) -> bool:
# if we do have all three directories, make sure that it's not
# just a coincidence by doing some heuristics on the rest of
# the directory
if len(dircontents) > 7:
# if there are more than 7 things it's probably not a virtualenvironment
return False

return True
# if there are more than 7 things it's probably not a virtualenvironment
return len(dircontents) <= 7
15 changes: 8 additions & 7 deletions prospector/postfilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,16 @@ def filter_messages(filepaths: List[Path], messages: List[Message]) -> List[Mess
continue

# some lines are skipped entirely by messages
if relative_message_path in lines_to_ignore:
if message.location.line in lines_to_ignore[relative_message_path]:
continue
if relative_message_path in lines_to_ignore and message.location.line in lines_to_ignore[relative_message_path]:
continue

# and some lines have only certain messages explicitly ignored
if relative_message_path in messages_to_ignore:
if message.location.line in messages_to_ignore[relative_message_path]:
if message.code in messages_to_ignore[relative_message_path][message.location.line]:
continue
if (
relative_message_path in messages_to_ignore
and message.location.line in messages_to_ignore[relative_message_path]
and message.code in messages_to_ignore[relative_message_path][message.location.line]
):
continue

# otherwise this message was not filtered
filtered.append(message)
Expand Down
10 changes: 5 additions & 5 deletions prospector/profiles/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ def __init__(self, filepath: str, parse_error: Exception) -> None:
self.parse_error = parse_error

def get_parse_message(self) -> str:
return "{}\n on line {} : char {}".format(
self.parse_error.problem, # type: ignore[attr-defined]
self.parse_error.problem_mark.line, # type: ignore[attr-defined]
self.parse_error.problem_mark.column, # type: ignore[attr-defined]
return (
f"{self.parse_error.problem}\n" # type: ignore[attr-defined]
f" on line {self.parse_error.problem_mark.line}: " # type: ignore[attr-defined]
f"char {self.parse_error.problem_mark.column}" # type: ignore[attr-defined]
)

def __repr__(self) -> str:
return "Could not parse profile found at %s - it is not valid YAML" % self.filepath
return f"Could not parse profile found at {self.filepath} - it is not valid YAML"
2 changes: 1 addition & 1 deletion prospector/profiles/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def _determine_strictness(profile_dict: dict[str, Any], inherits: list[str]) ->
strictness = profile_dict.get("strictness")
if strictness is None:
return None, False
return ("strictness_%s" % strictness), True
return (f"strictness_{strictness}"), True


def _determine_pep8(profile_dict: dict[str, Any]) -> tuple[Optional[str], bool]:
Expand Down
Loading

0 comments on commit a38e2ab

Please sign in to comment.