flask: Flask 1.1.1 broken with Werkzeug 2.0.0 release

    install_requires=[
        "Werkzeug>=0.15",
        "Jinja2>=2.10.1",
        "itsdangerous>=0.24",
        "click>=5.1",
    ],

from setup.py can install werkzeug 2.0.0, which breaks things.

from flask import Flask, request

app = Flask("bla")

@app.route("/")
def x():
    print(request.remote_addr)
    return ""

c = app.test_client()

print(repr(c))
c.get("/")

remote_addr is none when it should be localhost.

Python 3.8 Flask 1.1.1

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 22 (11 by maintainers)

Most upvoted comments

2.0.0 was just released yesterday; lots of people (including me!) rely on libraries and extensions that won’t be compatible with the new major version for a while. Breaking support for the previous major version unless someone searches closed github issues to find out that they need to pin a downstream library is a pretty rough user experience.

That doesn’t seem like a great answer? Right now if someone just does pip install flask==1.1.2 in a new project, they get a buggy installation because it will automatically pull in Werkzeug 2.0.0. It really seems like Flask 1.1.x should have a Werkzeug < 2.0.0 in its install_requires.

No longer having a valid remote_addr in testing is pretty broken for a web framework. To wit: multiple people have already hit this bug (including myself) in the few hours it’s been possible to hit it.

I really don’t think it’s that controversial to request a Flask 1.1.3 with the werkzeug dependency pinned appropriately to <2.0.0

I spent a while investigating it; the issue appears to be that EnvironBuilder as called by the Werkzeug testing client only allows HTTP headers in now, as opposed to other WSGI parameters, so requests made from the flask testing client no longer have REMOTE_ADDR set at all. You also can no longer override the REMOTE_ADDR in testing by passing it as an argument in environ_base / environ_overrides. The code in the OP prints the following with Flask 1.x and Werkzeug 1.x:

<FlaskClient <Flask 'bla'>>
127.0.0.1

and the following with Flask 1.x and Werkzeug 2.x

<FlaskClient <Flask 'bla'>>
None

I haven’t done a lot of digging to see if this change to EnvironBuilder affects more than the test setup because, well, once it broke my unit tests I wasn’t about to try pushing anything to production using Flask 1.x and Werkzeug 2.x.

I couldn’t quite follow where stuff was getting lost when poking at it in a debugger, but it seems like a behavioral change in the werkzeug.test.EnvironBuilder.from_environ method.

Hey all, I get that you’re frustrated, I’m sorry that you have run into this unexpectedly. However, please note that we will not be making a new release at this time, so arguing further is going to end up being counterproductive.

I was just making an argument why not pinning all your dependency is a problem, not only with flask/werkzeug.

Please use a tool like pip-compile to pin your dependencies and control when you upgrade them.

I was just making an argument why not pinning all your dependency is a problem, not only with flask/werkzeug.

I pin my direct dependencies. I should only need to pin indirect/downstream dependencies when I have two completely unrelated libraries that interact in a nasty way, and that really shouldn’t happen (though it does).

When my project depends on flask I pin a flask version. If I import something from werkzeug, I should have to pin werkzeug, but not before that.

I fully lock dependencies in my project, but this becomes a problem when resolving dependencies during an upgrade. If I have flask pinned to 1.1.2, and want to upgrade another library, the tooling automatically updates to werkzeug 2. That’s why this is a big deal.

Can I ask what the difficulty is with making a new release that just limits these direct dependencies appropriately?

If you get that we’re frustrated, perhaps a little more explanation would help ease our minds…

So you think that you are okay with doing same mistake that celery developers did and make other people pay for it? That’s all I hear.

Also, security is irrelevant here. It’s everyone’s responsibility to release something built on secure dependencies, which covers open source contributors too. But on the other hand, releasing sth that doesn’t work unless each user make a manual patch to cover it is the problem here.

pinning direct dependencies is good enough for a developer.

Except that this is not the case. I remember quite a few times where I had to make new releases of my own application because e.g. one of celery’s dependencies broke celery, and I had only pinned celery - after al,l I did not expect such changes (especialy since the broken dependency came from the same developers). Had I already pinned all dependencies back then, I would not have had that problem.

FWIW this is also a good idea from a security point of view: Do you really want someone who does a pip install yourapp to get a possibly bad dependency because the developer of some transitive dependency of your application get compromised and published a malicious update? With pinned dependencies that just doesn’t happen (to be fair, you might not notice something like this when doing a pip-compile -U to update everything, but users still get one stable set of dependencies where all tests passed etc.)

Which is why you need to pin all your dependencies, not only the direct ones. If you’re not doing that, this could happen to you with any upgrade, it’s not a specific issue with Flask or Werkzeug.

I don’t want to contribute to the flame but that is not the case at all. Excuse me sir, pining direct dependencies is good enough for a developer. Why don’t you pin/limit direct dependencies on your code too? Do you really think something like pip-compile will produce a working result with that error in flask code?

Flask 1.1 does run with Werkzeug 2.0 though. I just tried a basic application it in a fresh virtualenv. For example, the issue reported at the top here does not cause Flask to break.

IMHO that’s the perfect reason for pinning dependencies…

and FWIW, most somewhat actively maintained extensions are already compatible - those that still haven’t had updates may be worth a closer look if they aren’t abandoned (e.g. flask-restful, flask-mail) and thus not a good choice to begin with.

Also, you could say that those flask extensions should have pinned flask<2 - if that’s the case, pip install flask flask-whatever will give you a flask version compatible with the extension.

I’d say if someone installs an old version of flask in a new project, they are doing something wrong…

Which is why you need to pin all your dependencies, not only the direct ones. If you’re not doing that, this could happen to you with any upgrade, it’s not a specific issue with Flask or Werkzeug.

Yes, that’s the way to get around this, but you’re breaking everyone who pins flask but not werkzeug, unfortunately.