Skip to content

Commit

Permalink
Improve re-raises of Exceptions
Browse files Browse the repository at this point in the history
When we use try-except in our code base, often we raise a new exception to replace the original exception. It is good practice to either wrap around the original exception (`from exc`), or bin the original exception (`from None`) if the new exception is a self-contained description of what happened. The latter is for instance the case when we wrap around a dict, catch the KeyError, and provide more meaningful Exception with message, as the `KeyError` is not what the user is after, it wants to know that dtype1 cannot be converted to dtype2, etc.

Without re-raise, you will see `During handling of the above exception, another exception occurred`, which means the two exceptions, original and new, are treated unrelated. With `from exc`, you will see `The above exception was the direct cause of the following exception:`. With `from None`, you will get a clean traceback without the original exception.

Found all cases using pylint (`pylint polars --disable=all --enable=raise-missing-from`). Discussion on the rollout of pylint in CI takes place in pola-rs#4044.

Background reading: https://stefan.sofa-rockers.org/2020/10/28/raise-from/
  • Loading branch information
zundertj committed Jul 24, 2022
1 parent 5f8603b commit 72ff4a0
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 16 deletions.
28 changes: 21 additions & 7 deletions py-polars/polars/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,21 +421,27 @@ def dtype_to_ctype(dtype: PolarsDataType) -> type[_SimpleCData]:
try:
return _DTYPE_TO_CTYPE[dtype]
except KeyError: # pragma: no cover
raise NotImplementedError
raise NotImplementedError(
f"Conversion of polars data type {dtype} to C-type not implemented."
) from None


def dtype_to_ffiname(dtype: PolarsDataType) -> str:
try:
return _DTYPE_TO_FFINAME[dtype]
except KeyError: # pragma: no cover
raise NotImplementedError
raise NotImplementedError(
f"Conversion of polars data type {dtype} to FFI not implemented."
) from None


def dtype_to_py_type(dtype: PolarsDataType) -> type:
try:
return _DTYPE_TO_PY_TYPE[dtype]
except KeyError: # pragma: no cover
raise NotImplementedError
raise NotImplementedError(
f"Conversion of polars data type {dtype} to Python type not implemented."
) from None


def is_polars_dtype(data_type: Any) -> bool:
Expand All @@ -453,7 +459,9 @@ def py_type_to_dtype(data_type: Any) -> type[DataType]:
try:
return _PY_TYPE_TO_DTYPE[data_type]
except KeyError: # pragma: no cover
raise NotImplementedError
raise NotImplementedError(
f"Conversion of Python data type {data_type} to Polars data type not implemented."
) from None


def py_type_to_arrow_type(dtype: type[Any]) -> pa.lib.DataType:
Expand All @@ -463,7 +471,9 @@ def py_type_to_arrow_type(dtype: type[Any]) -> pa.lib.DataType:
try:
return _PY_TYPE_TO_ARROW_TYPE[dtype]
except KeyError: # pragma: no cover
raise ValueError(f"Cannot parse dtype {dtype} into Arrow dtype.")
raise ValueError(
f"Cannot parse Python data type {dtype} into Arrow data type."
) from None


def dtype_to_arrow_type(dtype: PolarsDataType) -> pa.lib.DataType:
Expand All @@ -473,7 +483,9 @@ def dtype_to_arrow_type(dtype: PolarsDataType) -> pa.lib.DataType:
try:
return _DTYPE_TO_ARROW_TYPE[dtype]
except KeyError: # pragma: no cover
raise ValueError(f"Cannot parse dtype {dtype} into Arrow dtype.")
raise ValueError(
f"Cannot parse data type {dtype} into Arrow data type."
) from None


def supported_numpy_char_code(dtype: str) -> bool:
Expand All @@ -484,7 +496,9 @@ def numpy_char_code_to_dtype(dtype: str) -> type[DataType]:
try:
return _NUMPY_CHAR_CODE_TO_DTYPE[dtype]
except KeyError: # pragma: no cover
raise NotImplementedError
raise NotImplementedError(
f"Cannot parse numpy data type {dtype} into Polars data type."
) from None


def maybe_cast(
Expand Down
6 changes: 4 additions & 2 deletions py-polars/polars/datatypes_constructor.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def polars_type_to_constructor(
try:
return _POLARS_TYPE_TO_CONSTRUCTOR[dtype]
except KeyError: # pragma: no cover
raise ValueError(f"Cannot construct PySeries for type {dtype}.")
raise ValueError(f"Cannot construct PySeries for type {dtype}.") from None


if _NUMPY_AVAILABLE and not _DOCUMENTING:
Expand Down Expand Up @@ -102,7 +102,9 @@ def numpy_type_to_constructor(dtype: type[np.dtype]) -> Callable[..., PySeries]:
except KeyError:
return PySeries.new_object
except NameError: # pragma: no cover
raise ImportError("'numpy' is required for this functionality.")
raise ImportError(
f"'numpy' is required to convert numpy dtype {dtype}."
) from None


if not _DOCUMENTING:
Expand Down
6 changes: 3 additions & 3 deletions py-polars/polars/internals/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -1587,8 +1587,8 @@ def __getattr__(self, item: Any) -> PySeries:
DeprecationWarning,
)
return pli.wrap_s(self._df.column(item))
except Exception:
raise AttributeError(item)
except Exception as exc:
raise AttributeError(item) from exc

def __contains__(self, key: str) -> bool:
return key in self.columns
Expand Down Expand Up @@ -6179,7 +6179,7 @@ def get_group(self, group_value: Any | tuple[Any]) -> DF:
try:
groups_idx = groups[mask][0] # type: ignore[index]
except IndexError:
raise ValueError(f"no group: {group_value} found")
raise ValueError(f"no group: {group_value} found") from None

df = self._dataframe_class._from_pydf(self._df)
return df[groups_idx]
Expand Down
6 changes: 3 additions & 3 deletions py-polars/polars/internals/lazy_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,17 +489,17 @@ def show_graph(
["dot", "-Nshape=box", "-Tsvg"], input=f"{dot}".encode()
)
return display(SVG(svg))
except Exception:
except Exception as exc:
raise ImportError(
"Graphviz dot binary should be on your PATH and matplotlib should be installed to show graph."
)
) from exc
try:
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
except ImportError:
raise ImportError(
"Graphviz dot binary should be on your PATH and matplotlib should be installed to show graph."
)
) from None
dot = self._ldf.to_dot(optimized)
if raw_output:
return dot
Expand Down
2 changes: 1 addition & 1 deletion py-polars/polars/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ def read_excel(
except ImportError:
raise ImportError(
"xlsx2csv is not installed. Please run `pip install xlsx2csv`."
)
) from None

if isinstance(file, (str, Path)):
file = format_path(file)
Expand Down
12 changes: 12 additions & 0 deletions py-polars/tests/test_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import polars as pl

df = pl.DataFrame({"IP": ["1.1.1.1", "2.2.2.2"]})

isp_names = {"1.1.1.1": "ABC", "2.2.2.2": "XYZ"}

df.with_column(pl.col("IP").apply(isp_names.get))


df = pl.DataFrame({"IP": ["1.1.1.1", "2.2.2.2"], "ISP": ["N/A", "N/A"]})
for i, row in enumerate(df.rows()):
df[i, "ISP"] = isp_names[row[0]]

0 comments on commit 72ff4a0

Please sign in to comment.