Splitting out the server


#1

Something that I’ve been considering for a while, that I think it’s time to raise.

There have been three big motivations behind API Star:

  • Pushing Python’s asyncio forward through support for ASGI. (Initially by formulating an ASGI-like interface, which helped inform the motivation and design for ASGI 2.0)
  • Revamping the schema validation, API docs, dynamic client libraries, that coreapi initially provided.
  • Using dependency injection for views, in order to self-document the schema types that each view accepts.

ASGI’s coming along nicely now. I’ve got some more work I’d like to focus on with the next versions of Uvicorn, plus a making a suite of reusable ASGI tooling available through Starlette.

The API docs and validation are available with the apistar command line tool, although not yet documented. The python client library is part of the core package, although again, not yet documented. The functionality that they provide is also going to be available in the web app that I’ve been working on and will be making public in the next few weeks.

The Uvicorn server, the Starlette library, and the apistar command-line tool, and upcoming web app, are all nicely scoped, and (along with REST framework) these are the bits that personally, I want to really focus on.

So…

Really I’d like to split out the server part of apistar.

It’d have the apistar package as a dependency (for the types, validation, docs generation etc…). It might also be a good time considering dropping WSGI support, and having it just as an ASGI framework.

This would happen alongside making the apistar web app public, and making a new release of the apistar package that has the command line client for API docs generation, schema validation, dynamic clients, and schema mocking.

It’s possible that the server package could just be “apistar-server”, or perhaps even move it to being more of community owned project, with a different name. (I’d like to keep apistar for the core schema library and web app.) Either way it’d have it’s docs separately to the apistar command line tooling.

This doesn’t necessarily mean any upgrade pain, just that the server bits would end up as a separate package.

Figure this all needs raising for discussion.

Taking this step would mean I could focus on the fundamentals of pushing ASGI forward, and bringing it to other frameworks, plus give me a much more narrowly scoped apistar library to manage, alongside the web app.

  • Would folks be up for stepping up as maintainer of the new package?
  • Should we be up for renaming it, or is “apistar-server” what you’d prefer to see?

#2

Dropping WSGI support in the framework would be big loss in my opinion and would hurt more than the bump from 3.9 to 4.x. I still live in the WSGI world and it allowed me to pick up apistar and use it to create some interesting services. I am not a trail blazer in the async world, but this framework is appealing because you can use both models.

Before making that decision have you considered how this would impact adoption of APIStar in the future and the impact to your current users? I know this is an alpha level framework, but I would basically have to rewrite all of my work for the ASGI model or abbandon APIStar to go back to Flask or Django. Additionally, I would lose out on synchronous packages that I am very productive with like SQLAlchemy.

So is it necessary to go full ASGI?


#3

I must say the wsgi drop is a big thing.

Every one using orm (django, sqlachemy, pony) will have to drop apistar to continue use them.


#4

Useful feedback - thanks!

The path of least resistance might be moving it out to ‘apistar-server’ and not changing anything else.


#5

AS you are to split, split in three: apistar, apistar-asgi, apisar-wsgi, people using asgi will look more on asgi, same for wsgi, no need to drop all the work, and once project is split by interface it can get clear if somethig is asgi only, wsgi only, or both supported. Just another idea.

PD1: for me it’s better to separate asgi from wsgi, being myself a user of both flavors of apistar

PD2: Finally streaming responses in starlette, this is looking really good and necessary.

PD3: Any chance you consider having on apistar-server for asgi uvicorn --reload as test server and even for unit testing client responder instead of a mocked interface with asyncio.run_until_complete? We have already run into problems where tests run smoothly and failed miserably in uvicorn unfortunately.


#6

for sure the decision keeping and maintaining wsgi, should be the decision of future maintainer since He’ll be the more concerned about it.


#7

@jgirardet Indeed…


#8

I’m getting to the end of initial WebSocket support for ApiStar, in it’s current form.

The WebSocket code is 100% ASGI 2 compliant, is async only, and I think would benefit from a split between WSGI and ASGI do to it being an ASGI only transport in ApiStar. The current ASGI HTTP response handling , in server/app.py, seems to be more of a wrapper around WSGI style instead of being ASGI from the ground up(which has worked well so far). This did cause some interesting issues with WebSocket closures/responses where a different code path is needed instead of just having a unified ASGI Response that can handle the two protocols/types.

For instance in server/app.py when it finalizes an ASGI response it expects things to be HTTP. It would be nice if it could treat things as either WSGI or ASGI and in the ASGI case a response could be for HTTP or WebSocket without server/app.py having to decide which it is to handle it properly. It seems to me if we had an ASGI Response, the service should just now how to handle an ASGI Response and not need to worry if it’s HTTP or WebSocket. By the way, the biggest difference in server/app.py for a WebSocket response is there’s usually not any content to send and the server needs to close the socket if it isn’t already. Where with the current state of things, server/app.py takes if for granted that it’s HTTP, like WSGI, and always sends on HTTP response.

