Repository: unionai-oss/pandera

Issue:
Not require `coerce == True` for PydanticModels
**Describe the bug**

`DataFrameSchema.__init__` still requires that `coerce == True` when `isinstance(DataFrameSchema.dtype, pandas_engine.PydanticModel)`, even though #798 has addressed this?

The proposal is simply to remove these lines:

https://github.com/unionai-oss/pandera/blob/a66d71f8918205403bb10f2289d392e362ba9650/pandera/schemas.py#L203-L208

- [x] I have checked that this issue has not already been reported.
- [x] I have confirmed this bug exists on the latest version of pandera.
- [x] (optional) I have confirmed this bug exists on the master branch of pandera.





Discussion:
@the-matt-morris Can I take this up?
> @the-matt-morris Can I take this up?

@adithyaakrishna please do!  I probably should have submitted a PR awhile back on this, but go for it!
hey @adithyaakrishna @the-matt-morris just a friendly ping on this, could be cool to support this use case/syntactic sugar.
> hey @adithyaakrishna @the-matt-morris just a friendly ping on this, could be cool to support this use case/syntactic sugar.

@cosmicBboy thanks for the reminder, I can submit a PR in the next day or 2

Resolving patch:
diff --git a/pandera/dtypes.py b/pandera/dtypes.py
--- a/pandera/dtypes.py
+++ b/pandera/dtypes.py
@@ -28,6 +28,8 @@ class DataType(ABC):
 
     continuous: Optional[bool] = None
     """Whether the number data type is continuous."""
+    auto_coerce: bool = False
+    """Whether to force coerce to be True in all cases"""
 
     def __init__(self):
         if self.__class__ is DataType:
diff --git a/pandera/engines/pandas_engine.py b/pandera/engines/pandas_engine.py
--- a/pandera/engines/pandas_engine.py
+++ b/pandera/engines/pandas_engine.py
@@ -1030,6 +1030,7 @@ class PydanticModel(DataType):
     """A pydantic model datatype applying to rows in a dataframe."""
 
     type: Type[BaseModel] = dataclasses.field(default=None, init=False)  # type: ignore # noqa
+    auto_coerce = True
 
     # pylint:disable=super-init-not-called
     def __init__(self, model: Type[BaseModel]) -> None:
diff --git a/pandera/schemas.py b/pandera/schemas.py
--- a/pandera/schemas.py
+++ b/pandera/schemas.py
@@ -216,16 +216,11 @@ def __init__(
         # set to True in the case that a schema is created by infer_schema.
         self._IS_INFERRED = False
 
-        # This restriction can be removed once logical types are introduced:
-        # https://github.com/pandera-dev/pandera/issues/788
-        if not coerce and isinstance(self.dtype, pandas_engine.PydanticModel):
-            raise errors.SchemaInitError(
-                "Specifying a PydanticModel type requires coerce=True."
-            )
-
     @property
     def coerce(self) -> bool:
         """Whether to coerce series to specified type."""
+        if isinstance(self.dtype, DataType):
+            return self.dtype.auto_coerce or self._coerce
         return self._coerce
 
     @coerce.setter


Test patch:
diff --git a/tests/core/test_pydantic_dtype.py b/tests/core/test_pydantic_dtype.py
--- a/tests/core/test_pydantic_dtype.py
+++ b/tests/core/test_pydantic_dtype.py
@@ -1,11 +1,14 @@
 """Unit tests for pydantic datatype."""
 
+from typing import Type
+
 import pandas as pd
 import pytest
 from pydantic import BaseModel
 
 import pandera as pa
 from pandera.engines.pandas_engine import PydanticModel
+from pandera.schemas import SeriesSchemaBase
 
 
 class Record(BaseModel):
@@ -23,7 +26,6 @@ class Config:
         """Config with dataframe-level data type."""
 
         dtype = PydanticModel(Record)
-        coerce = True
 
 
 class PanderaSchema(pa.SchemaModel):
@@ -72,16 +74,20 @@ def func(df: pa.typing.DataFrame[PydanticSchema]):
         )
 
 
-def test_pydantic_model_init_errors():
-    """SchemaInitError should be raised when coerce=False"""
+@pytest.mark.parametrize("series_type", [pa.SeriesSchema, pa.Column, pa.Index])
+def test_pydantic_model_init_errors(series_type: Type[SeriesSchemaBase]):
+    """
+    Should raise SchemaInitError with PydanticModel as `SeriesSchemaBase.dtype`
+    """
     with pytest.raises(pa.errors.SchemaInitError):
-        pa.DataFrameSchema(dtype=PydanticModel(Record), coerce=False)
+        series_type(dtype=PydanticModel(Record))
 
-    with pytest.raises(pa.errors.SchemaInitError):
-        pa.SeriesSchema(dtype=PydanticModel(Record))
 
-    with pytest.raises(pa.errors.SchemaInitError):
-        pa.Column(dtype=PydanticModel(Record))
+@pytest.mark.parametrize("coerce", [True, False])
+def test_pydantic_model_coerce(coerce: bool):
+    """Test that DataFrameSchema.coerce is always True with pydantic model"""
 
-    with pytest.raises(pa.errors.SchemaInitError):
-        pa.Index(dtype=PydanticModel(Record))
+    dataframe_schema = pa.DataFrameSchema(
+        dtype=PydanticModel(Record), coerce=coerce
+    )
+    assert dataframe_schema.coerce is True