Homing in on 0.4


#1

We’re getting close to a 0.4 release now.

Here’s the state of the pull request: https://github.com/encode/apistar/pull/400

It’s a complete overhaul which means there are still some big things that have come out and haven’t yet made their way back in.

  • The Interactive API docs.
  • The Command Routing.
  • The apistar command line tool.

However the project is in a much, much happier place. We’ve also got some new features coming in:

  • ASGI compatible. We’ll have both uvicorn and daphne as production ready servers for use with ASyncApp.
  • OpenAPI is now the default schema representation. (and it’s far more feature complete)
  • We’ll be getting an event hooks API so things like adding CORS support will finally be obvious and easy.
  • Components are get a much nicer simpler API and implementation.
  • The type system gets a much nicer simpler API and implementation, plus we now finally get eg. Date/Time/DateTime support.

Where we choose to make the cutoff for 0.4 isn’t all that important, since the other bits of functionality will also come back in pretty quickly (I’ll be on this full-time for a bit) and personally I think it’s really important that we get this released as soon as possible, since it overhauls everything.

I think the only big blockers are:

  • Getting the documentation up to scratch.
  • Upgrading uvicorn to support the new ASGI spec.
  • Static Files support for ASyncApp.

#2
  1. I agree rip the bandage off now, and get going on 0.4.
  2. Glad to hear I don’t have to roll my own DateTime now in the type system.
  3. Is there an ASGI PEP out there for it? All I can find is the Django community defined specification.

#3

There’s no PEP yet, since it’s still early days for ASGI.

The ASGI redesign only came out earlier this year, and it’s only since then that ASGI’s become suitable for what we need (previously we had an “ASGI-like” interface, which I was referring to as the “Uvicorn Messaging Interface”.

Adopting ASGI means we get to have daphne as another production-ready webserver, in alternative to uvicorn. Having multiple independently authored servers any of which are capable of acting as the server and process manager for ASyncApp is a pretty big deal in terms of maturity of the asyncio space.


#4

Yeah, I can see this is in its nascent still. I am not necessarily a trail blazer in this effort, I imagine with your work in the Django space that you are more on the front lines.

So if I want to start learning the ways of Async web programming do I start at the bottom in asyncio then work my way into using async coroutines in my APIStar apps? Will you have an Async specific section in the future documentation?


#5

IMO 0.4 without knowing how to add in sqlalchemy / django orm support is awful UX for me upgrading my apps.


#6

I agree, I wouldn’t be able to do anything myself without sqlalchemy. Having the backend support built in was a draw for me when I first came here. It’s fine if its a third party extension, but I wouldn’t feel confident trying to write that one myself.

I know how to write a session manager, and could limp along. But that would be painful.


#7

That’s a very fair point, yup.

One thing I haven’t quite resolved in my mind is if we should continue to support context managers as components, or if we should instead keep the dependency injection simple and instead use the event hooks instead to provide for eg. session commit/rollback.


#8

In pony orm we can access the database via instance db (don’t need to be injected, just imported) but we have to use a decorator or context manager called db_session to use db (we put it in transaction)

That’s the third-party I started to do : https://github.com/jgirardet/apistar_ponyorm

class PonyDBSession:
    def on_request(self):
        db_session.__enter__()

    def on_response(self, response: http.Response):
        db_session.__exit__()
        return response

    def on_error(self, response: http.Response):
        db_session.__exit__()
return response 

If it can help to think about the other orm,…
I think #400 should provide at least sqlalchemy (in addition to ponyorm which is ready :wink:)


#9

I was updating my apistar-related libraries today and ran into the fact that dependency-injected values aren’t cached in event hooks (and presumably handlers). I found that to be surprising. I feel it would be a much nicer dev experience if they were. For example, this code wouldn’t require any thread local state.

Edit: Also, it’s unclear what order the hooks run in. In this case I’d like the Prometheus on_request hook to always be the first hook to run and the on_response and on_errors ones to be the last in order to capture the entire request duration. How would I do that?

Edit 2: It looks like the on_request hook doesn’t fire for builtin/error handlers so there’s no way to instrument errors that happen during request processing (eg. if a component raises a 403, there’s no way to get the total time spent processing that request).

Edit 3: Reading this it looks like the hooks are run in the order they are registered. Could this be changed so that the response and error hooks run in reverse order? For example:

event_hooks = [ 
  Foo(),  # implements on_request, on_response
  Bar(),  # implements on_request, on_response
]

When calling a handler, the hooks should be called in this order

Foo.on_request
  Bar.on_request
    handler
  Bar.on_response
Foo.on_response

rather than

Foo.on_request
  Bar.on_request
    handler
  Foo.on_response
Bar.on_response

as they are now. This would be more in line with how middleware normally behave.


#10

In 0.3.9 I was making use of the Render functionality to extend the encoder used by the json.dumps() to convert Datetimes and Decimals into acceptable JSON values and registered that globally as the Render for the application/json MIME type. Looking at 0.4 for the first time I am not seeing that functionality anymore. Should I just recreate my own JSON Response now that uses my extended encoder? Is this an area where I would use the on_response hook instead?


#11

So, I did implement a SQLAlchemy Component and Hook using scoped sessions. It works and I know you are thinking through the context manager idea. It just seems a little strange to me that I am going to create a new session for every request even if the handler doesn’t need access to the session. Also how would you use a session component in a command? I assume commands won’t have access to the event hooks.


#12

Should I just recreate my own JSON Response now that uses my extended encoder?

Yup that’d be the sensible thing to do.

So, I did implement a SQLAlchemy Component and Hook using scoped sessions. It works and I know you are thinking through the context manager idea. It just seems a little strange to me that I am going to create a new session for every request even if the handler doesn’t need access to the session.

I’m not 100% either way on this, but 0.4 would essentially mean we’re in the same position as any other framework-with-middleware when it comes to handling session commits and rollbacks vs. using explicit sessions.

I’m a bit lukewarm on re-introducing the context managers. If we did then we’d need to re-organize things so that the response only gets sent after all the context managers get finalized.


#13

That surprised me too actually - that’s a bug that needs resolving for sure.


#14

Glad to hear it. Any feedback on the hook ordering issue?


#15

Thanks for the speedy reply.

For anyone following this thread here is a Gist for the code I mentioned above.