Repository: numpy/numpy

Issue:
Chain exceptions where appropriate
Picking up from #15731, there are many places in numpy where we do something like:
```python
try:
    something_which_throws_typeError
except TypeError:
    raise ValueError("a clearer explanation of what went wrong")
```

It would produce a marginally clearer error if we could change these to use traceback chaining. Our two options are either:

1. The inner error provides valuable information that is not present in the outer error. We should use `from e`.
    ```python
    try:
        something_which_throws_typeError
    except TypeError as e:
        raise ValueError("a clearer explanation of what went wrong") from e
    ```
2. The inner error provides  no valuable information that is not present in the outer error. We should use `from None`:
   ```python
   try:
       some_dict[name]
   except KeyError:
       raise ValueError("The name %s is not supported" % name) from None
   ```
   In that example all the information from the `KeyError` is already in the message of the new error, so there is no point chaining



An example of such a change would be [this line](https://github.com/numpy/numpy/pull/15731/files#diff-e55dc29434893b7f529192a2e77e9b07R7070) of this otherwise-discarded patch.

For now, I would recommend we only apply this to cases where the exception is thrown unconditionally, as other cases can be more nuanced.

---

### Please see [this list](https://github.com/numpy/numpy/issues/15986#issuecomment-660762674) for potential places to contribute. Special thanks to @nkleinbaer for compiling the list!


Discussion:
Sure @JanukanS, thanks. We don't usually assign issues. So you can search for these type of calls and simply open a PR with suggested changes linking this issue.
Hi @seberg 
I determined the part to be changed, can i work on this issue too?
Can I work on this issue? It is my first issue and I want to give it a try!
@ayao451 please feel free. A good place to start might be by checking the PRs linked in this issue: exception chaining has been added in quite a few places, and a few other instances have been identified as better off unchanged (e.g. nested exceptions in the test suite). 
A lot of these instances have been fixed (thanks to all the contributors for this!) It would be nice to have a "definition of done" for this issue if anyone wants to do a little forensics to see what's already been tackled, what's been reviewed and determined better left as-is, and what still remains to be done.
@rossbar I think I can help the 'forensics' you describe above. My thought was to create a regex expression to search for instances of nested exceptions in the codebase (excluding tests).  Something like  `(\s*)except .*\n(\1\s+.*\n)*\1\s+raise` to capture the indentation level of the original `except` statement, then searching for `raise` within that indent block. 

Then I could cross-reference that list with those instances that have already been addressed in the PRs above, and make a table of what's been addressed and what still needs to be reviewed.

 Does that seem like a reasonable approach? I'm a complete beginner, found this issue through the 'good first issue' tag, so just want to ensure I'm not going down a rabbit hole or creating additional confusion.
@nkleinbaer that approach sounds reasonable, and I think tabulating the results in this issue would be very useful. Even if the cross-referencing proves to be difficult to automate, having a list of all of the potential instances for exception chaining would help finish off this issue. If you are interested in taking this on I think that'd be great! Feel free ping with any questions/comments.
OK, to start off here is a table of the files that have been touched in reference to this issue, the number of instances reviewed, the action taken, and associated PRs. I've used the nomenclature **"Implemented exception chaining"** to indicate something of the form `except SomeError as e: raise SomeOtherError from e` and **"Explicitly silenced exception chaining"** to indicate the form `except SomeError as e: raise SomeOtherError from None`. Please correct if the this can be phrased better.

In some cases my regex found additional instances of 'nested' exceptions in these files that I do not see included in the PRs. It may be the case that they were intentionally left as is, but because I cannot find any documented 'review' of them I have included them for completeness.

| File                                         | Action Taken                                                                      | Associated PR(s) | Comments                                                                                            |
|----------------------------------------------|-----------------------------------------------------------------------------------|------------------|-----------------------------------------------------------------------------------------------------|
| ctypeslib.py                           | 1 instance each: explicitly silenced exception chaining and implemented exception chaining | #16418           |            1  additional instances may need review https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ctypeslib.py#L151  |                                                                              |
| core/_dtype.py                         | 1 instance(s): explicitly silenced exception chaining                                            | #16418           |                                                                                                     |
| core/_internal.py                      | 2 instance(s): explicitly silenced exception chaining                                            | #16747           |                                                                                                     |
| core/fromnumeric.py                    | 1 instance(s): implemented exception chaining                                                    | #16418           |                                                                                                     |
| core/memap.py                          | 1 instance(s): explicitly silenced exception chaining                                            | #16067           |                                                                                                     |
| core/records.py                        | 2 instance(s): implemented exception chaining                                                    | #16418           |                                1 additional instance may need review   https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/core/records.py#L495                                                                  |
| core/code_generators/generate_umath.py | 1 instance(s): implemented exception chaining                                                    | #16254           |                                                                                                     |
| distutils/command/build_clib.py        | 1 instance(s): implemented exception chaining                                                    | #16032           |                                                                                                     |
| distutils/command/build_ext.py         | 1 instance(s): implemented exception chaining                                                    | #16042           |                                                                                                     |
| lib/_datasource.py                     | 1 instance(s): Completely removed nested exception ->  left inner exception by itself            | #16761           |                                                                                                     |
| lib/hisograms.py                       | 1 instance(s): implemented exception chaining                                                    | #16064           | 1 additional instance added in #16129 - seems to implement exception chaining correctly but may need review                          |
| lib/index_tricks.py                    | 1 instance(s): implemented exception chaining                                                    | #16064           | 1 additional instance needs review   https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/index_tricks.py#L378                        |
| lib/npyio.py                           | 2 instance(s): implemented exception chaining                                                    | #16121           | A bit confused on what happened here. My regex finds 8 instances total - I think all have been reviewed and some were determined to be better left as is in #16218, may be worth a second look |
| lib/shape_base.py                      | 1 instance(s): explicitly silenced exception chaining                                            | #16064           |                                    2 additional instances may need review https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/shape_base.py#L774 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/shape_base.py#L868                                                               |
| lib/ufunclike.py                       | 2 instance(s): implemented exception chaining                                                    | #16064           |                                                                                                     |
| linalg/linalg.py                       | 2 instance(s): implemented exception chaining                                                    | #16061           |                                                                                                     |
| ma/core.py                             | 1 instance(s): explicitly silenced exception chaining                                            | #16067           |     3 additional instances may need review https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ma/core.py#L446 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ma/core.py#L463 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ma/core.py#L3794                                                                                        |
| matrixlib/defmatrix.py                 | 1 instance(s): explicitly silenced exception chaining                                            | #16215           |                                                                                                |
| polynomial/polyutils.py                | 2 instance(s): implemented exception chaining                                                    | #16061           |                                                                                                     |
And here is a table of additional files/instances found by my regex that have NOT yet been reviewed in a PR related to this issue. Some of these look like they are already handling exception chaining properly (example einsumfunc.py), but again included here for completeness because I see no documentation of an explicit review. 

| File                               | Instances found by  Regex | Instances                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
|------------------------------------|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| __init__.py                        | 2                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/__init__.py#L127 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/__init__.py#L292                                                                                                                                                                                                                                                                     |
| compat/py3k.py                     | 1                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/compat/py3k.py#L173                                                                                                                                                                                                                                                                                                                                                                      |
| core/_type_aliases.py              | 1                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/core/_type_aliases.py#L47                                                                                                                                                                                                                                                                                                                                                                |
| core/einsumfunc.py                 | 2                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/core/einsumfunc.py#L583 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/core/einsumfunc.py#L598                                                                                                                                                                                                                                                                                                                                                                  |
| core/code_generators/genapi.py     | 1                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/core/code_generators/genapi.py#L283                                                                                                                                                                                                                                                                                                                                                      |
| distutils/conv_template.py         | 4                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/conv_template.py#L219 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/conv_template.py#L239 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/conv_template.py#L287 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/conv_template.py#L324 |
| distutils/mingw32ccompiler.py      | 1                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/mingw32ccompiler.py#L563                                                                                                                                                                                                                                                                                                                                                       |
| distutils/unixccompiler.py          | 2                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/unixccompiler.py#L52 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/unixccompiler.py#L124                                                                                                                                                                                                                                          |
| distutils/command/config.py        | 3                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/command/config.py#L53 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/command/config.py#L96 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/command/config.py#L456                                                                                                                   |
| distutils/fcompiler/__init__.py    | 2                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/fcompiler/__init__.py#L613 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/fcompiler/__init__.py#L681                                                                                                                                                                                                                             |
| distutils/fcompiler/compaq.py      | 3                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/fcompiler/compaq.py#L82 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/fcompiler/compaq.py#L87 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/fcompiler/compaq.py#L91                                                                                                              |
| distutils/fcompiler/environment.py | 1                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/distutils/fcompiler/environment.py#L35                                                                                                                                                                                                                                                                                                                                                   |
| fft/_pocketfft.py                  | 1                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/fft/_pocketfft.py#L113                                                                                                                                                                                                                                                                                                                                                                   |
| lib/arraypad.py                    | 1                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/arraypad.py#L785                                                                                                                                                                                                                                                                                                                                                                     |
| lib/arraysetops.py                 | 2                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/arraysetops.py#L279 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/arraysetops.py#L303                                                                                                                                                                                                                                                       |
| lib/format.py                      | 4                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/format.py#L379 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/format.py#L596 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/format.py#L617 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/lib/format.py#L745                                                     |
| linalg/lapack_lite/make_lite.py    | 1                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/linalg/lapack_lite/make_lite.py#L264                                                                                                                                                                                                                                                                                                                                                     |
| ma/mrecords.py                     | 3                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ma/mrecords.py#L201 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ma/mrecords.py#L254 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ma/mrecords.py#L276                                                                                                                                                        |
| ma/timer_comparison.py             | 1                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ma/timer_comparison.py#L103                                                                                                                                                                                                                                                                                                                                                              |
| polynomial/_polybase.py            | 2                         | https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/polynomial/_polybase.py#L550 https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/polynomial/_polybase.py#L608                                                                                                                                                                                                                                             |
|                                    |                           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
|                                    |                           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
Thanks for taking the time to put this report together @nkleinbaer , this is a really nice review of the current state of things!

I think the most effective way to present the information to help close this issue would be if we could take all of the instances that you found in your regex search, and list them using a list of checkboxes, e.g.

```
 - [x] https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ctypeslib.py#L151
 - [ ] https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/__init__.py#L127
 ...
```
where instances that have been *changed or reviewed* get a check, and instances that haven't been addressed yet are left un-checked. That way, we have a running list of the instances where a decision has been made (whether to chain exceptions, use `from None`, or leave as-is) so that contributors can focus on the remaining ones.

This should give us a list like:
 - [x] https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/ctypeslib.py#L151
 - [ ] https://github.com/numpy/numpy/blob/6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527/numpy/__init__.py#L127

which I believe GitHub will summarize in a progress bar for the issue.

If it is easy for you to re-organize your results in this way and you don't mind the extra effort, it'd be great if you could add a list formatted as described in a comment below, then I will take the list and move it up to the top comment so it is more visible. You've already done plenty though, so if you don't have the bandwidth that's totally fine - just let me know and I'll organize the list using your results. Thanks for all you've done so far!
Sure, I can do that! I actually wanted to have check boxes in the tables above, but couldn't figure out how to do the formatting (not sure if possible at all). Mega-checklist to follow. 

Resolving patch:
diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py
--- a/numpy/lib/npyio.py
+++ b/numpy/lib/npyio.py
@@ -86,7 +86,7 @@ def __getattribute__(self, key):
         try:
             return object.__getattribute__(self, '_obj')[key]
         except KeyError:
-            raise AttributeError(key)
+            raise AttributeError(key) from None
 
     def __dir__(self):
         """
@@ -446,9 +446,9 @@ def load(file, mmap_mode=None, allow_pickle=False, fix_imports=True,
                                  "when allow_pickle=False")
             try:
                 return pickle.load(fid, **pickle_kwargs)
-            except Exception:
+            except Exception as e:
                 raise IOError(
-                    "Failed to interpret file %s as a pickle" % repr(file))
+                    "Failed to interpret file %s as a pickle" % repr(file)) from e
 
 
 def _save_dispatcher(file, arr, allow_pickle=None, fix_imports=None):
@@ -1435,10 +1435,10 @@ def first_write(self, v):
             for row in X:
                 try:
                     v = format % tuple(row) + newline
-                except TypeError:
+                except TypeError as e:
                     raise TypeError("Mismatch between array dtype ('%s') and "
                                     "format specifier ('%s')"
-                                    % (str(X.dtype), format))
+                                    % (str(X.dtype), format)) from e
                 fh.write(v)
 
         if len(footer) > 0:


Test patch: