Skip to content

Commit d3edd60

Browse files
authored
Fix exception causes all over the codebase (#8998)
In some parts of the code, an exception is being caught and replaced with a more user-friendly error. In these cases the syntax `raise new_error from old_error` needs to be used. Python 3's exception chaining means it shows not only the traceback of the current exception, but that of the original exception (and possibly more.) This is regardless of `raise from`. The usage of `raise from` tells Python to put a more accurate message between the tracebacks. Instead of this: ``` During handling of the above exception, another exception occurred: ``` You'll get this: ``` The above exception was the direct cause of the following exception: ``` The first is inaccurate, because it signifies a bug in the exception-handling code itself, which is a separate situation than wrapping an exception.
1 parent a810e0c commit d3edd60

File tree

12 files changed

+26
-25
lines changed

12 files changed

+26
-25
lines changed

mypy/build.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,13 +1988,13 @@ def parse_file(self) -> None:
19881988
raise CompileError([
19891989
"mypy: can't read file '{}': {}".format(
19901990
self.path, os.strerror(ioerr.errno))],
1991-
module_with_blocker=self.id)
1991+
module_with_blocker=self.id) from ioerr
19921992
except (UnicodeDecodeError, DecodeError) as decodeerr:
19931993
if self.path.endswith('.pyd'):
19941994
err = "mypy: stubgen does not support .pyd files: '{}'".format(self.path)
19951995
else:
19961996
err = "mypy: can't decode file '{}': {}".format(self.path, str(decodeerr))
1997-
raise CompileError([err], module_with_blocker=self.id)
1997+
raise CompileError([err], module_with_blocker=self.id) from decodeerr
19981998
else:
19991999
assert source is not None
20002000
self.source_hash = compute_hash(source)

mypy/dmypy/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,8 +534,8 @@ def read_status(status_file: str) -> Dict[str, object]:
534534
with open(status_file) as f:
535535
try:
536536
data = json.load(f)
537-
except Exception:
538-
raise BadStatus("Malformed status file (not JSON)")
537+
except Exception as e:
538+
raise BadStatus("Malformed status file (not JSON)") from e
539539
if not isinstance(data, dict):
540540
raise BadStatus("Invalid status file (not a dict)")
541541
return data

mypy/dmypy_util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ def receive(connection: IPCBase) -> Any:
2424
raise OSError("No data received")
2525
try:
2626
data = json.loads(bdata.decode('utf8'))
27-
except Exception:
28-
raise OSError("Data received is not valid JSON")
27+
except Exception as e:
28+
raise OSError("Data received is not valid JSON") from e
2929
if not isinstance(data, dict):
3030
raise OSError("Data received is not a dict (%s)" % str(type(data)))
3131
return data

mypy/ipc.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def write(self, data: bytes) -> None:
109109
assert err == 0, err
110110
assert bytes_written == len(data)
111111
except WindowsError as e:
112-
raise IPCException("Failed to write with error: {}".format(e.winerror))
112+
raise IPCException("Failed to write with error: {}".format(e.winerror)) from e
113113
else:
114114
self.connection.sendall(data)
115115
self.connection.shutdown(socket.SHUT_WR)
@@ -131,11 +131,11 @@ def __init__(self, name: str, timeout: Optional[float]) -> None:
131131
timeout = int(self.timeout * 1000) if self.timeout else _winapi.NMPWAIT_WAIT_FOREVER
132132
try:
133133
_winapi.WaitNamedPipe(self.name, timeout)
134-
except FileNotFoundError:
135-
raise IPCException("The NamedPipe at {} was not found.".format(self.name))
134+
except FileNotFoundError as e:
135+
raise IPCException("The NamedPipe at {} was not found.".format(self.name)) from e
136136
except WindowsError as e:
137137
if e.winerror == _winapi.ERROR_SEM_TIMEOUT:
138-
raise IPCException("Timed out waiting for connection.")
138+
raise IPCException("Timed out waiting for connection.") from e
139139
else:
140140
raise
141141
try:
@@ -150,7 +150,7 @@ def __init__(self, name: str, timeout: Optional[float]) -> None:
150150
)
151151
except WindowsError as e:
152152
if e.winerror == _winapi.ERROR_PIPE_BUSY:
153-
raise IPCException("The connection is busy.")
153+
raise IPCException("The connection is busy.") from e
154154
else:
155155
raise
156156
_winapi.SetNamedPipeHandleState(self.connection,
@@ -237,8 +237,8 @@ def __enter__(self) -> 'IPCServer':
237237
else:
238238
try:
239239
self.connection, _ = self.sock.accept()
240-
except socket.timeout:
241-
raise IPCException('The socket timed out')
240+
except socket.timeout as e:
241+
raise IPCException('The socket timed out') from e
242242
return self
243243

244244
def __exit__(self,

mypy/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,11 @@ def _python_executable_from_version(python_version: Tuple[int, int]) -> str:
192192
['-c', 'import sys; print(sys.executable)'],
193193
stderr=subprocess.STDOUT).decode().strip()
194194
return sys_exe
195-
except (subprocess.CalledProcessError, FileNotFoundError):
195+
except (subprocess.CalledProcessError, FileNotFoundError) as e:
196196
raise PythonExecutableInferenceError(
197197
'failed to find a Python executable matching version {},'
198-
' perhaps try --python-executable, or --no-site-packages?'.format(python_version))
198+
' perhaps try --python-executable, or --no-site-packages?'.format(python_version)
199+
) from e
199200

