Skip to content

Commit

Permalink
fix(python): tweaked property/accessor behaviour (#6021)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-beedie committed Jan 4, 2023
1 parent d41d0af commit b94fffc
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 30 deletions.
19 changes: 11 additions & 8 deletions py-polars/polars/internals/expr/expr.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import math
import os
import random
import warnings
from datetime import date, datetime, time, timedelta
Expand All @@ -23,7 +24,7 @@
from polars.internals.expr.meta import ExprMetaNameSpace
from polars.internals.expr.string import ExprStringNameSpace
from polars.internals.expr.struct import ExprStructNameSpace
from polars.utils import _timedelta_to_pl_duration, accessor, deprecated_alias
from polars.utils import _timedelta_to_pl_duration, deprecated_alias, sphinx_accessor

try:
from polars.polars import PyExpr
Expand All @@ -41,6 +42,8 @@
RankMethod,
RollingInterpolationMethod,
)
elif os.getenv("BUILDING_SPHINX_DOCS"):
property = sphinx_accessor


def selection_to_pyexpr_list(
Expand Down Expand Up @@ -5903,7 +5906,7 @@ def shrink_dtype(self) -> Expr:
"""
return wrap_expr(self._pyexpr.shrink_dtype())

@accessor
@property
def arr(self) -> ExprListNameSpace:
"""
Create an object namespace of all list related methods.
Expand All @@ -5913,7 +5916,7 @@ def arr(self) -> ExprListNameSpace:
"""
return ExprListNameSpace(self)

@accessor
@property
def cat(self) -> ExprCatNameSpace:
"""
Create an object namespace of all categorical related methods.
Expand All @@ -5939,12 +5942,12 @@ def cat(self) -> ExprCatNameSpace:
"""
return ExprCatNameSpace(self)

@accessor
@property
def dt(self) -> ExprDateTimeNameSpace:
"""Create an object namespace of all datetime related methods."""
return ExprDateTimeNameSpace(self)

@accessor
@property
def meta(self) -> ExprMetaNameSpace:
"""
Create an object namespace of all meta related expression methods.
Expand All @@ -5954,7 +5957,7 @@ def meta(self) -> ExprMetaNameSpace:
"""
return ExprMetaNameSpace(self)

@accessor
@property
def str(self) -> ExprStringNameSpace:
"""
Create an object namespace of all string related methods.
Expand All @@ -5978,7 +5981,7 @@ def str(self) -> ExprStringNameSpace:
"""
return ExprStringNameSpace(self)

@accessor
@property
def bin(self) -> ExprBinaryNameSpace:
"""
Create an object namespace of all binary related methods.
Expand All @@ -5987,7 +5990,7 @@ def bin(self) -> ExprBinaryNameSpace:
"""
return ExprBinaryNameSpace(self)

@accessor
@property
def struct(self) -> ExprStructNameSpace:
"""
Create an object namespace of all struct related methods.
Expand Down
17 changes: 10 additions & 7 deletions py-polars/polars/internals/series/series.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import math
import os
import warnings
from collections.abc import Sized
from datetime import date, datetime, time, timedelta
Expand Down Expand Up @@ -77,11 +78,11 @@
_date_to_pl_date,
_datetime_to_pl_timestamp,
_time_to_pl_time,
accessor,
is_bool_sequence,
is_int_sequence,
range_to_slice,
scale_bytes,
sphinx_accessor,
)

try:
Expand All @@ -104,6 +105,8 @@
SizeUnit,
TimeUnit,
)
elif os.getenv("BUILDING_SPHINX_DOCS"):
property = sphinx_accessor


def wrap_s(s: PySeries) -> Series:
Expand Down Expand Up @@ -4860,32 +4863,32 @@ def get_chunks(self) -> list[Series]:
# Below are the namespaces defined. Do not move these up in the definition of
# Series, as it confuses mypy between the type annotation `str` and the
# namespace `str`
@accessor
@property
def arr(self) -> ListNameSpace:
"""Create an object namespace of all list related methods."""
return ListNameSpace(self)

