litestar-org/polyfactory

incorrect exception raised when nested model validation fails

alessio-locatelli opened this issue · 3 comments

Description

Problem 1

Can not handle Predicate in a model that is used in a field of a primary model.

Problem 2

Misleading, wrong error.

The error says polyfactory.exceptions.ParameterException: received constraints for unsupported type list[__main__.Pet] but the real error is much deeper, in a specific field:

    codes: list[Code]

MCVE

from typing import Annotated
from annotated_types import MinLen, Predicate
from pydantic import BaseModel
from polyfactory.factories.pydantic_factory import ModelFactory


IsUpper = Predicate(str.isupper)
Code = Annotated[str, IsUpper]


class Pet(BaseModel):
    codes: list[Code]


class Person(BaseModel):
    pets_names: Annotated[list[Pet], MinLen(1)]


class PersonFactory(ModelFactory[Person]):
    __model__ = Person


print(PersonFactory.build())

Logs

Traceback (most recent call last):
  File "/opt/pysetup/try_polyfactory.py", line 23, in <module>
    print(PersonFactory.build())
          ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/pysetup/.venv/lib/python3.11/site-packages/polyfactory/factories/pydantic_factory.py", line 377, in build
    processed_kwargs = cls.process_kwargs(**kwargs)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/pysetup/.venv/lib/python3.11/site-packages/polyfactory/factories/base.py", line 844, in process_kwargs
    result[field_meta.name] = cls.get_field_value(field_meta, field_build_parameters=field_build_parameters)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/pysetup/.venv/lib/python3.11/site-packages/polyfactory/factories/base.py", line 617, in get_field_value
    return cls.get_constrained_field_value(annotation=unwrapped_annotation, field_meta=field_meta)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/pysetup/.venv/lib/python3.11/site-packages/polyfactory/factories/pydantic_factory.py", line 364, in get_constrained_field_value
    return super().get_constrained_field_value(annotation, field_meta)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/pysetup/.venv/lib/python3.11/site-packages/polyfactory/factories/base.py", line 586, in get_constrained_field_value
    raise ParameterException(msg)
polyfactory.exceptions.ParameterException: received constraints for unsupported type list[__main__.Pet]

Expected behavior

Ignore Annotated.__metadata__ in a model that is used in a field and continue execution.

Release Version

Version: 2.12.0

Platform

  • Linux
  • Mac
  • Windows
  • Other (Please specify in the description above)

Note

While we are open for sponsoring on GitHub Sponsors and
OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.
Fund with Polar

@olk-m thanks for reporting this. The Annotated.__metadata__ is already being parsed in order to generate values that satisfy the constraints. So, it seems there's some bug where it's not getting parsed or something. Also, the error message is indeed unexpected.

The reason this was failing is because polyfactory will sometimes create an empty string which fails the validation call to str.isupper. The exception thrown here was being supressed by polyfactory instead of properly allowing it to be raised. This has been fixed in #450.

This now poses a question on how to handle the cases where isupper or islower constraints are provided, but no min_length constraint is given. The options are:

  • throw an exception if min_length is not provided
  • default to a minimum length of 1 if min_length is not provided

Personally, I'm leaning towards the first option. It's more explicit whereas the second option is implicit.

Thank you for a quick response.

Personally, I'm leaning towards the first option.

I agree that if a user uses a bare str or Annotated[str, IsUpper] (without MinLen(1)) then polyfactory should not silently default to a non-empty string.