Repository navigation
Description
What happens
The shadow check in __pydantic_init_subclass__() walks cls.model_fields — ordinary fields only. Computed fields live in model_computed_fields, so a @computed_field whose name collides with a model method sails through: no ShadowedFieldError, and no pydantic warning either. _install_computed_field_queries() then wraps the property in a _ComputedFieldQuery descriptor, so class-level access becomes query sugar and the method is gone: Batch.count is a GuardedQuery, and Batch.count() — the classmethod every other model has — raises a TypeError about QueryInstance.__call__().
For an ordinary field this exact collision is a hard error whose message explains that the query sugar would "silently break". The computed-field door is the mirror image — the sugar wins and the method silently breaks — with no diagnostic at all. As far as I can tell this is the only fully silent collision left in the flat namespace, whose design (#121) leans on collisions being loud.
Why it matters
The failure is remote from the cause. The class defines cleanly, instances work (batch.count computes the property), queries work (Batch.count == 4 builds a valid condition) — only a later call to the replaced method fails, with a message that mentions neither the computed field nor shadowing.
What to do
Extend the check to computed fields: for each name in cls.model_computed_fields, flag it when the name appears in vars() of a class in the MRO other than the one that defines the property itself (the defining class legitimately owns that attribute). Whether shadowed_fields= should then cover computed names too — making today's behavior available as an explicit opt-in — can be decided with the fix; the silent default is the bug.
Reproduction
from pydantic import computed_field
from tinydb import TinyDB
from tinydb.storages import MemoryStorage
from tinydantic import TinydanticModel
db = TinyDB(storage=MemoryStorage)
class Batch(TinydanticModel, database=db):
size: int
@computed_field # type: ignore[prop-decorator]
@property
def count(self) -> int:
return self.size
print("class created: no error, no warning")
print("Batch.count is now:", Batch.count)
try:
Batch.count()
except TypeError as e:
print("Batch.count() raises:", e)Run with python repro.py (project venv; stderr included — no warning appears).
Actual output:
class created: no error, no warning
Batch.count is now: GuardedQuery()
Batch.count() raises: QueryInstance.__call__() missing 1 required positional argument: 'value'
Expected: ShadowedFieldError at class definition, exactly as for an ordinary field named count.
Environment: tinydantic 0.4.0 at 389c6d9, Python 3.10.20, TinyDB 4.9.0, pydantic 2.13.4, macOS. Run 2026-08-14.
The reproduction was run as shown. No fix was built or tested. Surfaced while working through the conflict-handling story for #121.
Assisted-by: Claude:claude-fable-5
Activity