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

Support PEP-585 style collection type hints for OpenAPI? #257

Closed
kevlarr opened this issue May 10, 2022 · 5 comments
Closed

Support PEP-585 style collection type hints for OpenAPI? #257

kevlarr opened this issue May 10, 2022 · 5 comments

Comments

@kevlarr
Copy link

kevlarr commented May 10, 2022

I'm newly discovering BlackSheep and really liking it so far! (Especially being able to use simple data classes for OpenAPI generation.)

PEP-585 adds support for generics syntax to standard collection types in Python 3.9+, eg. list and set so that we don't need to use their equivalents from the typing module.

I had hoped the following could work:

from dataclasses import dataclass

from blacksheep.server import Application
from blacksheep.server.openapi.v3 import OpenAPIHandler
from openapidocs.v3 import Info

app = Application()

docs = OpenAPIHandler(info=Info(title="Example API", version="0.0.1"))
docs.bind_app(app)


@dataclass
class Pet:
    name: str
    age: int | None


@app.router.get("/pets")
def pets() -> list[Pet]:
    return [
        Pet(name="Ren"),
        Pet(name="Stimpy", age=3),
    ]

But, on server startup, I receive the following error:

Traceback (most recent call last):
  File "[path]/blacksheep/server/application.py", line 714, in _handle_lifespan
    await self.start()
  File "[path]/blacksheep/server/application.py", line 703, in start
    await self.after_start.fire()
  File "[path]/blacksheep/server/application.py", line 108, in fire
    await handler(self.context, *args, **keywargs)
  File "[path]/blacksheep/server/openapi/common.py", line 394, in build_docs
    docs = self.generate_documentation(app)
  File "[path]/blacksheep/server/openapi/v3.py", line 276, in generate_documentation
    info=self.info, paths=self.get_paths(app), components=self.components
  File "[path]/blacksheep/server/openapi/v3.py", line 280, in get_paths
    own_paths = self.get_routes_docs(app.router, path_prefix)
  File "[path]/blacksheep/server/openapi/v3.py", line 915, in get_routes_docs
    responses=self.get_responses(handler) or {},
  File "[path]/blacksheep/server/openapi/v3.py", line 813, in get_responses
    {
  File "[path]/blacksheep/server/openapi/v3.py", line 817, in <dictcomp>
    content=self._get_content_from_response_info(value.content),
  File "[path]/blacksheep/server/openapi/v3.py", line 754, in _get_content_from_response_info
    ] = self._get_media_type_from_content_doc(content)
  File "[path]/blacksheep/server/openapi/v3.py", line 712, in _get_media_type_from_content_doc
    media_type.schema = self.get_schema_by_type(content_doc.type)
  File "[path]/blacksheep/server/openapi/v3.py", line 449, in get_schema_by_type
    schema = self._get_schema_by_type(child_type, type_args)
  File "[path]/blacksheep/server/openapi/v3.py", line 461, in _get_schema_by_type
    if self._can_handle_class_type(object_type):
  File "[path]/blacksheep/server/openapi/v3.py", line 357, in _can_handle_class_type
    any(
  File "[path]/blacksheep/server/openapi/v3.py", line 358, in <genexpr>
    handler.handles_type(object_type)
  File "[path]/blacksheep/server/openapi/v3.py", line 151, in handles_type
    and issubclass(object_type, BaseModel)  # type: ignore
  File "[path]/abc.py", line 123, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
TypeError: issubclass() arg 1 must be a class
ERROR:    Application startup failed. Exiting.

If I instead import typing and change...

def pets() -> list[Pet]:

... to...

def pets() -> typing.List[Pet]:

... then the server starts and I can load the docs.

The age field is hidden unless I change int | None to typing.Optional[int] but that's a separate issue for a different PEP.

@RobertoPrevato
Copy link
Member

Hi @kevlarr,
Thanks for your kind words and for opening this issue. I am interested in using these latest features, too, (including Annotated), the only thing that holded me back were limited time and easier handling of support for Python 3.7 and 3.8. But I think it's high time to add these.
During working hours I don't dedicate time to my open source projects, I'll look into this most probably in the weekend.

@kevlarr
Copy link
Author

kevlarr commented May 10, 2022

Makes a lot of sense! Honestly, I think support for 3.7+ is way more important than the convenience of skipping an import, and using typing is fine and familiar. I wouldn't even expect you to look into this soon - or at all, given how low priority it is compared to literally any other fixes or changes you might want to make.

If you wanted, I could spike on this for a bit?

the only thing that holded me back were limited time

Meanwhile, I don't understand how you even found (or made) the time to be so productive as to write a framework!

@RobertoPrevato
Copy link
Member

@kevlarr thank you for your kind words! 😄
Indeed, I dedicated an incredible amount of hours to these projects. And all in my spare time. As you can see, I also try to be responsive. Anyway, in the next weeks I will be less prolific, I need to restore a bit my work-life balance, take some holiday, and study for one exam.

To give a hint of how much time I dedicated to OSS contributions, one of my first contributions here in GitHub has been a simple tool to convert pictures into Base64 encoded strings: https://github.com/RobertoPrevato/Base64
Over the years, I shared much code. These two also took a lot of time: https://github.com/RobertoPrevato/KingTable, https://github.com/RobertoPrevato/DataEntry - but I don't maintain projects for Node.js anymore. The projects in Neoteroi are alive and I am willing to maintain them, the others under my first GH account are less lively.

Fixing this particular issue was simple, and I verified that BlackSheep already supports list[T] and Annotated. There is still an issue related to handling of PEP 604 T | None and I'll try to fix it before releasing the new version to PyPi.

Thank You for offering your help, I welcome contributions in general, in this case I already had an idea to try and it worked immediately.

@RobertoPrevato
Copy link
Member

Support for PEP 604 -> #261

@kevlarr
Copy link
Author

kevlarr commented May 16, 2022

@RobertoPrevato Wow, that's incredible - I honestly would not have expected these to be addressed, given how minor they are. I know how tough it can be to make time for open-source or any kind of project outside of work hours. Thank you for the hard work, I hope you're able to restore the work/life balance and get some (I'm sure) well-deserved time off.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants