Convert version compatibility checks to warnings

This patch will convert the dependency exceptions into warnings, preventing the forced halt while importing Requests. I condensed the message into a single response, but we can expand that out if needed. It just seemed like another place for updates to be forgotten.

I'd also like to test this function, but due to the requirements changing with each release, it's hard to write something that won't need to be updated in parallel. We could look at parsing the requirements out of `requires` but that seems less than ideal. If I'm being honest, I'm not crazy about expanding the code around this any more than we absolutely have to.

Err, force pushing dismisses reviews now? I took care of the flake8 issue with a `# noqa` because we will likely want to use `patch` in the future. Removing it creates a lot of clutter.

Alright, I'm going to merge this then since everything except the broken windows tests are passing.

Did @sigmavirus24 have any thoughts on this? Hard exceptions was his idea. 

Yeah, warnings are going to get missed by people in their log messages. People already tend to silence our warnings because our warnings and urllib3's warnings are far too noisy as it is. This is just going to cause us a headache. Yes. People are raising issues because someone shipped a version of a dependency using `dev` as the string, but that's no justification to switch away from exceptions.

Well, hang on then. What, exactly, is our intention here? I don't think we should be in the business of checking the versions of our dependencies: if we're going to specify dependencies let's just do that. This code is error-prone and requires constant updating, and any error we encounter in doing this causes us weird errors that we have to debug. I don't know that I see any value in having this code in "hard-fail" mode. I don't believe it's sensible for Requests to go on strike just because it's not happy with the version of urllib3 present in the environment: we should just try to use it and if it blows up at least we emitted some text that says what was wrong.

> at least we emitted some text that says what was wrong.

That's not reasonable. We emitted that at start up and by the time this might blow up those logs will have been rotated out. If we're going to start not restricting what versions of urllib3 we want to rely on (outside of pip or whatever package manager a user installs Requests with) then we need to start testing a much larger swath of urllib3 versions. Otherwise, we're now opening ourselves up to issues where someone has urllib3 1.2.1 installed, they see no warning because their version of Python silences them by default, they have no logs, and everything's blowing up in their face. We can tell people to upgrade urllib3 until we're blue in the face or we can just blow up in their face with a clear exception telling them why Requests won't function as they expect it to. I prefer the former. Preferably with an exception message that links to documentation.

> Otherwise, we're now opening ourselves up to issues where someone has urllib3 1.2.1 installed, they see no warning because their version of Python silences them by default, they have no logs, and everything's blowing up in their face

Are there any versions of Python that silence all warnings by default? I don't think there are: there are plenty that silence *deprecation* warnings, but I don't know any that silence all warnings. If not, I don't consider this a particularly meaningful example.

Let's consider the less extreme example: someone uses Requests with an out-of-support urllib3. There are two options: either it works, or it does not. If it does not, and they have lost the errors, we ask them to provide the output of `python -m requests.help` *at which point they see the warning again*. Even if they don't, and persist through the warning, we'll have an immediate issue close because of the unsupported version.

Hell, we could even have `python -m requests.help` print messages when run that say "your urllib3 is outside the acceptable version range, maybe deal with that".

If we're wedded to the idea of doing this, then we should discuss how resilient we should be. Should we be writing this code ourselves or should we use the packaging library to handle this? Should we be productising it and writing tests for it rather than YOLOing it into a `__init__.py` file?

Additionally, we should probably have a policy: what do we actually support? More importantly, if we're throwing exceptions, what do we *outright forbid*? Do we outright forbid running with an install from github? What about for development or testing purposes?

Because that's what the hard-fail policy is: it's saying that we know better than you to the extent that we simply will not allow you to run a non-supported configuration at all. I think that's a pretty strong position to take. I'd much rather take a position that running a non-supported configuration is non-supported, give ourselves a bunch of tools to diagnose such configurations and warn in the face of them, and then let people who choose to run outside of support to do so.

Sorry, if I'd know this was a contentious issue, I wouldn't have merged so quickly.

As I said in the initial PR message, I'm not excited about this block of code in general, it seems prone to unintentional failure. If we're going to hardline on which versions Requests will and won't work with, this should be built out more robustly and thoroughly tested. I'm not sure I am in favor of that approach though.

At the very least, we need to be catching exceptions like `ValueError` during unpacking and giving the user useful information. The amount of circling we did to determine `python-telegram-bot` was vendoring a copy of urllib3 was unnecessary. We should be outputting what version Requests is seeing when it fails to at least narrow down the issue. I also feel like refusing to allow projects to vendor copies of urllib3, or worse, forcing those vendoring to fake the version number is asking for more trouble.

> I also feel like refusing to allow projects to vendor copies of urllib3, or worse, forcing those vendoring to fake the version number is asking for more trouble.

I don't believe the issue was solely in their vendoring of urllib3. I don't think any of us dug in quite far, but they were almost certainly injecting their `urllib3` into `sys.modules` as *the* urllib3 which is a horrible thing to do at all. That was interfering with us only if we were imported after that `python-telegram-bot`. This is why `python -m requests.help` was useless. Run in isolation, it shows no information about what the user is experiencing. If we didn't have the version checking logic, we'd never have caught this and would have had a far harder bug to debug for the users that saw this.

Here's my main concern: Requests has likely wildly de-stabilized its users by unvendoring without a version of pip that has a real dependency resolver (which wouldn't have caught the `python-telegram-bot` issue or made it easier to debug). Except on some distros, we had some good guarantees about known good versions of our dependencies being there. With however million downloads we've hit, we should be considering the stability of the project. Raising exceptions like this is *ugly* but it's the next best thing.

So far, we've had little problem with users providing `python -m requests.help` but based on my experiences with similar utilities in other projects, some users will just refuse to provide it. Are we all in agreement that we just won't help those users? I doubt it. We already have a policy not to answer questions here and to instead refer people to StackOverflow but we rarely even enforce that anymore.

---

> Let's consider the less extreme example: someone uses Requests with an out-of-support urllib3. There are two options: either it works, or it does not.

I honestly think that's overly simplistic. How about a user using a version of urllib3 with older less secure cipher suite preferences that appears to work but is now compromising their security? Do we not care about that? We like to claim we're secure by default, and certainly, that's a minor issue overall in most of the cases we've removed cipher suites, but do we want to open ourselves up to that? On one machine 2.17.4 will negotiate with a stronger suite of ciphers while the same version on another will negotiate with subtly weaker ones? Don't we care about being secure and consistently secure for our users?

> Are there any versions of Python that silence all warnings by default?

You're right. The caffeine had not yet kicked in.

Created a new PR to revert this, further discussion can take place there (as it's an open issue instead of a closed one) https://github.com/requests/requests/pull/4146

> How about a user using a version of urllib3 with older less secure cipher suite preferences that appears to work but is now compromising their security?

How about it? This is a problem we need to tackle *anyway*: the simple act of unvendoring urllib3 means that presumably *at some point* we're going to start widening the version pin, and if that's the case then we need to tackle this problem at some point anyway. Why not now?

If Requests is responsible for its users security then it should take that responsibility. Before, because we vendored urllib3, the cipher string was within our codebase. Now it's not. We need to live with the reality that we don't own the cipher string anymore, and if we want to make assertions about security separate from the ones urllib3 provides then it's our responsibility to actually do that, rather than treat urllib3 version as a proxy for it.

Regardless, I don't care about warnings vs errors really: reasonable people can disagree. What I care about is that right now we've spent a bunch of dev cycles chasing our tails because this code errors out when it shouldn't. `"dev"` is a perfectly valid urllib3 version identifier, and if we can't handle it then our code is broken. More generally any failure to parse the version identifier shoudn't lead to a mystery error, it should say *very clearly* that we were unable to parse the version identifier.

While we're here, this code should have tests. Actual tests that actually confirm that it actually does what we think it does. It's mildly horrifying that we shipped this code without it.

@Lukasa, I completely agree on tests. That was something I brought up in the original PR but it wasn't addressed. The problem is we're testing a moving target, so what constitutes a valid version will not necessarily remain true between each release, breaking the tests. We need a way to dynamically retrieve "good" versions which would probably involved something like parsing the `requires` field. We also need to choose failing test values that will always fail but match expected formats.