Skip to content

Commit f8384ae

Browse files
ybresslerCopilot
andauthored
Bugfix: #2058 Check_types for callable (#2069)
* Enhance parameterization for one of the tests #2058 Signed-off-by: Yaakov Bressler <yaakovbressler@gmail.com> Signed-off-by: ybressler <40807730+ybressler@users.noreply.github.com> * Test cases which we expect to pass Signed-off-by: ybressler <40807730+ybressler@users.noreply.github.com> * Fix failing tests - use different method for determining if type is literal Signed-off-by: ybressler <40807730+ybressler@users.noreply.github.com> * Update tests/pandas/test_decorators.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Signed-off-by: Yaakov Bressler <yaakovbressler@gmail.com> Signed-off-by: ybressler <40807730+ybressler@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 72af270 commit f8384ae

File tree

2 files changed

+92
-31
lines changed

2 files changed

+92
-31
lines changed

pandera/typing/common.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import copy
44
import inspect
5+
import typing
56
from typing import ( # type: ignore[attr-defined]
67
TYPE_CHECKING,
78
Any,
@@ -238,7 +239,8 @@ def _parse_annotation(self, raw_annotation: type) -> None:
238239
self.arg = typing_inspect.get_args(self.arg)[0]
239240

240241
self.metadata = metadata
241-
self.literal = typing_inspect.is_literal_type(self.arg)
242+
243+
self.literal = typing_inspect.get_origin(self.arg) is typing.Literal
242244

243245
if self.literal:
244246
self.arg = typing_inspect.get_args(self.arg)[0]

tests/pandas/test_decorators.py

Lines changed: 89 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import asyncio
44
import pickle
55
import typing
6+
from contextlib import nullcontext
67

78
import numpy as np
89
import pandas as pd
@@ -536,42 +537,67 @@ class OnlyOnesSchema(DataFrameModel):
536537
a: Series[int] = Field(eq=1)
537538

538539

539-
def test_check_types_arguments() -> None:
540+
@pytest.mark.parametrize(
541+
("check_types_args", "df_return", "expected"),
542+
[
543+
pytest.param(
544+
dict(),
545+
pd.DataFrame({"a": [0, 0]}),
546+
nullcontext(),
547+
id="validate entire df (2 rows)",
548+
),
549+
pytest.param(
550+
dict(head=1),
551+
pd.DataFrame({"a": [0, 1]}),
552+
nullcontext(),
553+
id="validate header (row 1) - while row 2 is bad",
554+
),
555+
pytest.param(
556+
dict(tail=1),
557+
pd.DataFrame({"a": [1, 0]}),
558+
nullcontext(),
559+
id="validate tail, (row 2) - while row 1 is bad",
560+
),
561+
pytest.param(
562+
dict(lazy=True),
563+
pd.DataFrame({"a": [0, 0]}),
564+
nullcontext(),
565+
id="validate entire df (2 rows) - lazy mode ",
566+
),
567+
pytest.param(
568+
dict(lazy=True),
569+
pd.DataFrame({"a": [1, 1]}),
570+
pytest.raises(
571+
errors.SchemaErrors,
572+
match=r"DATA", # error msg is specific for lazy-
573+
),
574+
id="1's not allowed in schema - lazy mode",
575+
),
576+
pytest.param(
577+
dict(),
578+
pd.DataFrame({"a": [1, 1]}),
579+
pytest.raises(
580+
errors.SchemaError,
581+
match=r"failed element-wise validator", # error msg is specific for regular-mode
582+
),
583+
id="1's not allowed in schema - regular mode",
584+
),
585+
],
586+
)
587+
def test_check_types_arguments(
588+
check_types_args: dict, df_return: pd.DataFrame, expected
589+
) -> None:
540590
"""Test that check_types forwards key-words arguments to validate."""
541591
df = pd.DataFrame({"a": [0, 0]})
542592

543-
@check_types()
544-
def transform_empty_parenthesis(
593+
@check_types(**check_types_args)
594+
def transform_with_checks(
545595
df: DataFrame[OnlyZeroesSchema],
546596
) -> DataFrame[OnlyZeroesSchema]: # pylint: disable=unused-argument
547-
return df
548-
549-
transform_empty_parenthesis(df) # type: ignore
550-
551-
@check_types(head=1)
552-
def transform_head(
553-
df: DataFrame[OnlyZeroesSchema], # pylint: disable=unused-argument
554-
) -> DataFrame[OnlyZeroesSchema]:
555-
return pd.DataFrame({"a": [0, 0]}) # type: ignore
556-
557-
transform_head(df) # type: ignore
558-
559-
@check_types(tail=1)
560-
def transform_tail(
561-
df: DataFrame[OnlyZeroesSchema], # pylint: disable=unused-argument
562-
) -> DataFrame[OnlyZeroesSchema]:
563-
return pd.DataFrame({"a": [1, 0]}) # type: ignore
564-
565-
transform_tail(df) # type: ignore
597+
return df_return
566598

567-
@check_types(lazy=True)
568-
def transform_lazy(
569-
df: DataFrame[OnlyZeroesSchema], # pylint: disable=unused-argument
570-
) -> DataFrame[OnlyZeroesSchema]:
571-
return pd.DataFrame({"a": [1, 1]}) # type: ignore
572-
573-
with pytest.raises(errors.SchemaErrors, match=r"DATA"):
574-
transform_lazy(df) # type: ignore
599+
with expected:
600+
transform_with_checks(df) # type: ignore
575601

576602

577603
def test_check_types_unchanged() -> None:
@@ -754,6 +780,39 @@ def transform(
754780
assert transform(None) is None
755781

756782

783+
@pytest.mark.parametrize(
784+
"callable_annotation",
785+
[
786+
pytest.param(typing.Callable[[None], None], id="no args, no return"),
787+
pytest.param(typing.Callable[[None], int], id="no args, returns int"),
788+
pytest.param(
789+
typing.Callable[..., int], id="no info on args, returns int"
790+
),
791+
pytest.param(
792+
typing.Callable[..., list[int]],
793+
id="no info on args, returns list of int",
794+
),
795+
pytest.param(
796+
typing.Callable[[typing.Any], int],
797+
id="includes info on callable args",
798+
),
799+
],
800+
)
801+
def test_check_types_callables(callable_annotation: typing.Callable) -> None:
802+
"""
803+
Ensures `check_types` validates a dataframe, while passing in an additional callable argument
804+
"""
805+
806+
class MySchema1(DataFrameModel):
807+
a: int
808+
809+
@check_types
810+
def some_transformation(df: MySchema1, f: callable_annotation): # type: ignore[valid-type]
811+
pass
812+
813+
_ = some_transformation(pd.DataFrame({"a": [1, 2]}), lambda x: 1)
814+
815+
757816
def test_check_types_coerce() -> None:
758817
"""Test that check_types return the result of validate."""
759818

0 commit comments

Comments
 (0)