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

Support for string fields that inherit from Generic #2264

Closed
danni opened this issue Jan 15, 2021 · 4 comments · Fixed by #5629
Closed

Support for string fields that inherit from Generic #2264

danni opened this issue Jan 15, 2021 · 4 comments · Fixed by #5629
Assignees

Comments

@danni
Copy link
Contributor

danni commented Jan 15, 2021

Checks

  • [-] I added a descriptive title to this issue
  • [-] I have searched (google, github) for similar issues and couldn't find anything
  • [-] I have read and followed the docs and still think this feature/change is needed
  • [-] After submitting this, I commit to one of:
    • Look through open issues and helped at least one other person
    • Hit the "watch" button on this repo to receive notifications and I commit to help at least 2 people that ask questions in the future
    • Implement a Pull Request for a confirmed bug

Feature Request

Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":

             pydantic version: 1.7.3
            pydantic compiled: False
                 install path: /Users/danni/src/AP/apsys-vixen/.venv/lib/python3.9/site-packages/pydantic
               python version: 3.9.1 (default, Jan  4 2021, 12:22:24)  [Clang 12.0.0 (clang-1200.0.32.28)]
                     platform: macOS-11.1-x86_64-i386-64bit
     optional deps. installed: ['typing-extensions']

We have a graph of entities that are linked by ref tuples of (typename, uuid). The Ref has a generic type to represent the return type of the Entity. We want to validate when parsing the model that typename refers to a valid reference for this field.

It's not currently possible to build a string field that inherits generic following the example in the docs, because the field value is suddenly misinterpreted as a sequence.

from pydantic import BaseModel

class TypeName(str, Generic[T]):
    """This is a string, but that is validated against T."""

    ... # validation

class Ref(BaseModel, Generic[T]):
    typename: TypeName[T]
    uuid: UUID

    def resolve(self, graph: Graph) -> T: ...

class Entity(BaseModel, abc.ABC):
    typename: ClassVar[str]
    def resolve(self: T, document: Graph) -> T: return self

class Model(Entity):
    typename: ClassFinal = "model"
    parent: Ref["Model"]

The inclusion of Generic into TypeName results in.

E   pydantic.error_wrappers.ValidationError: 1 validation error for GazelleRef
E   typename
E     value is not a valid sequence (type=type_error.sequence)

pydantic/main.py:362: ValidationError
@danni
Copy link
Contributor Author

danni commented Jan 15, 2021

FYI I ultimately implemented this on Ref.

    @classmethod
    def validate_typename(cls, value: Any, field: ModelField) -> Any:
        """Validate the typename implements the generic type."""

        if field.sub_fields is None:
            return value

        if not isinstance(value, cls):
            return value

        # Get the generic type
        (sub_field,) = field.sub_fields
        klass = sub_field.type_

        # Determine all subclasses of T that are EntityData types
        if value.typename not in _get_valid_targets(klass):
            raise TypeError(
                f"{value.typename} does not implement {klass.__name__}"
            )

        return value

@mjog
Copy link

mjog commented Feb 11, 2023

Also reported as a bug in #4121 - which tbh seems like the correct characterisation (rather than a feature request) because Pydantic is effectively interpreting something like TypeName[SomeClass] (to riff off the OP's example above) as str[SomeClass], which doesn't make sense in itself, but then also treating that as a sequence, which makes less sense.

@samuelcolvin
Copy link
Member

@dmontagu might be worth looking at this while working on genetics.

@dmontagu
Copy link
Contributor

It looks to me like this issue has its source at least partially in the fact that we don't currently support subclasses for any primitive types. It would be easy to make things fail for generic str entirely (which would be better than the current behavior of assuming you meant a Sequence), but I suspect the goal is to actually perform some validation.

I'll open a PR to prevent generic subclasses of Sequence from doing this unintended approach to validation, but I think you'll end up needing to implement a custom __get_pydantic_core_schema__ for your type in a manner similar to that shown in tests.test_edge_cases.test_custom_generic_validators to get it to do the validation you want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants