Importing requests takes additional 13MB, more than doubling Python's mem usage

Simple running:

    import requests

In a new, blank process, raises memory consumption from ~11MB to ~24MB

## Expected Result

Even big libraries like Django raise it by less than 1MB

## Reproduction Steps

```python
import requests

```
:)

## System Information

    $ python -m requests.help

```
{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": ""
  },
  "idna": {
    "version": "2.6"
  },
  "implementation": {
    "name": "CPython",
    "version": "3.5.3"
  },
  "platform": {
    "release": "4.10.13-200.fc25.x86_64",
    "system": "Linux"
  },
  "pyOpenSSL": {
    "openssl_version": "",
    "version": null
  },
  "requests": {
    "version": "2.18.4"
  },
  "system_ssl": {
    "version": "100020bf"
  },
  "urllib3": {
    "version": "1.22"
  },
  "using_pyopenssl": false
}
```
(Note that pyopenssl doesn't seem to be the cause this time)

P.S. the same effect happens in Python 2

I've been looking into this, and it's about what you'd expect: 'requests' uses a lot of libraries, and some of them use a lot of memory.

(In comparison, Django uses very few memory-hungry modules.)

The biggest offenders that I've found in 'requests':


 1. urllib3 (> 6 MB)

I know this one makes dealing with URLs and HTTP a million times easier, and there's no chance 'requests' will stop using it, but there could be sub-dependencies here that have memory optimization opportunities.

For example, 'urllib3' (eagerly) imports 'uuid', for creating unique boundaries when encoding multipart/form-data.  The 'uuid' module adds almost 2 MB.  Is there an alternative that could work?  For example, plain 'random' is about half that size.  Or import it lazily.


 2. cgi (> 4 MB)

This is only used for one method (parse_header), which is about 20 lines of code.

Idea: write our own.  Requests doesn't even use the full parse output -- a regex that looked at two parts of the string could do the trick, I think.


 3. chardet (> 3 MB)

This one seems to include a lot of character frequency tables.  In the common case for 'requests' (where the server supplies a Content-Type), it's not even used.

Idea: lazy load this, when needed.  It seems to do some fancy runtime version checking in `__init__`, so I'm not sure what's involved here.


### Regarding lazy loading:

The 'urllib3' module [lazy-loads 'idna'][idna].  It looks like [this was done][lazy] partially to allow ['requests' to lazy-load it, as well][reqlazy].

[idna]: https://github.com/shazow/urllib3/pull/1076/files
[lazy]: https://github.com/shazow/urllib3/issues/1075
[reqlazy]: https://github.com/requests/requests/pull/3789

That work seems to have been undone.  The 'requests' `__init__` now imports `packages`, which calls `__import__()` on 'idna' (and others).

Unless I'm mistaken, that's no longer lazy.  On my system, at least, the memory usage jumps quite a bit when I `import requests`, and then not at all when I `import idna` after that.  When I `import urllib3`, and then `import idna`, I see another jump in memory usage, so it does seem to be lazy-loaded by urllib3 still.

Has #3780 been regressed?


It looks like #4442 and shazow/urllib3#1270 help mitigate the uuid and cgi issues.

As for IDNA, when we unvendored our dependencies, we added a "symlink" of sorts in packages.py. We now auto import our formerly vendored packages to allow things like `import requests.packages.idna` to continue working. This is what's causing idna to be imported unconditionally. I don't immediately see a good solution to maintain this linking and the lazy load functionality of #3780. We may have to wait till 3.0.0 to break that API.

We've removed the mandatory requirement on two of the heaviest libraries in our dependency tree (pyOpenSSL and idna). If import time is still not up to standards we should reopen a new issue investigating the latest performance.