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

Make repr work for instances that failed initialization when handling ValidationErrors #7439

Merged
merged 1 commit into from Sep 20, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion pydantic/main.py
Expand Up @@ -878,7 +878,11 @@ def __repr_args__(self) -> _repr.ReprArgs:
field = self.model_fields.get(k)
if field and field.repr:
yield k, v
pydantic_extra = self.__pydantic_extra__
# `__pydantic_extra__` can fail to be set if the model is not yet fully initialized.
# This can happen if a `ValidationError` is raised during initialization and the instance's
# repr is generated as part of the exception handling. Therefore, we use `getattr` here
# with a fallback, even though the type hints indicate the attribute will always be present.
pydantic_extra = getattr(self, '__pydantic_extra__', None)
if pydantic_extra is not None:
yield from ((k, v) for k, v in pydantic_extra.items())
yield from ((k, getattr(self, k)) for k, v in self.model_computed_fields.items() if v.repr)
Expand Down
35 changes: 35 additions & 0 deletions tests/test_edge_cases.py
Expand Up @@ -2559,3 +2559,38 @@ class Model(BaseModel):
m = Model(a=bytes, b=int)
assert m.model_dump() == {'a': bytes, 'b': int}
assert m.a == bytes


def test_custom_exception_handler():
from traceback import TracebackException

from pydantic import BaseModel

traceback_exceptions = []

class MyModel(BaseModel):
name: str

class CustomErrorCatcher:
def __enter__(self):
return None

def __exit__(self, _exception_type, exception, exception_traceback):
if exception is not None:
traceback_exceptions.append(
TracebackException(
exc_type=type(exception),
exc_value=exception,
exc_traceback=exception_traceback,
capture_locals=True,
)
)

return True
return False

with CustomErrorCatcher():
data = {'age': 'John Doe'}
MyModel(**data)

assert len(traceback_exceptions) == 1