prospector 1.13.1__py3-none-any.whl → 1.13.3__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.
prospector/blender.py CHANGED
@@ -85,10 +85,9 @@ def blend(messages: list[Message], blend_combos: Optional[list[list[tuple[str, s
85
85
  blend_combos = blend_combos or BLEND_COMBOS
86
86
 
87
87
  # group messages by file and then line number
88
- msgs_grouped: dict[Path, dict[int, list[Message]]] = defaultdict(lambda: defaultdict(list))
88
+ msgs_grouped: dict[Optional[Path], dict[Optional[int], list[Message]]] = defaultdict(lambda: defaultdict(list))
89
89
 
90
90
  for message in messages:
91
- assert message.location.line is not None
92
91
  msgs_grouped[message.location.path][message.location.line].append(
93
92
  message,
94
93
  )
@@ -28,7 +28,8 @@ class Formatter(ABC):
28
28
  raise NotImplementedError
29
29
 
30
30
  def _make_path(self, location: Location) -> Path:
31
- return location.relative_path(self.paths_relative_to)
31
+ path_ = location.relative_path(self.paths_relative_to)
32
+ return Path() if path_ is None else path_
32
33
 
33
34
  def _message_to_dict(self, message: Message) -> dict[str, Any]:
34
35
  loc = {
@@ -1,5 +1,6 @@
1
1
  from collections import defaultdict
2
2
  from pathlib import Path
3
+ from typing import Optional
3
4
 
4
5
  from prospector.formatters.text import TextFormatter
5
6
  from prospector.message import Message
@@ -15,10 +16,9 @@ class GroupedFormatter(TextFormatter):
15
16
  "",
16
17
  ]
17
18
 
18
- groups: dict[Path, dict[int, list[Message]]] = defaultdict(lambda: defaultdict(list))
19
+ groups: dict[Path, dict[Optional[int], list[Message]]] = defaultdict(lambda: defaultdict(list))
19
20
 
20
21
  for message in self.messages:
21
- assert message.location.line is not None
22
22
  groups[self._make_path(message.location)][message.location.line].append(message)
23
23
 
24
24
  for filename in sorted(groups.keys()):
@@ -28,16 +28,26 @@ class PylintFormatter(SummaryFormatter):
28
28
  # Missing function docstring
29
29
 
30
30
  template_location = (
31
- "%(path)s"
31
+ ""
32
+ if message.location.path is None
33
+ else "%(path)s"
32
34
  if message.location.line is None
33
35
  else "%(path)s:%(line)s"
34
36
  if message.location.character is None
35
37
  else "%(path)s:%(line)s:%(character)s"
36
38
  )
37
39
  template_code = (
38
- "%(code)s(%(source)s)" if message.location.function is None else "[%(code)s(%(source)s), %(function)s]"
40
+ "(%(source)s)"
41
+ if message.code is None
42
+ else "%(code)s(%(source)s)"
43
+ if message.location.function is None
44
+ else "[%(code)s(%(source)s), %(function)s]"
45
+ )
46
+ template = (
47
+ f"{template_location}: {template_code}: %(message)s"
48
+ if template_location
49
+ else f"{template_code}: %(message)s"
39
50
  )
40
- template = f"{template_location}: {template_code}: %(message)s"
41
51
 
42
52
  output.append(
43
53
  template
prospector/message.py CHANGED
@@ -3,9 +3,11 @@ from typing import Optional, Union
3
3
 
4
4
 
5
5
  class Location:
6
+ _path: Optional[Path]
7
+
6
8
  def __init__(
7
9
  self,
8
- path: Union[Path, str],
10
+ path: Optional[Union[Path, str]],
9
11
  module: Optional[str],
10
12
  function: Optional[str],
11
13
  line: Optional[int],
@@ -15,6 +17,8 @@ class Location:
15
17
  self._path = path.absolute()
16
18
  elif isinstance(path, str):
17
19
  self._path = Path(path).absolute()
20
+ elif path is None:
21
+ self._path = None
18
22
  else:
19
23
  raise ValueError
20
24
  self.module = module or None
@@ -23,13 +27,15 @@ class Location:
23
27
  self.character = None if character == -1 else character
24
28
 
25
29
  @property
26
- def path(self) -> Path:
30
+ def path(self) -> Optional[Path]:
27
31
  return self._path
28
32
 
29
- def absolute_path(self) -> Path:
33
+ def absolute_path(self) -> Optional[Path]:
30
34
  return self._path
31
35
 
32
- def relative_path(self, root: Optional[Path]) -> Path:
36
+ def relative_path(self, root: Optional[Path]) -> Optional[Path]:
37
+ if self._path is None:
38
+ return None
33
39
  return self._path.relative_to(root) if root else self._path
34
40
 
35
41
  def __repr__(self) -> str:
@@ -46,6 +52,13 @@ class Location:
46
52
  def __lt__(self, other: "Location") -> bool:
47
53
  if not isinstance(other, Location):
48
54
  raise ValueError
55
+
56
+ if self._path is None and other._path is None:
57
+ return False
58
+ if self._path is None:
59
+ return True
60
+ if other._path is None:
61
+ return False
49
62
  if self._path == other._path:
50
63
  if self.line == other.line:
51
64
  return (self.character or -1) < (other.character or -1)
prospector/postfilter.py CHANGED
@@ -1,11 +1,10 @@
1
1
  from pathlib import Path
2
- from typing import List
3
2
 
4
3
  from prospector.message import Message
5
4
  from prospector.suppression import get_suppressions
6
5
 
7
6
 
8
- def filter_messages(filepaths: List[Path], messages: List[Message]) -> List[Message]:
7
+ def filter_messages(filepaths: list[Path], messages: list[Message]) -> list[Message]:
9
8
  """
10
9
  This method post-processes all messages output by all tools, in order to filter
11
10
  out any based on the overall output.
@@ -29,7 +28,7 @@ def filter_messages(filepaths: List[Path], messages: List[Message]) -> List[Mess
29
28
  filtered = []
30
29
  for message in messages:
31
30
  # first get rid of the pylint informational messages
32
- relative_message_path = Path(message.location.path)
31
+ relative_message_path = message.location.path
33
32
 
34
33
  if message.source == "pylint" and message.code in (
35
34
  "suppressed-message",
prospector/suppression.py CHANGED
@@ -24,6 +24,7 @@ import re
24
24
  import warnings
25
25
  from collections import defaultdict
26
26
  from pathlib import Path
27
+ from typing import Optional
27
28
 
28
29
  from prospector import encoding
29
30
  from prospector.exceptions import FatalProspectorException
@@ -63,9 +64,11 @@ _PYLINT_EQUIVALENTS = {
63
64
  }
64
65
 
65
66
 
66
- def _parse_pylint_informational(messages: list[Message]) -> tuple[set[Path], dict[Path, dict[int, list[str]]]]:
67
- ignore_files: set[Path] = set()
68
- ignore_messages: dict[Path, dict[int, list[str]]] = defaultdict(lambda: defaultdict(list))
67
+ def _parse_pylint_informational(
68
+ messages: list[Message],
69
+ ) -> tuple[set[Optional[Path]], dict[Optional[Path], dict[int, list[str]]]]:
70
+ ignore_files: set[Optional[Path]] = set()
71
+ ignore_messages: dict[Optional[Path], dict[int, list[str]]] = defaultdict(lambda: defaultdict(list))
69
72
 
70
73
  for message in messages:
71
74
  if message.source == "pylint":
@@ -86,15 +89,15 @@ def _parse_pylint_informational(messages: list[Message]) -> tuple[set[Path], dic
86
89
 
87
90
  def get_suppressions(
88
91
  filepaths: list[Path], messages: list[Message]
89
- ) -> tuple[set[Path], dict[Path, set[int]], dict[Path, dict[int, set[tuple[str, str]]]]]:
92
+ ) -> tuple[set[Optional[Path]], dict[Path, set[int]], dict[Optional[Path], dict[int, set[tuple[str, str]]]]]:
90
93
  """
91
94
  Given every message which was emitted by the tools, and the
92
95
  list of files to inspect, create a list of files to ignore,
93
96
  and a map of filepath -> line-number -> codes to ignore
94
97
  """
95
- paths_to_ignore: set[Path] = set()
98
+ paths_to_ignore: set[Optional[Path]] = set()
96
99
  lines_to_ignore: dict[Path, set[int]] = defaultdict(set)
97
- messages_to_ignore: dict[Path, dict[int, set[tuple[str, str]]]] = defaultdict(lambda: defaultdict(set))
100
+ messages_to_ignore: dict[Optional[Path], dict[int, set[tuple[str, str]]]] = defaultdict(lambda: defaultdict(set))
98
101
 
99
102
  # first deal with 'noqa' style messages
100
103
  for filepath in filepaths:
@@ -113,12 +116,12 @@ def get_suppressions(
113
116
  # now figure out which messages were suppressed by pylint
114
117
  pylint_ignore_files, pylint_ignore_messages = _parse_pylint_informational(messages)
115
118
  paths_to_ignore |= pylint_ignore_files
116
- for filepath, line in pylint_ignore_messages.items():
119
+ for pylint_filepath, line in pylint_ignore_messages.items():
117
120
  for line_number, codes in line.items():
118
121
  for code in codes:
119
- messages_to_ignore[filepath][line_number].add(("pylint", code))
122
+ messages_to_ignore[pylint_filepath][line_number].add(("pylint", code))
120
123
  if code in _PYLINT_EQUIVALENTS:
121
124
  for equivalent in _PYLINT_EQUIVALENTS[code]:
122
- messages_to_ignore[filepath][line_number].add(equivalent)
125
+ messages_to_ignore[pylint_filepath][line_number].add(equivalent)
123
126
 
124
127
  return paths_to_ignore, lines_to_ignore, messages_to_ignore
@@ -1,5 +1,4 @@
1
1
  from io import StringIO
2
- from typing import List
3
2
 
4
3
  from pylint.exceptions import UnknownMessageError
5
4
  from pylint.message import Message as PylintMessage
@@ -35,5 +34,5 @@ class Collector(BaseReporter):
35
34
  message = Message("pylint", msg_symbol, loc, msg.msg)
36
35
  self._messages.append(message)
37
36
 
38
- def get_messages(self) -> List[Message]:
37
+ def get_messages(self) -> list[Message]:
39
38
  return self._messages
@@ -47,6 +47,16 @@ class RuffTool(ToolBase):
47
47
  completed_process = subprocess.run( # noqa: S603
48
48
  [self.ruff_bin, *self.ruff_args, *found_files.python_modules], capture_output=True
49
49
  )
50
+ if not completed_process.stdout:
51
+ messages.append(
52
+ Message(
53
+ "ruff",
54
+ "",
55
+ Location(None, None, None, None, None),
56
+ completed_process.stderr.decode(),
57
+ )
58
+ )
59
+ return messages
50
60
  for message in json.loads(completed_process.stdout):
51
61
  sub_message = {}
52
62
  if message.get("url"):
prospector/tools/utils.py CHANGED
@@ -4,8 +4,9 @@ from typing import Optional
4
4
 
5
5
 
6
6
  class CaptureStream(TextIOWrapper):
7
- def __init__(self) -> None:
7
+ def __init__(self, tty: bool) -> None:
8
8
  self.contents = ""
9
+ self._tty = tty
9
10
 
10
11
  def write(self, text: str, /) -> int:
11
12
  self.contents += text
@@ -17,6 +18,9 @@ class CaptureStream(TextIOWrapper):
17
18
  def flush(self) -> None:
18
19
  pass
19
20
 
21
+ def isatty(self) -> bool:
22
+ return self._tty
23
+
20
24
 
21
25
  class CaptureOutput:
22
26
  _prev_streams = None
@@ -28,14 +32,16 @@ class CaptureOutput:
28
32
 
29
33
  def __enter__(self) -> "CaptureOutput":
30
34
  if self.hide:
35
+ is_a_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
36
+
31
37
  self._prev_streams = (
32
38
  sys.stdout,
33
39
  sys.stderr,
34
40
  sys.__stdout__,
35
41
  sys.__stderr__,
36
42
  )
37
- self.stdout = CaptureStream()
38
- self.stderr = CaptureStream()
43
+ self.stdout = CaptureStream(is_a_tty)
44
+ self.stderr = CaptureStream(is_a_tty)
39
45
  sys.stdout, sys.__stdout__ = self.stdout, self.stdout # type: ignore[misc]
40
46
  sys.stderr, sys.__stderr__ = self.stderr, self.stderr # type: ignore[misc]
41
47
  return self
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prospector
3
- Version: 1.13.1
3
+ Version: 1.13.3
4
4
  Summary: Prospector is a tool to analyse Python code by aggregating the result of other tools.
5
5
  Home-page: http://prospector.readthedocs.io
6
6
  License: GPLv2+
@@ -1,7 +1,7 @@
1
1
  prospector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  prospector/__main__.py,sha256=-gdHYZxwq_P8er7HuZEBImY0pwaFq8uIa78dQdJsTTQ,71
3
3
  prospector/autodetect.py,sha256=Ok8S6jpDiGyhQlnRCMWpsLpSAIXWxA-NQphQuPaOm6o,3112
4
- prospector/blender.py,sha256=dncHyG5nPVaugyGbgK_lPWx3w0yyY38U1t389wCfFNQ,4955
4
+ prospector/blender.py,sha256=ldQSkfoEKv6pd72B9YCYdapeGUzgfhGzieAu7To3l6Y,4926
5
5
  prospector/blender_combinations.yaml,sha256=yN7BOUCDomDZVRZzYxsRdxQPLpzxm9TDhv18B_GdLPU,6551
6
6
  prospector/compat.py,sha256=p_2BOebzUcKbUAd7mW8rn6tIc10R96gJuZS71QI0XY4,360
7
7
  prospector/config/__init__.py,sha256=4nYshBncKUvZrwNKmp2bQ2mQ8uRS7GU20xPbiC-nJ9g,14793
@@ -11,19 +11,19 @@ prospector/encoding.py,sha256=67sbqzcUoQqi3PRm_P3GNGwcL1N56RZ3T_YHmSrICEE,1549
11
11
  prospector/exceptions.py,sha256=3P58RNF7j1n4CUIZ8VM5BVhB4Q6UtVs-dQRG8TRCZ7o,1248
12
12
  prospector/finder.py,sha256=-gqGVEv6c6FEyKWZIcQ7FquSlVKvpVWsJK9-HkTeg3c,4778
13
13
  prospector/formatters/__init__.py,sha256=ixMDeM27mmaLBxHmcRYoC1tPPi17SBXS91Rc2dMv30s,467
14
- prospector/formatters/base.py,sha256=dGe715MOwy80-iWxFW-Qv3hc-_L8Ha6QauOUvJvkxRU,1385
14
+ prospector/formatters/base.py,sha256=SMRvrX1xAQ1aaM8S-wPSH7cbs-x8QEXmLW0PeSf6PIg,1436
15
15
  prospector/formatters/base_summary.py,sha256=C2O6XAU4l-rOHL1q4rA56jO9sU7Sf0sHRvqgiY6PQgw,1410
16
16
  prospector/formatters/emacs.py,sha256=FwMqdDxCKA51B76ORuSJE1E2xy-glNpORIzAQaw7LUM,807
17
- prospector/formatters/grouped.py,sha256=4JRA8Pz-kcrbmhz3Ssm-G-0iRPy5iBLgy-M44uTzwKQ,1372
17
+ prospector/formatters/grouped.py,sha256=-vD8I3unoO0FBISoUPHZPCa0p-btWMvt1D_UxzmcGDA,1357
18
18
  prospector/formatters/json.py,sha256=_DbZ_Eb0fmZf6qZMXCU__QRXv_awkBisZ-jvGfOr4aw,1000
19
- prospector/formatters/pylint.py,sha256=XzX9byC9qAuY_wp5mORYpdaVrkchYTfkd7rTwnlsE7U,2568
19
+ prospector/formatters/pylint.py,sha256=WAdD9tMYkk4BWfhoqJJBRc2d3xxQ0bJdQXOZU0ZzYTE,2871
20
20
  prospector/formatters/text.py,sha256=5czha8YfdJ9SD6vWLHE85LgB3-d0lHOsxpYhtokRmTU,1611
21
21
  prospector/formatters/vscode.py,sha256=ffP-JmrgZhhdirTj1ldV5fG10hdDiHjRfekSqQpQmx0,1643
22
22
  prospector/formatters/xunit.py,sha256=e5ObAWSLm-ekvWs8xsi-OaIL2yoYedlxuJdUhLZ8gkk,2464
23
23
  prospector/formatters/yaml.py,sha256=0RAL5BaaL9A0DfWZ-bdpK1mwgr8zJ_ULVwlnSXVPlDU,684
24
- prospector/message.py,sha256=RkxE_tJfce-LH30I9v4uv4SOd7rwz9xkn_eBTKcG_KI,2907
24
+ prospector/message.py,sha256=ty_e5T7fpZcZb69sAUIgvV-9qHuAx6-Nkq3bP6IVAqw,3279
25
25
  prospector/pathutils.py,sha256=CyZIj4WXGill8OfnqRvcVqZYX0lzL3QcIxpkxCz-dkE,1219
26
- prospector/postfilter.py,sha256=cmZOcKp_EAEJ7Fieb45QMlyq-Ym-LHXG8A6tATKuiak,2231
26
+ prospector/postfilter.py,sha256=sbUv8P8XE1-7mXh12iZ539FVBK1JSoC-VmnsQyr6EOk,2201
27
27
  prospector/profiles/__init__.py,sha256=q9zPLVEwo7qoouYFrmENsmByFrKKkr27Dd_Wo9btTJI,683
28
28
  prospector/profiles/exceptions.py,sha256=MDky4KXVwfOlW1yCbyp8Y07D8Kfz76jL3z-8T3WQIFI,1062
29
29
  prospector/profiles/profile.py,sha256=U8vDdyfka0_Ht9cYT2i_c-xbMcktSpS1h53cU7tGerk,17828
@@ -44,7 +44,7 @@ prospector/profiles/profiles/strictness_veryhigh.yaml,sha256=m93J1OzGCRVTWrIQbzh
44
44
  prospector/profiles/profiles/strictness_verylow.yaml,sha256=YxZowcBtA3tAaHJGz2htTdAJ-AXmlHB-o4zEYKPRfJg,833
45
45
  prospector/profiles/profiles/test_warnings.yaml,sha256=arUcV9MnqiZJEHURH9bVRSYDhYUegNc-ltFYe_yQW44,23
46
46
  prospector/run.py,sha256=hjNyzY-wyedGwJspT80jCz4usk-HqL6FmqDcinASJZA,8403
47
- prospector/suppression.py,sha256=oN4sd4b7puum_zv84YqrNoarSgrc28-SYXTK1aJ7ZpY,4818
47
+ prospector/suppression.py,sha256=5VPSvw1ECIR1_4spf0Q2jUx04GygEoptA4LDFaErYVU,4954
48
48
  prospector/tools/__init__.py,sha256=9tDmxL_kn5jmAACeSi1jtSvT-9tI468Ccn1Up2wUFi0,2956
49
49
  prospector/tools/bandit/__init__.py,sha256=oQZANkiQh3MI2HjtLNXbW-yWrtzlbdq2oaGl2_7B4dM,2711
50
50
  prospector/tools/base.py,sha256=T1F-vq4rNcaToA4fXjZmcozkABpeiz0odFAMVMEJM1w,1838
@@ -57,15 +57,15 @@ prospector/tools/pycodestyle/__init__.py,sha256=uMpUxqsPsryEsfyfGxpLzwoWUjIvfxIQ
57
57
  prospector/tools/pydocstyle/__init__.py,sha256=WB-AT-c1FeUUUWATUzJbBLeREtu-lxT03bChh4nablo,2776
58
58
  prospector/tools/pyflakes/__init__.py,sha256=53NQFODU416KO991NxW14gChjagbSAhhfErx1ll7VUQ,5631
59
59
  prospector/tools/pylint/__init__.py,sha256=WoI23QXmGlumgZMRg1-tQJ8Tpzl9_KpLUQIKlb7vEkE,11108
60
- prospector/tools/pylint/collector.py,sha256=_PEV_8nV77_87m8rA2Td4WOqO9F2UaIQ4Bb_aUv_WQg,1377
60
+ prospector/tools/pylint/collector.py,sha256=OSDV_58EE5C-fykXxDykIji88N1e-GLfHB849HrjefA,1353
61
61
  prospector/tools/pylint/linter.py,sha256=YQ9SOna4WjbbauqSgUio6Ss8zN08PCr3aKdK9rMi7Ag,2143
62
62
  prospector/tools/pyright/__init__.py,sha256=USqauZofh-8ZSKGwXRXoaM2ItzfSFo2nGwPtLGEWICU,3346
63
63
  prospector/tools/pyroma/__init__.py,sha256=GPQRJZfbs_SI0RBTyySz-4SIuM__YoLfXAm7uYVXAS8,3151
64
- prospector/tools/ruff/__init__.py,sha256=rvjkGDTjRXOMqQnGCfDbbKmMkXN6m1Um8bq0UFkzSkU,2978
65
- prospector/tools/utils.py,sha256=4wj7Lz-mYs6QIdvP-kCLlXs-ZRSkrwLpSKn9xHvDpgw,1585
64
+ prospector/tools/ruff/__init__.py,sha256=HRj2S-I45DCg3y4T6035PvHyRutTPU9Boeh3IWU9_pw,3300
65
+ prospector/tools/utils.py,sha256=cRCogsMCH0lPBhdujPsIY0ovNAL6TAxBMohZRES02-4,1770
66
66
  prospector/tools/vulture/__init__.py,sha256=eaTh4X5onNlBMuz1x0rmcRn7x5XDVDgqftjIEd47eWI,3583
67
- prospector-1.13.1.dist-info/entry_points.txt,sha256=SxvCGt8MJTEZefHAvwnUc6jDetgCaaYY1Zpifuk8tqU,50
68
- prospector-1.13.1.dist-info/LICENSE,sha256=WoTRadDy8VbcIKoVzl5Q1QipuD_cexAf3ul4MaVLttc,18044
69
- prospector-1.13.1.dist-info/WHEEL,sha256=gSF7fibx4crkLz_A-IKR6kcuq0jJ64KNCkG8_bcaEao,88
70
- prospector-1.13.1.dist-info/METADATA,sha256=_2kLunjpAQK6LUUep2TZiZ9DMjJgdcWE_W2dm8Peduo,9962
71
- prospector-1.13.1.dist-info/RECORD,,
67
+ prospector-1.13.3.dist-info/entry_points.txt,sha256=SxvCGt8MJTEZefHAvwnUc6jDetgCaaYY1Zpifuk8tqU,50
68
+ prospector-1.13.3.dist-info/LICENSE,sha256=WoTRadDy8VbcIKoVzl5Q1QipuD_cexAf3ul4MaVLttc,18044
69
+ prospector-1.13.3.dist-info/WHEEL,sha256=gSF7fibx4crkLz_A-IKR6kcuq0jJ64KNCkG8_bcaEao,88
70
+ prospector-1.13.3.dist-info/METADATA,sha256=Ds5fPmZfhk-Lnx4mXweNkbi5w6Pwa8g8CP_1LknAMJ0,9962
71
+ prospector-1.13.3.dist-info/RECORD,,