GAE fails with v2.16.3 | error: required argument is not a float

I have just installed the requests library in my appengine app following the instructions at https://cloud.google.com/appengine/docs/standard/python/issue-requests . When making the following request (within the app or within the interactive console)

```
import requests
import requests_toolbelt.adapters.appengine

# Use the App Engine Requests adapter. This makes sure that Requests uses
# URLFetch.
requests_toolbelt.adapters.appengine.monkeypatch()

r = requests.get('https://api.github.com/events')
```

I get the following error

```
> Traceback (most recent call last):
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/python/request_handler.py", line 226, in handle_interactive_request
>     exec(compiled_code, self._command_globals)
>   File "<string>", line 8, in <module>
>   File "/Users/developer/Desktop/myapp-web/libraries/requests/api.py", line 72, in get
>     return request('get', url, params=params, **kwargs)
>   File "/Users/developer/Desktop/myapp-web/libraries/requests/api.py", line 58, in request
>     return session.request(method=method, url=url, **kwargs)
>   File "/Users/developer/Desktop/myapp-web/libraries/requests/sessions.py", line 523, in request
>     resp = self.send(prep, **send_kwargs)
>   File "/Users/developer/Desktop/myapp-web/libraries/requests/sessions.py", line 643, in send
>     r = adapter.send(request, **kwargs)
>   File "/Users/developer/Desktop/myapp-web/libraries/requests/adapters.py", line 440, in send
>     timeout=timeout
>   File "/Users/developer/Desktop/myapp-web/libraries/requests_toolbelt/adapters/appengine.py", line 172, in urlopen
>     **response_kw)
>   File "/Users/developer/Desktop/myapp-web/libraries/urllib3/contrib/appengine.py", line 149, in urlopen
>     validate_certificate=self.validate_certificate,
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/appengine/api/urlfetch.py", line 293, in fetch
>     return rpc.get_result()
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 613, in get_result
>     return self.__get_result_hook(self)
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/appengine/api/urlfetch.py", line 413, in _get_fetch_result
>     rpc.check_success()
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 579, in check_success
>     self.__rpc.CheckSuccess()
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/appengine/api/apiproxy_rpc.py", line 157, in _WaitImpl
>     self.request, self.response)
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py", line 206, in MakeSyncCall
>     self._MakeRealSyncCall(service, call, request, response)
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py", line 255, in _MakeRealSyncCall
>     request_pb.set_request(request.Encode())
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/net/proto/ProtocolBuffer.py", line 103, in Encode
>     self.Output(e)
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/net/proto/ProtocolBuffer.py", line 347, in Output
>     self.OutputUnchecked(e)
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/appengine/api/urlfetch_service_pb.py", line 484, in OutputUnchecked
>     out.putDouble(self.deadline_)
>   File "/usr/local/y/google-cloud-sdk/platform/google_appengine/google/net/proto/ProtocolBuffer.py", line 592, in putDouble
>     a.fromstring(struct.pack("<d", v))
> error: required argument is not a float
```

I have the following packages installed (along with dependencies) via pip
```
requests-2.16.3.dist-info
requests_toolbelt-0.8.0.dist-info

```

Wow, this looks like it involves a lot of GAE code here. @jonparrott any ideas?

