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

chore(python): Add various unit tests #9903

Merged
merged 2 commits into from
Jul 15, 2023
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
2 changes: 1 addition & 1 deletion py-polars/polars/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@


# dummy func required (so docs build)
def _get_float_fmt() -> str:
def _get_float_fmt() -> str: # pragma: no cover
return "n/a"


Expand Down
2 changes: 1 addition & 1 deletion py-polars/polars/utils/show_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,6 @@ def _get_dependency_version(dep_name: str) -> str:
if hasattr(module, "__version__"):
module_version = module.__version__
else:
module_version = importlib.metadata.version(dep_name)
module_version = importlib.metadata.version(dep_name) # pragma: no cover

return module_version
6 changes: 0 additions & 6 deletions py-polars/polars/utils/various.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
Int64,
Time,
Utf8,
is_polars_dtype,
unpack_dtypes,
)
from polars.dependencies import _PYARROW_AVAILABLE
Expand Down Expand Up @@ -72,11 +71,6 @@ def is_bool_sequence(val: object) -> TypeGuard[Sequence[bool]]:
return isinstance(val, Sequence) and _is_iterable_of(val, bool)


def is_dtype_sequence(val: object) -> TypeGuard[Sequence[PolarsDataType]]:
"""Check whether the given object is a sequence of polars DataTypes."""
return isinstance(val, Sequence) and all(is_polars_dtype(x) for x in val)


def is_int_sequence(val: object) -> TypeGuard[Sequence[int]]:
"""Check whether the given sequence is a sequence of integers."""
return isinstance(val, Sequence) and _is_iterable_of(val, int)
Expand Down
11 changes: 11 additions & 0 deletions py-polars/tests/unit/namespaces/test_array.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import numpy as np

import polars as pl
from polars.testing import assert_frame_equal


def test_arr_min_max() -> None:
Expand All @@ -14,6 +15,16 @@ def test_arr_sum() -> None:
assert s.arr.sum().to_list() == [3, 7]


def test_arr_unique() -> None:
df = pl.DataFrame(
{"a": pl.Series("a", [[1, 1], [4, 3]], dtype=pl.Array(width=2, inner=pl.Int64))}
)

out = df.select(pl.col("a").arr.unique(maintain_order=True))
expected = pl.DataFrame({"a": [[1], [4, 3]]})
assert_frame_equal(out, expected)


def test_array_to_numpy() -> None:
s = pl.Series([[1, 2], [3, 4], [5, 6]], dtype=pl.Array(width=2, inner=pl.Int64))
assert (s.to_numpy() == np.array([[1, 2], [3, 4], [5, 6]])).all()
2 changes: 2 additions & 0 deletions py-polars/tests/unit/namespaces/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def test_struct_various() -> None:
assert s[1] == {"int": 2, "str": "b", "bool": None, "list": [3]}
assert s.struct.field("list").to_list() == [[1, 2], [3]]
assert s.struct.field("int").to_list() == [1, 2]
assert s.struct["list"].to_list() == [[1, 2], [3]]
assert s.struct["int"].to_list() == [1, 2]

assert_frame_equal(df.to_struct("my_struct").struct.unnest(), df)
assert s.struct._ipython_key_completions_() == s.struct.fields
Expand Down
25 changes: 25 additions & 0 deletions py-polars/tests/unit/test_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import pytest

import polars as pl
from polars.testing import assert_frame_equal

Expand Down Expand Up @@ -137,3 +139,26 @@ def test_class_namespaces_are_registered() -> None:
assert (
ns in namespaces
), f"{ns!r} should be registered in {pcls.__name__}._accessors"


def test_namespace_cannot_override_builtin() -> None:
with pytest.raises(AttributeError):

@pl.api.register_dataframe_namespace("dt")
class CustomDt:
def __init__(self, df: pl.DataFrame):
self._df = df


def test_namespace_warning_on_override() -> None:
@pl.api.register_dataframe_namespace("math")
class CustomMath:
def __init__(self, df: pl.DataFrame):
self._df = df

with pytest.raises(UserWarning):

@pl.api.register_dataframe_namespace("math")
class CustomMath2:
def __init__(self, df: pl.DataFrame):
self._df = df
43 changes: 42 additions & 1 deletion py-polars/tests/unit/test_cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ def test_string_cache() -> None:

