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

Add Types that needs to be parsed from strings. #195

Closed
jaheba opened this issue Jun 8, 2018 · 5 comments
Closed

Add Types that needs to be parsed from strings. #195

jaheba opened this issue Jun 8, 2018 · 5 comments
Labels
feature request help wanted Pull Request welcome

Comments

@jaheba
Copy link
Contributor

jaheba commented Jun 8, 2018

Sometimes data needs to be parsed recursively.

class MyModel(BaseModel):
    xs: JsonList[int]

assert MyModel(xs='["1"]').xs == [1]

Partly taken from:

https://github.com/samuelcolvin/pydantic/blob/3ef595556cfc1b98d97be84f259091aa1568da3a/pydantic/types.py#L321

Is there any progress or plans to implement this?

Edit:
On a second thought, maybe something like this is more flexible:

class MyModel(BaseModel):
    xs: Nested[List[int]]
@samuelcolvin
Copy link
Member

there's been no progress because I haven't needed it and no one else has asked for it.

I think it's a pretty rare case and would probably be best implemented as a validator.

samuelcolvin added a commit that referenced this issue Jun 11, 2018
@jaheba
Copy link
Contributor Author

jaheba commented Jun 11, 2018

I need it!

The issue is that I have to handle a config which is passed as a Dict[str, str]. E.g.:

{
  "key_a": "42",
  "key_b": "[1, 2, 3]"
}

Ideally, I can apply List[int] to key_b somehow. From what I understand I only can define attributes within a class and something like this isn't possible at the moment:

key_b = Field(List[int])
key_b.parse("[1, 2, 3]")

@samuelcolvin
Copy link
Member

As I say, much easier to user a validator, then all the subsiquent validation logic should work as normal.

import json
from typing import List

from pydantic import BaseModel, validator


class SubModel(BaseModel):
    foo: int
    bar: List[int]


class Model(BaseModel):
    foobar: SubModel

    @validator('foobar', pre=True)
    def to_json(cls, v):
        try:
            return json.loads(v)
        except ValueError as e:
            raise ValueError('foobar value was not valid json') from e


d = Model(foobar='{"foo": 1, "bar": [1,2,3]}')
print(d.dict())

try:
    Model(foobar='{"foo": 1, "bar": [1,2,"x"]}')
except Exception as e:
    print(e)

try:
    Model(foobar='{"foo": 1, "bar": not json}')
except Exception as e:
    print(e)

outputs:

{'foobar': {'foo': 1, 'bar': [1, 2, 3]}}
1 validation error
foobar -> bar -> 2
  value is not a valid integer (type=type_error.integer)
1 validation error
foobar
  value was not valid json (type=value_error)

@jaheba
Copy link
Contributor Author

jaheba commented Jun 11, 2018

Thanks, I hadn't thought about pre.

This is essentially what I wanted:

class MyModel(BaseModel):
    key_a: int
    key_b: List[int]

    @validator('key_b', pre=True, whole=True)
    def _b_as_json(cls, v):
        return json.loads(v)


print(MyModel(**{
  "key_a": "42",
  "key_b": "[1, 2, 3]"
}))

I still like the idea of receiving a parser by just specifying things like List[int] though.

@samuelcolvin
Copy link
Member

I guess the best solutions is would be a Json type with can be used on its own or like Json[List[int]] etc.

PR welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request help wanted Pull Request welcome
Projects
None yet
Development

No branches or pull requests

2 participants