Just a further comment. I did the same thing using the simple hello world app (set up as per https://cloud.google.com/appengine/docs/standard/python/quickstart ) to ensure there weren't any errors introduced by my own code. Same issue. I tested previous versions of requests, and the last version that seems to work was 2.16.1

I'll take a look at this first thing Tuesday when I get back in the office.
Thanks!

On Sat, May 27, 2017, 1:22 PM A <notifications@github.com> wrote:

> Just a further comment. I did the same thing using the simple hello world
> app (set up as per
> https://cloud.google.com/appengine/docs/standard/python/quickstart ) to
> ensure there weren't any errors introduced by my own code. Same issue.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/kennethreitz/requests/issues/4078#issuecomment-304474528>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAPUcwCsf7nzMQ_plRUJda_YkI3tUedOks5r-IYLgaJpZM4NodTG>
> .
>


Hey Guys!

I've spent some time investigating this issue. It's been caused by the refactoring of requests.packages in version 2.16.2. 

**The quick fix is to downgrade to requests 2.16.0.**

What I've found being broken:

In `urllib3/contrib/appengine.py` at `AppEngineManager._get_absolute_timeout()` is not returning a number as expected.

It's because the Timeout class imported from urllib3.timeout is not the same as what it receives as a timeout object.

I've put a couple brutal prints in the function:

    def _get_absolute_timeout(self, timeout):
        print "\n\n", "AppEngineManager._get_absolute_timeout", "0000", self
        print "\n\n", "AppEngineManager._get_absolute_timeout", "timeout", timeout
        print "\n\n", "AppEngineManager._get_absolute_timeout", "timeout.__class__", timeout.__class__
        print "\n\n", "AppEngineManager._get_absolute_timeout", "Timeout", Timeout
        print "\n\n", "AppEngineManager._get_absolute_timeout", "Timeout.DEFAULT_TIMEOUT.__class__", Timeout.DEFAULT_TIMEOUT.__class__
        print "\n\n", "AppEngineManager._get_absolute_timeout", "timeout is Timeout.DEFAULT_TIMEOUT", timeout is Timeout.DEFAULT_TIMEOUT
        if timeout is Timeout.DEFAULT_TIMEOUT:
            print "\n\n", "AppEngineManager._get_absolute_timeout", "2222"
            return None  # Defer to URLFetch's default.
        if isinstance(timeout, Timeout):
            print "\n\n", "AppEngineManager._get_absolute_timeout", "3333"
            if timeout._read is not None or timeout._connect is not None:
                warnings.warn(
                    "URLFetch does not support granular timeout settings, "
                    "reverting to total or default URLFetch timeout.",
                    AppEnginePlatformWarning)
            print "\n\n", "AppEngineManager._get_absolute_timeout", "4444"
            return timeout.total
        print "\n\n", "AppEngineManager._get_absolute_timeout", "5555"
        return timeout

When it's executed with requests 2.16.0, it gives the following output: 

```AppEngineManager._get_absolute_timeout 0000 <urllib3.contrib.appengine.AppEngineManager object at 0x114f7d6d0>
AppEngineManager._get_absolute_timeout timeout Timeout(connect=None, read=None, total=None)
AppEngineManager._get_absolute_timeout timeout.__class__ <class 'urllib3.util.timeout.Timeout'>
AppEngineManager._get_absolute_timeout Timeout <class 'urllib3.util.timeout.Timeout'>
AppEngineManager._get_absolute_timeout Timeout.DEFAULT_TIMEOUT.__class__ <type 'object'>
AppEngineManager._get_absolute_timeout timeout is Timeout.DEFAULT_TIMEOUT False
AppEngineManager._get_absolute_timeout 3333
AppEngineManager._get_absolute_timeout 4444
```

It works as expected, returns an integer number.

But when it's executed with requests>=2.16.2, the same code returns:
```
AppEngineManager._get_absolute_timeout 0000 <requests.packages.urllib3.contrib.appengine.AppEngineManager object at 0x10df5a1d0>
AppEngineManager._get_absolute_timeout timeout Timeout(connect=None, read=None, total=None)
AppEngineManager._get_absolute_timeout timeout.__class__ <class 'urllib3.util.timeout.Timeout'>
AppEngineManager._get_absolute_timeout Timeout <class 'requests.packages.urllib3.util.timeout.Timeout'>
AppEngineManager._get_absolute_timeout Timeout.DEFAULT_TIMEOUT.__class__ <type 'object'>
AppEngineManager._get_absolute_timeout timeout is Timeout.DEFAULT_TIMEOUT False
AppEngineManager._get_absolute_timeout 5555
```

As you can see the **timeout argument's class is != the Timeout class it's being compared to**. Because of this `isinstance(timeout, Timeout):` returns `False`, so the function **returns the timeout object instead of the integer value of the timeout**.

I'm not sure where to fix this, but I hope these findings will help.


Hrm, this looks very similar to #4096. Can you both try creating clean virtual environments and installing the newest release of Requests to see if the problem persists?

I've recreated the whole virtualenv from scratch. The problem persist with Request 2.16.5.

It's happening when it's used under google app engine with the adaptor from requests-toolbelt package. 

The actual code that breaks is in urllib3 package, as per my previous comment.

However, I believe the reason why it's breaking is related to the hack in the requests/packages.py file

Here is the full call-stack for an example of a breaking HTTPS request:

```self.request('PATCH', url,  data=data, **kwargs)
  File "/Volumes/Data/Documents/Ride/repos/rideapp/virtualenv/lib/python2.7/site-packages/requests/sessions.py", line 523, in request
    resp = self.send(prep, **send_kwargs)
  File "/Volumes/Data/Documents/Ride/repos/rideapp/virtualenv/lib/python2.7/site-packages/requests/sessions.py", line 643, in send
    r = adapter.send(request, **kwargs)
  File "/Volumes/Data/Documents/Ride/repos/rideapp/virtualenv/lib/python2.7/site-packages/requests/adapters.py", line 440, in send
    timeout=timeout
  File "/Volumes/Data/Documents/Ride/repos/rideapp/virtualenv/lib/python2.7/site-packages/requests_toolbelt/adapters/appengine.py", line 172, in urlopen
    **response_kw)
  File "/Volumes/Data/Documents/Ride/repos/rideapp/virtualenv/lib/python2.7/site-packages/urllib3/contrib/appengine.py", line 149, in urlopen
    validate_certificate=self.validate_certificate,
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/appengine/api/urlfetch.py", line 293, in fetch
    return rpc.get_result()
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 613, in get_result
    return self.__get_result_hook(self)
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/appengine/api/urlfetch.py", line 413, in _get_fetch_result
    rpc.check_success()
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 579, in check_success
    self.__rpc.CheckSuccess()
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/appengine/api/apiproxy_rpc.py", line 157, in _WaitImpl
    self.request, self.response)
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py", line 206, in MakeSyncCall
    self._MakeRealSyncCall(service, call, request, response)
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py", line 255, in _MakeRealSyncCall
    request_pb.set_request(request.Encode())
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/net/proto/ProtocolBuffer.py", line 103, in Encode
    self.Output(e)
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/net/proto/ProtocolBuffer.py", line 347, in Output
    self.OutputUnchecked(e)
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/appengine/api/urlfetch_service_pb.py", line 484, in OutputUnchecked
    out.putDouble(self.deadline_)
  File "/Users/dana/Downloads/google-cloud-sdk/platform/google_appengine/google/net/proto/ProtocolBuffer.py", line 592, in putDouble
    a.fromstring(struct.pack("<d", v))
error: required argument is not a float


@neonihil Do the problems with identity persist though?

@Lukasa Yes it does. 

What happens is that in `lib/python2.7/site-packages/urllib3/contrib/appengine.py` there is: AppEngineManager.urlopen, which has the following block:

```
            response = urlfetch.fetch(
                url,
                payload=body,
                method=method,
                headers=headers or {},
                allow_truncated=False,
                follow_redirects=self.urlfetch_retries and follow_redirects,
                deadline=self._get_absolute_timeout(timeout),
                validate_certificate=self.validate_certificate,
            )
```

The problem is that `self._get_absolute_timeout(timeout)` returns a `urllib3.utils.Timeout` object instead of an `int`. **This is the breaking call.**

It returns a `Timeout` object is because in `_get_absolute_timeout(timeout)` the following `isinstance()` call returns `False`: `if isinstance(timeout, Timeout):` 

The module `lib/python2.7/site-packages/urllib3/contrib/appengine.py` is importing the `Timeout` object with the following statement: `from ..util.timeout import Timeout`. 

Interestingly, the imported class is `<class 'requests.packages.urllib3.util.timeout.Timeout'>`. That is different from what `_get_absolute_timeout(timeout)` receives as the `timeout` argument. It's receiving  `<class 'urllib3.util.timeout.Timeout'>`.

It's not a typo, it's really this way. I've confirmed it with the prints in my previous comment.








I appreciate that you confirmed it, but I wanted to try to confirm it again with the clean virtualenv. A very similar problem appeared elsewhere that I cannot reproduce with the current master, and I was wondering if any holdovers from the old installation may be affecting this. 

So my new theory is that the grafting done in 2.16.2 only works for anything imported as part of `import urllib3`. This will *not* include contrib modules, so they get their namespaces messed up when they eventually are imported. 

I've re-created the virtualenv from scratch, starting with `rm -rf virtualenv`.

Yeah, so I think that @kennethreitz has some work to do to ensure that the contrib modules are *also* imported and grafted into the namespace, if possible. 

This work had been done to support downstream redistributor unvendoring but I don't know what broke that. What @kennethreitz needs to do is go back to an older version of `requests/packages/__init__.py` to clean this up and see what was being done there. This was a fixed issue for downstream redistributor unvendoring.

@sigmavirus24 our code now is almost identical to the code we were using there, but i'll double check. 

The entirety of our old `__init__.py` was as follows:

```python
from __future__ import absolute_import
import sys

try:
    from . import urllib3
except ImportError:
    import urllib3
    sys.modules['%s.urllib3' % __name__] = urllib3

try:
    from . import chardet
except ImportError:
    import chardet
    sys.modules['%s.chardet' % __name__] = chardet
```

What we're doing now is identical, save for the `__name__` stuff. 

It's quite possible that the fallback code never worked either; and we didn't notice because the people who used the contrib modules never used the distribution-unbundled code 

I know how to make it work, it's just a matter of identifying all the un-imported namespaces in the urllib3 codebase.

I'll start with contrib.

Here's a report of the monkeypatching not working with Requests v2.14.2 https://github.com/sigmavirus24/requests-toolbelt/issues/184. Related?

@kennethreitz: Have been watching this issue today (thanks for collecting info here).

We have 2.14.2 working fine on our end with `requests_toolbelt` on GAE Standard. We hit this issue this morning on new CI builds, and pinned to 2.14.2 to solve it (after reading what you found out).

I believe the issue at sigmavirus24/requests-toolbelt#184 is specifically because the user was vendoring in a separate (un-patched) copy of the code in a different import path after the monkey patching took place. So, probably un-related.

@bsmithyman can you try 2.17.3?

@kennethreitz: Can do; will hit it first thing in the morning (EDT) with one of our build servers. Setting a reminder for myself now.

Testing looks pretty solid so far with 2.17.3. Definitely we aren't hitting the same issue as before. We are using the global monkey patching in `requests_toolbelt`. Confirmed that it's working both with our own code and with vendored libraries that don't know about GAE (e.g., `braintree`). This is on App Engine Standard.

@bsmithyman excellent news!

Oh, good deal. I'm glad this wasn't my fault for once! :D

You guys are freaking awesome! Thanks for looking at this!

🍰 Closing this

This issue happened again for 2.18. I used 2.17.3 instead per the comment above. 

@robincrlee if this has regressed, please open a new issue with more detail than you've already provided.