@pytest.mark.write_disk()
def test_config_load_save(tmp_path: Path) -> None:
for file in (None, tmp_path / "polars.config"):
for file in (None, tmp_path / "polars.config", str(tmp_path / "polars.config")):
# set some config options...
pl.Config.set_tbl_cols(12)
pl.Config.set_verbose(True)
Expand Down Expand Up @@ -577,3 +577,44 @@ def test_config_scope() -> None:

# expect scope-exit to restore original state
assert pl.Config.state() == initial_state


def test_config_raise_error_if_not_exist() -> None:
with pytest.raises(AttributeError), pl.Config(i_do_not_exist=True):
pass


def test_config_state_env_only() -> None:
pl.Config.set_verbose(False)
pl.Config.set_fmt_float("full")

state_all = pl.Config.state(env_only=False)
state_env_only = pl.Config.state(env_only=True)
assert len(state_env_only) < len(state_all)
assert "set_fmt_float" in state_all
assert "set_fmt_float" not in state_env_only
zundertj marked this conversation as resolved.
Show resolved Hide resolved


def test_activate_decimals() -> None:
with pl.Config() as cfg:
cfg.activate_decimals(True)
assert os.environ.get("POLARS_ACTIVATE_DECIMAL") == "1"
cfg.activate_decimals(False)
assert "POLARS_ACTIVATE_DECIMAL" not in os.environ


def test_set_streaming_chunk_size() -> None:
with pl.Config() as cfg:
cfg.set_streaming_chunk_size(8)
assert os.environ.get("POLARS_STREAMING_CHUNK_SIZE") == "8"

with pytest.raises(ValueError), pl.Config() as cfg:
cfg.set_streaming_chunk_size(0)


def test_set_fmt_str_lengths_invalid_length() -> None:
with pl.Config() as cfg:
with pytest.raises(ValueError):
cfg.set_fmt_str_lengths(0)
with pytest.raises(ValueError):
cfg.set_fmt_str_lengths(-2)
15 changes: 15 additions & 0 deletions py-polars/tests/unit/test_show_graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import polars as pl


def test_show_graph() -> None:
# only test raw output, otherwise we need graphviz and matplotlib
ldf = pl.LazyFrame(
{
"a": ["a", "b", "a", "b", "b", "c"],
"b": [1, 2, 3, 4, 5, 6],
"c": [6, 5, 4, 3, 2, 1],
}
)
query = ldf.groupby("a", maintain_order=True).agg(pl.all().sum()).sort("a")
out = query.show_graph(raw_output=True)
assert isinstance(out, str)
8 changes: 8 additions & 0 deletions py-polars/tests/unit/utils/test_parse_expr_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,11 @@ def test_parse_as_expression_structify() -> None:
result = wrap_expr(parse_as_expression(pl.col("a", "b"), structify=True))
expected = pl.struct("a", "b")
assert_expr_equal(result, expected)


def test_parse_as_expression_structify_multiple_outputs() -> None:
# note: this only works because assert_expr_equal evaluates on a dataframe with
# columns "a" and "b"
result = wrap_expr(parse_as_expression(pl.col("*"), structify=True))
expected = pl.struct("a", "b")
assert_expr_equal(result, expected)
13 changes: 12 additions & 1 deletion py-polars/tests/unit/utils/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
_timedelta_to_pl_timedelta,
)
from polars.utils.decorators import deprecate_nonkeyword_arguments, redirect
from polars.utils.various import parse_version
from polars.utils.meta import get_idx_type
from polars.utils.various import _in_notebook, parse_version

if TYPE_CHECKING:
from polars.type_aliases import TimeUnit
Expand Down Expand Up @@ -158,3 +159,13 @@ def bar(self, upper: bool = False) -> str:
return "BAZ" if upper else "baz"

assert DemoClass2().foo() == "BAZ" # type: ignore[attr-defined]


def test_get_idx_type_deprecation() -> None:
with pytest.deprecated_call():
get_idx_type()


def test_in_notebook() -> None:
# private function, but easier to test this separately and mock it in the callers
assert not _in_notebook()