2023年8月1日发(作者:)
什么是Werkzeug上⼀节介绍了,这⼀节我们看看Werkzeug按照官⽅的说法,Werkzeug(源⾃德语,⼯具的意思)是⼀个WSGI⼯具库,它开始于⼀个适⽤于WSGI的多样化的⼯具集,后来发展成了现在⾮常流⾏的WSGI⼯具库。Werkzeug可以在程序中单独使⽤,也作为许多Python Web框架的底层库,例如现在⾮常流⾏的Flask Web框架。Werkzeug的基本功能正如官⽅的说法,Werkzeug提供了⾮常丰富的功能,但是其功能总的可分为两个⽅⾯:开发测试⽅⾯的功能和其⽤于Web程序中的⼯具函数及⼯具类开发测试⽅⾯⼀、Werkzeug提供了⼀个简易的开发⽤服务器⼆、Werkzeug提供了⼀些测试⼯具,如Client类、EnvironBuilder类。三、Werkzeug提供了Debug的⼯具,提供了可⽤于Debug的中间件。当程序出错时,并不会返回500错误,⽽是显⽰程序出错的地⽅以及出错的原因,这就为程序的开发提供了⽅便。⼯具⽅⾯Werkzeug主要提供了如下⼏种⼯具⼀、请求和相应对象。提供了Request和Response。Request可以包装WSGI服务器传⼊的environ参数,并对其进⾏进⼀步的解析,以使我们更容易的使⽤请求中的参数。Response可以根据传⼊的参数,来发起⼀个特定的响应。你可以认为Response是你可以创建的另⼀个标准的WSGI应⽤,这个应⽤可以根据你传⼊的参数,来帮你做发起响应这件事。from rs import Request, Responsedef application(environ, start_response): request = Request(environ) response = Response("Hello %s!" % ('name', 'World!')) return response(environ, start_response)⼆、路由解析。Werkzeug提供了强⼤的路由解析功能。⽐如Flask框架中经常⽤到的Rule、Map类等。如下⾯⼀个程序。from g import Map, Rule, NotFound, RequestRedirecturl_map = Map([ Rule('/', endpoint='blog/index'), Rule('////', endpoint='blog/archive'), Rule('/about', endpoint='blog/about_me'), Rule('/feeds/.rss', endpoint='blog/show_feed')])def application(environ, start_response): urls = url__to_environ(environ) try: endpoint, args = () except HTTPException, e: return e(environ, start_response) start_response('200 OK', [('Content-Type', 'text/plain')]) return ['Rule points to %r with arguments %r' % (endpoint, args)]我们创建了⼀个Map类实例url_map来保存⼀系列的URL规则。并且给它传递了⼀个Rule对象的列表。其中,每个Rule对象都包含两个参数:⼀个字符串和endpoint。字符串代表了URL匹配的规则(也叫路由规则),endpoint(也叫端点)代表了该路由规则对应的视图函数。即当对⼀个URL匹配成功后,便可获取到它对应的视图函数。不同的规则可以对应相同的endpoint,但是必须有不同的参数⽤于URL的构建,不能产⽣歧义,类似于函数的重载。在application函数中,我们使⽤Map的bind_to_environ⽅法将url_map与environ绑定,这会返回给我们⼀个新的MapAdapter对象,这个对象可⽤于URL的匹配。随后,我们调⽤MapAdapter对象中的match()⽅法,获取当前请求的URL匹配到的endpoint和其参数信息,最后,我们⽤获取到的endpoint和参数信息发起⼀个响应。⽤于匹配URL的路由规则字符串是由基本的URL加上占位符组成的。例如Rule('/pages/'),尖括号中,冒号后⾯为变量名,前⾯为变量的类型。path类型表⽰只匹配路径。这⾥的path也可以是int,表⽰匹配⼀个整型以及float等。当不包含尖括号中的变量不写明类型时,如Rule('/pages/'),这⾥的page可以匹配任何字符串,但是只能就受⼀个路径段,因此不能含有/。更详细的规则还请参见三、本地上下⽂在许多Web程序中,本地上下⽂是个⾮常重要的概念。⽽实现本地上下⽂需要⽤到不同线程间数据的隔离。中定义了Local、LocalStack和LocalProxy等类⽤于实现全局数据的隔离。在Python中,我们可以使⽤thread locals来保证多线程状态下数据的隔离,但是这在Web程序中,却并不是很好使。⼀是因为有些Web应⽤是使⽤协程实现的,⽆法保证数据的隔离。⼆是即使使⽤的是线程,WSGI也不能保证每次请求使⽤的线程都是⼀个全新的线程,可能是⼀个之前请求的线程,⽽⾥⾯的数据也是原线程剩下的。所以,Werkzeug给我们提供了Local这个更好⽤的解决⼯具。下⾯是⼀个如何使⽤例⼦:from import Local, LocalManagerlocal = Local()local_manager = LocalManager([local])def application(environ, start_response): t = request = Request(environ) ...application = local__middleware(application)可以看到,我们把⼀个Request对象赋值给了全局对象t,这样,我们就可以在全局范围内使⽤t,⽽且获取到的仅仅是当前请求的数据。因为Local对象不会在请求结束后⾃动清除本地上下⽂,所以这⾥我们需要使⽤LocalManager来管理。我们需要将管理的Local对象以列表的⽅式传给LocalManager,并在最后使⽤LocalManager的make_middleware⽅法为WSGI程序添加中间件,来使请求结束后⾃动清除本次请求的数据。那么Local是如何实现的呢?其实很简单,在Local中,重写了__getattr__和__setattr__⽅法,使得在获取数据和存储数据之前,先获取到线程id(或协程id),以线程id(或协程id)为键,数据为值,存储在⼀个字典中。这样我们在操作数据的时候,操作的只会是当前线程(或协程)的数据,从⽽实现了数据隔离。感兴趣的同学可以查看⼀下⽂末Local的源码。LocalStack对Local进⾏了封装,使其可以以栈的⽅式使⽤。如下:>>> ls = LocalStack()>>> (42)>>> 42>>> (23)>>> 23>>> ()23>>> 42LocalProxy类⽤于实现werkzeug本地代理,将所有的操作转发给代理对象。如果你熟悉C++的话,你会发现这和C++的引⽤很像,但⽐引⽤更强⼤。使⽤⽅法如下:from import Locall = Local()# 以下是两个代理request = l('request') # Local中实现了__call__⽅法,⽤于返回⼀个代理,具体可以查看⽂末Local的源码user = l('user')from import LocalStack_response_local = LocalStack()# 这也是个代理response = _response_local() # 同理,LocalStack返回的也是代理除了以上创建代理的⽅式外,还可以⼿动创建⼀个代理from import Local, LocalProxylocal = Local()request = LocalProxy(local, 'request')如果你想拥有⼀个根据指定函数来返回不同的对象代理,也是⽀持的。session = LocalProxy(lambda: get_current_request().session)但我们为什么要使⽤代理呢。这⾥简单说⼀下,我们知道,⼀个变量被赋值后如果不重新赋值,它的值是不会改变的,那么这在程序的某些地⽅就会变得很不⽅便。但是如果使⽤代理的话,那么我们在使⽤这个变量的时候就能动态的获取到它所代理的对象的最新的值。四、其他除了上⾯三个⽅⾯外,Werkzeug还提供了很多⼯具,例如WSGI中间件、HTTP异常类、数据结构等。这⾥就不在⼀⼀详述,感兴趣的同学可以参考。Local对象部分源码:try: from greenlet import getcurrent as get_identexcept ImportError: try: from thread import get_ident except ImportError: from _thread import get_identclass Local(object): __slots__ = ('__storage__', '__ident_func__') def __init__(self): object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__ident_func__', get_ident) def __iter__(self): return iter(self.__storage__.items()) def __call__(self, proxy): """Create a proxy for a name.""" return LocalProxy(self, proxy) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) # 清除数据 def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)参考:本篇参考Werkzeug⽂档写成,如有错误或与⽂档不符的地⽅,还请以⽂档为准,也欢迎您反馈给我。
2023年8月1日发(作者:)
什么是Werkzeug上⼀节介绍了,这⼀节我们看看Werkzeug按照官⽅的说法,Werkzeug(源⾃德语,⼯具的意思)是⼀个WSGI⼯具库,它开始于⼀个适⽤于WSGI的多样化的⼯具集,后来发展成了现在⾮常流⾏的WSGI⼯具库。Werkzeug可以在程序中单独使⽤,也作为许多Python Web框架的底层库,例如现在⾮常流⾏的Flask Web框架。Werkzeug的基本功能正如官⽅的说法,Werkzeug提供了⾮常丰富的功能,但是其功能总的可分为两个⽅⾯:开发测试⽅⾯的功能和其⽤于Web程序中的⼯具函数及⼯具类开发测试⽅⾯⼀、Werkzeug提供了⼀个简易的开发⽤服务器⼆、Werkzeug提供了⼀些测试⼯具,如Client类、EnvironBuilder类。三、Werkzeug提供了Debug的⼯具,提供了可⽤于Debug的中间件。当程序出错时,并不会返回500错误,⽽是显⽰程序出错的地⽅以及出错的原因,这就为程序的开发提供了⽅便。⼯具⽅⾯Werkzeug主要提供了如下⼏种⼯具⼀、请求和相应对象。提供了Request和Response。Request可以包装WSGI服务器传⼊的environ参数,并对其进⾏进⼀步的解析,以使我们更容易的使⽤请求中的参数。Response可以根据传⼊的参数,来发起⼀个特定的响应。你可以认为Response是你可以创建的另⼀个标准的WSGI应⽤,这个应⽤可以根据你传⼊的参数,来帮你做发起响应这件事。from rs import Request, Responsedef application(environ, start_response): request = Request(environ) response = Response("Hello %s!" % ('name', 'World!')) return response(environ, start_response)⼆、路由解析。Werkzeug提供了强⼤的路由解析功能。⽐如Flask框架中经常⽤到的Rule、Map类等。如下⾯⼀个程序。from g import Map, Rule, NotFound, RequestRedirecturl_map = Map([ Rule('/', endpoint='blog/index'), Rule('////', endpoint='blog/archive'), Rule('/about', endpoint='blog/about_me'), Rule('/feeds/.rss', endpoint='blog/show_feed')])def application(environ, start_response): urls = url__to_environ(environ) try: endpoint, args = () except HTTPException, e: return e(environ, start_response) start_response('200 OK', [('Content-Type', 'text/plain')]) return ['Rule points to %r with arguments %r' % (endpoint, args)]我们创建了⼀个Map类实例url_map来保存⼀系列的URL规则。并且给它传递了⼀个Rule对象的列表。其中,每个Rule对象都包含两个参数:⼀个字符串和endpoint。字符串代表了URL匹配的规则(也叫路由规则),endpoint(也叫端点)代表了该路由规则对应的视图函数。即当对⼀个URL匹配成功后,便可获取到它对应的视图函数。不同的规则可以对应相同的endpoint,但是必须有不同的参数⽤于URL的构建,不能产⽣歧义,类似于函数的重载。在application函数中,我们使⽤Map的bind_to_environ⽅法将url_map与environ绑定,这会返回给我们⼀个新的MapAdapter对象,这个对象可⽤于URL的匹配。随后,我们调⽤MapAdapter对象中的match()⽅法,获取当前请求的URL匹配到的endpoint和其参数信息,最后,我们⽤获取到的endpoint和参数信息发起⼀个响应。⽤于匹配URL的路由规则字符串是由基本的URL加上占位符组成的。例如Rule('/pages/'),尖括号中,冒号后⾯为变量名,前⾯为变量的类型。path类型表⽰只匹配路径。这⾥的path也可以是int,表⽰匹配⼀个整型以及float等。当不包含尖括号中的变量不写明类型时,如Rule('/pages/'),这⾥的page可以匹配任何字符串,但是只能就受⼀个路径段,因此不能含有/。更详细的规则还请参见三、本地上下⽂在许多Web程序中,本地上下⽂是个⾮常重要的概念。⽽实现本地上下⽂需要⽤到不同线程间数据的隔离。中定义了Local、LocalStack和LocalProxy等类⽤于实现全局数据的隔离。在Python中,我们可以使⽤thread locals来保证多线程状态下数据的隔离,但是这在Web程序中,却并不是很好使。⼀是因为有些Web应⽤是使⽤协程实现的,⽆法保证数据的隔离。⼆是即使使⽤的是线程,WSGI也不能保证每次请求使⽤的线程都是⼀个全新的线程,可能是⼀个之前请求的线程,⽽⾥⾯的数据也是原线程剩下的。所以,Werkzeug给我们提供了Local这个更好⽤的解决⼯具。下⾯是⼀个如何使⽤例⼦:from import Local, LocalManagerlocal = Local()local_manager = LocalManager([local])def application(environ, start_response): t = request = Request(environ) ...application = local__middleware(application)可以看到,我们把⼀个Request对象赋值给了全局对象t,这样,我们就可以在全局范围内使⽤t,⽽且获取到的仅仅是当前请求的数据。因为Local对象不会在请求结束后⾃动清除本地上下⽂,所以这⾥我们需要使⽤LocalManager来管理。我们需要将管理的Local对象以列表的⽅式传给LocalManager,并在最后使⽤LocalManager的make_middleware⽅法为WSGI程序添加中间件,来使请求结束后⾃动清除本次请求的数据。那么Local是如何实现的呢?其实很简单,在Local中,重写了__getattr__和__setattr__⽅法,使得在获取数据和存储数据之前,先获取到线程id(或协程id),以线程id(或协程id)为键,数据为值,存储在⼀个字典中。这样我们在操作数据的时候,操作的只会是当前线程(或协程)的数据,从⽽实现了数据隔离。感兴趣的同学可以查看⼀下⽂末Local的源码。LocalStack对Local进⾏了封装,使其可以以栈的⽅式使⽤。如下:>>> ls = LocalStack()>>> (42)>>> 42>>> (23)>>> 23>>> ()23>>> 42LocalProxy类⽤于实现werkzeug本地代理,将所有的操作转发给代理对象。如果你熟悉C++的话,你会发现这和C++的引⽤很像,但⽐引⽤更强⼤。使⽤⽅法如下:from import Locall = Local()# 以下是两个代理request = l('request') # Local中实现了__call__⽅法,⽤于返回⼀个代理,具体可以查看⽂末Local的源码user = l('user')from import LocalStack_response_local = LocalStack()# 这也是个代理response = _response_local() # 同理,LocalStack返回的也是代理除了以上创建代理的⽅式外,还可以⼿动创建⼀个代理from import Local, LocalProxylocal = Local()request = LocalProxy(local, 'request')如果你想拥有⼀个根据指定函数来返回不同的对象代理,也是⽀持的。session = LocalProxy(lambda: get_current_request().session)但我们为什么要使⽤代理呢。这⾥简单说⼀下,我们知道,⼀个变量被赋值后如果不重新赋值,它的值是不会改变的,那么这在程序的某些地⽅就会变得很不⽅便。但是如果使⽤代理的话,那么我们在使⽤这个变量的时候就能动态的获取到它所代理的对象的最新的值。四、其他除了上⾯三个⽅⾯外,Werkzeug还提供了很多⼯具,例如WSGI中间件、HTTP异常类、数据结构等。这⾥就不在⼀⼀详述,感兴趣的同学可以参考。Local对象部分源码:try: from greenlet import getcurrent as get_identexcept ImportError: try: from thread import get_ident except ImportError: from _thread import get_identclass Local(object): __slots__ = ('__storage__', '__ident_func__') def __init__(self): object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__ident_func__', get_ident) def __iter__(self): return iter(self.__storage__.items()) def __call__(self, proxy): """Create a proxy for a name.""" return LocalProxy(self, proxy) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) # 清除数据 def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)参考:本篇参考Werkzeug⽂档写成,如有错误或与⽂档不符的地⽅,还请以⽂档为准,也欢迎您反馈给我。
发布评论