Python – Global request context – anti-pattern

anti-patternsdesign-patternsflaskpython

I was talking today to a colleague of mine about Python web frameworks and our impressions about them. I told him I think Flask having a global request smells badly and is an anti-pattern.

The docs say about request context:

In contrast, during request handling, a couple of other rules exist:

  • while a request is active, the context local objects (flask.request and others) point to the current request.
  • any code can get hold of these objects at any time.

I think I understand the idea behind this design decision — to make the application simpler. It's just a compromise, like in the case of Thread Locals:

Yes it is usually not such a bright idea to use thread locals. They
cause troubles for servers that are not based on the concept of
threads and make large applications harder to maintain. However Flask
is just not designed for large applications or asynchronous servers.
Flask wants to make it quick and easy to write a traditional web
application.

Is patching a global object with the current request information an anti-pattern?

I believe it is, because it is in the view of static code analyzer a global state, though it's not. And I as a programmer will not understand how it works without reading the docs carefully. And this has consequences on tests.

Isn't it a good practice to pass the request as an argument to views? I think it's more readable, explicit and easier to debug. And avoids global state.

Best Answer

Many web frameworks have this same structure: a global request. In a sense, it's the right thing to do because hey, there really IS only one request at a time.

So is there any point in passing the request around as a parameter? No. The request is the request, and parameters are for passing in different things at different times.

The real problem comes as you start to consider lower levels of a larger application. With a global request there is the temptation to write code all over the place that accesses the request globally. That is a very bad thing. It produces coupling between different parts of the code, makes it hard to change things and makes it hard to test things.

So my answer is: keep the global request and live with it. However, wherever an individual module or function does not need the whole request, pass only the data it needs in as a parameter. Pass just the referrer, or the url, or the command tail and whatever bits you need into your functions. This will help keep the code modular, reduce coupling and improve testability.

For tiny programs it scarcely matters, but for bigger ones this can be a real lifesaver.