11

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.

  • 2
    You haven't really stated what the specific negative effects of such an antipattern might be. I distrust sweeping generalities that have no factual basis. – Robert Harvey Mar 11 '14 at 6:08
  • 2
    Good question, but sadly not many quality answers – sleepycal Jan 18 '16 at 11:40
4

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.

3

(I'm going to go bold and make this an answer, although I might get some downvotes.)

Flask is a micro-framework; you benefit from the simplicity while giving up on frills. While on a gut level I agree with you, I do know that I used flask + gunicorn at one shop to give me the multi-threading that I needed. It worked really well. Each instance of the script just handed one request (i.e. one thread), and gunicorn handled the "fan out" among multiple threads. It was great at that.

So the perceived downside you're feeling -- that multiple threads could contend for global state -- just isn't an issue, because it's one script per thread.

(Here's where I may get into trouble) Threading and concurrency is just different in the Python world, and if you come to it with a Java frame of mind, it's hard to squeeze it in. My experience was that concurrency issues that I took for granted in Java, or that are handled transparently by the application container, are a lot closer to the surface in Python.

It was strange to me that one thread would handle one invocation of my script, but after I had a few dozen running on a box at the same time, I felt better about it.

  • 4
    I am not worried about thread safety and such. I believe Flask works well in these cases. My question is about application design and architecture. 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. – warvariuc Mar 11 '14 at 7:08
2

In Python you have the print command(function since v3) that prints to the standard output. You don't specify explicitly that you want to print to STDOUT - it's done for you implicitly behind the scenes.

Implicitly. In Python. And no one has a problem with that. Why?

print is part of the Python language, and one requirement of programming in Python is... well... knowing Python. And if you know Python, you know that print targets STDOUT. No surprises there.

Python - as a language - can define it's own convention and assume that the programmers are aware of them.

Frameworks also enjoy that privilege - that's one of the key differences between a framework and a library. You don't have to learn a library in order to use it - you just need to find the part of the API that you need, and assume it follows the conventions of the language(or framework). That's why you don't see recruiters looking for people with knowledge in GSON or Apache Commons. But you do see recruiters looking for people with experience with JQuery or Ruby on Rails or ASP.NET MVC - because those are frameworks that define their own conventions that you need to learn and be aware of.

Flask, as a framework, can define a convention for storing the context in a thread-local global - and it shouldn't surprise anyone, so it's not an anti-pattern.

  • 2
    Note that "stdout" means whatever file descriptor is pointed to by sys.stdout. If you change that, print goes elsewhere. – Phoshi Mar 11 '14 at 9:40
  • 1
    Also, you can override the output stream using >> operator or passing file argument to print function in Python3. So, sys.stdout is just a default value which can be overridden. – warvariuc Mar 11 '14 at 10:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.