After having worked on ASGI WebSocket I’m very much for a clean code split between WSGI and ASGI with ongoing support for WSGI as it’s everywhere and supporting both also provides a fairly clean path for devs to move from WSGI to ASGI as necessary within the same framework: adding WebSockets alongside an existing WSGI app, for instance.

If I’m understanding the proposed split properly:

  • Starlette for dealing with ASGI in a high level and familiar way.
    • I’d like this to mean an ASGI Response is about ASGI, not just HTTP.
  • ApiStar Server for dealing with Requests/Responses provided by Starlette (and maybe WSGI?) and the
    view handling, components and injection goodness.
  • And then ApiStar to bring it all together, providing the docs, validation, cli, web service client, etc.

I’d love to see a cleaner and more formal ASGI 2 stack with a server that could handle ASGI and WSGI similar to the current state of things, ie: components and param injection etc.

I like the idea of a split, and agree with continuing support for WSGI.

For the curious, check out my WebSocket WIP: https://github.com/jeffbuttars/apistar/tree/websocket-initial


#9

As others have said, dropping WSGI support would have a massive impact on those of us who prefer the “standard” way of doing things/prefer implicit async via gevent/hate the absolute shit show that asyncio has been so far so my hope is you’ll reconsider dropping it.


#10

‎This might be off topic, but a link to a write-up that explains this position would be educational.


#11

I really should do a write up when I find some time. I agree with much of Mike Bayer’s post on the topic though he doesn’t really go deep on the bigger problem that I see with asyncio which is that it splits the library ecosystem (as demonstrated in this very thread where Tom is considering dropping WSGI support because supporting both async and non-async code is a lot of work (correct me if I’m wrong on the reasoning, @tom! I don’t want to put words in your mouth, but that was my impression)). Bob Nystrom has a post on this topic, although he doesn’t talk about Python specifically.


#12

Broadly: Benchmarking HTTP servers doesn’t seem bear out Bayer’s core point, and using a pure python DB driver isn’t necessarily what you’d want in a high-volume production environment. Also there are plenty of services where you’re not just Database-bound, but network bound, making outgoing HTTP requests or so on.

Nystrom’s post is really well written, but the take-home isn’t “don’t use co-operative concurrency” - it’s that co-operative concurrency isn’t a magic bullet, and that there are language design choices that can be made to remove the explicit split. (eg. as in Go.) In existing languages and ecosystems that isn’t an option.

This isn’t a philosophical issue, it comes down to trade-offs.

For most users async just isn’t relevant - you’re not working at high enough throughput that the potential saving in server costs will offset the additional development cost of working against a less mature ecosystem. However for some use-cases it really is important - you generally wouldn’t want to write a high-throughput proxy server or gateway service in Python, but you might want to in Node. For myself I’m interested in pushing ASGI forward because it does all the foundational work of making higher-throughput Python webservices available.

I’m likely not going to force users into only one corner here, but if I did, then the argument would be that APIStar Server as ASGI-only is attractive because it’s more differentiated and better suited for a particular niche that existing mature frameworks don’t cover well.


#13

Having thought it through some more, a really good path to take would be to allow either async or sync style code, by using ASGI-only, but running standard handlers inside a threadpool.

That’d me we’ve only got a single ‘App’ class (No more ASyncApp vs App), and both use cases are covered. (You could even do a mix of both if needed. Eg if you have a few handlers that make outgoing HTTP requests)

It’d also answer how we should add websockets (ASGI powered, but run under a threadpool for sync code)


#14

Sounds a bit like the Channels solution, but I thought they still had the two code paths WSGI and ASGI and let you jump to the other execution model as needed.

  1. Does this mean that the internals of APIStar will be handling the Thread pool assignment of synchronous handlers, or are you envisioning a run_as_sync type of method or decorator?
  2. As a developer how does my code base change under what you are proposing? Will this significantly impact the current API for components, or hooks or other items I would be interfacing with?
  3. I imagine that Werkzeug then will no longer be necessary as a dependency. Will it be removed or still be providing functionality for items like the multipart codec?

#15

Does this mean that the internals of APIStar will be handling the Thread pool assignment of synchronous handlers, or are you envisioning a run_as_sync type of method or decorator?

APIStar would manage everything for you. If using a regular handler function you’ll be within a thread pool. If using an async handler you’ll get asyncio.

As a developer how does my code base change under what you are proposing? Will this significantly impact the current API for components, or hooks or other items I would be interfacing with?

Ideally as little as possible. You don’t see much of the difference between WSGI/ASGI anyways so hopefully it’s more a case of us being able to remove a bunch of code paths under the hood, perhaps with some import changes being needed if we split the server out from the rest of the codebase.

I imagine that Werkzeug then will no longer be necessary as a dependency. Will it be removed or still be providing functionality for items like the multipart codec?

Currently we use it for the routing, for the debug display, and for multipart parsing. I’d imagine it’d stay in to start with.