AttributeError: 'NoneType' object has no attribute 'setdefault' on environment proxies

AttributeError: 'NoneType' object has no attribute 'setdefault' on environment proxies

It appears there is a small bug in ‎`Session.merge_environment_settings` where the proxies variable can be `None` but setdefault is called on it anyway when env_proxies are present, causing an AttributeError to be thrown:

https://github.com/psf/requests/blob/main/src/requests/sessions.py#L759-L762


How are you encountering this? On which versions? Why did you skip the issue template? 

Hello! Apologies for not using the template above. I have included it below.

Additionally, I have unearthed more context while working on reproduction steps.

It appears that although the type hint signature in typeshed defines the proxies parameter as optional:

https://github.com/python/typeshed/blob/main/stubs/requests/requests/sessions.pyi#L307

In practice requests fallbacks to an empty dictionary which prevents hitting the issue:

https://github.com/psf/requests/blob/main/src/requests/sessions.py#L577

## Expected Result

No Exception to be thrown.

## Actual Result

AttributeError: 'NoneType' object has no attribute 'setdefault'

## Reproduction Steps

Invoke this code with an environment that has a variable that ends with _PROXY, example:

```shell
HELLO_PROXY=abc python script.py
```

```python
from requests import Request, Session

request = Request(method="GET", url="<https://disney.com")>

session = Session()
prepared_request = session.prepare_request(request)
settings = session.merge_environment_settings(prepared_request.url, None, None, None, None)
response = session.send(prepared_request, **settings)
```

## System Information

```
{
 "chardet": {
 "version": null
 },
 "charset_normalizer": {
 "version": "3.3.2"
 },
 "cryptography": {
 "version": ""
 },
 "idna": {
 "version": "3.6"
 },
 "implementation": {
 "name": "CPython",
 "version": "3.9.6"
 },
 "platform": {
 "release": "23.2.0",
 "system": "Darwin"
 },
 "pyOpenSSL": {
 "openssl_version": "",
 "version": null
 },
 "requests": {
 "version": "2.31.0"
 },
 "system_ssl": {
 "version": "20000000"
 },
 "urllib3": {
 "version": "1.26.18"
 },
 "using_charset_normalizer": true,
 "using_pyopenssl": false
}
```

I'd looked at this when the ticket first opened and the typing is due to ambiguity created by our top-level API defaults and the code inside [merge_environment_settings](https://github.com/psf/requests/blob/main/src/requests/sessions.py#L759).

We imply `proxies` can be None and even have some code written around that assumption. I'd personally rather be explicit that we require a Mapping and only a Mapping for this API since it doesn't really work without that. The only downside is a minor performance hit in the case we were previously skipping the merge in [`merge_settings`](https://github.com/psf/requests/blob/d6dded3f00afcf56a7e866cb0732799045301eb0/src/requests/sessions.py#L70-L71). A sufficiently large proxies dictionary _may_ see some performance impact, but I'm not sure that's going to be a noticeable case.

@nateprewitt Should this issue be reopened then?

@nateprewitt @sigmavirus24 please confirm whether issue should be reopened or not. many thanks!

I don't think it's worth re-opening this. We can't really fix this without introducing other issues into the API and the issue lies outside of the intended usage. While it's an odd type mismatch, these exist throughout requests because the code base was written at a time that pre-dates typing. Many of these issues we cannot fix without introducing breakages, so they've been left to meet their existing behavior.

We ensure that proxies will never be `None` before invoking this method and the public signature should probably be `Mapping` despite us handling `None` within the code. That's how we document it and is the only way this reasonably works right now.

Is there an open issue in Typeshed for this? That's probably where this should be remedied.