4

For reasons beyond the scope of this question, I'd like to create a dynamic Python-Pydantic class. I'm close, but am not aware of how to add the type hint.

Given:

class MyClass(BaseModel):

    class Config:
        extra = Extra.allow

        schema_extra = {
            '$id': "http://my-site.com/production.json",
            '$schema': "https://json-schema.org/draft/2020-12/schema",
            'title': 'pattern="^.+-.*"'
        }


if __name__ == '__main__':

    cls = type('A', (MyClass,), {'__doc__': 'class created by type', 'my_field': Field("test", type='int')})
    p = cls()
    schema = p.schema()
    pprint(schema)

I get this:

{'$id': 'http://my-site.com/production.json',
 '$schema': 'https://json-schema.org/draft/2020-12/schema',
 'description': 'class created by type',
 'properties': {'my_field': {'default': 'test',
                             'title': 'My Field',
                             'type': 'string'}},
 'title': 'pattern="^.+-.*"',
 'type': 'object'}

I would like "properties -> myfield -> type" to be an int instead of string.

SteveJ
  • 3,034
  • 2
  • 27
  • 47

1 Answers1

6

Create an __annotations__ dict object and add your type definitions there. This is the same mapping that gets translated when you define it like my_field: int = Field('test').

Note: in below, I only show the parts of the code that were added / modified.

    cls_annotations = {'my_field': int} ## Added

    cls = type('A', (MyClass,), {'__doc__': 'class created by type',
                                 ## Added
                                 '__annotations__': cls_annotations,
                                 ##
                                 'my_field': Field("test")})

Output:

...
 'properties': {'my_field': {'default': 'test',
                             'title': 'My Field',
                             'type': 'integer'}},
...
rv.kvetch
  • 9,940
  • 3
  • 24
  • 53
  • 1
    Just what I needed, thanks. If you are willing to tackle a follow-up, how did you know this? I don't see it in the docs for type nor in any of the tutorials on dynamically creating classes. – SteveJ Nov 16 '21 at 16:42
  • 1
    @SteveJ well, I kinda assumed most people knew about it. The annotations field is actually populated from at least Python 3.6 IIRC - since [PEP 526](https://www.python.org/dev/peps/pep-0526/) when they introduced variable annotations anyway. If you create just a regular class (manually, without metaclasses) with some fields, and then call `vars(MyClass)`, if you inspect it you should see that an `__annotations__` field is populated with the variable annotations for the class. – rv.kvetch Nov 16 '21 at 16:49
  • 1
    Thank you muchly. – SteveJ Nov 16 '21 at 16:54