prospector 1.12.1__py3-none-any.whl → 1.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. prospector/autodetect.py +10 -9
  2. prospector/blender.py +18 -11
  3. prospector/config/__init__.py +66 -49
  4. prospector/config/configuration.py +14 -12
  5. prospector/config/datatype.py +1 -1
  6. prospector/encoding.py +2 -2
  7. prospector/exceptions.py +1 -5
  8. prospector/finder.py +9 -8
  9. prospector/formatters/__init__.py +1 -1
  10. prospector/formatters/base.py +16 -11
  11. prospector/formatters/base_summary.py +43 -0
  12. prospector/formatters/emacs.py +5 -5
  13. prospector/formatters/grouped.py +9 -7
  14. prospector/formatters/json.py +3 -2
  15. prospector/formatters/pylint.py +31 -8
  16. prospector/formatters/text.py +10 -58
  17. prospector/formatters/vscode.py +17 -6
  18. prospector/formatters/xunit.py +6 -6
  19. prospector/formatters/yaml.py +4 -2
  20. prospector/message.py +18 -13
  21. prospector/pathutils.py +3 -10
  22. prospector/postfilter.py +8 -7
  23. prospector/profiles/exceptions.py +14 -11
  24. prospector/profiles/profile.py +69 -58
  25. prospector/run.py +20 -18
  26. prospector/suppression.py +12 -10
  27. prospector/tools/__init__.py +19 -13
  28. prospector/tools/bandit/__init__.py +27 -15
  29. prospector/tools/base.py +11 -3
  30. prospector/tools/dodgy/__init__.py +7 -3
  31. prospector/tools/mccabe/__init__.py +13 -6
  32. prospector/tools/mypy/__init__.py +44 -81
  33. prospector/tools/profile_validator/__init__.py +24 -15
  34. prospector/tools/pycodestyle/__init__.py +22 -15
  35. prospector/tools/pydocstyle/__init__.py +12 -6
  36. prospector/tools/pyflakes/__init__.py +35 -19
  37. prospector/tools/pylint/__init__.py +47 -28
  38. prospector/tools/pylint/collector.py +3 -5
  39. prospector/tools/pylint/linter.py +11 -9
  40. prospector/tools/pyright/__init__.py +18 -7
  41. prospector/tools/pyroma/__init__.py +10 -6
  42. prospector/tools/ruff/__init__.py +75 -0
  43. prospector/tools/utils.py +25 -17
  44. prospector/tools/vulture/__init__.py +25 -15
  45. {prospector-1.12.1.dist-info → prospector-1.13.0.dist-info}/METADATA +4 -3
  46. prospector-1.13.0.dist-info/RECORD +71 -0
  47. prospector-1.12.1.dist-info/RECORD +0 -69
  48. {prospector-1.12.1.dist-info → prospector-1.13.0.dist-info}/LICENSE +0 -0
  49. {prospector-1.12.1.dist-info → prospector-1.13.0.dist-info}/WHEEL +0 -0
  50. {prospector-1.12.1.dist-info → prospector-1.13.0.dist-info}/entry_points.txt +0 -0
@@ -1,33 +1,38 @@
1
1
  from abc import ABC, abstractmethod
2
2
 
3
+ from prospector.profiles.profile import ProspectorProfile
4
+
3
5
  __all__ = ("Formatter",)
4
6
 
5
7
  from pathlib import Path
8
+ from typing import Any, Optional
6
9
 
7
- from prospector.message import Message
10
+ from prospector.message import Location, Message
8
11
 
9
12
 
10
13
  class Formatter(ABC):
11
- def __init__(self, summary, messages, profile, paths_relative_to: Path = None):
14
+ def __init__(
15
+ self,
16
+ summary: dict[str, Any],
17
+ messages: list[Message],
18
+ profile: ProspectorProfile,
19
+ paths_relative_to: Optional[Path] = None,
20
+ ) -> None:
12
21
  self.summary = summary
13
22
  self.messages = messages
14
23
  self.profile = profile
