Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

depr(python): Remove re-export of exceptions at top-level #17059

Merged
merged 3 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 (
stinodego marked this conversation as resolved.
Show resolved Hide resolved
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)