I'm using asyncio with the requests module to make an asynchronous HTTP request.
I can make a GET request like this:
@asyncio.coroutine
def do_checks():
loop = asyncio.get_event_loop()
req = loop.run_in_executor(None, requests.get, 'https://api.github.com/user')
resp = yield from req
print(resp.status_code)
loop = asyncio.get_event_loop()
loop.run_until_complete(do_checks())
However, I need to do support Basic HTTP Auth (described here) in the request.
According to the documentation, url and auth are both named parameters for requests.get().
But, if I run this (note the addition of url='' and auth = ''):
@asyncio.coroutine
def do_checks():
loop = asyncio.get_event_loop()
req = loop.run_in_executor(None, requests.get, url='https://api.github.com/user', auth=HTTPBasicAuth('user', 'pass'))
resp = yield from req
print(resp.status_code)
loop = asyncio.get_event_loop()
loop.run_until_complete(do_checks())
I get this error:
TypeError: run_in_executor() got an unexpected keyword argument 'url'
In the prototype for asyncio.run_in_executor(), additional arguments are supported:
BaseEventLoop.run_in_executor(executor, callback, *args)
requests.get() clearly supports named parameters (get, auth, etc.). What's wrong?
The essence of asyncio is that it allows the program to continue executing other instructions while waiting for specific processes to finish (e.g., a request to an API). In this tutorial, you will see how to use asyncio for accelerating a program that makes multiple requests to an API.
@ospider You are right, here is the question: stackoverflow.com/questions/56523043/… Requests does not currently support asyncio and there are no plans to provide such support. It's likely that you could implement a custom "Transport Adapter" (as discussed here) that knows how to use asyncio.
Making a single asynchronous HTTP request is great because we can let the event loop work on other tasks instead of blocking the entire thread while waiting for a response. But this functionality truly shines when trying to make a larger number of requests.
HTTP requests are a classic example of something that is well-suited to asynchronicity because they involve waiting for a response from a server, during which time it would be convenient and efficient to have other code running. Make sure to have your Python environment setup before we get started.
This is actually a design decision in asyncio
. From the docstring of asyncio/base_events.py:
"""Base implementation of event loop.
The event loop can be broken up into a multiplexer (the part
responsible for notifying us of IO events) and the event loop proper,
which wraps a multiplexer with functionality for scheduling callbacks,
immediately or at a given time in the future.
Whenever a public API takes a callback, subsequent positional
arguments will be passed to the callback if/when it is called. This
avoids the proliferation of trivial lambdas implementing closures.
Keyword arguments for the callback are not supported; this is a
conscious design decision, leaving the door open for keyword arguments
to modify the meaning of the API call itself.
"""
Note the last sentence there.
The asyncio PEP notes this as well, and recommends a lambda to work around it:
This convention specifically does not support keyword arguments. Keyword arguments are used to pass optional extra information about the callback. This allows graceful evolution of the API without having to worry about whether a keyword might be significant to a callee somewhere. If you have a callback that must be called with a keyword argument, you can use a lambda. For example:
loop.call_soon(lambda: foo('abc', repeat=42))
Two ways to do that. Create a wrapper function, or just use a session to provide the auth.
Using a session:
@asyncio.coroutine
def do_checks():
loop = asyncio.get_event_loop()
session = requests.Session()
session.auth = HTTPBasicAuth('user', 'pass')
req = loop.run_in_executor(None, session.get, 'https://api.github.com/user')
resp = yield from req
print(resp.status_code)
Writing a wrapper function (note that I'm using def
for clarity here, but that a lambda
would obviously work too):
@asyncio.coroutine
def do_checks():
def do_req():
return requests.get('https://api.github.com/user', auth=HTTPBasicAuth('user', 'pass'))
loop = asyncio.get_event_loop()
req = loop.run_in_executor(None, do_req)
resp = yield from req
print(resp.status_code)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With