按廖老师的代码,运行后出现警告:
DeprecationWarning: loop argument is deprecated app = web.Application(loop=loop) DeprecationWarning: Application.make_handler(...) is deprecated, use AppRunner API instead
我用的Python版本是3.6.6,虽然运行没问题,但是看着IDE中标记app.make_handler(),强迫症表示不能忍。而且这个方法在高版本或者将来可能会被移除,因此最好使用官方推荐的最新方法,那么我们就来尝试来解决它。
查看make_handler()方法的源码:
def make_handler(self, *, loop=None, access_log_class=AccessLogger, **kwargs): warnings.warn("Application.make_handler(...) is deprecated, use AppRunner API instead", DeprecationWarning, stacklevel=2) return self._make_handler(loop=loop, access_log_class=access_log_class, **kwargs)
意思就是这段代码过时了,推荐使用AppRunner类的API来代替make_handler()方法,于是查看AppRunner源码:
class AppRunner(BaseRunner): """Web Application runner""" ... def __init__(self, app, *, handle_signals=False, **kwargs): super().__init__(handle_signals=handle_signals, **kwargs) if not isinstance(app, Application): raise TypeError("The first argument should be web.Application instance, got {!r}".format(app)) self._app = app ... async def _make_server(self): loop = asyncio.get_event_loop() self._app._set_loop(loop) self._app.on_startup.freeze() await self._app.startup() self._app.freeze() return self._app._make_handler(loop=loop, **self._kwargs)
AppRunner的构造__init__()方法中,需要传递一个app参数,这个app必须是Application实例,而_make_server()方法中,正好返回了我们想调用的app._make_handler(),即app.make_handler()!那么直接调用_make_server()可以吗?别忘了,以“_”开头的方法或属性,虽然可以调用,但并不是官方推荐的方式,所以让我们继续往下找。
我们发现在AppRunner类源码中,_make_server()并没有被调用,但却惊奇地发现_make_server()方法其实是重载了父类BaseRunner的_make_server(),那么可以肯定的是,_make_server()必定是被从父类继承而来的方法调用了。让我们看看在BaseRunner中,_make_server()被谁调用了:
class BaseRunner(ABC): ... def __init__(self, *, handle_signals=False, **kwargs): self._handle_signals = handle_signals self._kwargs = kwargs self._server = None self._sites = [] @property def server(self): return self._server ... async def setup(self): loop = asyncio.get_event_loop() if self._handle_signals: try: loop.add_signal_handler(signal.SIGINT, _raise_graceful_exit) loop.add_signal_handler(signal.SIGTERM, _raise_graceful_exit) except NotImplementedError: # pragma: no cover # add_signal_handler is not implemented on Windows pass self._server = await self._make_server() ... @abstractmethodasync def _make_server(self): pass # pragma: no cover
在BaseRunner类中,通过注解可以看到_make_server()仅仅是一个抽象方法,没有实际意义。这正好验证了我们的猜想!一般抽象类的方法,在父类方法中都有重要用途,但是希望子类去实现它,这样子类实例对象调用的时候,实际上是调用了子类实现的_make_server()。
setup()方法中,正好调用了_make_server()方法,并把结果绑定到了_server属性,而通过server()方法,我们可以轻松调用到这个属性!这里复习一下装饰器@property,调用的时候是“对象.server”形式,而不是“对象.server()”。
重新梳理一下,调用思路是:
实例化Application类(app) --> 实例化AppRunner类(apprunner)并将app传递给apprunner --> 调用AppRunner从父类BaseRunner继承而来的setup()方法 --> 调用server()方法得到handler对象
经过这么层层分析,我们终于找到了官方推荐的调用方式,让我们改写一下程序:
async def init(loop): host = '127.0.0.1' port = 9000 app = web.Application(loop=loop) app.router.add_route("GET", "/", index) ############################################################################################################ # srv = await loop.create_server(app.make_handler(), '127.0.0.1', 9000) # 廖老师的方法 # ############################################################################################################ # 新的方法 apprunner = web.AppRunner(app) # 构造AppRunner对象 await apprunner.setup() # 调用setup()方法,注意因为源码中这个方法被async修饰,所以前面要加上await,否则报错 srv = await loop.create_server(apprunner.server, host, port) # 将apprunner的server属性传递进去 ############################################################################################################ logging.info('server started at %s:%d...' % (host, port)) return srv
make_handler()方法和loop参数都过时了,需要同步去掉。
这里虽然替换掉了make_handler()方法,但是,还是用到了loop参数,这个恐怕不行吧。
Sign in to make a reply
想名好纠结
按廖老师的代码,运行后出现警告:
我用的Python版本是3.6.6,虽然运行没问题,但是看着IDE中标记app.make_handler(),强迫症表示不能忍。而且这个方法在高版本或者将来可能会被移除,因此最好使用官方推荐的最新方法,那么我们就来尝试来解决它。
查看make_handler()方法的源码:
意思就是这段代码过时了,推荐使用AppRunner类的API来代替make_handler()方法,于是查看AppRunner源码:
AppRunner的构造__init__()方法中,需要传递一个app参数,这个app必须是Application实例,而_make_server()方法中,正好返回了我们想调用的app._make_handler(),即app.make_handler()!那么直接调用_make_server()可以吗?别忘了,以“_”开头的方法或属性,虽然可以调用,但并不是官方推荐的方式,所以让我们继续往下找。
我们发现在AppRunner类源码中,_make_server()并没有被调用,但却惊奇地发现_make_server()方法其实是重载了父类BaseRunner的_make_server(),那么可以肯定的是,_make_server()必定是被从父类继承而来的方法调用了。让我们看看在BaseRunner中,_make_server()被谁调用了:
在BaseRunner类中,通过注解可以看到_make_server()仅仅是一个抽象方法,没有实际意义。这正好验证了我们的猜想!一般抽象类的方法,在父类方法中都有重要用途,但是希望子类去实现它,这样子类实例对象调用的时候,实际上是调用了子类实现的_make_server()。
setup()方法中,正好调用了_make_server()方法,并把结果绑定到了_server属性,而通过server()方法,我们可以轻松调用到这个属性!这里复习一下装饰器@property,调用的时候是“对象.server”形式,而不是“对象.server()”。
重新梳理一下,调用思路是:
实例化Application类(app) --> 实例化AppRunner类(apprunner)并将app传递给apprunner --> 调用AppRunner从父类BaseRunner继承而来的setup()方法 --> 调用server()方法得到handler对象
经过这么层层分析,我们终于找到了官方推荐的调用方式,让我们改写一下程序: