用于AIOHTTP的Python装饰器,用作异步上下文管理器

t3irkdon  于 2023-04-22  发布在  Python
关注(0)|答案(1)|浏览(146)

我正在尝试为aiohttp编写一个 Package 器来检测慢速查询。
这是我的装饰:

import time

def report_slow_call(seconds=1.0):
    def decorator(func):
        async def wrapper(*args, **kwargs):
            start_ts = time.perf_counter()
            result = await func(*args, **kwargs)
            elapsed = time.perf_counter() - start_ts
            url = args[0] if args else kwargs.get('url')
            if elapsed > seconds:
                print(f"Slow call to: {url} ({elapsed:.2f} seconds).")
            return result
        return wrapper
    return decorator

我给客户端对象get函数打了个补丁,它工作得很好(在ipython中执行):

In [2]: import aiohttp
   ...: http_client = aiohttp.ClientSession()
   ...: http_client.get = report_slow_call(seconds=0.03)(http_client.get)
   ...: await http_client.get("http://stackoveflow.com")
Slow call to: http://stackoveflow.com (0.11 seconds).
Out[2]: 
<ClientResponse(http://stackoveflow.com) [200 OK]>
<CIMultiDictProxy('accept-ch': 'Sec-CH-UA, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Mobile', 'Cache-Control': 'max-age=0, private, must-revalidate', 'Connection': 'close', 'Content-Length': '477', 'Content-Type': 'text/html; charset=utf-8', 'Date': 'Mon, 17 Apr 2023 15:42:50 GMT', 'Server': 'nginx', 'Set-Cookie': 'sid=82900a62-dd36-11ed-85ae-a4d3e99a9525; path=/; domain=.stackoveflow.com; expires=Sat, 05 May 2091 18:56:57 GMT; max-age=2147483647; HttpOnly')>

**问题:**当我尝试通过上下文管理器运行它时,我得到以下错误:

In [4]: async with http_client.get("http://stackoveflow.com", raise_for_status=True) as response:
   ...:     print(response)
   ...: 
<ipython-input-4-fd5ea6565740>:1: RuntimeWarning: coroutine 'report_slow_call.<locals>.decorator.<locals>.wrapper' was never awaited
  async with http_client.get("http://stackoveflow.com", raise_for_status=True) as response:
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 async with http_client.get("http://stackoveflow.com", raise_for_status=True) as response:
      2     print(response)

TypeError: 'coroutine' object does not support the asynchronous context manager protocol

你们谁能提出一个正确实施的想法,好吗?

ctzwtxfj

ctzwtxfj1#

我检查了httpaio的源代码,了解了函数是如何执行的。

def get(
        self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
    ) -> "_RequestContextManager":
        """Perform HTTP GET request."""
        return _RequestContextManager(
            self._request(hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs)
        )

所以,我没有修补ClientSession.get,而是修补了ClientSession._request,装饰器现在工作得很好。

解决方案

import time
import aiohttp

def report_slow_call(seconds=1.0):
    def decorator(func):
        async def wrapper(*args, **kwargs):
            start_ts = time.perf_counter()
            result = await func(*args, **kwargs)
            elapsed = time.perf_counter() - start_ts
            url = args[0] if args else kwargs.get('url')
            if elapsed > seconds:
                print(f"Slow call to: {url} ({elapsed:.2f} seconds.)")
            return result
        return wrapper
    return decorator

url = "https://duckduckgo.com"
http_client = aiohttp.ClientSession()

# Monkey patch object's _request method
http_client._request = report_slow_call(seconds=0.01)(http_client._request)

# Execute ClientSession's functions
await http_client.get(url)

async with http_client.get(url, raise_for_status=True) as response:
    print(response)

相关问题