-
-
Notifications
You must be signed in to change notification settings - Fork 998
Improvements to SchemaGenerator
#172
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
Comments
First, Starlette source code is a pleasure to read. Here are my plans. I imagine you would prefer me to build it as a separate project to not bloat Starlette, to keep it as pure ASGI as possible and to avoid putting very interdependent stuff together here. But if you think it would be acceptable/desirable to have it integrated into Starlette, my first option would be, of course, to create a huge PR / set of PRs implementing it as part of Starlette. Here's what I want to build. First, my main point/concern is to achieve a specific set of features, that overlap a lot with what you did in APIStar. I'm actually starting from the development experience I want to have, and then finding, mixing and creating the tools that would provide it. It want it to have this:
A "code colored fragment" is worth a thousand words. So, I want to be able to have something like this (part of my experiments): from typing import List
from pydantic import BaseModel
class Pet(BaseModel):
name: str
age: int
class User(BaseModel):
name: str
pets: List[Pet]
class StatusMessage(BaseModel):
message: str
@app.put("/users/{user_id}/")
def route_get_users(user_id: int, user: User) -> StatusMessage:
print(user_id)
print(user.pets[0].age)
return {"message": "created!"}
@app.get("/users/{user_id}/pets/")
def route_get_users(user_id: int, skip: int, limit: int) -> List[Pets]:
some_user: User = get_user(user_id)
return some_user.pets An example of how it could look like, and how the completion could work. See that the received Latter I plan on adding an equivalent of (or similar to) APIStar components, to put things like JWT Token auth as more function parameters, that return the current user only if all the auth validation passed. How to implement it:
I'm still thinking about the best way to declare returned values, as if I declare a Pydantic class but actually return a dict, mypy will complain. And also I'm still unsure about the best way to declare the HTTP status codes. I still haven't defined how to finally do that, but even the current idea works (again, all inspired by APIStar). So, what do you think? Do you think any of all this would be a good fit to Starlette? Up to which features would you like to have here, if any? |
Sounds great! I think that most likely any integrations with Pydantic, Marshmallow, API Star, or anything like that ought to be as a third party package. It shouldn't really be in scope for Starlette to make opinionated choices at that kind of level. However I think we should aim to do a really good job of promoting third party packages from the main docs. I wasn't clear how you'd indicate if a model is for query parameters or for the request body. If there's extra information you want to put in there, then one way would be to use decorators, eg. @app.get("/users/{user_id}/pets/")
@annotate(query_params=['skip', 'limit'])
def route_get_users(user_id: int, skip: int, limit: int) -> List[Pets]:
... You'd want to use subclasses of We might want to think about how we can support that from |
Cool! I agree. I'll keep it separated and add a PR to the docs once I have something 😄
I'll add the extra info as the default value. I see now that in my example I didn't include that. The same way Pydantic uses a But I'm planning on adding some defaults as follows:
For the rest of the locations (header, cookie, etc) and declarable metadata, or to override the defaults, or add more metadata, use default class instances, e.g.: from some_package import QueryParameter
@app.get("/users/{user_id}/pets/")
def route_get_users(
user_id: int,
skip: int = QueryParameter(None, title="Items to skip"),
limit: int = QueryParameter(None, title="Limit items to get", description="Limit the results to have only so many items, after skip."),
) -> List[Pets]:
some_user: User = get_user(user_id)
return some_user.pets That way I can add everything related to one param together. I think that's the most common error I make when working with decorators and declaring OpenAPI schemas and validation with them, as I have to put And Python could provide this funny way to avoid that and simplify the code, passing information via type hints (that are also used by the editor) and default values for meta information. And both can be inspected / introspected. But also, having that default logic described above, many common cases could be easily coded and "just work", e.g.: @app.put("/users/{user_id}/pets/")
def route_get_users(user_id: int, pet: Pet, make_favorite: bool = False) -> Pet:
created_pet = put_pet(user_id, pet)
return create_pet In this case:
And with that simple code, and simple syntax, we get:
It's like "killing a bunch of birds with one stone". And those features, with that simplicity, have several advantages (in my point of view) over all the other similar options I know (Python Flask-apispec, TypeScript Nest for NodeJS, yaml in docstring based alternatives, etc).
Yes! that's exactly what I'm doing. I'm glad to see you agree, it means I got it right then 😂 I'm also subclassing |
We'll probably want to add |
Good idea 👍 I'll see how my experiments go and report back (as a PR to docs with "third party projects" ) 😄 |
I'm doing some work regarding to type system and parameters validation based on API Star v0.5, so as you mentioned about routing I also needed to modify the It's on early development stages yet, but here you can found the code: starlette-api. I'll update the readme with some doc and examples that I've been using to test it. I think we're aiming the same goal so we can discuss if we could join forces to get it :) |
@perdy Ace. Yup I'd also been thinking that Starlette's getting to the point that an API Star layer on top would work well. 😎 |
Hey ppl, what do you think about molten validation? field api # molten
from molten import field, schema
from typing import Optional
@schema
class Todo:
id: Optional[int] = field(response_only=True)
description: str
status: str = field(choices=["todo", "done"], default="todo") # python dataclasses
from dataclasses import dataclass, field
@dataclass
class C:
x: int
y: int = field(repr=False)
z: int = field(repr=False, default=10)
t: int = 20 It would be really interesting to see an integration between this + starlette + openapi schema generation What do you think? |
@woile Yup to any and all of the above. The important thing for Starlette to do is to ensure that it's easy to adapt to various endpoint styles. Any third party stuff that can be implemented on top of that is much welcomed. |
Cool @perdy , I think we are thinking about similar solutions. Although I'm more inclined to try using Pydantic, as its type declarations are based on the same "standard" Python types. So a declaration in APIStar with: class Product(types.Type):
name = validators.String()
rating = validators.Integer(minimum=1, maximum=5) In Pydantic would be like: class Product(BaseModel):
name: str
rating: int = Schema(..., gt=0, lt=6) the difference is that editors will give support, checks, and completions for prod = Product()
prod.name For example, the editor will give completions for
Update: with the latest additions to Pydantic's But I guess we are all trying to achieve something like what APIStar was achieving. @woile Thanks for pointing it out! I think I hadn't seen Molten, it looks quite interesting, seems pretty close to what I want to have. I'll play with it. I think we'll still benefit from having something like that but based on ASGI with Starlette, to get its massive performance (kudos to @tomchristie for these awesome tools and ideas). But I'll definitively check how Molten works, the development experience and its source code 😁 BTW, Pydantic has a dataclass based system too, with a backport to make it work for Python 3.6 too: from datetime import datetime
from pydantic.dataclasses import dataclass
@dataclass
class User:
id: int
name: str = 'John Doe'
signup_ts: datetime = None
user = User(id='42', signup_ts='2032-06-21T12:00')
print(user)
# > User(id=42, name='John Doe', signup_ts=datetime.datetime(2032, 6, 21, 12, 0)) |
@tiangolo I'm not specifically interested in use API Star types and validators, I just started with it because it sounded familiar to me. Pydantic seems elegant so, if we can get the same functionality that API Star types and validators provides I don't have any problem to move into it. |
Sorry for the delay coming back to this guys. I had been busy implementing updates to Pydantic to support the things I needed:
I also took quite some time reading, re-reading and studying the specs for JSON Schema, OpenAPI, etc. And re-studying similar tools, as previous versions of APIStar, Molten, Flask-apispec (also based on Marshmallow), NestJS (but that is not even Python), etc. And then, using all that to create FastAPI. It has the following:
I still need to:
...but I thought that at this point I can start sharing it with you guys 😁 🎉 Now, question related to Starlette, before I stop hijacking this issue, should I make a PR to add it to Starlette's docs? I don't see any "third party packages" section or similar. Should I propose one or where do you think it should go @tomchristie ? I guess it could include: @woile 's https://github.com/Woile/starlette-apispec |
Wow tiangolo, that's a great addition to the Starlette community. Awesome work. I'll explore it more during the week. I was wondering, are you thinking about adding a pydyntic plugin for APISpec? |
Awesome!
Actually, before getting to this point, I was using Flask-apispec, which is based on APISpec, Marshmallow and Webargs. All of them built by the same guys. And it is awesome for pre-types code (Flask). I actually built a couple of project generators around them, which is the main stack I was using up to now. But now having types in Python 3.6+, I ended up preferring Pydantic that uses them directly, instead of Marshmallow (used by APISpec). Also APISpec doesn't support OpenAPI 3.0 yet, it was initially built for Swagger 2.0 (OpenAPI 2.0). I'm starting FastAPI from OpenAPI 3.0.3 (which is the current version). And also, I'm integrating all the type declarations everywhere, to do the validations and generate the OpenAPI schema. So, for example, only when there are parameters declared in the functions I include the schemas for validation errors. And only when there are security dependencies I include the schemas and OpenAPI "security schemes". If you want to test a quick "ready-made", project generator (with Docker, Couchbase, Vue, etc), I made this one (based on the previous ones I had for Flask-apispec): https://github.com/tiangolo/full-stack-fastapi-couchbase |
Yes please! |
I would propose something like hapijs.
https://hapijs.com/plugins
I find it quite simple
…On Sun, Dec 16, 2018, 13:05 Tom Christie ***@***.*** wrote:
I don't see any "third party packages" section or similar. Should I
propose one
Yes please!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#172 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ACd02NIB5G4Vck906rtsxFktOSVi_lBVks5u5m9fgaJpZM4YGZtD>
.
|
Yup agreed. We can have a “frameworks” section as one part of that. |
* Include mounted paths in schemas (part of #172) * Remove unnecessary indirection * Refactor: cleaner interface, return a dict always
There's also a stack of really nice third party work that we can do here, eg packaging up Swagger UI or Redoc or API Star into API docs apps.
Contributor work towards any of the above would be very much welcome! 😄
The text was updated successfully, but these errors were encountered: