Generate a type from another type and change fields to optional

Viewed 106

I have this type:

class SomeResource:
    id: int
    name: str

And I need this type:

class SomeResourceQuery:
    id: Optional[int]
    name: Optional[str]

But I'd like to avoid having to write it by hand. Is it possible to generate this SomeResourceQuery type from the SomeResource type? Just convert all the types of the fields to optional. (Update: Already optional fields can stay optional - no nested optionals.)

I plan to use this SomeResourceQuery in a repository, like this:

class SomeResourceRepository:
    def get_one_or_none(self, query: SomeResourceQuery) -> Optional[SomeResource]:
        ...

Update: Just to show what I'm thinking currently:

class SomeResource:
    id: int
    name: str


# I don't want to write this by hand:
# class SomeResourceQuery:
#     id: Optional[int]
#     name: Optional[str]

# So what can I do here to make all fields that are not already optional, optional?
SomeResourceQuery = type("SomeResourceQuery", SomeResource) # What to do here?
1 Answers

You can use the type constructor to construct the new type with the appropriate annotations.

def construct_query_class(cls: type) -> type:
    annotations = {key: typing.Optional[value]
                    for key, value in cls.__annotations__.items()}

    return dataclasses.dataclass(type(cls.__name__ + 'Query', (),
                                      {'__annotations__': annotations}))

class SomeResource:
    id: int
    name: str

SomeResourceQuery = construct_query_class(SomeResource) # type: typing.Any
Related