-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
JSON Schema update/refactor/augment, to conform to spec #308
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
Conversation
|
a7669b8 to
8c555ba
Compare
|
|
Run just |
Codecov Report
@@ Coverage Diff @@
## master #308 +/- ##
======================================
Coverage 100% 100%
======================================
Files 12 13 +1
Lines 1532 1703 +171
Branches 285 321 +36
======================================
+ Hits 1532 1703 +171 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good initially, but quite a lot to change.
I'll probably have some more stuff I want fixing once this is resolved but hard to see without fixing my initial concerns.
If there's loads more to do I might submit a PR against this PR to save you some time.
pydantic/schema.py
Outdated
|
|
||
|
|
||
| def get_flat_models_from_model(model: Type['main.BaseModel']) -> Set[Type['main.BaseModel']]: | ||
| flat_models: Set[Type[main.BaseModel]] = set() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we generally don't both with type annotations inside methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I removed the others too.
pydantic/schema.py
Outdated
| from .utils import clean_docstring | ||
|
|
||
|
|
||
| def get_flat_models_from_model(model: Type['main.BaseModel']) -> Set[Type['main.BaseModel']]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is main.* enough for mypy or ids to work out what BaseModel is? Surely pydantic.BaseModel or pydantic.main.BaseModel is required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also if we can import BaseModel, just use that rather than a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't import it directly as these functions are imported and used inside of BaseModel. I had to use that "trick" from PEP 484.
If you want I can change it to:
def get_flat_models_from_model(model: Type) -> Set[Type]:But for it to work it needs the models to be Pydantic, i.e, to have __fields__. That's why I annotated it like that.
Let me know what do you prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you already import main above, so therefore surely you can use main.BaseModel or better import BaseModel from main and use it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can't be done with circular imports. main.BaseModel already depends on these methods, so BaseModel is not yet defined by the moment these methods are defined, as they are imported and used by main.BaseModel first, check the errors in CI: https://travis-ci.org/samuelcolvin/pydantic/jobs/457945798
From PEP 484: Forward references:
When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.
A situation where this occurs commonly is the definition of a container class, where the class being defined occurs in the signature of some of the methods. For example, the following code (the start of a simple binary tree implementation) does not work:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = rightTo address this, we write:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = rightLater in the same section:
[...] The solution is to switch to module-only imports and reference the models by their module.class name:
# File models/a.py
from models import b
class A(Model):
def foo(self, b: 'b.B'): ...
pydantic/schema.py
Outdated
| return flat_models | ||
|
|
||
|
|
||
| def get_flat_models_from_sub_fields(fields) -> Set[Type['main.BaseModel']]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to get_flat_models_from_fields and reuse in get_flat_models_from_model
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
pydantic/schema.py
Outdated
| if field.type_.le is not None: | ||
| f_schema.update({'maximum': field.type_.le}) | ||
| # Sub-classes of str must go before str | ||
| if issubclass(field.type_, EmailStr): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again some kind of iterable and loop would be much better here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| from .types import DSN, UUID1, UUID3, UUID4, UUID5, DirectoryPath, EmailStr, FilePath, Json, NameEmail, UrlStr | ||
| from .utils import clean_docstring | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
best to put all these "helper" methods at the bottom of the file and the "primary" methods (those people might want to read first) at the top.
Also, you should probably implement __all__ here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| @@ -0,0 +1,5 @@ | |||
| from pydantic import BaseModel | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove all this and put it into the tests.
If you really need some python modules to test use tmpdir and write the files required within the test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
tests/test_schema.py
Outdated
| } | ||
|
|
||
|
|
||
| def test_date_types(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all these multiple type tests would be better rewritten as parameterised tests - if one case here fails it'll be a lot easier to work out which one an fix if they're 4 separate tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
|
||
| class Pizza(BaseModel): | ||
| name: str | ||
| ingredients: List[Ingredient] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really need all these models to test the functionality? Unit tests should be as simple as possible while testing the desired behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed one of the submodels. But having at least two models with submodels would verify that the submodels are included not only for the first model in the list. Let me know if you still want me to reduce more.
|
Byt the way,
|
|
@tiangolo so far I have 51 emails from you (via github) in the last few days. Please get the PR to the point where you'd like me to review it and let me know. I don't need replies to every single code comment and every line changed doesn't need its own commit. |
Thanks for the tips.
Sorry for that. Now I tried to avoid pushing before finishing with the rest of the review. I see now that there are new conflicts with Edit: I see you requested a rebase in another PR so I did that. |
including datetime and all supported Pydantic.types
and update/simplify internal Schema methods
and generation of a single JSON Schema with definitions from multiple (unrelated) models
517bad9 to
a73818e
Compare
|
OK, that's it for now. Python 3.7-dev keeps failing with some random race condition or something that comes from time to time. I could try re-pushing empty commits to try and make it run again, but I guess I that would trigger more emails. And we non-owners don't see the "Re-start build" button in Travis CI. I think I already implemented all your requested changes. If something else is missing or should be changed, let me know. I'll (hopefully) check it in the next days. But it's almost midnight here and I can't "extend" the weekend more. ...and my wife is not loving the project right now 😂 |
|
@samuelcolvin let me know if there's anything missing. |
|
frustratingly although it should let me, github isn't letting me push to your branch and therefore make changes to the PR. Instead, I've submitted a PR tiangolo#1 to your repo on this branch. If you could review that, ask any questions and merge it. If that's all ok, I think we'll be ok to merge this. |
Strange...
Awesome! I'll check it right away. |
|
I changed the links to the specs. If you want, check them. |
|
I'm sure they're right, are you happy for me to merge this? |
|
Yes! 🌮 |
|
I'll release in a couple of days. If anyone has any further feedback on this, let us know here. |
|
Awesome, thanks. 🎉 I'll start working on a PR to integrate validation and schema annotations passed to the |
* uprev-deps * cargo update
Update/augment JSON Schema implementation to conform to JSON Schema spec
Note
I renamed
BaseModel.schema_jsontoBaseModel.schema_str, as it was a bit confusing for me the first time I tried Pydantic. As this is a change mostly related to "taste", let me know if you want me to revert it. Or maybe rename it to something likeschema_dump.Description
max_length,regex, etc.EmailStrgenerates a"type": "string", "format": "email", equivalent fordatetime.bytesget a"format": "binary", although there is no equivalent in JSON Schema, that is the format defined in OpenAPI, etc.definitionskey, this allows better re-use of the definitionstitle, etc)definitionskey are checked and processed to have unique names, somodulea.Modelwill be namedModelif there is no competing class, but if there is amoduleb.Model, both models will have corresponding unique names (modulea__Modelandmoduleb__Model)components).Related issue number
#291
Performance Changes
pydantic cares about performance, if there's any risk performance changed on this PR,
please run
make benchmark-pydanticbefore and after the change:JSON Schemas are generaly only run once, and are also cached. As they are more like a "colateral" feature that is not involved in the normal worflow of Pydantic, I don't think performance could be affected or relevant for this PR.
Checklist
HISTORY.rsthas been updated: Updated.#<number>@whatever