15
24
  self.paths_relative_to = paths_relative_to
16
25
 
17
26
  @abstractmethod
18
- def render(self, summary=True, messages=True, profile=False):
27
+ def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str:
19
28
  raise NotImplementedError
20
29
 
21
- def _make_path(self, path: Path) -> str:
22
- if self.paths_relative_to is None:
23
- path = path.absolute()
24
- elif path.is_absolute():
25
- path = path.relative_to(self.paths_relative_to)
26
- return str(path)
30
+ def _make_path(self, location: Location) -> Path:
31
+ return location.relative_path(self.paths_relative_to)
27
32
 
28
- def _message_to_dict(self, message: Message) -> dict:
33
+ def _message_to_dict(self, message: Message) -> dict[str, Any]:
29
34
  loc = {
30
- "path": self._make_path(message.location.path),
35
+ "path": str(self._make_path(message.location)),
31
36
  "module": message.location.module,
32
37
  "function": message.location.function,
33
38
  "line": message.location.line,
@@ -0,0 +1,43 @@
1
+ from prospector.formatters.base import Formatter
2
+
3
+
4
+ class SummaryFormatter(Formatter):
5
+ """
6
+ This abstract formatter is used to output a summary of the prospector run.
7
+ """
8
+
9
+ summary_labels = (
10
+ ("started", "Started", None),
11
+ ("completed", "Finished", None),
12
+ ("time_taken", "Time Taken", lambda x: f"{x} seconds"),
13
+ ("formatter", "Formatter", None),
14
+ ("profiles", "Profiles", None),
15
+ ("strictness", "Strictness", None),
16
+ ("libraries", "Libraries Used", ", ".join),
17
+ ("tools", "Tools Run", ", ".join),
18
+ ("adaptors", "Adaptors", ", ".join),
19
+ ("message_count", "Messages Found", None),
20
+ ("external_config", "External Config", None),
21
+ )
22
+
23
+ def render_summary(self) -> str:
24
+ output = [
25
+ "Check Information",
26
+ "=================",
27
+ ]
28
+
29
+ label_width = max(len(label[1]) for label in self.summary_labels)
30
+
31
+ for key, label, formatter in self.summary_labels:
32
+ if key in self.summary:
33
+ value = self.summary[key]
34
+ if formatter is not None:
35
+ value = formatter(value)
36
+ output.append(f" {label.rjust(label_width)}: {value}")
37
+
38
+ return "\n".join(output)
39
+
40
+ def render_profile(self) -> str:
41
+ output = ["Profile", "=======", "", self.profile.as_yaml().strip()]
42
+
43
+ return "\n".join(output)
@@ -1,26 +1,26 @@
1
1
  from prospector.formatters.text import TextFormatter
2
+ from prospector.message import Message
2
3
 
3
4
  __all__ = ("EmacsFormatter",)
4
5
 
5
6
 
6
7
  class EmacsFormatter(TextFormatter):
7
- def render_message(self, message):
8
+ def render_message(self, message: Message) -> str:
8
9
  output = [
9
10
  "%s:%s:%d:"
10
11
  % (
11
- self._make_path(message.location.path),
12
+ self._make_path(message.location),
12
13
  message.location.line,
13
14
  (message.location.character or 0) + 1,
14
15
  ),
15
- " L%s:%s %s: %s - %s"
16
- % (
16
+ " L{}:{} {}: {} - {}".format(
17
17
  message.location.line or "-",
18
18
  message.location.character if message.location.line else "-",
19
19
  message.location.function,
20
20
  message.source,
21
21
  message.code,
22
22
  ),
23
- " %s" % message.message,
23
+ f" {message.message}",
24
24
  ]
25
25
 
26
26
  return "\n".join(output)
@@ -1,37 +1,39 @@
1
1
  from collections import defaultdict
2
+ from pathlib import Path
2
3
 
3
4
  from prospector.formatters.text import TextFormatter
5
+ from prospector.message import Message
4
6
 
5
7
  __all__ = ("GroupedFormatter",)
6
8
 
7
9
 
8
10
  class GroupedFormatter(TextFormatter):
9
- def render_messages(self):
11
+ def render_messages(self) -> str:
10
12
  output = [
11
13
  "Messages",
12
14
  "========",
13
15
  "",
14
16
  ]
15
17
 
16
- groups = defaultdict(lambda: defaultdict(list))
18
+ groups: dict[Path, dict[int, list[Message]]] = defaultdict(lambda: defaultdict(list))
17
19
 
18
20
  for message in self.messages:
19
- groups[self._make_path(message.location.path)][message.location.line].append(message)
21
+ assert message.location.line is not None
22
+ groups[self._make_path(message.location)][message.location.line].append(message)
20
23
 
21
24
  for filename in sorted(groups.keys()):
22
25
  output.append(str(filename))
23
26
 
24
27
  for line in sorted(groups[filename].keys(), key=lambda x: 0 if x is None else int(x)):
25
- output.append(" Line: %s" % line)
28
+ output.append(f" Line: {line}")
26
29
 
27
30
  for message in groups[filename][line]:
28
31
  output.append(
29
- " %s: %s / %s%s"
30
- % (
32
+ " {}: {} / {}{}".format(
31
33
  message.source,
32
34
  message.code,
33
35
  message.message,
34
- (" (col %s)" % message.location.character) if message.location.character else "",
36
+ (f" (col {message.location.character})") if message.location.character else "",
35
37
  )
36
38
  )
37
39
 
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  from datetime import datetime
3
+ from typing import Any
3
4
 
4
5
  from prospector.formatters.base import Formatter
5
6
 
@@ -7,8 +8,8 @@ __all__ = ("JsonFormatter",)
7
8
 
8
9
 
9
10
  class JsonFormatter(Formatter):
10
- def render(self, summary=True, messages=True, profile=False):
11
- output = {}
11
+ def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str:
12
+ output: dict[str, Any] = {}
12
13
 
13
14
  if summary:
14
15
  # we need to slightly change the types and format
@@ -1,44 +1,67 @@
1
1
  import os
2
2
  import re
3
3
 
4
- from prospector.formatters.base import Formatter
4
+ from prospector.formatters.base_summary import SummaryFormatter
5
5
 
6
6
 
7
- class PylintFormatter(Formatter):
7
+ class PylintFormatter(SummaryFormatter):
8
8
  """
9
9
  This formatter outputs messages in the same way as pylint -f parseable , which is used by several
10
10
  tools to parse pylint output. This formatter is therefore a compatibility shim between tools built
11
11
  on top of pylint and prospector itself.
12
12
  """
13
13
 
14
- def render(self, summary=True, messages=True, profile=False):
15
- # this formatter will always ignore the summary and profile
14
+ def render_messages(self) -> list[str]:
16
15
  cur_loc = None
17
16
  output = []
18
17
  for message in sorted(self.messages):
19
18
  if cur_loc != message.location.path:
20
19
  cur_loc = message.location.path
21
- module_name = self._make_path(message.location.path).replace(os.path.sep, ".")
20
+ module_name = str(self._make_path(message.location)).replace(os.path.sep, ".")
22
21
  module_name = re.sub(r"(\.__init__)?\.py$", "", module_name)
23
22
 
24
- header = "************* Module %s" % module_name
23
+ header = f"************* Module {module_name}"
25
24
  output.append(header)
26
25
 
27
26
  # ={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
28
27
  # prospector/configuration.py:65: [missing-docstring(missing-docstring), build_default_sources] \
29
28
  # Missing function docstring
30
29
 
31
- template = "%(path)s:%(line)s: [%(code)s(%(source)s), %(function)s] %(message)s"
30
+ template_location = (
31
+ "%(path)s"
32
+ if message.location.line is None
33
+ else "%(path)s:%(line)s"
34
+ if message.location.character is None
35
+ else "%(path)s:%(line)s:%(character)s"
36
+ )
37
+ template_code = (
38
+ "%(code)s(%(source)s)" if message.location.function is None else "[%(code)s(%(source)s), %(function)s]"
39
+ )
40
+ template = f"{template_location}: {template_code}: %(message)s"
41
+
32
42
  output.append(
33
43
  template
34
44
  % {
35
- "path": self._make_path(message.location.path),
45
+ "path": self._make_path(message.location),
36
46
  "line": message.location.line,
47
+ "character": message.location.character,
37
48
  "source": message.source,
38
49
  "code": message.code,
39
50
  "function": message.location.function,
40
51
  "message": message.message.strip(),
41
52
  }
42
53
  )
54
+ return output
55
+
56
+ def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str:
57
+ output: list[str] = []
58
+ if messages:
59
+ output.extend(self.render_messages())
60
+ if profile:
61
+ output.append("")
62
+ output.append(self.render_profile())
63
+ if summary:
64
+ output.append("")
65
+ output.append(self.render_summary())
43
66
 
44
67
  return "\n".join(output)
@@ -1,63 +1,20 @@
1
- from prospector.formatters.base import Formatter
1
+ from prospector.formatters.base_summary import SummaryFormatter
2
+ from prospector.message import Message
2
3
 
3
4
  __all__ = ("TextFormatter",)
4
5
 
5
6
 
6
- # pylint: disable=unnecessary-lambda
7
-
8
-
9
- class TextFormatter(Formatter):
10
- summary_labels = (
11
- ("started", "Started"),
12
- ("completed", "Finished"),
13
- ("time_taken", "Time Taken", lambda x: "%s seconds" % x),
14
- ("formatter", "Formatter"),
15
- ("profiles", "Profiles"),
16
- ("strictness", "Strictness"),
17
- ("libraries", "Libraries Used", lambda x: ", ".join(x)),
18
- ("tools", "Tools Run", lambda x: ", ".join(x)),
19
- ("adaptors", "Adaptors", lambda x: ", ".join(x)),
20
- ("message_count", "Messages Found"),
21
- ("external_config", "External Config"),
22
- )
23
-
24
- def render_summary(self):
25
- output = [
26
- "Check Information",
27
- "=================",
28
- ]
29
-
30
- label_width = max(len(label[1]) for label in self.summary_labels)
31
-
32
- for summary_label in self.summary_labels:
33
- key = summary_label[0]
34
- if key in self.summary:
35
- label = summary_label[1]
36
- if len(summary_label) > 2:
37
- value = summary_label[2](self.summary[key])
38
- else:
39
- value = self.summary[key]
40
- output.append(
41
- " %s: %s"
42
- % (
43
- label.rjust(label_width),
44
- value,
45
- )
46
- )
47
-
48
- return "\n".join(output)
49
-
50
- def render_message(self, message):
7
+ class TextFormatter(SummaryFormatter):
8
+ def render_message(self, message: Message) -> str:
51
9
  output = []
52
10
 
53
11
  if message.location.module:
54
- output.append(f"{message.location.module} ({self._make_path(message.location.path)}):")
12
+ output.append(f"{message.location.module} ({self._make_path(message.location)}):")
55
13
  else:
56
- output.append("%s:" % self._make_path(message.location.path))
14
+ output.append(f"{self._make_path(message.location)}:")
57
15
 
58
16
  output.append(
59
- " L%s:%s %s: %s - %s"
60
- % (
17
+ " L{}:{} {}: {} - {}".format(
61
18
  message.location.line or "-",
62
19
  message.location.character if message.location.character else "-",
63
20
  message.location.function,
@@ -66,11 +23,11 @@ class TextFormatter(Formatter):
66
23
  )
67
24
  )
68
25
 
69
- output.append(" %s" % message.message)
26
+ output.append(f" {message.message}")
70
27
 
71
28
  return "\n".join(output)
72
29
 
73
- def render_messages(self):
30
+ def render_messages(self) -> str:
74
31
  output = [
75
32
  "Messages",
76
33
  "========",
@@ -83,12 +40,7 @@ class TextFormatter(Formatter):
83
40
 
84
41
  return "\n".join(output)
85
42
 
86
- def render_profile(self):
87
- output = ["Profile", "=======", "", self.profile.as_yaml().strip()]
88
-
89
- return "\n".join(output)
90
-
91
- def render(self, summary=True, messages=True, profile=False):
43
+ def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str:
92
44
  output = []
93
45
  if messages and self.messages: # if there are no messages, don't render an empty header
94
46
  output.append(self.render_messages())
@@ -1,26 +1,25 @@
1
1
  import os
2
2
  import re
3
3
 
4
- from prospector.formatters.base import Formatter
4
+ from prospector.formatters.base_summary import SummaryFormatter
5
5
 
6
6
 
7
- class VSCodeFormatter(Formatter):
7
+ class VSCodeFormatter(SummaryFormatter):
8
8
  """
9
9
  This formatter outputs messages in the same way as vscode prospector linter expects.
10
10
  """
11
11
 
12
- def render(self, summary=True, messages=True, profile=False):
13
- # this formatter will always ignore the summary and profile
12
+ def render_messages(self) -> list[str]:
14
13
  cur_loc = None
15
14
  output = []
16
15
 
17
16
  for message in sorted(self.messages):
18
17
  if cur_loc != message.location.path:
19
18
  cur_loc = message.location.path
20
- module_name = self._make_path(message.location.path).replace(os.path.sep, ".")
19
+ module_name = str(self._make_path(message.location)).replace(os.path.sep, ".")
21
20
  module_name = re.sub(r"(\.__init__)?\.py$", "", module_name)
22
21
 
23
- header = "************* Module %s" % module_name
22
+ header = f"************* Module {module_name}"
24
23
  output.append(header)
25
24
 
26
25
  template = "%(line)s,%(character)s,%(code)s,%(code)s:%(source)s %(message)s"
@@ -34,5 +33,17 @@ class VSCodeFormatter(Formatter):
34
33
  "message": message.message.strip(),
35
34
  }
36
35
  )
36
+ return output
37
+
38
+ def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str:
39
+ output: list[str] = []
40
+ if messages:
41
+ output.extend(self.render_messages())
42
+ if profile:
43
+ output.append("")
44
+ output.append(self.render_profile())
45
+ if summary:
46
+ output.append("")
47
+ output.append(self.render_summary())
37
48
 
38
49
  return "\n".join(output)
@@ -1,4 +1,4 @@
1
- from xml.dom.minidom import Document
1
+ from xml.dom.minidom import Document # nosec
2
2
 
3
3
  from prospector.formatters.base import Formatter
4
4
 
@@ -10,13 +10,13 @@ class XunitFormatter(Formatter):
10
10
  to use Xunit and prospector itself.
11
11
  """
12
12
 
13
- def render(self, summary=True, messages=True, profile=False):
13
+ def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str:
14
14
  xml_doc = Document()
15
15
 
16
16
  testsuite_el = xml_doc.createElement("testsuite")
17
17
  testsuite_el.setAttribute("errors", str(self.summary["message_count"]))
18
18
  testsuite_el.setAttribute("failures", "0")
19
- testsuite_el.setAttribute("name", "prospector-%s" % "-".join(self.summary["tools"]))
19
+ testsuite_el.setAttribute("name", "prospector-{}".format("-".join(self.summary["tools"])))
20
20
  testsuite_el.setAttribute("tests", str(self.summary["message_count"]))
21
21
  testsuite_el.setAttribute("time", str(self.summary["time_taken"]))
22
22
  xml_doc.appendChild(testsuite_el)
@@ -34,14 +34,14 @@ class XunitFormatter(Formatter):
34
34
 
35
35
  for message in sorted(self.messages):
36
36
  testcase_el = xml_doc.createElement("testcase")
37
- testcase_el.setAttribute("name", f"{self._make_path(message.location.path)}-{message.location.line}")
37
+ testcase_el.setAttribute("name", f"{self._make_path(message.location)}-{message.location.line}")
38
38
 
39
39
  failure_el = xml_doc.createElement("error")
40
40
  failure_el.setAttribute("message", message.message.strip())
41
- failure_el.setAttribute("type", "%s Error" % message.source)
41
+ failure_el.setAttribute("type", f"{message.source} Error")
42
42
  template = "%(path)s:%(line)s: [%(code)s(%(source)s), %(function)s] %(message)s"
43
43
  cdata = template % {
44
- "path": self._make_path(message.location.path),
44
+ "path": self._make_path(message.location),
45
45
  "line": message.location.line,
46
46
  "source": message.source,
47
47
  "code": message.code,
@@ -1,3 +1,5 @@
1
+ from typing import Any
2
+
1
3
  import yaml
2
4
 
3
5
  from prospector.formatters.base import Formatter
@@ -6,8 +8,8 @@ __all__ = ("YamlFormatter",)
6
8
 
7
9
 
8
10
  class YamlFormatter(Formatter):
9
- def render(self, summary=True, messages=True, profile=False):
10
- output = {}
11
+ def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str:
12
+ output: dict[str, Any] = {}
11
13
 
12
14
  if summary:
13
15
  output["summary"] = self.summary
prospector/message.py CHANGED
@@ -4,12 +4,17 @@ from typing import Optional, Union
4
4
 
5
5
  class Location:
6
6
  def __init__(
7
- self, path: Union[Path, str], module: Optional[str], function: Optional[str], line: int, character: int
7
+ self,
8
+ path: Union[Path, str],
9
+ module: Optional[str],
10
+ function: Optional[str],
11
+ line: Optional[int],
12
+ character: Optional[int],
8
13
  ):
9
14
  if isinstance(path, Path):
10
- self._path = path
15
+ self._path = path.absolute()
11
16
  elif isinstance(path, str):
12
- self._path = Path(path)
17
+ self._path = Path(path).absolute()
13
18
  else:
14
19
  raise ValueError
15
20
  self.module = module or None
@@ -18,14 +23,14 @@ class Location:
18
23
  self.character = None if character == -1 else character
19
24
 
20
25
  @property
21
- def path(self):
26
+ def path(self) -> Path:
22
27
  return self._path
23
28
 
24
29
  def absolute_path(self) -> Path:
25
- return self._path.absolute()
30
+ return self._path
26
31
 
27
- def relative_path(self, root: Path) -> Path:
28
- return self._path.relative_to(root)
32
+ def relative_path(self, root: Optional[Path]) -> Path:
33
+ return self._path.relative_to(root) if root else self._path
29
34
 
30
35
  def __repr__(self) -> str:
31
36
  return f"{self._path}:L{self.line}:{self.character}"
@@ -38,7 +43,7 @@ class Location:
38
43
  return False
39
44
  return self._path == other._path and self.line == other.line and self.character == other.character
40
45
 
41
- def __lt__(self, other: object) -> bool:
46
+ def __lt__(self, other: "Location") -> bool:
42
47
  if not isinstance(other, Location):
43
48
  raise ValueError
44
49
  if self._path == other._path:
@@ -65,7 +70,7 @@ class Message:
65
70
  return self.code == other.code
66
71
  return False
67
72
 
68
- def __lt__(self, other) -> bool:
73
+ def __lt__(self, other: "Message") -> bool:
69
74
  if self.location == other.location:
70
75
  return self.code < other.code
71
76
  return self.location < other.location
@@ -76,10 +81,10 @@ def make_tool_error_message(
76
81
  source: str,
77
82
  code: str,
78
83
  message: str,
79
- line: int = 0,
80
- character: int = 0,
81
- module: str = None,
82
- function: str = None,
84
+ line: Optional[int] = None,
85
+ character: Optional[int] = None,
86
+ module: Optional[str] = None,
87
+ function: Optional[str] = None,
83
88
  ) -> Message:
84
89
  location = Location(path=filepath, module=module, function=function, line=line, character=character)
85
90
  return Message(source=source, code=code, location=location, message=message)
prospector/pathutils.py CHANGED
@@ -12,11 +12,7 @@ def is_python_module(path: Path) -> bool:
12
12
 
13
13
 
14
14
  def is_virtualenv(path: Path) -> bool:
15
- if os.name == "nt":
16
- # Windows!
17
- clues = ("Scripts", "lib", "include")
18
- else:
19
- clues = ("bin", "lib", "include")
15
+ clues = ("Scripts", "lib", "include") if os.name == "nt" else ("bin", "lib", "include")
20
16
 
21
17
  try:
22
18
  # just get the name, iterdir returns absolute paths by default
@@ -37,8 +33,5 @@ def is_virtualenv(path: Path) -> bool:
37
33
  # if we do have all three directories, make sure that it's not
38
34
  # just a coincidence by doing some heuristics on the rest of
39
35
  # the directory
40
- if len(dircontents) > 7:
41
- # if there are more than 7 things it's probably not a virtualenvironment
42
- return False
43
-
44
- return True
36
+ # if there are more than 7 things it's probably not a virtualenvironment
37
+ return len(dircontents) <= 7
prospector/postfilter.py CHANGED
@@ -42,15 +42,16 @@ def filter_messages(filepaths: List[Path], messages: List[Message]) -> List[Mess
42
42
  continue
43
43
 
44
44
  # some lines are skipped entirely by messages
45
- if relative_message_path in lines_to_ignore:
46
- if message.location.line in lines_to_ignore[relative_message_path]:
47
- continue
45
+ if relative_message_path in lines_to_ignore and message.location.line in lines_to_ignore[relative_message_path]:
46
+ continue
48
47
 
49
48
  # and some lines have only certain messages explicitly ignored
50
- if relative_message_path in messages_to_ignore:
51
- if message.location.line in messages_to_ignore[relative_message_path]:
52
- if message.code in messages_to_ignore[relative_message_path][message.location.line]:
53
- continue
49
+ if (
50
+ relative_message_path in messages_to_ignore
51
+ and message.location.line in messages_to_ignore[relative_message_path]
52
+ and message.code in messages_to_ignore[relative_message_path][message.location.line]
53
+ ):
54
+ continue
54
55
 
55
56
  # otherwise this message was not filtered
56
57
  filtered.append(message)
@@ -1,28 +1,31 @@
1
+ from pathlib import Path
2
+
3
+
1
4
  class ProfileNotFound(Exception):
2
- def __init__(self, name, profile_path):
5
+ def __init__(self, name: str, profile_path: list[Path]) -> None:
3
6
  super().__init__()
4
7
  self.name = name
5
8
  self.profile_path = profile_path
6
9
 
7
- def __repr__(self):
10
+ def __repr__(self) -> str:
8
11
  return "Could not find profile {}; searched in {}".format(
9
12
  self.name,
10
- ":".join(self.profile_path),
13
+ ":".join(map(str, self.profile_path)),
11
14
  )
12
15
 
13
16
 
14
17
  class CannotParseProfile(Exception):
15
- def __init__(self, filepath, parse_error):
18
+ def __init__(self, filepath: str, parse_error: Exception) -> None:
16
19
  super().__init__()
17
20
  self.filepath = filepath
18
21
  self.parse_error = parse_error
19
22
 
20
- def get_parse_message(self):
21
- return "{}\n on line {} : char {}".format(
22
- self.parse_error.problem,
23
- self.parse_error.problem_mark.line,
24
- self.parse_error.problem_mark.column,
23
+ def get_parse_message(self) -> str:
24
+ return (
25
+ f"{self.parse_error.problem}\n" # type: ignore[attr-defined]
26
+ f" on line {self.parse_error.problem_mark.line}: " # type: ignore[attr-defined]
27
+ f"char {self.parse_error.problem_mark.column}" # type: ignore[attr-defined]
25
28
  )
26
29
 
27
- def __repr__(self):
28
- return "Could not parse profile found at %s - it is not valid YAML" % self.filepath
30
+ def __repr__(self) -> str:
31
+ return f"Could not parse profile found at {self.filepath} - it is not valid YAML"