First off, it should be noted that during validation, everything resolves correctly (as also noted in the discussion in one of the project's issues #983). The explanation below the example deals with implementation details which all happen under the hood, but they are important to understand in order to answer your question.
import pandas as pd
import pandera as pa
from pandera.typing import Series
class A(pa.SchemaModel):
foo: Series[int]
class Config:
unique = ["foo"]
coerce = False
class B(A):
bar: Series[int]
class Config:
coerce = True
df = pd.DataFrame({"foo": ["1", "1"], "bar": ["1", "1"]})
print(B.validate(df, lazy=True)) # coercion to int works, uniqueness check on "foo" fails
So, config properties do propagate from parents to children, albeit using a config-assembling method that walks the mro
instead of relying on inheritance. The Config
attribute is bound to the default if it wasn't defined. While causing some confusion, this also has some mild upsides.
The Config
being only part of the recipe and not the source of truth fits with the overall design, because a SchemaModel
does not implement any validation itself -- it is a blueprint to create a DataFrameSchema
instance. The actual config is cached lazily at __config__
(essentially behaving like a class-property) on every SchemaModel
and is not written back to Config
.
It probably could have, but it seems more sane not to. If inheritance isn't done, pretending that it took place isn't necessary and might ultimately introduce weird edge cases or bugs.