Skip to content

Commit 99b08ba

Browse files
committed
fix: capture mypy stderr output on fatal options error instead of generic message
1 parent aeafb1a commit 99b08ba

1 file changed

Lines changed: 68 additions & 57 deletions

File tree

prospector/tools/mypy/__init__.py

Lines changed: 68 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import io
12
import json
23
import re
4+
import sys
35
from multiprocessing import Process, Queue
46
from typing import (
57
TYPE_CHECKING,
@@ -126,68 +128,77 @@ def run(self, found_files: FileFinder) -> list[Message]:
126128

127129
def _run_std(self, args: list[str]) -> list[Message]:
128130
messages = []
131+
original_stderr = sys.stderr
132+
captured_stderr = io.StringIO()
133+
sys.stderr = captured_stderr
129134
try:
130-
sources, options = mypy.main.process_options(args, fscache=self.fscache)
131-
except (SystemExit, Exception) as e:
132-
message = "The error(s) will be displayed before the messages" if isinstance(e, SystemExit) else str(e)
133-
messages.append(
134-
Message(
135-
"mypy",
136-
code="fatal-options-error",
137-
message=message,
138-
location=Location(
139-
path="",
140-
module=None,
141-
function=None,
142-
line=0,
143-
character=0,
144-
),
135+
try:
136+
sources, options = mypy.main.process_options(args, fscache=self.fscache)
137+
except (SystemExit, Exception) as e:
138+
stderr_output = captured_stderr.getvalue()
139+
message = stderr_output or (
140+
"The error(s) will be displayed before the messages" if isinstance(e, SystemExit) else str(e)
145141
)
146-
)
147-
return messages
148-
options.output = "json"
149-
try:
150-
res = mypy.build.build(sources, options, fscache=self.fscache)
151-
except Exception as e:
152-
messages.append(
153-
Message(
154-
"mypy",
155-
code="fatal-build-error",
156-
message=str(e),
157-
location=Location(
158-
path="",
159-
module=None,
160-
function=None,
161-
line=0,
162-
character=0,
163-
),
142+
messages.append(
143+
Message(
144+
"mypy",
145+
code="fatal-options-error",
146+
message=message,
147+
location=Location(
148+
path="",
149+
module=None,
150+
function=None,
151+
line=0,
152+
character=0,
153+
),
154+
)
164155
)
165-
)
166-
return messages
167-
168-
for mypy_json in res.errors:
169-
mypy_message = json.loads(mypy_json)
170-
message = f"{mypy_message['message']}."
171-
if mypy_message.get("hint", ""):
172-
message = f"{message} {mypy_message['hint']}."
173-
code = mypy_message["code"]
174-
messages.append(
175-
Message(
176-
"mypy",
177-
code=code,
178-
location=Location(
179-
path=mypy_message["file"],
180-
module=None,
181-
function=None,
182-
line=mypy_message["line"],
183-
character=mypy_message["column"],
184-
),
185-
message=message,
186-
doc_url=f"{mypy.errors.BASE_RTD_URL}-{code}",
156+
return messages
157+
options.output = "json"
158+
try:
159+
res = mypy.build.build(sources, options, fscache=self.fscache)
160+
except Exception as e:
161+
messages.append(
162+
Message(
163+
"mypy",
164+
code="fatal-build-error",
165+
message=str(e),
166+
location=Location(
167+
path="",
168+
module=None,
169+
function=None,
170+
line=0,
171+
character=0,
172+
),
173+
)
174+
)
175+
return messages
176+
177+
for mypy_json in res.errors:
178+
mypy_message = json.loads(mypy_json)
179+
message = f"{mypy_message['message']}."
180+
if mypy_message.get("hint", ""):
181+
message = f"{message} {mypy_message['hint']}."
182+
code = mypy_message["code"]
183+
messages.append(
184+
Message(
185+
"mypy",
186+
code=code,
187+
location=Location(
188+
path=mypy_message["file"],
189+
module=None,
190+
function=None,
191+
line=mypy_message["line"],
192+
character=mypy_message["column"],
193+
),
194+
message=message,
195+
doc_url=f"{mypy.errors.BASE_RTD_URL}-{code}",
196+
)
187197
)
188-
)
189198

190-
return messages
199+
return messages
200+
finally:
201+
sys.stderr = original_stderr
191202

192203
def get_ignored_codes(self, line: str) -> list[tuple[str, int]]:
193204
match = _IGNORE_RE.search(line)

0 commit comments

Comments
 (0)