13

我正在使用带有requests模块的asyncio来发出异步 HTTP 请求。

我可以发出这样的 GET 请求:

@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())

但是,我需要在请求中支持 Basic HTTP Auth(在此处描述)。

根据文档,urlauth都是 requests.get() 的命名参数。

但是,如果我运行它(注意添加url=''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())

我收到此错误:

TypeError: run_in_executor() got an unexpected keyword argument 'url'

在 asyncio.run_in_executor() 的原型中,支持附加参数:

BaseEventLoop.run_in_executor(executor, callback, *args)

requests.get() 明确支持命名参数(get、auth 等)。怎么了?

4

2 回答 2

9

有两种方法可以做到这一点。创建一个包装函数,或者只使用一个会话来提供身份验证。

使用会话:

@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)

编写一个包装函数(请注意,我在这里使用def是为了清楚起见,但 alambda显然也可以工作):

@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)
于 2014-05-30T14:21:01.027 回答
8

这实际上是asyncio. 从 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.
"""

注意那里的最后一句话。

asyncio PEP也注意到了这一点,并建议使用 lambda 来解决它:

此约定特别不支持关键字参数。关键字参数用于传递有关回调的可选额外信息。这允许 API 的优雅演变,而不必担心关键字是否对某个地方的被调用者很重要。如果您有一个必须使用关键字参数调用的回调,则可以使用 lambda。例如:

loop.call_soon(lambda: foo('abc', repeat=42))

于 2014-05-30T14:27:41.080 回答