Skip to content

Commit

Permalink
depr(python): Remove re-export of exceptions at top-level (#17059)
Browse files Browse the repository at this point in the history
  • Loading branch information
stinodego authored Jun 19, 2024
1 parent 235ebee commit 760067c
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 76 deletions.
86 changes: 21 additions & 65 deletions py-polars/polars/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import contextlib
import os

Expand Down Expand Up @@ -75,38 +77,6 @@
Unknown,
Utf8,
)
from polars.exceptions import (
CategoricalRemappingWarning,
ChronoFormatWarning,
ColumnNotFoundError,
ComputeError,
CustomUFuncWarning,
DataOrientationWarning,
DuplicateError,
InvalidOperationError,
MapWithoutReturnDtypeWarning,
ModuleUpgradeRequiredError,
NoDataError,
NoRowsReturnedError,
OutOfBoundsError,
PanicException,
ParameterCollisionError,
PerformanceWarning,
PolarsError,
PolarsInefficientMapWarning,
PolarsWarning,
RowsError,
SchemaError,
SchemaFieldNotFoundError,
ShapeError,
SQLInterfaceError,
SQLSyntaxError,
StringCacheMismatchError,
StructFieldNotFoundError,
TooManyRowsReturnedError,
UnstableWarning,
UnsuitableSQLError,
)
from polars.expr import Expr
from polars.functions import (
align_frames,
Expand Down Expand Up @@ -242,39 +212,6 @@
"exceptions",
"plugins",
"selectors",
# exceptions - errors
"PolarsError",
"ColumnNotFoundError",
"ComputeError",
"DuplicateError",
"InvalidOperationError",
"ModuleUpgradeRequiredError",
"NoDataError",
"NoRowsReturnedError",
"OutOfBoundsError",
"ParameterCollisionError",
"RowsError",
"SQLInterfaceError",
"SQLSyntaxError",
"SchemaError",
"SchemaFieldNotFoundError",
"ShapeError",
"StringCacheMismatchError",
"StructFieldNotFoundError",
"TooManyRowsReturnedError",
"UnsuitableSQLError",
# exceptions - warnings
"PolarsWarning",
"CategoricalRemappingWarning",
"ChronoFormatWarning",
"CustomUFuncWarning",
"DataOrientationWarning",
"MapWithoutReturnDtypeWarning",
"PerformanceWarning",
"PolarsInefficientMapWarning",
"UnstableWarning",
# exceptions - panic
"PanicException",
# core classes
"DataFrame",
"Expr",
Expand Down Expand Up @@ -462,3 +399,22 @@
]

os.environ["POLARS_ALLOW_EXTENSION"] = "true"


def __getattr__(name: str) -> type[Exception]:
# Deprecate re-export of exceptions at top-level
if name in dir(exceptions):
from polars._utils.deprecation import issue_deprecation_warning

issue_deprecation_warning(
message=(
f"Accessing `{name}` from the top-level `polars` module is deprecated."
" Import it directly from the `polars.exceptions` module instead:"
f" from polars.exceptions import {name}"
),
version="1.0.0",
)
return getattr(exceptions, name)

msg = f"module {__name__!r} has no attribute {name!r}"
raise AttributeError(msg)
14 changes: 7 additions & 7 deletions py-polars/polars/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,30 @@ def _import(self) -> ModuleType:
self.__dict__.update(module.__dict__)
return module

def __getattr__(self, attr: Any) -> Any:
def __getattr__(self, name: str) -> Any:
# have "hasattr('__wrapped__')" return False without triggering import
# (it's for decorators, not modules, but keeps "make doctest" happy)
if attr == "__wrapped__":
msg = f"{self._module_name!r} object has no attribute {attr!r}"
if name == "__wrapped__":
msg = f"{self._module_name!r} object has no attribute {name!r}"
raise AttributeError(msg)

# accessing the proxy module's attributes triggers import of the real thing
if self._module_available:
# import the module and return the requested attribute
module = self._import()
return getattr(module, attr)
return getattr(module, name)

# user has not installed the proxied/lazy module
elif attr == "__name__":
elif name == "__name__":
return self._module_name
elif re.match(r"^__\w+__$", attr) and attr != "__version__":
elif re.match(r"^__\w+__$", name) and name != "__version__":
# allow some minimal introspection on private module
# attrs to avoid unnecessary error-handling elsewhere
return None
else:
# all other attribute access raises a helpful exception
pfx = self._mod_pfx.get(self._module_name, "")
msg = f"{pfx}{attr} requires {self._module_name!r} module to be installed"
msg = f"{pfx}{name} requires {self._module_name!r} module to be installed"
raise ModuleNotFoundError(msg) from None


Expand Down
8 changes: 4 additions & 4 deletions py-polars/tests/unit/io/database/test_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ def __init__(
self.batched = batched
self.n_calls = 1

def __getattr__(self, item: str) -> Any:
if "fetch" in item:
self.called.append(item)
def __getattr__(self, name: str) -> Any:
if "fetch" in name:
self.called.append(name)
return self.resultset
super().__getattr__(item) # type: ignore[misc]
super().__getattr__(name) # type: ignore[misc]

def close(self) -> Any:
pass
Expand Down
22 changes: 22 additions & 0 deletions py-polars/tests/unit/test_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pytest

import polars as pl
from polars.exceptions import ComputeError


def test_init_nonexistent_attribute() -> None:
with pytest.raises(
AttributeError, match="module 'polars' has no attribute 'stroopwafel'"
):
pl.stroopwafel


def test_init_exceptions_deprecated() -> None:
with pytest.deprecated_call(
match="Accessing `ComputeError` from the top-level `polars` module is deprecated."
):
exc = pl.ComputeError

msg = "nope"
with pytest.raises(ComputeError, match=msg):
raise exc(msg)

0 comments on commit 760067c

Please sign in to comment.