200201

201202
def infer_python_executable(options: Options,

mypy/moduleinspect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def get_package_properties(package_id: str) -> ModuleProperties:
4444
try:
4545
package = importlib.import_module(package_id)
4646
except BaseException as e:
47-
raise InspectError(str(e))
47+
raise InspectError(str(e)) from e
4848
name = getattr(package, '__name__', None)
4949
file = getattr(package, '__file__', None)
5050
path = getattr(package, '__path__', None) # type: Optional[List[str]]

mypy/stubgen.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,7 +1191,7 @@ def collect_build_targets(options: Options, mypy_opts: MypyOptions) -> Tuple[Lis
11911191
try:
11921192
source_list = create_source_list(options.files, mypy_opts)
11931193
except InvalidSourceList as e:
1194-
raise SystemExit(str(e))
1194+
raise SystemExit(str(e)) from e
11951195
py_modules = [StubSource(m.module, m.path) for m in source_list]
11961196
c_modules = []
11971197

@@ -1362,7 +1362,7 @@ def generate_asts_for_modules(py_modules: List[StubSource],
13621362
try:
13631363
res = build(list(py_modules), mypy_options)
13641364
except CompileError as e:
1365-
raise SystemExit("Critical error during semantic analysis: {}".format(e))
1365+
raise SystemExit("Critical error during semantic analysis: {}".format(e)) from e
13661366

13671367
for mod in py_modules:
13681368
mod.ast = res.graph[mod.module].tree

mypy/stubtest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@ def build_stubs(modules: List[str], options: Options, find_submodules: bool = Fa
957957
except mypy.errors.CompileError as e:
958958
output = [_style("error: ", color="red", bold=True), "failed mypy compile.\n", str(e)]
959959
print("".join(output))
960-
raise RuntimeError
960+
raise RuntimeError from e
961961
if res.errors:
962962
output = [_style("error: ", color="red", bold=True), "failed mypy build.\n"]
963963
print("".join(output) + "\n".join(res.errors))

mypy/stubutil.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def find_module_path_and_all_py2(module: str,
9191
except subprocess.CalledProcessError as e:
9292
path = find_module_path_using_py2_sys_path(module, interpreter)
9393
if path is None:
94-
raise CantImport(module, str(e))
94+
raise CantImport(module, str(e)) from e
9595
return path, None
9696
output = output_bytes.decode('ascii').strip().splitlines()
9797
module_path = output[0]
@@ -153,7 +153,7 @@ def find_module_path_and_all_py3(inspect: ModuleInspect,
153153
# Fall back to finding the module using sys.path.
154154
path = find_module_path_using_sys_path(module, sys.path)
155155
if path is None:
156-
raise CantImport(module, str(e))
156+
raise CantImport(module, str(e)) from e
157157
return path, None
158158
if mod.is_c_module:
159159
return None

mypy/suggestions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,8 @@ def find_node_by_file_and_line(self, file: str, line: int) -> Tuple[str, SymbolN
568568
raise SuggestionFailure('Source file is not a Python file')
569569
try:
570570
modname, _ = self.finder.crawl_up(os.path.normpath(file))
571-
except InvalidSourceList:
572-
raise SuggestionFailure('Invalid source file name: ' + file)
571+
except InvalidSourceList as e:
572+
raise SuggestionFailure('Invalid source file name: ' + file) from e
573573
if modname not in self.graph:
574574
raise SuggestionFailure('Unknown module: ' + modname)
575575
# We must be sure about any edits in this file as this might affect the line numbers.

mypy/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def decode_python_encoding(source: bytes, pyversion: Tuple[int, int]) -> str:
132132
try:
133133
source_text = source.decode(encoding)
134134
except LookupError as lookuperr:
135-
raise DecodeError(str(lookuperr))
135+
raise DecodeError(str(lookuperr)) from lookuperr
136136
return source_text
137137

138138

mypyc/test/test_run.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def run_setup(script_name: str, script_args: List[str]) -> bool:
8282
# "interrupted" as the argument. Convert it back so that
8383
# pytest will exit instead of just failing the test.
8484
if code == "interrupted":
85-
raise KeyboardInterrupt
85+
raise KeyboardInterrupt from e
8686

8787
return code == 0 or code is None
8888

0 commit comments

Comments
 (0)