@accessor
@property
def cat(self) -> CatNameSpace:
"""Create an object namespace of all categorical related methods."""
return CatNameSpace(self)

@accessor
@property
def dt(self) -> DateTimeNameSpace:
"""Create an object namespace of all datetime related methods."""
return DateTimeNameSpace(self)

@accessor
@property
def str(self) -> StringNameSpace:
"""Create an object namespace of all string related methods."""
return StringNameSpace(self)

@accessor
@property
def bin(self) -> BinaryNameSpace:
"""Create an object namespace of all binary related methods."""
return BinaryNameSpace(self)

@accessor
@property
def struct(self) -> StructNameSpace:
"""Create an object namespace of all struct related methods."""
return StructNameSpace(self)
Expand Down
7 changes: 5 additions & 2 deletions py-polars/polars/internals/series/struct.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from __future__ import annotations

import os
from typing import TYPE_CHECKING

import polars.internals as pli
from polars.internals.series.utils import expr_dispatch
from polars.utils import accessor
from polars.utils import sphinx_accessor

if TYPE_CHECKING:
from polars.polars import PySeries
elif os.getenv("BUILDING_SPHINX_DOCS"):
property = sphinx_accessor


@expr_dispatch
Expand All @@ -34,7 +37,7 @@ def to_frame(self) -> pli.DataFrame:
"""Convert this Struct Series to a DataFrame."""
return pli.wrap_df(self._s.struct_to_frame())

@accessor
@property
def fields(self) -> list[str]:
"""Get the names of the fields."""
if getattr(self, "_s", None) is None:
Expand Down
4 changes: 2 additions & 2 deletions py-polars/polars/internals/series/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def _is_empty_method(func: SeriesMethod) -> bool:
fc = func.__code__
return (fc.co_code in _EMPTY_BYTECODE) and (
(len(fc.co_consts) == 2 and fc.co_consts[1] is None)
# account for potentially optimized-out docstrings
# account for optimized-out docstrings (eg: running 'python -OO')
or (sys.flags.optimize == 2 and fc.co_consts == (None,))
)

Expand All @@ -74,7 +74,7 @@ def _expr_lookup(namespace: str | None) -> set[tuple[str | None, str, tuple[str,
if not name.startswith("_"):
try:
m = getattr(expr, name)
except AttributeError: # May be raised for @property methods
except AttributeError: # may raise for @property methods
continue
if callable(m):
# add function signature (argument names only) to the lookup
Expand Down
26 changes: 15 additions & 11 deletions py-polars/polars/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,20 +367,24 @@ def _rename_kwargs(
kwargs[new] = kwargs.pop(alias)


if not os.getenv("BUILDING_SPHINX_DOCS"):
# if NOT building docs we use a simple @property decorator for namespace accessors,
# which plays much better with mypy, pylint, and the various IDEs' autocomplete.
accessor = property
else:
# however, when building docs (with Sphinx) we need access to the functions
# associated with the namespaces from the class, as we don't have an instance.
NS = TypeVar("NS")

class accessor(property): # type: ignore[no-redef]
def __get__(self, instance: Any, cls: type[NS]) -> NS: # type: ignore[override]
# when building docs (with Sphinx) we need access to the functions
# associated with the namespaces from the class, as we don't have
# an instance; @sphinx_accessor is a @property that allows this.
NS = TypeVar("NS")


class sphinx_accessor(property):
def __get__( # type: ignore[override]
self,
instance: Any,
cls: type[NS],
) -> NS:
try:
return self.fget( # type: ignore[misc]
instance if isinstance(instance, cls) else cls
)
except AttributeError:
return None # type: ignore[return-value]


def scale_bytes(sz: int, unit: SizeUnit) -> int | float:
Expand Down

0 comments on commit b94fffc

Please sign in to comment.