PK܉8ͥ  docs/api_stability.html API Stability // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

API Stability

Table of Contents

Werkzeug has not yet reached 1.0 and as a matter of fact some things might change over time. The following modules will very likely change in one of the next release versions:

werkzeug.contrib
The community-contributed modules are yet undocumented and we expect some upcoming changes there.

Backwards Incompatible Changes

0.3
  • Werkzeug 0.3 will be the last release with Python 2.3 compatibility.
  • The environ_property is now read-only by default. This decision was made because the request in general should be considered read-only.
0.2
  • The BaseReporterStream is now part of the contrib module, the new module is werkzeug.contrib.reporterstream. Starting with 0.3, the old import will not work any longer.
  • RequestRedirect now uses a 301 status code. Previously a 302 status code was used incorrectly. If you want to continue using this 302 code, use response = redirect(e.new_url, 302).
  • lazy_property is now called cached_property. The alias for the old name will disappear in Werkzeug 0.3.
  • match can now raise MethodNotAllowed if configured for methods and there was no method for that request.
  • The response_body attribute on the response object is now called data. With Werkzeug 0.3 the old name will not work any longer.
  • The file-like methods on the response object are deprecated. If you want to use the response object as file like object use the Response class or a subclass of BaseResponse and mix the new ResponseStreamMixin class and use response.stream.
PK܉8(j  docs/debug.html Debugging System // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Debugging System

Depending on the WSGI gateway/server, exceptions are handled differently. But most of the time, exceptions go to stderr or the error log.

Since this is not the best debugging environment, Werkzeug provides a WSGI middleware that renders nice debugging tracebacks, optionally with an AJAX based debugger (which allows to execute code in the context of the traceback’s frames).

Usage:

from werkzeug import DebuggedApplication, run_simple
from myapplication import application

application = DebuggedApplication(application, evalex=True)

run_simple('localhost', 5000, application)

This code spawns a debugging server on localhost:5000 with the debugger enabled. If you set evalex to False, the debugger is disabled.

Warning

Don’t ever use the debugging middleware in a production environment since it can leak internal information that is part of the variable debug table. Even worse is a debugger with the evalex feature enabled, since it can be used to execute code on the server!

PK܉8Doodocs/deploying.html Deploying WSGI Applications // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Deploying WSGI Applications

The best thing about WSGI is that there are so many gateways for productive usage. If you want to switch from Apache 2 with mod_wsgi to lighttpd with FastCGI, it’s a matter of a few minutes. If you want to distribute your application on an USB stick for presentation, you can bundle everything into an executable using py2exe and the built-in wsgiref module.

The following list shows the most often used server configurations and how you can serve your WSGI applications.

Generic Gateways

The following gateways are webserver agnostic and will work with any webserver out there that suppors the underlaying interface:

FastCGI
For FastCGI, two well known python libraries exist. One is implemented in Python and handles pretty every FastCGI configuration. It’s called flup and supports also some other interfaces besides FastCGI. The other one is called python-fastcgi and is implemented in C. However, the latter has a much smaller feature set.
SCGI
SCGI is a simple protocol with the same benefits of FastCGI (persistent interpreters). Like FastCGI, this is supported by flup.
CGI
CGI is known to work on every major webserver out there, but has the disadvantage of being slow. With Python 2.5 and onwards, you can use wsgiref as CGI gateway. In older Python versions, you can either install wsgiref yourself or use the CGI wrapper code from PEP 333.

Apache Centric Gateways

The following gateways are either written especially for the Apache webserver or work best with it.

mod_wsgi
Without doubt the best deployment platform for the Apache webserver is mod_wsgi. Even though it’s an Apache module, there are also ways to switch the underlaying interpreter into another user context. This allows you to mass host Python applications like you would do with suexec/suphp.
mod_python

For a long time, mod_python was the preferred way to deploy Python applications on the Apache webserver, and frameworks like Django still recommend this setup. However, some bugs in mod_python make it hard to deploy WSGI applications, especially the undefined behavior of SCRIPT_NAME makes routing hard.

If you want to use mod_python or have to use it, you can try the mod_python WSGI wrapper from this blog post about mod_python and WSGI.

AJP
AJP is the Apache JServ Protocol and is used by Tomcat. flup provides ways to talk it.

Example Configuration

Here is a small example configuration for Apache and mod_wsgi:

from yourapplicaiton import YourWSGIApplication

# mod_wsgi just wants an object called `application` it will use
# for dispatching.  Pretty simple, huh?
application = YourWSGIApplication(configuration='probably', goes='here')

Save it as yourapplication.wsgi and add this to your virtual host config:

WSGIScriptAlias / /path/to/yourapplication.wsgi/

(Note the trailing slash). Or, if you want to have the application in a subfolder:

WSGIScriptAlias /foo /path/to/yourapplication.wsgi

(Without the trailing slash). Detailed usage examples for the WSGI gateways usually come with the gateways or can be found in their wikis.

Selecting the Best Gateway

Selecting the best gateway is mainly a matter of what you have available on your server and how your application is written. Some applications might not be thread safe, others work better with threads etc.

The following IRC channels on freenode deal a lot with WSGI and you might be able to find some help there:

  • #wsgi
  • #pylons
  • #pocoo
  • #pythonpaste
PK܉8TZoCoCdocs/exceptions.html HTTP Exceptions // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

HTTP Exceptions

This module implements a number of Python exceptions you can raise from within your views to trigger a standard non 200 response.

Usage Example

from werkzeug import BaseRequest, responder
from werkzeug.exceptions import HTTPException, NotFound

def view(request):
    raise NotFound()

@responder
def application(environ, start_response):
    request = BaseRequest(environ)
    try:
        return view(request)
    except HTTPException, e:
        return e

As you can see from this example those exceptions are callable WSGI applications. Because of Python 2.3 / 2.4 compatibility those do not extend from the response objects but only from the python exception class.

As a matter of fact they are not Werkzeug response objects. However you can get a response object by calling get_response() on a HTTP exception.

Keep in mind that you have to pass an environment to get_response() because some errors fetch additional information from the WSGI environment.

If you want to hook in a different exception page to say, an 404 status code, you can add a second except for a specific subclass of an error:

@responder
def application(environ, start_response):
    request = BaseRequest(environ)
    try:
        return view(request)
    except NotFound, e:
        return not_found(request)
    except HTTPException, e:
        return e

All the exceptions must be imported from the werkzeug.exceptions module unless you are using the abort callable which is available in werkzeug too.

Error Classes

The following error classes exist in Werkzeug:

class BadRequest

400 Bad Request

Raise if the browser send something to the application the application or server cannot handle.

class Unauthorized

401 Unauthorized

Raise if the user is not authorized. Also used if you want to use HTTP basic auth.

class Forbidden

403 Forbidden

Raise if the user doesn’t have the permission for the requested resource but was authenticated.

class NotFound

404 Not Found

Raise if a resource does not exist and never existed.

class MethodNotAllowed

405 Method Not Allowed

Raise if the server used a method the resource does not handle. For example POST if the resource is view only. Especially useful for REST.

The first argument for this exception should be a list of allowed methods. Strictly speaking the response would be invalid if you don’t provide valid methods in the header which you can do with that list.

class NotAcceptable

406 Not Acceptable

Raise if the server cant return any content conforming to the Accept headers of the client.

class RequestTimeout

408 Request Timeout

Raise to signalize a timeout.

class Gone

410 Gone

Raise if a resource existed previously and went away without new location.

class LengthRequired

411 Length Required

Raise if the browser submitted data but no Content-Length header which is required for the kind of processing the server does.

class PreconditionFailed

412 Precondition Failed

Status code used in combination with If-Match, If-None-Match, or If-Unmodified-Since.

class RequestEntityTooLarge

413 Request Entity Too Large

The status code one should return if the data submitted exceeded a given limit.

class RequestURITooLarge

414 Request URI Too Large

Like 413 but for too long URLs.

class UnsupportedMediaType

415 Unsupported Media Type

The status code returned if the server is unable to handle the media type the client transmitted.

class InternalServerError

500 Internal Server Error

Raise if an internal server error occoured. This is a good fallback if an unknown error occoured in the dispatcher.

class NotImplemented

501 Not Implemented

Raise if the application does not support the action requested by the browser.

class BadGateway

502 Bad Gateway

If you do proxing in your application you should return this status code if you received an invalid response from the upstream server it accessed in attempting to fulfill the request.

class ServiceUnavailable

503 Service Unavailable

Status code you should return if a service is temporarily unavailable.

Baseclass

All the exceptions implement this common interface:

class HTTPException

Baseclass for all HTTP exceptions. This exception can be called as WSGI application to render a default error page or you can catch the subclasses of it independently and render nicer error messages.

get_response (environ)
Get a response object.
__call__ (environ, start_response)
Call the exception as WSGI application.

Special HTTP Exceptions

Starting with Werkzeug 0.3 some of the builtin classes raise exceptions that look like regular python exceptions (eg KeyError) but are BadRequest HTTP exceptions at the same time. This decision was made to simplify a common pattern where you want to abort if the client tampered with the submitted form data in a way that the application can’t recover properly and should abort with 400 BAD REQUEST.

Assuming the application catches all HTTP exceptions and reacts to them properly a view function could do the following savely and doesn’t have to check if the keys exist:

def new_post(request):
    post = Post(title=request.form['title'], body=request.form['body'])
    post.save()
    return redirect(post.url)

If title or body are missing in the form a MultiDict.KeyError will be raised which behaves like a KeyError but also a BadRequest exception.

Simple Aborting

Sometimes it’s convenient to just raise an exception by the error code, without importing the exception and looking up the name etc. For this purpose there is the abort function.

It can be passed a WSGI application or a status code. If a status code is given it’s looked up in the list of exceptions from above and will raise that exception, if passed a WSGI application it will wrap it in a proxy WSGI exception and raise that:

abort(404)
abort(Response('Hello World'))

If you want to use this functionality with custom excetions you can create an instance of the aborter class:

class Aborter

When passed a dict of code -> exception items it can be used as callable that raises exceptions. If the first argument to the callable is a integer it will be looked up in the mapping, if it’s a WSGI application it will be raised in a proxy exception.

The rest of the arguments are forwarded to the exception constructor.

Custom Errors

As you can see from the list above not all status codes are available as errors. Especially redirects and ather non 200 status codes that represent do not represent errors are missing. For redirects you can use the redirect function from the utilities.

If you want to add an error yourself you can subclass HTTPException:

from werkzeug.exceptions import HTTPException

class PaymentRequred(HTTPException):
    code = 402
    description = '<p>Payment required.</p>'

This is the minimal code you need for your own exception. If you want to add more logic to the errors you can override the get_description(), get_body(), get_headers() and get_response() methods. In any case you should have a look at the sourcecode of the exceptions module.

You can override the default description in the constructor with the description parameter (it’s the first argument for all exceptions except of the MethodNotAllowed which accepts a list of allowed methods as first argument):

raise BadRequest('Request failed because X was not present')
PK܉8~~docs/favicon.ico h(   { v r 9Jm~ !`|gy "-htth,9yWp yg,: Jaj ~l pWpe t y  ??PK܉8d!bWSWSdocs/header.pngPNG  IHDRiZPsRGB pHYs^tIME-j IDATxڴٓ$u;KfV[XAAMllFf:&MyiDIBڲrŗ{#җ NUVfF|;/STYWҳ &e&& Qb$ Ft8J@K&wo]9M-ńͦ$MbDG' cV;77K^.y{$Ƹ5IJ " "Éx͆O?|ݒSs<{tAYf*PlzD=YB'b8A/lM0Y`N Ee6EH|:!7d9%bNGL')'3NGޟ׳OS^z4W u1TVGk_ko~ZdMd~|~W'7 @$HT "$_rq6KTKnnoKQ&Kd F<U#G\P\=b^Qc1N=O___OO˵G Ejh٘ uZ?;t4޻9v_JhWב ji*oj{L%g#J=:o1sN7~ȚC'>$76wg܂\)uX*~#24yb 7WqRjo k-97rxuh <359t={w`:d'Μ;c 6Qcnex#^ ,T@3p[p1lOpwL{\:g_ H-Pm ^k1cOCm|.!gg/vT~~{Oqi!{[|7?E73vW=7iEҾ~cc<@dYI:קu;ʌ<<#Aq6[46C"A 2&`~t[? f"e hZnn2ʻLۄPRM4ͶN@Zߍp% DY/SkIƆC462h@Rj;bF}M7:*ɜhI'{>j:Bv)b}"S_baf0R6e by 8f}:# 5+&ŋ!+1$/lͺ|udyS&zަĊEUq`L޽XpH͂ynWkĂ(^<#cb,8[S&??3?^;G_{9Yb,9u98W !NP&[ewbk1Ep(䥒%Eγq%,i|MR޾fO%Gr',Z^q8|s./4E˸*rs8ȺPi m͍w{&PId~f c-QlͦDQQJu1U)bcpL㘻"'2  |Qbc8;%I'(PWL>lWT 0[z7iR<2`Pp~4tؒ*}wNylJ;_o d;Edzg:Jx>xՓkm!֒1S/&hs)xb)ē|Eae:<9!6v݊\ WW,&' ` q̓42c8UM-M9D7K4WMdK>z#k;Pby%_ɓw@4D eE Z:T&b/&Ic"# /Z%w9Y#bSkn ח|Ŝ 49ׯrl]aJGe`=n6IL7 aJإA]I656[+;[O5.9c+Do%jadqN%N0yJIT WXS0xU(^!w ⵒ_YC-WQD鄓 UzL݋Ukyh,ԛ C3z(`fD}GYVQ7ͮs ~`.{Nf|0zi~@"iהJ5IAi;-wBV IKP> %Ĩ!=%¢1}Bڐq0!NE32{B^o=kz^ҖB4ts ڑty+'+# dAFwz HvFogn9 n&3 v~% 6aniRqc_GhB3쯄rөc Udo\!{y|.`:끁䠼oHK>F䈡ґ>G-y@V;Ewy]ֽy^~ڧ4 { !f$ mu׷$k ȴ{vtra!LFmeZt# #%Id0DV*vW MXr6fvl) j:/ɜ۫%}+Nclbν:\EG۔Yo&<}(3:` 6» *£ cggGL MƫLfG|OϘ&1wkϳ'\A(b(5whD(qe3Z`L 8_`m&,on]pܬ2́(8x>%M$ #l#AV }i~m̜vGRPmhGӖ\)L[ޔyH[@?v-[=r@]N0bYS\Q[xssW_ e&IS@I=h=+M{|TIʂnu:[㵲[sJWQsق,[s1M"iJ[YU~J/[m1gDw)p_)[S@dL0lmR-=[vF6Rem]ekVQ"-r{W<#(BƼdKU[x̮pS%V"z*R7SS ^a6I8.\xGŸNe l/п@{ڑ\zmhHfݴA@^buUB^:ڠ;8KXՖwG[?Rcr6] d@cqh`*fzWw6܃pA?qi> UyՃI_"p_[eO\H7v̌79KW Ug1i`"T{n"@>5\{8ZzpV26=+{!rf_?Wϻw_"~CdI2iIJK&UOjʁev]V.jSR5 #fI/7{Ύ'D&bZ6yfq}a,%F$J@`:Ɍ\OO9/(ʒ9/6vĘ!bG9ccl$ D[J箕'/ W!G .0ݕ2Mג,GH@FEI ljiڌwţm\ծwqϣ[jƛ`\z׬%42g:?fr,D9J&B%W_ 9?e[uD*@1OJRWh-V<F,MIU^8qg|d2==~£hB(Mս3PWTlI!p wyWC)+nﮘĆ۲wL2g>GAkn*SG[DwD_,xofxVr6䢕b݆d}D(Tj(PyhtLCՒib\jC1p JW VQD]5ah:A0ڧNg<4lEɥd4Cݽkga5 Jq\ةj0i:P7Py qx8^{V6AM1$b h[QA1 W*Y?f|˨3^4,f8C,dWiAc .ͦ@x}j&$Ι ' Lg$4f\,xW Nh$Qa"KQfyNdb043IMfMg\^#J&Bҕ8-+<DYoYo[Jϛ IdH"(2/_g|>d= IDAT6X4Am͸֓MUQuAxϑ8b͙?H ?^yE\euڑ7&َbt7 Ӕ? tµsxYg4ӥ-G{$kduS+ 0')M(g6/ T,*BVUQf퐢T0pEUÿիz(Q%#N*#zQ85ڴ2{_cY?]2Fz_ί>]ol&k71: SŌ:D˥`IRC/0{I3f=Eqf\F#+j0YK~Fq Qlk3rxЀ`9 "$ҁЖz@Nhͤjw*g%$pTvnuO6mKH>,<__ _xso1p+9& f3u-о9lCkL;Qh8n}^WK^oȮ`sǜv#m$~ oPc*c׿dãҡw^p R[;"; b#Ȱod%)]2ǣ^XCZch:$1g@"cϢv$-ۯL}]J=E{B ]^;WWFAAvp%/ݽözn*ZC >2¤ SCϣ\:yTև.J/L`ў^eH`~w?[_kfǧ^V?r^2OqޓY<߾a)횳9qe۸%f17$XyE-Kw[>|t$O%96~7/|رӫ CO06: |C^l ,`1%~!yڽ&X^vFqewuK-z˷.Ja -b=w/ ׏~inZL4 ư#2=84\toYq#X%+>f?D$»8۔bUsĘ <;]QUV.,HeqG &d,++N_o;ͮ#ţ>g29fOQzy9YQAٸ^)aP0p(~dDX+ Gxu7KI^Ӱ6{OvYN* U$l7zhѕˮSLy󚮣м p>oZwE/r?40!uݳ.nP $4R u݆+l;Seyix*diB{r8t$]ZFeecС{=lClHH57MBD{ d[!Dl5h5 d_1t!3/ ϒ|_xV7d,[ 2\9+<ビ)BX2nV%̦4k,ޕ S|~BY)ʂo"UԖvB)$ q;~7 =Q2e-I䮠Xg`QX%R.vb&F1#4‘.(48CX.Lx빧6V☲LV|j/Rgg'|-osB^h׮53=MOf0%}mM״:.45 c Q'/(_8jZ _8M)ŋ^0V%/"o+Bt`"TpT9ՀQ_wV!ZDB;^շxt|B8{[׸W߮w`;w ۴ e{j_|èĥ MقuZ7>2 hb 3>cr`KpkEp'@;z|Ցhwv>/A>`0~}< x#tݵhlzm(ם E/c{"}yH#[f2]tc BfXϪ[9$X:0]:k{z4M-Tmѷ8XI]cEl;?Vz{0 ?b~O?L]2UјA@ EH-;=|#}&\PObiT`nt}{rA,tS͒tب*}݅P7²L)IuO5Fyo1x݁5jt6Wm,&S?`Ŕ]:EVX> mvpz:i=cpo:0HS6kD;Kٵ\!2.S|ɰcL0 e5B.Le< `b?=Tz >dYpz$,uh^i@}5RYM#`hW߲;pjXg% + = iĆjƚJFD8O{uHBFmiukۉ7Bm`. #):{W5uj3-[֞4+xm p{~]rΧjl.yytvQ›WC$!{A||sgyŋKv]rœ9Gi5cdN$i̬\oaS&rb??oQk)6+b(`T 'GefQ³`JVbeq<'MT: s׊ժrؚʖ]7n F1-"iW}{)oGʟ|Weﲘ+)bo~nmf-Dڙ]nEt:ujڟ˧b W<~[sP%ɔk\Si(e"<∝5r?nThU|܁w|:#!RnKRÍTpZ! :$ d7:ӡcs[֊@W<d7A߮j،ddIl/=$5]pdLP뢵_J8m!)K2\;8ֲclc$j0cCӻ{ lw3cl- zkG°^ie@k,}z ںt~5Axءn5}M7غf59>P'nm|Hn4mǨǸuLL޶ӻ[)Sx]*7?g)Mx'[XJu&ūQSdId-5%WF_pz4!M,Iam-6IcˣyOӓ;>yv+ cSz\Q.(yM&[Wljs4r2c,>/nG1M8-wv{/OR<ƠjEEU8\<*7xj3pnx__'D\&3>OǣS~&m^{}zbZ[-LIRL㐾H;u欮IjxX Pn{^^`(*[SxCY8)]eU*X"#[y\z] uV"NO/X*3ĵوF>;א^x5:lI /RU% Fwaaݐ c/0CmH玌8fU2,o)?v|Dd,Xkvȧ) ;6j%l@2,]3Ԧx{>)>!ʀ}aWc:1ԿchRTv6;60I] ʥNk@5H l43vCzn-k?$=Q[,yJi݌-_i n#*(jiv>4TL*vC+0y,Ǔ'|<{̏wx/YHB 'y E\ΓS|[)dz-G)~r8+1 :)r6Oxℯ^_SJW2&M>8XS*Mįx篯ki/_ŧ?nuݥ֡ z*֌JR[k"qg\b$I;JWyW%bbU(J&#-?JNbr ?/Ez7exh;a",H-n2iȩ Bw/ }y&.x0''9lT:ެXrk+x"VśsW$` D֢$Di1 Gcd0Niw'635>Į?}F,3zCdcw.0 !tmw=;%v2vf~V K$# Sj"2ˎg[22MXFfCeK1W^{AY3-$筎Ŵ&5`b0V\)`hbͬkiGbCfz ;p)YC1td&Cқ'gio&[=Yö[Q4\DitrҦK r|/9L>G\ݓ/xg$NZOeL:9x~ԕlxw{(1 4->]`Lf}Cų3)B,6J)U H]=2GҕD&"^m6d+9sq{kw6$..25[.︻g4#8DȘZnPz s(-y|įS~=Ōc}ఊޔrR-o@e~5`t?v΂fiubx]S|NOA2_Q͗|z[X|5Zr|ݭ 8REcW5nAMW/9e"q|Lf)W?omsn)j㋧]qus[,uJY 59GGxUXLwT5gIz|Zg< %Vקj_o櫑"itԚ)`<J/uO'v߹{%"v}GY锫+8N)(ȋf"8"mVr{;^Nܭ.{bSgEhEP@,<'"ʲy[2s~$5*pV*iX[ule ~{ӚTmtaX*1"H$nY=Q*E^0iֶ:bs|'/c~2=rׄ]YY^4mf16Z̸e&5K.QŎ|ǿ;f62$>YK4]9_yCKvBd- M}vA)|PU&I>CJ[e+5R`bLTe#s׷W,f 6{|R wZ2 aFGE"f@Qa7iڬjmPuql}&Ahc tw :GHz|}`\?]4,!D^T3#zDx #{6 e::R74 rEB]2bph^ްȚ@`5`j=BKA '`(^3.}zSn BHn4$k"2Uoî1=侸Etgwub3*]m{(kdQѝtt‹r?Mgo[~d}d:s6yFLY7o^!X?! w;(J>YE3)xu}W$jDf+Ƽ_Gųւ5(t:P P׭B|Qu( F λZ[^Y/KtIG*Tڪ<7֢ηvg%8r¯y/fO/ õ`f[ὕ8mn%+m( Lotl:uVߙ>%lSɓ",-͚e7Dq\Zbl5ΊbkwY8bĶTnIuVNEuwZ7` I"$IB^fSwr>?bkh+Oک9.Ґ_~ 4iX 3lmلJG˜ iQ@7N|җNwkϯ@u01#5חH~M4 B, qSNÝy?@f}k. V75m9\B9,T舄`O@8' 02m) IDAT9`j {2` d!uH KZr%Кݚw44|Aۘ0䉮J#t:SڕYȞ4R Lҗ5 'mW91Do}wR{8 י#MF~MVB7Nx%|1yʯGyz'Lo$ IJxU6ekՠ lY`l,I3x9{`(*EP/ꑢDۭR٦`&6YcD"T g;w#c0mKTE1iZnW*+CigmueIZ$r97,dle| ?S?eiK5aY9(R8ח7W=e*bʘ9Ui]_`7dS8E0I'\.FUM񪣩KŨW~F5ClDB&6q"乯X5_5HzM8,65Y]xoFih À7vjw}v[Wo(]mXc tBkq3wK HivBe0nUZNS;{f뀔% \ bD+" ڸf{(IgG :v7{ug T:$h4ĴoF>kP-j9!f"zo X=8 ^ź.`5vb֕u. &xg=ڕl[׺{CB t i>ޝ"n mi 8 4xFil#tiC9'8 rGU кM@ƲsAzHm,&|!Kf~KdxvbV,Op~zÃ%7tS֎qbst3ܸTL)F ,J`́KS}r _᭿p/.Y/0yXEևY#ExsWsX@U+ASUX.VBIK$002:-C"8ZaŊ+LXgH1I 6  [ϗ8]_ฐx\t5e#n ֔lJ\ngn@s2I+oJ=iOrD GT64˽+ Ɯ8A /bSR8T!f)/5LKՉ~9aʳ=9@ YcnFCC&5c] cqAE`xr2P*}8=#eἭhkĞAJ'^G6lZ8Qu(2qyh) 98w<(wvvS;E u{Q7s}p?ymv7)/T#b@Q1$]fx8Jkq [~}76/qF=_|3Ǻ kVW/`M ,`e+ 'dv J`[6k\p)5qqz/muik0Frİp'Q(/޹?ܴJh3 T =}mA j*Og@ bFo%|yh!o߿CQ8Xq0?9g}>nt&^N-;J;wZi~ZZѿbs%Vl*X'P$njJy`΁!G/A PX0h7+nڀ|Hӳ+l h<0 HO3J|atl*@|go2=Cƪқ[D@qBl9D vڢBBVqak)ς.;ÐW:,{#'ss@֩skĄ>]IAgRvi@KErRc\h^{W`5ݎ޿sf^x披sc9"pɜXSמRm5tI 4%F|K}v_9\_XF@cGcۂQwq'Hc/hڶѾM^B¹<6ğ^SMs7.P6^.q"AXB(l*"T HUqj>T8C03q+l\Γ+<>{_} b,u !uD0$;tn.`"=Sb\@B@t̲7G tb'u"!zu]`='̘ _㏏eYbXbVHBz/ţx_S\Aɘs 9$gݬPCz f̊00k $ȃ)ufԆ!|zq,bG* K';:{y-A`(|/?j)5@'L%,f-(fof[Tʊ.SxpLɣ!ǔȯ%9|tMp3%b?LqJ΃EJ|.$:J&%IL.C=MCE#37 ٜ,9422#9yDj"nO⑮)N3*ㅍ5SNt22J"uC%N˕b$sB6+yxrF#h(<#EV| 焣^p?񏡵g/❿-w#]q >W8 ~X]ĦB{owh\WqqDrVkܽ}Up7A ¼(ƺB XxDJP*j ) J VXƢ5-fsr/7 pG*6 f eNS0yexw߉ ty4gNyE1{!y PE8˜8XL S,O &S)qLȧ\cK|DjMPFCf>1rR~b,hpM9Ly-7ꖧ?ҹ09)EgV$n6\ CoZ;Jjݻ .'3N#sT h.!3¯id;>z} `V$PκO*$ dkodȱKv~BMZR8,fX7534A1[@)fWxvq )?~̌h[mGHxֽ;fK/P.\;{J<$]GW B0Z[j[|cmQ`l!IlY`Vz<ڭPtu1W`A0nn ڸ`&"ԍ`%m(cR` (45.X79 lj]aa5n B :z3ntw=FuԔ#zk/c/U@#Lq;R)1؂bV%|vkoJ2e a ˆ]%Ҿ统|ѷVΉg&uN>2ggS>#yj Y%bG/.!M _w_­vUpgPo6PO7p~-%ԩkTO+PUAk Ƹ`#&Xҹ@@k+%k,f mְ`HIB` mm\IV̤_\@)YbZcQqÝp/>_|U௾8bubC`토;jS ̑iɍ}.`,g1N`F1[jZEA?,)9M̄Hiuwo6p`%x GUөl8OG921|a_1T%3t)#{4g aiA>D7kA)[@QˌlNf #F Y?"w@sLhR0#T< i0ԝ@\ܟ@5?&yW4[R{׺vT]8)J%"d3h$ѾKn $\ 3pjcgЇ'X%UXΗ8>:EYX.Opt MBކR zPBB@-j%zM)~Bea ZvQaZn0`XnXp0_@1vÊbqX|xvjw^{ _ocBD~pug]&an44k|Xmep VNXpK=C}9l NXlt`jBLfTe'/6%^9>[۪ƶiuöv B$ c\qŀw.x_w`A j6oubO/ . Z8Mn~Fc Ѡ~#g2E/#톊C/ | !M\;e/)S$~Qھ*^Η)$1kLWQ?ȼGܞoχ@_ܹgHۉtOEv;7q1f=8znZ+x1~zrlp>GhLq|tMu[wŋoPq~hy`p]kh/> IDAT|:p=3%hwֱzRg{|wOt1 )\YkyI tгCXcaƢ(p%~m NDÝ@ ~C)!SPʲ?rw 8lH]v9}7t2xR`~}&9y^S+#"WlJ;M|~R| "ou:*ǍagyD HNDRCw C{)AZq#P|*k?@֏vPrK|Zp;>1dyH:3c1 W.XR&u=JL]-ГHќܻT ;2SD_J)$%w0v]v?n-ߵ 0D Aij <ZHJ)>{'_]ᴹ'+lavv-X^dyggk,%u J?wOwK,h’tY o Ac-0ǼZ"?ph`P +|?9YkQ%H8eX.V(%%.qYUxb^DJ8kl 31ݙ\Ncwɧ Wxqzkk x6CU34pv&^ %d]ªkXk`u.@e /-YK q@֍%B 0~PVJսsiik6tII )j C( Ks'R8wH;b֗[lV@K+g2̐/5H6<1g+|hvoXKR@xw hb + l $ X]s"pCB² 6Y=i fk#]m|C a [;I$䇟,Bd>p5S&50 f,hs!Od9qŨ.96Hc'j]9ڹutaqš""pp#C<׽X$()(#>߽19[kľ7;7mPbpX*l$I}Rh)T{'']<Eigڪ=>ei )ü (pk Q#G!1S%nw~tKp((OaVoa5G'8>>fºFUk\758=}w~lើ[kohLlLݽ?6 %l=k%-J'[vYB@on60Ƣϰ6J.Ο[mx=f3bٰ`QwŎ%RdR!g!R$fRk-`-g@ faFS7`+Pו f\`&0Il J @0 vh %`dNbxW wK "wTV}F<_? ߑx~ח6sg}ȩ]oS*1Ȟ]·,1ئeC̤yxtBSy4g>ak_Mq Q udx&vNte`ĺ}-0A& >F9$C)$ ֏N%#yC [э֛tBt8Sl.KԴsٻPܵ׬\bpI=b9]tu}8rIFz+}bx<*'Xu#`o*AE\ET;(ȁ7Б0RV$_pF߶MUUgq\LlF0Xc)(u!d8=|'PXXk Xk[,ުp (aWqq!\LxA%nɻ gxW} .؇46Bya^g FC k,`WpD³K#:l9n2r/;#=@,2AJ9k@pK|#b]m<8x <}~,k6^kvfVkX>Q @I\JkYhcQJI;|kր+)Į-F@ٴm&s-ݞs2 Kame<;;9lƝe9>037t#}9ɍ$)}p|4ۄU*0P2`=˯>s (Hx nsI, s J&Xc1,='RfK)v:NL~# na y 7ksXU˧ăS'ð%0x6M=V;'cĆ~uo4K^}ܹ֜p?",^M}SDG.nC1V TqYٛ4Ozr1Tr=ņRƮuc9ι.ƒVVjC˞d( m\$6 |-V?ƻ %^ט—º֖ 2Fvxk7οi V*!K|Zc[k/JXY`!TYB(6ABk"v9N. $Il ƣ_c~ 1j]WW3fggߠhr5. H,#K mf"PcAJBS{ 48fs Y8 _ح~)K;%xOxK$ $A7v7sL\4O. xquX̀ b%!) 7Q۰h]4[=08 R4CY'ڙH@J`3mIJ@]lr"c6i8i {or5G`D\N(o45"<.--309u>o0LU~X9-2aS96tw+DrpWs-(#ߺ(a#x%Js`k ݗcgL j8s"D'1`c;S sE#UM!S_BFrǭA 2,4'IܢyR:%ysC'@Pn-G 8 pr>Mt[}{hDp#_`p6$g ?d3\ׄBOo%>xW8z7j]?* maVβZ[`/'$qkl~ 2佺WZ?t#6WXo6 )Y_B mAUm0e ]7F lB)2l z\+Cg$ -X 2v hEQ}R}3 bWؐdHR; rב 3T*EQ% 6-%WE(N$5c,桔tNg2e:q>J "|=`}~ :0{N$HV1y |9L3^1M:.͔ 3nA9e")Io x7wZr94SOwj2c}b~;KAn8*-#r` w/tЃ)c&O՞}Nu;A?t`"dF3)8 "t\>(:>~`Ɂ1J/G u=i/ J|Jcy Ҟ^1טY2%NcƱsWX,J\^m0`X oё57)x,C%Go%aF1CVNS^wW0^_cϾ 3`$ E nluI$P ,ðCB^B d"r\ZYR(34ڸso=w> FyjgWb+0w| ,"ُfL0>P)}c7tO4 1n͸; 6H=[g L )IEׅ#ZaD4`&&INHv\D2h@heoޜweNhs)hU0gxi\,!_95+Li, ٕNBܐ.VPR)o0˧pqi7Lj!q$TJB$n`45cefHpM FF+/.qT`3(QW,pWB %$1XX(%!$Ak})$: ~ l%eIͺ&$'y_L\.`5-}%UGq维w84yh=Dz$w{CH!ԱL̵c=4וs S%^?x} DCt9`my}i1 #~~4[jИi<&y8q\=2t2EWu IDATIIZ4z#v9l@RI<)r18Lq| %q>| ]|oB܄)߻EZ=;@:so;]$I%{^ Ϯ ;NTˁGm\{x6,-w^Ss4iK.1 w`j)/r'o78n%7p3l#Hrzlc$f9 epxuR*P8}͙LՐ;Ɇ,$MŶޢ*0%`#H }F7Ra *d0 k\bM$^O4n$; ǎK"l0FC !a%^m(iƫ3aeɳM H?@B@A PQ9kB)9-p l'&A11+C#i< ;sY0k](iOa9w^237ж >'1{ȱc!Ifcʴg~1ʉM,ܸ-<{d(o$#hATBcs=#GܟHH2^:sB?IfRUcF YS۱d6$[=/f<ņqU8@WMF"2"C"S>߽\;tom A:)O!1w`J;C Q.}pՙ ֭ \4QB7o:*X_ ܮW(@J|u| g/V(% I7jowh2R@l DYb^c fwS[yGȶ#V1S_֒#Rjε0m"_9 aJb3Q1o%~yt_VOV}cSΖZ c y=|' c0_1W5>O!Mh6{vvj,dMf0Mf}&KJΠEkr!%J YP@kzkpZ`X:tև0Zbm,$93XXk{:8[²-Sn,kA; S [v۝#4jb^v"a81R9{߈aK 扜4K%1~)ޓ8a\Z <8Ai*36g="-;77YYvAx_prdG|6g ,:δ?^TčA;Vws畃hNyf2l5 HRdMYde+)Px= $4t!bI5-ȧ1'ik%cx觖A [A0&j$%'b=ө!X&<cD9oa"$pu`v3E% 0wP3e,?l.9N5 Ђ,޹0n+\n*})޸{!N a5!q]mvC$%i?)TuQ*T ]Y`kX6h?j٢l]i!Pv HI0Z!$`;ֹ A-KbƂܱېe,u<> J0$.|$jAMʂ+Ǥ}p;Fw}x(ȴi7ڤ9O1`:l̽K$ =yJ}@!}5,Ӎ nFy׮ulđQD:3:A$Rpǚt|"IílH^,ْQ4ʢPFŢ`dGJ4Nm\<"VLS?5/)IR]414S7r7g'~ҜwW8o.?{f{=|/.7XW ^Yޏ\B7D = Gxkl64B(RN*Ҭf30 MFc< \nM l6@ d[1A{̰   G1BIIx`ohE31at*`^SOk@>`Y/=[@al3TChM`"j{mxDg7՞ϦfwF:go8t*#!.nmz?y/#YFLgB;'Nd.cEHig&µn}Nro ҙ2!0Oa;3MpȆONo}JYS׺،Ӷx$@ܠXݗ`MvlSMb?n+stjNndB#bH`NjnqLPG0էL A%E24s_Ȩ5X`)I*suH@C!3a4W& r7gZd[^Y#o Q|%RᵻrEpCF»hJH6+n6kV7`MI`a`L {dlDX1QG!f 4 tiRYB r0TU(ւi ^&b9D )T7I@mv2JQ@,J*:Bi2"X+8f@l#GR|wτi]$ ':u{kGS쏞OX{6/\%L5#ҟ:ڌp1Q7"qK35a{ H GpwT)]n'i!XbNc[ҜSU](r-p΄ !#ܚ?|8W wN_+ggDxw7ހ5ƥTzq}gخ+NR 'gN#78 m5f4h5$ZziI@J)7EV%)1hEx~P*}1¡)66;xNsדFF[hk"J@ ghE9NbmxLp>* MoJa@(5dJgAdF1v| $hD-Gq}0%: g.::4o'+=LNc ~,fW>At]d,qHN"'%fDϱ{zb Kq֘P}?qo,q] {="3{ ;H$D(&1ɦ{iI=8\"AJXbBU-#-2_A+#*_fd/?  :էs-<&)S _ '3ɱS!W2gAH♦~NrsєmMIG<1פYG^$Pc">YE0Xr(p9l*eؑ^)YXB32(9?ߛ n|{`C@Ry3|?>+xx^}\NZ`5x^f( Zoaj՞Aopy 2=+X ҹdL1 aD@k4V,4 E;pvzlvԀWv-#.HH1l?c C p<t$;wu[Nhe?CMo ZCLl2U N{p'?F:f\¿/w;ĵLJYv$dٙ)zm*NӬ(7e.fQkc3> :rRrp<4s*~^VNQc* i/gJL>A~PŇ^::4tvg ( c~̬Кxu{|\h"aG1K^ԑ&51;7;d)/G8͈Sc DxC#kdfaDdPEoQRIs2<74ޠR*{=Ċr߁Ft,#Ѓw}g?o_3/pl]6W-$6vs\o/ EVΑEhW^$NhW-501;+Į+\S"@ -A i+h=$pk +Ƴc#(yJ= 쵆фU;8JxHU5{^܄TQ|dg:ǀNQ*HSZx:]}45fͱp錦K *f8 &@? #b~IS QBq %DU/=n9 ‰bbhZ%v>d$?RsjMq+Τ9dNI}(ݗ9!)ǯU.tS4P%VKиgs}D70 {B- A *h2|5& a=&jrsDNw9P7^MR"[:+NBY(ԯ j$4pG&7z A"d mSgȩ7w85xXhέ`GpkRƶXfm#jXk bt}@losE`5ah@`ARsqpq AJXRc(W"Cæs!5f"(c`!ba` =<[YŽ-'{\4.S9#Y ujS}P Ua24xFND*(ʠL`/Ji?& OJJd`,2ɯ_)Iy$ȑj*h*U??4ǡQǦ'WXjۂBr(?a>K_;í[r$9X3"^?XZ~ @γ8=901o z1Ƹ&ObWMV1 ޘX10P3 Z jUc4 ~,0\dãr,;m*dPPа6-7جV8_ޅ։FnnI`Ы9ni,H>OlĹsIɰTn~Chۑ)^pKP!6oJSh*ޱ=wkX󾾿t̙64'UIR>UjJ*Wf5}@b-k@1/[=۳RM1/|9 YIR9BQ2Hy}VJ#iv'3) L*O?FB%vQ0k #4Eҙ`1J@RN0d߱WՔ&U֯K~3|RK.!{o1_CkM-W"5eo=|~7xoW?~?oz1zw)9z}X ]uà j BH׭ȱ[qtFֺ લnz +J9C0V'2@ihk&ߌ'`!$ PjC]' %#siV'ƫC^K#գ|?Qf s"f_(0T̛SV]^$&JA?SlIr;΁duE CMM G^G=#*{Y}%%ZlE@Fl1 N(ҩ,YP, jU"$Zxc5L5${IfS]/Ce.3Q 0sw+BBX0L/ %Q`pPC&;G '^,)F#)SϋPFݺ!6) 8`OgvR%PALK;o8>)Ow[נN wL`Z\`ܬqu 5x} )Bwy{6hU⎣؅4 Tc~[zm=K0"hzKP4EҨ]%lĺFQ3ZXއ q1 0JHd;5c'X*9NٿRO,$͊(P) (gLb^[IHζqDH%NbM4f&liBf mkŀ, 兕T?U6!ЦY5 @9t].,B﹆Y*W0{La8[sZ[cTf `Mtb{M+|ns⏯+ƽO nZZ c 5vEc a m W@mbht0`60CYA5z2'Y)]nAc k Vׇ5 AOT1B۴zcv.)?b7td_ M~N~HR,tqv@ z =RL6$VPJɀXoxω$߼vk[|TmT /*;b.#&$Nj@KJϨV8 吃ݪ{(ׇH?\TFE oT/j(vU1%! &hX=ur\0abӘq+JL!*%]wdٝ&I ei1H1@BclW ?'A| l 9 VLn %M}LULqqlErDGhVޛ(NAkWh(杳 1dC57<6z9JVA.,iYS#Tr\bg,%J0Ú֋ <0}(jZ\[X# f\w=>vB0-ck z#!$uȤ(.,723C`&t|BB̽KƁ!ndL$5, fnZ2>;>b/~s&NF6dp<7IޏDa$QK{%=UdEթX[ % #Al-5}ِq٩Cٔ\!6 $p;Y1ϚzJ<; )RP 7..qDH=k\ 䥢xv4y"M4lJ!3ux@]bT"ibn_M&9< }G3|h3(>Tt(P(9Q,!JM!|/adzh& yr캑_SI˽_!zV3=[/ 9Xϟ`ڦbnX1k pk@A櫆]:20V2K0Vp a5nuBPӞW[ + X0\Nbhm]S0xM*P,Q0::$ y`ȦDgjϐ}Kl|G\$ᜋI{P6!2s$PbLsa$\xOr+aT*Q0OQĹ"3#$ӢC%;[Ԩ!5!x %GT-n'URW48ļD_2`rI"?rbYzNuJ5OlWD|t;M&LxbsQ6:{Ԯ S{nU;t(ُKEsf;~CxDG>RB ЙّG|O{y &;9hm`)ײf|Co N :4LC)7.Z5N0E@7)juGWLA3}`oD\3H 0 C)F\1Z΢ȓ]ͽ^6n.LN{h,S*ݙپϙ!$^8xS4!wƶ^5O 7$!mٳ79rš\d%%njR`ʊ0`=^L,5qbH]rsL:HX#RJ%qBp2rc߇h&PWѱ擞\ i8@{ )S6C ұ>7:|x]jC5L-X N,XF}mZNEg&O;[XqQںQr=8XrDͦ)Tc, m˰Ƣ mKh@[_:cډ+zݭC|WwE2X`EjU2 .rs#0& A͎-Ma:P3 KK#'TY>+)SU,7ߏ9z1۵/ !t$2DDG6a~+-^CDCf߱অ\tV'C b^ 6nYצ4Y(6V\Z"B/=:8RD+n1lB I^fnhkab5lom6mmS-+ko`{rk7w! JDNX\|@8A@UZP}YȶY:HcUj(ȥ&&@: (ŠKKKʳ_ /4gV% yTTAг¶?6IbDĊӒ-Bد^C5B({!O-z={=$vO "4'ǘKc^[?}0nss9h'̛6F>yjyqwctKNsZW:Ɍ $0^ ,xmXco/aLm{V8oa46-zm`ALJ6+(BӰKTcuCk7?N7J}c+҃13%: @@hAdqoD Ӑ(*ӚH* qrg"BRtџT&^!`/9dUEhz08RsK fb!>X](JB :*\nJS ʐ)(09 VI=X*f " ]|(ԃOTXEpsa^לP.J;)zpi/N Żk%,%/~1Y`:dJIO`NU5 hh=K4Fg$ Ў  ɑꨩ %DҷX84-feJŎv+Xq^}`ܬB+ ł^,zCFzNY E JA`n\s%ݻ<3WNmFPl8V_Unи!r½Ɲqaia!Z@j'ip8vWpl ~#q$u"T_JU䩵7¾hFLLG {Xrh钾IɜdXɃ⧥F"5.fH9NdNcc FpԐ^[^P^/~TTdq^ `'\+NjO6L|E.@<p#p+cT`{8|FgL@|2mŔ۱% )̞6۹,9NbɦXT~HH۫96OwEKu\2L-[߀@BXa6= &B{((@+,`µNFCVCWF[q:F4;klFo>3#Jxz[(+V`lZ4 6ϰz:.]x3]Jݕxs:QCԓq(S9Th"3R6Rۥƻ7V0,&(%1@Ym,m8zZKAzY,pH ?+dM ,f.%d&ϔ\[|%VG_iͼ *0K{,V"/8<1̱'S?gY"qnjls6{:ˊG|r}IȈw|HkS)=# 6b32lHMMED;]/rJ޽q@#S><`U0&Kh~_p#l=siuf q: [5:ZVK蘢Hv'I:L'pyՁն4 n0F"8E3vk>R[+Mbt'an@[EiBN4g+ Du eWLdtHa؟և&{R(k@HIcM}6XQ.FPaQ&Iܟj1Zr99BPpD1ǒEp%IPFZ fKMșG],릀oxk3%N~YL*{'6) _n3ů*˴V]|cn=3PD]}I.H2lIzM(J"N?Y.6=CbVH[a'2yjdA$Qs151N)uRP X8>dٵKLݟ9b#Vl=O%Tȑj"c͚R8Hׯd*ed aX275n 3/t@%x w!P2qfb)s;IpVMe#+>}DF֫40-F5,䈊 )STNpl/`o݅}|!nF)m}kaM"AF I.!;4bJ1B"33V b*=934<{R5B6XF10:mpZa1zqXUJz"^Y^tKÂ#Xז86,(@$nggmPG@'TۄBOxߤ"(g ,~u#-*UKO{*#R%{K̕L zN'|<\j,Fd-p⺆}Ԓ*XJЖ/"$ḩ 9:>3#v*0ysԔdA߁o4g Bg9/70/ң(RzK0SيīF ÙܔBiҢ= N|CL7q]^A)onLBgU6N'Agg 3@Z\u=U }ף:v\)v PA% &1cZiC d  ek aZjEX(|ZdHz;ȃkWq M~Ct$3X 1PT}f9(ڲ@,zlC~?㱌{@c̶4 3?TEk+vt>k ֛'K!UBȁ}>WC^O-8cLJ 7GşJvajh&I8>ioLiBOH%@6LN4Kʒf %$ҥ&>+]f@T#<X³  m9jDU%ba( @E7mbT:A"y(li'`AopY -+׀:\B}-BuPK_e"4P_Ȟ}Y@BN0WP MN,~f *0*",bMgyrA'qo7gcБ_ > <EZ %FXԆ-)QdHﳰ"BmX1 a(Epm +UbXk!b Ê sK:({K"wdc/ IQ1cG1 zZw7/"J=`U%7MYtO]@hо'&(0Qt)_Dezh])zŅ6rQLe͢w'܆tv2]Bu![j wZC4 l'CmtM;B 6 ƌNa}f0a:+ ܗRJ]: IDATAN@' ZF3umtD;;+&hkqvBu0z>^fr%e=΂Ve(Qr $Bԭ^F62k0=dmJbZ6Ȝ%sT.%Ԧ ~=#(69֙#zX47!8-*:q\IP ('%x4yZT.$G. )HJMS r('hjj$+707glM 8c@D2hhHt^;X) >'lz uAJ5_$8\R "c5E紈X xjCԋ3ka |N1[hh^ƠE̡# q$[赁U b!`k(5\ӔvֺE߃@ "tb5.SZ37+(pkxr0dAv4ADɻHbzd٩NӣI6M1~H 26J!{%TF˂ML pK2r^c0РX3CMg8 6 YHJl".f'%c~vRjd{-B["r35oR*H_bƟQ"y 4;t3>f隘`QKqxbW*W"r@b%|;\o70%XX1`&h;:8zk q+&zc[ւxX, ZX@ .h`40X V\ V+X,55.3K# Y3nm`#|ݜڗ 78#~1sV<¯g(c)ʼ7QEt-?$L`^=5F<'wZo'K,KE \eb̺,C!fM,() /%WS2% nD y๵PM Ѩ +q@{\M;q"9 qbhZL7رyQ qh`;=A $5 , V57Va2&(А RL0l>śƗɾ38oz2|$MrtO̻-$)S!Ű;{Fʀ#c}bԥS]䪀р,OEpI7Zfy|!;^KW耱jX"Tn|/)d_ g~#) %^ 4xa!%0w`4i!VSZ`] rT<:=?R'K&yP<rvA]bkA'=x 0Lhlm)+((&tl v@pl:XU1`Ţa7P^"EkkOI{׸%M"Ɗw^Wg{}{P\)>坱L\͎_Rl/Ȁt$WV(D˼$f)JA2cQrXs#q/k5h&~aEjewx4-%Hel 7KMghT /K t )]N4K Yt r*Wm2D{1h5ku4?R$PJv^Ch*l^%ɂ]_*-Y{N7EEr,&"^y ׸) Ɵ,Y:k}#\@u \lZV)[c \sV,Vj f07"A4 6c Z;=AOnA,n[{=E$ xE5$p-jijV%rٯ~r5gW%&pjM뽑,ǜx(=NyMߝ6gJTEԇ6ZY'#s9||&!Gr&cw苸8ľGXDig/rqhvGR^(> Bֶpe)H7*aMRuv9 Wb@@ /5n}~%;}"˧ؚXŠ`X&p`ae=ًٙ21хERh i\)xӋ-1V c!pøծaF 'P7xp%zcX@QXmi/m̄VJ"BnR5:[ILygq@ƩQILyݴ?awX:oʎluccn$ ֟U`EָbJ$n&l!љxIѢ`'yf6iWYbVzDo%*eT1!>*[,֒ɲס )#.qyNUeOi䂉MF*2Iu _~n͑zE,XñNJVn[ϟX &#34d H,Z&ƅ‸Kdr~#g0b`D7$ytB \#h*lݗnW_8Ed-֪ X'-֛[x\l{TVĂգWЮY E2גOaA׿oGJr/7K&u K'M8ǤR)h&k(|$NE06J2(ax ) Lii zo8ROؗ*xddHRQ.gS)]'ŞԦf!AIVƉ!$$şY0Eg2IIآtN0&T4zOj*c$*,0aS!U^Y&ɔ4ԪWPChE#0 XTiX6s0"0ѭe03-zmq5X7 -VW8;]O=x o ;_xr@Љ +' >3YNrجkq9ɫ§wҧa?ce< K0^^[nx(V(@dʅYv!KJ@5Edf )%MzeIy7. 2ձI@TēPVNA9x|Ʃ#̮[`%hFG/T_CRVc_Xc+|za@Ɉ dz!rbn``J0q\I3k\IVM|ey gBqX)nnķsq8"T(cHG4R`ݡeS9!s8V|r`mHNd BuۧBޢe>7cOo~zk F\bն6Rha3o+Sow) "kPY8)djU0h*[űf:DRP[R)r[JOي Uߟr޻{=/R ]򉮘$T8UdH=NF (=iH⇩&\~MLaS-USl`RI~8˭; q;d%zN3''偘`wZ*7ZگR/%\_*P2Nڹ4*= &rx^W++p ŸѨրz3_rC C BZ`!2D ..;6"6=]1>ó+ܿuoZ7x1>zƏGuvO5=/Ͽr12e%*+;w!o~Q.S/<*5'i_|cNd8+^F ^'lU17ٳ%U$79xKV8|hmj{"1^ǸzQmp)#YFAaoHrlY]_y_}/=($FN$ S"-3|)B(HJ( ::)a$rmRq",C,#׹ceEX$Kňj \]JPԮRx5O*oNӾ!4:1-iq٠^`.v(L׃aɅ(MhXk b(%جZ\=zkpY ϞemE ktk<{%Ο>ŏ>w}wX:+Ek,iʷtœ׾yU 8x@ P-#/ %4"u!=z[U8Ə4rLPu#R2ET/,:D/\)4p!тe,ɠCƆd2<„҅S %95㴴Iy|GX8g3S6.ֿǟb"RHuu+o&a3a,oN, Ry4B#ODр5O&T@J8A^zZjVЬ:n{2qPaG "X]_b dv{ m.$V5Y0$X5 ;ǘ3rC}v`i{T!U ]pQg_ bϚǑ ^ך#mIUXp"ᗒ"]H'y8/ݒO^8'Â*J z徜R"E&fHck!udH:9sN7Bxd-: a#VL<FRCtfV+ZH(x~M q7ȦȤo.G=c V4V[%hlA;`9D. Ď.ΦfN $?hČo|W^z]/ƟZW9+|wqί.Çx7ыm ~ h7q:5y2M"->sN3e;%D&R貔 XxIi"YTK|, Uewq^@֖=U6KlBM-֡IDQ,-Rn1P "$}"T K&7K"`IEwqY<$ N ㆕ĝS(yij/ͼp$f:r}ͤ'ъ]!*'I HkQ;%{upd\@fQR("ֳbd{ͱ]E%Qw$ D.1)ww `SvT OJ=F'C f"}(AdˑiJia#89˳[hd8\j-I@ _Om;E^7,@.!f}'x=ty Z0.Ə8@O{tFX)ba+ӣ=6.N}qm]$^C3wf}'b_]{}O˯jϟbur@Fw8?k 65X5Bńu fPi"ekp&@&Ӣj:hjCAbp&e4𜯢ڬtJ\o)u)2J?5߻: z>2!Vj= 6gM<[jNac{@G܃{Fa%]T+@`@b4/ݿ/RW_~/@lb0o oݿy*5NKaS~l@μn1S":r pFhEu<-#šD^Va\slli%3;U*KReUZRƖ:v%GR`oF#R 0 DbRBbxɉwQR"[zTC I'37]؏ݡ @jhBj!ʏtK>PjE"N&ǒ#I'Jn3Azupl| sjmvXqX/!9% ` C׌i X k#N (""1CQ' +׽XAÌV8PkЃЬk ~5nWo47?>~_?+/"s s--AAz.$˄ kJhe@1Q:UJΩEH3( xL;lʁD N{,U`KsBTV#ñ[_ 6mI r*9wKX})⨅7Q时B)%N* ,G:і yuL_,55fJm%Yt>0d1ϘڹQ}(z4F6ǚRæb-ggױIVɟZsyV,F9w)ޑ5h ;-'3!!XrRb 3&ICjjd/eos>avcq+ 5n054 >s_`"|z̴= 7=.F_jGYݐ>鼉Kwor|}M=_)aRˑ$⠺M9 Kla2F5W'^EbLrE_342 £}efH)p5"tt)]x 6R>+-9'"zHd` Gx A: IDATopXAOPzka `87at +B$@V9IL"Fc\o{<=yϿ}>{_y}!~vgP-'h??{ p7keA.^2?Xi?8,a@?]3\nF{.G'0bEE$Ml` MwrIϟ6&vjcpm+~Ӓ4:&"u$" 3(aNԮMu=lYB@ bg{>a6=BfA?0$ 5P2ZJkص5D`nH33XP!V\sk$8]7xvr &A0? ~¿| Vŝw|:a#9 mldPg.ɃhQXGE qW%1n1X+\cld3QzZMS.VH2̡1+?X1%;c"яBrsLfo S"XQ P%F,FT!b%֙ћ'NcpIgݚp}\L$K2l̲ V@nCjR,zF'[JL]!i1r C_]5 Dahq-XP "`a18B[%Fg:4`v:j$uUZ+ ralhO+I3ΐ$w8hQ]ⓙCpeV(et~sUDHֺKvtF΢ $,!\/^$`5&04 P ,|-w_ael>W};Dk'I2ǂ,.+>Ϻ{D^|1]s| 9f,Α9v2I;޿M9 %è YeG B: 8:XLq_c<ݝ$AILebwhፍgl3׻s/W5fhb}z$Y& Ԛi䁌~B?l8{{޲Y]_B}`HA+ipY'O;>jsL.aZf=W jAB@@FR"I@)D)n&Ukh2ABDƥe#`FBR4ݳj A JCHk@iLJ ( L(tXT52!!S ~=|~".׍~yne]w6m.va'~aeid]Cbvx=bqcP3Tb.!@ `Z?7 .P߽S@6t\Ğ\ך<4I$ [7Wm4~"sL/'jq(u09/S";{}oF sonޕSXlc๜jH-+xt!GLx܄ Q+y;"JK7{ =@UUB*`LN% $ h*tz 6l f <#,-xB@ FwՍى$ D ZCPQ)bmPkLĀF%RgPvna2jxi s*A q("JuGw."ƢnӉ9tp.?Ym϶ 1$ФunY39glo ,d>͆!6w 8/E mBHȦS2o֜'f E=u[ɼnACL5`Wۯk҈!v-.@>j&H#!B@dBPMO7.)D@Q+' T5+E%14$Xi$@m/MƑ&!U7zu08;>_~R7u44AJL^~^x -wV/Nw!;w2oq89#DX&>\ƣZ }@",,nm}eYt2;3q ڶsP6p`m]\"֨B|KxIhַ)]rĞv71+h[3-F(k)BrZRqp:t c g^v2B'x)a ; DW"̗(=@fF ͤq7jXI@B@QӁSs'7̓#\`*K"07E;WE$!D+F GY , $֠)W%H'X(Hgxxe,Zciѭc FAy8%[f6,9#FuKmlcb5|ڳzX=:X؝}<3SnW.ŷ4~|WH0XA#OzvZ"bي 02:l}k#Aylw?R4"cKL,E0O10qna?uVwW޲-229 vrP}>ѧ9!FCƒ&HISgywl[6ƿ p$?iU^+T9BӤGB$Aƛ I(Њp<͑IUYAkI2< BF3u_X!]p=<9BQVLR$Rãdsn;5,&2XzJ똻A=poZ)flc(wzvj0s{> 9:[5(h8F ~[b p8 m8@g>d6X:$3I0I6!(jՀ|qF*BIBgkIRXE&VF(1dY?.+iVRs/_#%޺gC3a&YFv -6C7D*"v<,Nxo3?.dc{AC@<͐!12ex)tnc:ؑhno{&Cy X'D96wcb&n) \k̎i j1'.!\zg\׹o۞wbl{%.wN3I9\ߖ\iڈwzb,ol˒ -{Z.lz)OBd\f]pU?C U@&F&iPi Z3&D]ut<;#OU~ETku JCKR "H4bB YTj );wNP5?EZaYH "Oo~_fgZ0[;\E:yh \;#v.#"=GI,ױuPr$X\d$lKC!Y[<tPVyn˹h}hZ?H,;ҁ >k+~8q{yt4e=v Y9XԱqd} `<n >6sΖi]ZMV[]8Ni;?/E&C7]GӃ ogL^O~EU@3PiF5t&Lk4?/jZ,iB5*pt03''( t3w/p,@jvʦiP hh, Ӄ)X1n0>Qa8|%o9F˜ M[h;tv;-˲aęwlc ǫsԾXؿ@qYk~Lb0"gr[1ZLp4Xȏ&\ ;~Cx+XD{Ũ8H4 +Yʛ61cxH뤄c }b=#}ўo޹;g1 |AgP`S,Pry#!}Z!9 Qcz!v6Sj{<Γ= [bd,{.:F0*kحжmc"?KFն#hQ-&D6~<෸X-nP+V 5`[hκh05 y.qpbY3k jJ94+ i12\VH!$AHgNP*Y*k)J 'bU㦬 "s7Da綋3nI?SDΖ- >e`iC6aF<pfsѷXF=~ p;6 [ϳOU hÑ296NmHԎIOǸ^}̞r \#\=9XMnlh{edior0CczzG1 g IH*=,u Ppۡ(u[?~&Oxj);B Rzl5ΜwOqoP^^aQ(uVZV ֬7bFB҄M I9R${|[8:<>!' !M "jgFHЂ $HBYՐpe( $YsXIrB@IUv;:c͙$u6- 1rqG,6΍tcb*c9;fWH⇴~爭WAOʷ;ٺ@{ܮ(ސ%t nl`FfJ66b:Q7~HHa#`0qLsƇYz"={tr6k[O_/`-0l~0NѴRb{6 Ko̭nk܉^ j62& -~b6%d%=gk(-2fE5'0;>Xo5_x6CbYXuti| w 5P\ =8@f}"3f/2O~ I@ʦ9֌!S F]BzYV< BYihMf$IS\*HDvxWpjt =нo2ǰQ@f8ܲS?sM=tx`ܶp䔕!p`@66ApmCdu-A?{וA݁d‡|}jF ?wha6do5tlf QqULA؛EPd0H7Қ-YAc9%w٤I]8FI& P0fy-ѦР iuX8N-V(+ (]!Q f̍\h]XI;pN G^޽ Wq\BnTt +֐A$FQ!Q49o!z_=~p]'6r b !)A t"AR^["68(H&Ww/׻ 荳fpimo髱5۩iY~)f`, sĶ5pq\xsFyꈅh ˘16[tOl4h'o9b1'Gl3anlPv`3(vt}vm ytcsd5 CP1s1AjÖ1Gbbflw DXV&YȂ@" ƂGi0Cv 5< ChR}90HpY?Ckpyr?$g=SmeHl٫ݢ FQ4C\A!Ay\N|Ʊo[zi;4%[݀xGV֠\oQbh" k}'C3.hd 57v f@_<|!% Lli$ LZPH@Bi!_JN* k#dĸs ^=A?GBNFmXöP}I!5AD=r 8("8jb4挰  z@8$+1k!LTl8a)fg8b, # o=|\1jh&h&dmi-nPC8!U 0##fIAY(9Ʒyp2?z]ݢ&ſIgÃ]BL(;X5PF[7%wr G\5*ĵhHAJ57]/` [GY]3 c%cgOy5 Z$^E[3dvݱ(،aC,#Z46²ϢyݍDw<LًȢм++`PGāvA#!K 9[ kv%6+4mo' YA318B1v ?1IG,^sj&B m#a FoNII%з$ec'HZmȳ3SuM{A2DԌT@&,iKu l(6@.b~W?ZM2jL[nf$ZF/ BT5R)1Kdl "Qj@Gtb'H@*hQuCQ10232fuݽ!T"277DX=s (6,3w9;UGۈ:`S"Ʊka[h!Pqsq>EՄgwd\ O1#`-tDj;Ĥ̀0X!Drs2%+g-0a 9 "$[_5%WKnc1T MaDfd2| AYޘ+IPl'!Ժ8@$)$yJYBJ?BK$iXp+XN_ƲyxS5Q> cY l6ϑ0ᐔ#HC?fb0Q!@81N#htcȍ^H^0g* .!,{lbhw=+ibvkE.rǦ)0`!#h-$I ΀-FL`)qd(Â%1Mllvb$0{jV:֝V+蹵MnZ"2Gv4[%ۅ:q.sWP" 3l>+vIJÆQI$Jq_g,>=DUVPh% (|O(n 3 `QXAAhͫZd7}dp2?Fu#ׄ}S@ըd .^ܠʺV ~`2^W/lIzm!mr-D4Vlf }c4+ɡQQm Xb!{qXں K N%LL$h,bc1=i6(gcC MP^Ƹ^x@raY d}0Lvdǵu 86ț-ii:[u'm7 5'~˯?Ui>M3JAJ(QUxQZ$0qY"/HJ7=Z4`&EQ-$ q$2AYAf03 ҘͦsvP ;w *,Mh`OwzѾܝ|Bd %_@{.sL<߳ul kQsX'ql^i|ft+P~l#P"x|$MKb@H́ ΅g="Bi%hu.0v rR4/;wa;k;ѠN'nYj3tN 16t |xՍs`erL荭rh -4D,s/cWwe?G IVڟF7.!/?{KC]AQ, y>a$"t]pv|:WKauY\~;6q[$ ۝֘:. G i/7s44,a(5¬#?byDby8w SCC9!%CC dd܋O{lm"H nкGĴdqMe]IsVtt$1"`}#[#QxMll#`L6 ,- ڌ{cǃ; 0'b.;7+̵}Atl#1~Trc.nrj]ec=pwc\WsF3 ]$Z(Bh^`xH }MD',.xɷ3,:)lύ_! ާo}UJ3 h)Q׺if %1$1"@'X);Z3y*D ɄR+H0d/A] <ËksdHP5U,Og$d6Cg8LU5[ï^&~(@S-Ý?:6uT[@sXʍc-tmQa׵\V"Nzvds,b0G,J,fEp0Dez7i8y{މ*n%]۷ƾc>cJG bΡdd&,|Tv0`OEW=CUK@_^:9h x|tfk-c|YsQVhTA/k1d,(-\f}!l n9egԵ: ;]8*?Gˏ~UY,B !vj`HP&jd- BJQ$BBU5H/q~>{IA9$h-iQ͎Mg ǘ`uu)2Gx?ǯ8JymsnՀREG&{@f'Ɨܖк"-b; CN^b< Tlr`]Os>DSa\v}Ի1 ևAV;b1\fwzDzG>b`z΢y^s8e,Ζd`ʹWE8=!y48^/ IX|5Ӽ1/1ir[+d\Mt0yZm-avb]U}}[; I^SOΞ`JNmN¹B.5^!V ˪9~0 P5T&+Th jQ* -I F* 2H T`"sL'HAPZA+泇(x [8=8Ī\10M! H89>knPi0i,E_:M s079finҴ.^  rn=f{˃&bs<cGAXZ'"yiNQ oeP=԰c(ڇݍPz ݵ/è"!!'_F@|&ٶ #o,^gz6Sy6h|Dv!Rrk?i2sh[ꐬdH}$BDN 8KH$Txٴ=4rG,H[;:jNu 䡦la,"َGxd]7]ޠni;k?NεDe"qsJؔ֨F$j"QV5@㙐R(I kFQVsxyD$n4|/}Nΰe dB8<8BR&M!@UWy*;CcV0=i˭ g;Hcc5vEC~!hE{/grcdA6_=lASd2=4bp#wtZvl!4QPl>ρ2mae" @%|&Xڬ|rrTl=j-};'2c;ߛZ_Јu3;&a%ހp=`ndEʣyw5#*]2v- Ŵ-Vv,z.BfBE. ]# L'an!toܹ@ǦKJR't[7.3 |Wx'  47( UWVЌeU#@Y) Z3r) 1)!P#@TC ,EL0Wу/%44}Y|wpWO(5Hhܻ}' @kUIR<~9Jpб2qn,82ᶮc[f;sa#C(|mvv5 Z`Y#osB'7?_H$N&˞=Qٝ=5.xT؁Ðe\QѦ;9L}z.;.h|CCϚεtrO>>=ǧ+so.$j,YB`2A$pȲ)t4jdJ)\>_~r5;0qS [yk gc==X$8;QAUv',v~eKC9t QI!(-au3!tm߆'`閬\ ,]{6y%\G:= 7M 6- vHGHm IDAT3(źXF,@i kI,-$- P>=Or7y0])Dy;3y@RingV F;J#Wxӟ5G 7Ŧ;ZCHDBj@& ƪp+0i\J")I)JAgwej;k|i"^÷_}77's%b! L %!)d"S?̟ nM7Ůz[n@m ~oޱma춰s@J7̎޺U c |V^1߷c]HXgLX XcsSwr;}vŻ< f˚C}gcȱC`g{k59͂g{.+C'`9O7\c8r6G&<ȋ !ہxq[21Ft/[d*E<9bjn{ٜL(=㑵gIP}dLG2G!a]& FM}eQ[KI?=>5HiTu V Z˲UQ`nf/uPF.$Tb"%Y!$|>êT.'$b6=Fg5_'W'vg%)Lgx\!L1@UK\||~DIk<2>x>*qY7kM{0o04-wS#:w7Nԥ 2UdiDꨭQc]6$m@ r5E-{5q0C-_VO{(OGS޶/w317xw1cTmQgdY<]{i-}I:* q6-VJba2qsM9wB뮯wEX~/I L \Oۊ\̺{ [ jE|XdB[%&1ѼE"mK,3jGhg*W&5  r%t Pq,ղu]@iF&Rb"dIi.&d ݁Vu 5ʚ W7xDH\/ |%8›/޷pĄZ\!"Q ZAf3 ZW֨u!x]>fxm&Q6NV6`|co'#2[)亾K:M'x ގBO1jlB/ԋaci`]o5< S(6jX7v!t>v=+* x ֜m-<_{{.v۸͇v/T99rryØx{6 a\BsF<z%p36"HB/*1=LPC$KH3`b8vf71l۬y;0 *Ҷ졸ƱGcb'_u"uJXtֻϲh=v'w$ۿ[dWJ\5$J(kZ)EU:͠Q 7UAI4IeGS$PT`QTDʵӛ%2A8=*_=ƽ|<:GoK8$R& ͑UBY.Z\R'P"WgwRxO[e6`WBı~C_#WK^a#_Z{XVc_gͥl{Քɫfμ5WZƶ qXXv#C/]v}=e ct̩gj3ҌWRǐx.bʧ0Z4=4)bSp\ס1 ӻC=rU"v.JK \ge)ŋw 'kmFUbOB5ZjZcU+\,K$Tugw8IQD'ȥ)>k/+ __,0H+kO/pzxw^{>sLB\@gfJ,!d#eX<~~m?f+| u7-8:`d6R {pR0hqʑ}SL %.ܗ 4 ştsgd|b'Og899b5?ӨcHella 61?M-AP6x4.oh߭9ym(v,~Mboc"G^oҶ#l+n|GpYnZ#K$ qS*,Uմ\f)%N&8i!vp|C3㫧HJX5ZCJ3s$DPJcPkJמCeJrb躂b83Ȅ@jj : w8~ Q9%wޱߦrV3`/m]uV!t#-Kl;lsqwWqT=Z[_߆f1>E1 :Xb+n;g_=) fwHk&ۭ}ulncmalwJ=V>-zd" ƚ9asb.=ym̵R6vbbV֋vd?@ncCMcb6}"×HS?y&08 |lBD|u#19pya1C:HЪ_COdad׈8± 4m9YԖkQӀ{M;ӭVi,Heff.U8 )h|wom1W6vF6[!O  EUR @,W^)JY`V)&) ) aQ lU'x?BO2|}~eU|y1RfUW^0tzǧ+0+ ܔ++IvkbmW~.T,ZF aG"Яw9  b#|1kсʎd}?`w>9BLRb{Uda"d&#NJ/Et-֍Ys3G̓b#;:z Qh>6Yxhm ![PqzH$vcP5 y7 DbNxʈ7420:F  31e喂 ݁j8x1ү>]}7 EUVBf̳RʣSL'b|^'!.,f r]HhݹNoQߔ8b/p(0S<{weP89>_^ 2 \cu%pt< x:;ÇϿ%Xx0 2ͼ{يŽA]|snhail]"{g9RlG!Fپ `!K}YYt>;=%mӅ!7,@d:;`6b8c 3< d@hm F8Gr[X΀.r(cPq3"zg4uN15H~ '\}Wr]PZCiV D(*s=W˯U,qUWXx%E|b\)8l&uDoňCmihL"j'91Lv _G nж~Ɍ8EI~bC6pv7k+6:v`ۊ|aCǍ rZ>7]FmR+Xհ8E)(<*oӸ+Mj%2tn:Xo犓)SDS~i(|g9cxIͦ|@Fb7efA,,0c@oAWX?TH @א%D`=;D,{Mm0к ZAV+ @凘.ϑnPT5Ľq x/ݽg9<>%nԮfG{ a76Ҕ+Nl62v-8X1}c}m>si7}K.kcn'n^~˱-zAY,"}ylNϮ5@p@&2os e|cvqD,xGvmhDFuub*fP7)&WwLOa"}qGlxIm1VVϊI ɿ\32| r>x"4FtK #i7{7]IPiܢ6!h} QCogWFV=a>7nA2 .`>"#GDoD?oMg ]7t/ '* tK۰ŊYFv.dMcORlR&) `W%%"hPǪKW/'/nO5#`.90h܆ Ή`ׄN&emh,͘Y3@|fp\GIxdT6jp"]':o0s4^T}JZȜy+u#p^A0l ߽-'gDe+܂k|omK2)kkgMi<dgAq+]R师M3V) E N;X5屒gCǔḘK{B40u )5&xRr`U^JlڭL&95;!725`ge^΁C)ˋ2cFk~oi CLxV< >r0j!a,~FdǯsjIKF_2g6, Pϖܫb4GCӂ{2"/+Q?`*w'tSFO~FZø0Lrnz4(]8yxN_%˲qCPha )g8jh9(ؗ:a}~a&yZ[0鰧 Yp3u9)O90Jyϰ{#`Mg'tz^F% ]m5|ѽksF'5<,6YQЌgg2JE+RI93ر6ʙܰDP,hс)BR}F`Q`^wbt2T ؛{=?n6kC5Tk.2*]=xPvPӐH'Y=u!t.a_a;8qG1[ Nk@g z;~V<[:l)INj5 (GluOyDΌ8}JG`RS=f6"`$gzVk'>Q5$AYN7־csA&mfsG?5+e*X@lZ#N'&Ss%o C(W.0U&Z$gզYXKa 9uMUKݢVuϬ`R@ ᪦ml6%ME؉P٥à0}bU_<:Dب3ć}yD4v]SY gUHFH #r""x/{W0jvd#Z-dU^*~6tOW<v]K"~,W&z]~^,QŸg ǃpXN;ώ܆?/썜Ul(>*N%埪xѠ"3#Vr ' I8e~ #Y5[N}NQ[U_3:TQa۵TqS.KK;?p`4b魵g=K͔0Q Ny,hIG93D% @(icOڔZ? tivΩg `GMYJr=9wȌsCe\`5lyM]iΣ33,5tywT+ [HQ Z=e `TNWn30\^.L_mU_ `:B ,{c2};g.Xt,&v?覺D0=˚m4a:-qX){ɁCj}uz6"6D45`1kLrG+DJ%Vz=-3t/Έ=J: RF'Ƕ0َ+ls8gg<l@ -]APyHqwmqNz(8$h]Om!l1cp`4=xNyퟒ]/2|402ơhH ?_VFDtZr\@?ވ~U:+W;6C_xG-aʛFW<}fj_$X 4J3m:*Z[LeR'h  A< aA ѽ.J؝ P8pQ-t>(2pXo9w2}15 lVwtbܔֿV * l3Ŷ+]]lDTm2 lCkdy~/X>:M3'W5ii~0яN]/L=1Oϕ?-haЯw?@?2 3DG;#rEB`0E ]Cmv^cGL#(-#+PO8eY`Fxj뻭aG `=#!gX^ad8&#xk׶87Z|*b4xt2qi6p*y Q'큵ٹQ饻G[usGe30 G8=9%؏=J]GBp_Jig M:Z hs:Gɬ}1䛥,5.Ͼd`_sUnj7W ߘli(&xSy5~b SY+Ww邞5>?{ϾG01Qf)E#I1ݾ-e"h"i 8㕌d"_F'@D3R(z$ՙRSò=fYp9w)Ϲ: md/ 銚yv|͖À>_]*/qKÀCcjJVZxK{:wO о&'3k"CV\GW F2C:X+EdfއT+/[˕ik!~~.t`ş%gXj S;[lnv5JU!l|CUEAmd<0RfQ94S}-ˆy (mOdgcϞ3@:Z{玭tOeSFlbr!3#ދY*3NqǃܮNw&2u` a]J=Ɔ?&Ѵle76 TH"Nk_Bj~tRYj[@w:cF";&%lҬEqH[|  Fjof! am ʾ|uTl1Le\<]~0}ybZR Nm!F}Y0nwDJb(>%w{ jX=*2AtPv5dYC(yVh4 j"V2Q3zެ,i1H>b=u@Fr@"(:ץDE:wz԰GM 1ᄝ!x36; AP c1#@L,'*.1 }vEPޏSl`%#( 3#.uGJW/0"wjjgc[uUh2o2g;k@!(3`l OL.Dm GQrڽn1;HKBVCa3P8r.z軄ހK:,1s,1L;)}=hOxI$ҝS b:xD#m҃bNc$y8N^gbYwpX`DjSv!7m:$~бW5TyX 6쌾g0rbER:{PS^s}-5Xu[<*:Dsm3-i҇}_Y5J[PN)F̞~)&T#+/p!nr1Z}k2 k14xhE5roٌs{2{N~W[{$.8JXr%b)-V N8;zhȋ<٣~{OMڍGɻkvj|; Xs}Of3 = !i\=o |Lu31auu?2# =HwyJՏzk8|c.k@u;uq-+̂CРneI V0ZpTsׯKH][^ 1:k__(E-vg7 L`@( SRJхW< bQ;ԄgDW,qzôIENDB`PK܉8ލ;>docs/index.html Documentation Overview // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Documentation Overview

Welcome to the Werkzeug documentation.

Reference

Documentation regarding the Werkzeug modules:

General Development Information

This part of the documentation mainly explains how to develop WSGI applications. This is also interesting if you don’t want to use Werkzeug but other WSGI utilities; the ideas are the same.

  • How WSGI Works — short introduction to WSGI and Werkzeug.
  • Organizing Code — gives you an idea how you can organize your code when using Werkzeug.
  • Other Libraries — links to other libraries you can use with Werkzeug.
  • Deploying WSGI Applications — ready for production? This page covers all the details you have to know to deploy your application on various webservers.
PK܉8KKdocs/installation.html Installation // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Installation

Werkzeug requires at least Python 2.3 to work correctly.

Installing a released version

As a Python egg (via easy_install)

You can install the most recent Werkzeug version using easy_install:

sudo easy_install Werkzeug

This will install a Werkzeug egg in your Python installation’s site-packages directory.

From the tarball release

  1. Download the most recent tarball from the download page.
  2. Unpack the tarball.
  3. sudo python setup.py install

Note that the last command will automatically download and install setuptools if you don’t already have it installed. This requires a working Internet connection.

This will install Werkzeug into your Python installation’s site-packages directory.

Installing the development version

If you want to play around with the code

  1. Install Mercurial
  2. hg clone http://dev.pocoo.org/hg/werkzeug-main werkzeug
  3. cd werkzeug
  4. ln -s werkzeug /usr/lib/python2.X/site-packages

As an alternative to step 4 you can also do python setup.py develop which will install the package via setuptools in development mode.

If you just want the latest features and use them

sudo easy_install Werkzeug==dev

This will install a Werkzeug egg containing the latest mercurial tip in your Python installation’s site-packages directory. Every time the command is run, the sources are updated from the mercurial repository.

Documentation

The egg builds include a documentation which is available in the docs folder of the egg. If you’re running Linux you will find the documentation here:

file:///usr/lib/python2.X/site-packages/Werkzeug-Y.Z-py2.X.egg/docs/index.html

where X, Y and Z must be replaced by the Python/Werkzeug version number.

PK܉8sMdocs/libraries.html Other Libraries // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Other Libraries

Because Werkzeug is just a thin layer over WSGI, it’s very easy to use WSGI middlewares together with Werkzeug-powered applications. It’s also possible to just use small parts of Werkzeug like the URL mapper etc. with complete different implementations or frameworks.

Here a small list of libraries you may want to try out.

Database Layers

If you want to use relational databases in your application.

SQLAlchemy
SQLAlchemy is a great database layer and object relational mapper that lets you construct SQL queries using Python expressions. It also provides connection pools and plays nicely with the WSGI standard.
Elixir
Elixir is a declarative layer on top of the SQLAlchemy library. It is a fairly thin wrapper, which provides the ability to create simple Python classes that map directly to relational database tables (this pattern is often referred to as the Active Record design pattern), providing many of the benefits of traditional databases without losing the convenience of Python objects.
Storm
Storm is an object-relational mapper (ORM) for Python developed at Canonical. It has been in development for more than a year for use in Canonical projects such as Launchpad, and has been released as an open-source product.

Template Engines

Bigger applications deserve something better than minitmpl :)

Genshi
If you like XML template engines, check out Genshi. Ass-kicking template engine, but unfortunately not the fastest.
Mako
The fastest designer friendly template engine for Python. Similar to ERB http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ in terms of syntax, but with a powerful template inheritance system and multiple namespaces.
Jinja
Sandboxed, Django-/Smarty-like template engine, but with inline expressions that let you execute a subset of Python expressions in your templates.

Form Validation

Here some form validation packages for WSGI applications:

What The Forms
WTForms is a HTTP/HTML forms handling library, written in Python. It handles definition, validation and rendering in a flexible and i18n friendly way. It heavily reduces boilerplate and is completely unicode aware.
Newforms Extracted
This is a project to extract Django’s newforms and make that package usable by other projects, since Django doesn’t seem interested in making this code framework independent.
FormEncode
FormEncode is a validation and form generation package. The validation can be used separately from the form generation. The validation works on compound data structures, with all parts being nestable. It is separate from HTTP or any other input mechanism.

Tools and Utilities

Something’s missing? Check here first:

wsgitools
Various small WSGI utilities like a minimal traceback or auth middleware. It also includes an SCGI server.
Paste
Many tools for use with WSGI: dispatching, composition, simple applications (e.g., file serving), and more.
Routes
A port of the Rails URL mapping system.
Python OpenID
The OpenID library with batteries included.
AuthKit
WSGI Authentication and Authorization Tools. Built in support for HTTP basic, HTTP digest, form, cookie and OpenID authentication methods plus others.

You can find a more complete list on the wsgi.org webpage.

PK܉82{''docs/local.html Context Locals // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Context Locals

Table of Contents

Sooner or later you have some things you want to have in every single view or helper function or whatever. In PHP the way to go are global variables. However that is not possible in WSGI applications without a major drawback: As soon as you operate on the global namespace your application is not thread safe any longer.

The python standard library comes with a utility called “thread locals”. A thread local is a global object where you can put stuff on and get back later in a thread safe way. That means whenever you set or get an object to / from a thread local object the thread local object checks in which thread you are and delivers the correct value.

This however has a few disadvantages. For example beside threads there are other ways to handle concurrency in Python. A very popular approach are greenlets. Also, whether every request gets its own thread is not guaranteed in WSGI. It could be that a request is reusing a thread from before and data is left in the thread local object.

Nutshell

Here a simple example how you can use werkzeug.local:

from werkzeug import Local, LocalManager

local = Local()
local_manager = LocalManager([local])

def application(environ, start_response):
    local.request = request = Request(environ)
    ...

application = local_manager.make_middleware(application)

Now what this code does is binding request to local.request. Every other piece of code executed after this assignment in the same context can safely access local.request and will get the same request object. The make_middleware method on the local manager ensures that everything is cleaned up after the request.

The same context means the same greenlet (if you’re using greenlets) in the same thread and same process.

If a request object is not yet set on the local object and you try to access it you will get an AttributeError. You can use getattr to avoid that:

def get_request():
    return getattr(local, 'request', None)

This will try to get the request or return None if the request is not (yet?) available.

Note that local objects cannot manage themselves, for that you need a local manager. You can pass a local manager multiple locals or add additionals later by appending them to manager.locals and everytime the manager cleans up it will clean up all the data left in the locals for this context.

Objects

class LocalManager

Local objects cannot manage themselves. For that you need a local manager. You can pass a local manager multiple locals or add them later by appending them to manager.locals. Everytime the manager cleans up it, will clean up all the data left in the locals for this context.

cleanup ()
Manually clean up the data in the locals for this context. Call this at the end of the request or use make_middleware().
make_middleware (app)
Wrap a WSGI application so that cleaning up happens after request end.
middleware (func)

Like make_middleware but for decorating functions.

Example usage:

@manager.middleware
def application(environ, start_response):
    ...

The difference to make_middleware is that the function passed will have all the arguments copied from the inner application (name, docstring, module).

get_ident ()
Return the context identifier the local objects use internally for this context. You cannot override this method to change the behavior but use it to link other context local objects (such as SQLAlchemy’s scoped sessions) to the Werkzeug locals.
class LocalProxy

Acts as a proxy for a werkzeug local. Forwards all operations to a proxied object. The only operations not supported for forwarding are right handed operands and any kind of assignment.

Example usage:

from werkzeug import Local
l = Local()
request = l('request')
user = l('user')

Whenever something is bound to l.user / l.request the proxy objects will forward all operations. If no object is bound a RuntimeError will be raised.

_get_current_object ()
Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context.

Keep in mind that repr() is also forwarded, so if you want to find out if you are dealing with a proxy you can do an isinstance() check:

>>> from werkzeug import LocalProxy
>>> isinstance(request, LocalProxy)
True

You can also create proxy objects by hand:

from werkzeug import Local, LocalProxy
local = Local()
request = LocalProxy(local, 'request')
PK܉8Ht00docs/organizing.html Organizing Code // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Organizing Code

Werkzeug doesn’t limit you in what you do; thus there is no general rule where to locate your modules, what to do with your data etc.

However, there is a general pattern which is used in many web applications, the “Model View Controller” pattern (MVC), which makes sense for many applications.

The idea is that you have “models” in one file, the “views” in another and the “controllers” in the third file or folder. The Django framework came up with a new idea of naming those components which is called “Model Template View” (MTV) which probably makes more sense for web applications although it means nearly the same. We will use the latter naming in this file.

So here is what those terms mean:

Model
A model is an abstraction of your data. For example if you have a database you can use the excellent SQLAlchemy library that maps database tables to classes or just provides a thin layer that lets you write your own classes.
Template

The templates contain the actual HTML markup with placeholders for data passed to them. Although there is a minimal template language in the Werkzeug package we don’t recommend using it for larger applications. There are many good and powerful template engines out there that support debugging, template inheritance, XML output etc.

The integrated templating language is a good idea if you have one or two templates and don’t want to install one of the big template engines, but it is not suitable for more complex tasks!

View
The view is the code that processes data, connects the model with templates, and then outputs the result. This is the part where Werkzeug helps you.

Entry Point

There should be one central file that provides the WSGI application and dispatches the requests. It’s also a good idea to assemble the final URL map there if you use the Werkzeug routing system.

It’s also a good idea to call the file something like main.py or application.py and locate it in the root of the package. This is what it could look like:

from werkzeug import Request, Response, import_string
from werkzeug.exceptions import HTTPException
from werkzeug.routing import RequestRedirect
from mypackage.urls import url_map

def application(environ, start_response):
    url_adapter = url_map.bind_to_environ(environ)
    req = Request(environ)
    try:
        endpoint, values = url_adapter.match()
        view = import_string('mypackage.views.' + endpoint)
        resp = view(req, **values)
    except (RequestRedirect, HTTPException), e:
        resp = e
    return resp(environ, start_response)

You can even further simplify the dispatching by using urls.dispatch as explained in the routing documentation.

This will look for the controller functions in mypackage.views.module. If the URL is configured with an endpoint of 'static.index', the module mypackage.views.static is loaded and index(req) is called.

The URL rule parameters are passed to the function as keyword arguments then.

Note: This is just one idea of how things can look like. This doesn’t necessarily have to represent your application. You can, for example, save the request object in a thread-local storage or have your views as methods of a controller class you instantiate with the request as argument for each incoming request. You can also use other request objects or no request object at all etc.

Utils

If we continue the example above we need an utils module with the request and response objects. This would also contain other utilities and a bridge to a template engine. In this example we will use the Jinja template engine - basically because it’s something we use, too - but you can of course use any template engine:

from jinja import Environment, PackageLoader
from werkzeug import Response

env = Environment(loader=PackageLoader('mypackage', 'templates'))

class TemplateResponse(Response):

    def __init__(self, template_name, **values):
        tmpl = env.get_template(template_name)
        output = tmpl.render(values)
        Response.__init__(self, output, mimetype='text/html')

Note: Templates in this example are saved in the templates folder inside the mypackage package. The way template loading and rendering works depends on the template engine, so have a look at it’s documentation regarding that.

We just subclass request and response classes for our needs and provide a second response subclass that is used to render templates. It’s used in the example view below.

URLs

Because we use the Werkzeug URL routing system in this example and the URLs are stored in urls.py, we need that file:

from werkzeug.routing import Map, Rule

url_map = Map([
    Rule('/', 'static.index')
])

This is just one small URL rule for one view.

View

Here is the view defined above. It must be saved in myprojects/views/static.py as defined above:

from myproject.utils import TemplateResponse

def index(req):
    return TemplateResponse('index.html', title='Welcome')

Models and Templates

Models and templates are out of the scope for this documentation, but you should have gotten an idea on how you can organize your WSGI application built with the help of Werkzeug.

PK܉8_Ldocs/print.cssdiv.header, ul.navigation, div.aside, a.underthehood { display: none; } div.page, div.withaside, div.contents { margin: 0; padding: 0; width: auto; float: none; border: none; } PK܉8)9docs/pygments.css.syntax { background: #f0f3f3; } .syntax .c { color: #0099FF; font-style: italic } /* Comment */ .syntax .err { color: #AA0000; background-color: #FFAAAA } /* Error */ .syntax .k { color: #006699; font-weight: bold } /* Keyword */ .syntax .o { color: #555555 } /* Operator */ .syntax .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ .syntax .cp { color: #009999 } /* Comment.Preproc */ .syntax .c1 { color: #0099FF; font-style: italic } /* Comment.Single */ .syntax .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */ .syntax .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ .syntax .ge { font-style: italic } /* Generic.Emph */ .syntax .gr { color: #FF0000 } /* Generic.Error */ .syntax .gh { color: #003300; font-weight: bold } /* Generic.Heading */ .syntax .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ .syntax .go { color: #AAAAAA } /* Generic.Output */ .syntax .gp { color: #000099; font-weight: bold } /* Generic.Prompt */ .syntax .gs { font-weight: bold } /* Generic.Strong */ .syntax .gu { color: #003300; font-weight: bold } /* Generic.Subheading */ .syntax .gt { color: #99CC66 } /* Generic.Traceback */ .syntax .kc { color: #006699; font-weight: bold } /* Keyword.Constant */ .syntax .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */ .syntax .kp { color: #006699 } /* Keyword.Pseudo */ .syntax .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */ .syntax .kt { color: #007788; font-weight: bold } /* Keyword.Type */ .syntax .m { color: #FF6600 } /* Literal.Number */ .syntax .s { color: #CC3300 } /* Literal.String */ .syntax .na { color: #330099 } /* Name.Attribute */ .syntax .nb { color: #336666 } /* Name.Builtin */ .syntax .nc { color: #00AA88; font-weight: bold } /* Name.Class */ .syntax .no { color: #336600 } /* Name.Constant */ .syntax .nd { color: #9999FF } /* Name.Decorator */ .syntax .ni { color: #999999; font-weight: bold } /* Name.Entity */ .syntax .ne { color: #CC0000; font-weight: bold } /* Name.Exception */ .syntax .nf { color: #CC00FF } /* Name.Function */ .syntax .nl { color: #9999FF } /* Name.Label */ .syntax .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */ .syntax .nt { color: #330099; font-weight: bold } /* Name.Tag */ .syntax .nv { color: #003333 } /* Name.Variable */ .syntax .ow { color: #000000; font-weight: bold } /* Operator.Word */ .syntax .w { color: #bbbbbb } /* Text.Whitespace */ .syntax .mf { color: #FF6600 } /* Literal.Number.Float */ .syntax .mh { color: #FF6600 } /* Literal.Number.Hex */ .syntax .mi { color: #FF6600 } /* Literal.Number.Integer */ .syntax .mo { color: #FF6600 } /* Literal.Number.Oct */ .syntax .sb { color: #CC3300 } /* Literal.String.Backtick */ .syntax .sc { color: #CC3300 } /* Literal.String.Char */ .syntax .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ .syntax .s2 { color: #CC3300 } /* Literal.String.Double */ .syntax .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */ .syntax .sh { color: #CC3300 } /* Literal.String.Heredoc */ .syntax .si { color: #AA0000 } /* Literal.String.Interpol */ .syntax .sx { color: #CC3300 } /* Literal.String.Other */ .syntax .sr { color: #33AAAA } /* Literal.String.Regex */ .syntax .s1 { color: #CC3300 } /* Literal.String.Single */ .syntax .ss { color: #FFCC33 } /* Literal.String.Symbol */ .syntax .bp { color: #336666 } /* Name.Builtin.Pseudo */ .syntax .vc { color: #003333 } /* Name.Variable.Class */ .syntax .vg { color: #003333 } /* Name.Variable.Global */ .syntax .vi { color: #003333 } /* Name.Variable.Instance */ .syntax .il { color: #FF6600 } /* Literal.Number.Integer.Long */PK܉8tP docs/READMEThe documentation here is updated before a release from the wzweb application. I know it's weird but it works for the moment ;) PK܉8modocs/routing.html Routing System // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Routing System

When it comes to combining multiple controller or view functions (however you want to call them), you need a dispatcher. A simple way would be applying regular expression tests on PATH_INFO and call registered callback functions that return the value.

Werkzeug provides a much more powerful system, similar to Routes. All the objects mentioned on this page must be imported from werkzeug.routing, not from werkzeug!

Quickstart

Here a simple example which could be the URL definition for a blog:

from werkzeug.routing import Map, Rule, NotFound, RequestRedirect

url_map = Map([
    Rule('/', endpoint='blog/index'),
    Rule('/<int:year>/', endpoint='blog/archive'),
    Rule('/<int:year>/<int:month>/', endpoint='blog/archive'),
    Rule('/<int:year>/<int:month>/<int:day>/', endpoint='blog/archive'),
    Rule('/<int:year>/<int:month>/<int:day>/<slug>',
         endpoint='blog/show_post'),
    Rule('/about', endpoint='blog/about_me'),
    Rule('/feeds/', endpoint='blog/feeds'),
    Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
])

def application(environ, start_response):
    urls = url_map.bind_to_environ(environ)
    try:
        endpoint, args = urls.match()
    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)]

So what does that do? First of all we create a new Map which stores a bunch of URL rules. Then we pass it a list of Rule objects.

Each Rule object is instantiated with a string that represents a rule and an endpoint which will be the alias for what view the rule represents. Multiple rules can have the same endpoint but should have different arguments to allow URL construction.

The format for the URL rules is straightforward, but explained in detail below.

Inside the WSGI application we bind the url_map to the current request which will return a new MapAdapter. This url_map adapter can then be used to match or build domains for the current request.

The match method can then either return a tuple in the form (endpoint, args) or raise one of the three exceptions NotFound, MethodNotAllowed, or RequestRedirect. For more details about those exceptions have a look at the documentation of the match method.

Rule Format

Rule strings basically are just normal URL paths with placeholders in the format <converter(arguments):name>, where converter and the arguments are optional. If no converter is defined, the default converter is used (which means string in the normal configuration).

URL rules that end with a slash are branch URLs, others are leaves. If you have strict_slashes enabled (which is the default), all branch URLs that are visited without a trailing slash will trigger a redirect to the same URL with that slash appended.

The list of converters can be extended, the default converters are explained below.

Builtin Converters

Here a list of converters that come with Werkzeug:

string

This converter is the default converter and accepts any string but only one one path segment. Thus the string can not include a slash.

Supported arguments:

  • minlength - the minimum length of the string. must be greater than 1.
  • maxlength - the maximum length of the string.
  • length - the exact length of that string.
path
Like the default string converter, but it also matches slashes.
any

Matches one of the items provided. Items can either be Python identifiers or unicode strings:

Rule('/<any(about, help, imprint, u"class"):page_name>')
int

This converter only accepts integer values:

Rule('/page/<int:page>')

Supported arguments:

  • fixed_digits - the number of fixed digits in the URL. If you set this to 4 for example, the application will only match if the url looks like /0001/. The default is variable length.
  • min - the minimal value.
  • max - the maximal value.
float

This converter only accepts floating point values:

Rule('/probability/<float:probability>')

Supported arguments:

  • min - the minimal value.
  • max - the maximal value.

Important

Werkzeug evaluates converter arguments as if they are Python method calls. Thus, you should never create rules from user submitted data since they could insert arbitrary Python code in the parameters part.

As a matter of fact this is a legal definition and sets fixed_digits to 2:

url_map = Map([
    Rule('/picture/<int(fixed_digits=1 + 1):id>.png',
         endpoint='view_image')
])

However, evaluating Python expressions is currently an implementation detail and might be unavailable in the future.

Maps, Rules and Adapters

class Map

The map class stores all the URL rules and some configuration parameters. Some of the configuration values are only stored on the Map instance since those affect all rules, others are just defaults and can be overridden for each rule. Note that you have to specify all arguments beside the rules as keywords arguments!

__init__ (rules=None, default_subdomain='', charset='utf-8', strict_slashes=True, redirect_defaults=True, converters=None)

Initializes the new URL map.

Parameters

rules: sequence of url rules for this map.

default_subdomain: The default subdomain for rules without a subdomain defined.

charset: charset of the url. defaults to "utf-8"

strict_slashes: Take care of trailing slashes.

redirect_defaults: This will redirect to the default rule if it wasn’t visited that way. This helps creating unique URLs.

converters: A dict of converters that adds additional converters to the list of converters. If you redefine one converter this will override the original one.

is_endpoint_expecting (endpoint, *arguments)
Iterate over all rules and check if the endpoint expects the arguments provided. This is for example useful if you have some URLs that expect a language code and others that do not and you want to wrap the builder a bit so that the current language code is automatically added if not provided but endpoints expect it.
iter_rules (endpoint=None)
Iterate over all rules or the rules of an endpoint.
add (rulefactory)
Add a new rule or factory to the map and bind it. Requires that the rule is not bound to another map.
bind (server_name, script_name=None, subdomain=None, url_scheme='http', default_method='GET', path_info=None)

Return a new MapAdapter with the details specified to the call. Note that script_name will default to '/' if not further specified or None. The server_name at least is a requirement because the HTTP RFC requires absolute URLs for redirects and so all redirect exceptions raised by Werkzeug will contain the full canonical URL.

If no path_info is passed to match() it will use the default path info passed to bind. While this doesn’t really make sense for manual bind calls, it’s useful if you bind a map to a WSGI environment which already contains the path info.

subdomain will default to the default_subdomain for this map if no defined. If there is no default_subdomain you cannot use the subdomain feature.

bind_to_environ (environ, server_name=None, subdomain=None, calculate_subdomain=False)

Like bind but you can pass it an WSGI environment and it will fetch the information from that directory. Note that because of limitations in the protocol there is no way to get the current subdomain and real server_name from the environment. If you don’t provide it, Werkzeug will use SERVER_NAME and SERVER_PORT (or HTTP_HOST if provided) as used server_name with disabled subdomain feature.

If subdomain is None but an environment and a server name is provided it will calculate the current subdomain automatically. Example: server_name is 'example.com' and the SERVER_NAME in the wsgi environ is 'staging.dev.example.com' the calculated subdomain will be 'staging.dev'.

If the object passed as environ as an environ attribute, the value of this attribute is used instead. This allows you to pass request objects. Additionally PATH_INFO added as a default ot the MapAdapter so that you don’t have to pass the path info to the match method.

class MapAdapter

Retured by Map.bind or Map.bind_to_environ and does the URL matching and building based on runtime information.

match (path_info=None, method=None)

The usage is simple: you just pass the match method the current path info as well as the method (which defaults to GET). The following things can then happen:

  • you receive a NotFound exception that indicates that no URL is matching. A NotFound exception is also a WSGI application you can call to get a default page not found page (happens to be the same object as werkzeug.exceptions.NotFound)
  • you receive a MethodNotAllowed exception that indicates that there is a match for this URL but non for the current request method. This is useful for RESTful applications.
  • you receive a RequestRedirect exception with a new_url attribute. This exception is used to notify you about a request Werkzeug requests by your WSGI application. This is for example the case if you request /foo although the correct URL is /foo/ You can use the RequestRedirect instance as response-like object similar to all other subclasses of HTTPException.
  • you get a tuple in the form (endpoint, arguments) when there is a match.

If the path info is not passed to the match method the default path info of the map is used (defaults to the root URL if not defined explicitly).

All of the exceptions raised are subclasses of HTTPException so they can be used as WSGI responses. The will all render generic error or redirect pages.

Here is a small example for matching:

>>> from werkzeug.routing import Map, Rule
>>> m = Map([
...     Rule('/', endpoint='index'),
...     Rule('/downloads/', endpoint='downloads/index'),
...     Rule('/downloads/<int:id>', endpoint='downloads/show')
... ])
>>> urls = m.bind("example.com", "/")
>>> urls.match("/", "GET")
('index', {})
>>> urls.match("/downloads/42")
('downloads/show', {'id': 42})

And here is what happens on redirect and missing URLs:

>>> urls.match("/downloads")
Traceback (most recent call last):
  ...
werkzeug.routing.RequestRedirect: http://example.com/downloads/
>>> urls.match("/missing")
Traceback (most recent call last):
  ...
werkzeug.routing.NotFound: /missing
test (path_info=None, method=None)
Test if a rule would match. Works like match but returns True if the URL matches, or False if it does not exist.
dispatch (view_func, path_info=None, method=None, catch_http_exceptions=False)

Does the complete dispatching process. view_func is called with the endpoint and a dict with the values for the view. It should look up the view function, call it, and return a response object or WSGI application. http exceptions are not catched by default so that applications can display nicer error messages by just catching them by hand. If you want to stick with the default error messages you can pass it catch_http_exceptions=True and it will catch the http exceptions.

Here a small example for the dispatch usage:

from werkzeug import Request, Response, responder
from werkzeug.routing import Map, Rule

def on_index(request):
    return Response('Hello from the index')

url_map = Map([Rule('/', endpoint='index')])
views = {'index': on_index}

@responder
def application(environ, start_response):
    request = Request(environ)
    urls = url_map.bind_to_environ(environ)
    return urls.dispatch(lambda e, v: views[e](request, **v),
                         catch_http_exceptions=True)

Keep in mind that this method might return exception objects too, so use Response.force_type to get a response object.

build (endpoint, values=None, method=None, force_external=False)

Building URLs works pretty much the other way round. Instead of match you call build and pass it the endpoint and a dict of arguments for the placeholders.

The build function also accepts an argument called force_external which, if you set it to True will force external URLs. Per default external URLs (include the server name) will only be used if the target URL is on a different subdomain.

With the same map as in the example above this code generates some target URLs:

>>> urls.build("index", {})
'/'
>>> urls.build("downloads/show", {'id': 42})
'/downloads/42'
>>> urls.build("downloads/show", {'id': 42}, force_external=True)
'http://example.com/downloads/42'

Because URLs cannot contain non ASCII data you will always get bytestrings back. Non ASCII characters are urlencoded with the charset defined on the map instance.

Additional values are converted to unicode and appended to the URL as URL querystring parameters:

>>> urls.build("index", {'q': 'My Searchstring'})
'/?q=My+Searchstring'

If a rule does not exist when building a BuildError exception is raised.

The build method accepts an argument called method which allows you to specify the method you want to have an URL builded for if you have different methods for the same endpoint specified.

class Rule

A Rule represents one URL pattern. There are some options for Rule that change the way it behaves and are passed to the Rule constructor. Note that beside the rule-string all arguments must be keyword arguments in order to not break the application on Werkzeug upgrades.

string

Rule strings basically are just normal URL paths with placeholders in the format <converter(arguments):name> where the converter and the arguments are optional. If no converter is defined the default converter is used which means string in the normal configuration.

URL rules that end with a slash are branch URLs, others are leaves. If you have strict_slashes enabled (which is the default), all branch URLs that are matched without a trailing slash will trigger a redirect to the same URL with the missing slash appended.

The converters are defined on the Map.

endpoint
The endpoint for this rule. This can be anything. A reference to a function, a string, a number etc. The preferred way is using a string as because the endpoint is used for URL generation.
defaults

An optional dict with defaults for other rules with the same endpoint. This is a bit tricky but useful if you want to have unique URLs:

url_map = Map([
    Rule('/all/', defaults={'page': 1}, endpoint='all_entries'),
    Rule('/all/page/<int:page>', endpoint='all_entries')
])

If a user now visits http://example.com/all/page/1 he will be redirected to http://example.com/all/. If redirect_defaults is disabled on the Map instance this will only affect the URL generation.

subdomain

The subdomain rule string for this rule. If not specified the rule only matches for the default_subdomain of the map. If the map is not bound to a subdomain this feature is disabled.

Can be useful if you want to have user profiles on different subdomains and all subdomains are forwarded to your application:

url_map = Map([
    Rule('/', subdomain='<username>', endpoint='user/homepage'),
    Rule('/stats', subdomain='<username>', endpoint='user/stats')
])
methods
A sequence of http methods this rule applies to. If not specified, all methods are allowed. For example this can be useful if you want different endpoints for POST and GET. If methods are defined and the path matches but the method matched against is not in this list or in the list of another rule for that path the error raised is of the type MethodNotAllowed rather than NotFound.
strict_slashes
Override the Map setting for strict_slashes only for this rule. If not specified the Map setting is used.
build_only
Set this to true and the rule will never match but will create a URL that can be build. This is useful if you have resources on a subdomain or folder that are not handled by the WSGI application (like static data)
redirect_to

If given this must be either a string or callable. In case of a callable it’s called with the url adapter that triggered the match and the values of the URL as keyword arguments and has to return the target for the redirect, otherwise it has to be a string with placeholders in rule syntax:

def foo_with_slug(adapter, id):
    # ask the database for the slug for the old id.  this of
    # course has nothing to do with werkzeug.
    return 'foo/' + Foo.get_slug_for_id(id)

url_map = Map([
    Rule('/foo/<slug>', endpoint='foo'),
    Rule('/some/old/url/<slug>', redirect_to='foo/<slug>'),
    Rule('/other/old/url/<int:id>', redirect_to=foo_with_slug)
])

When the rule is matched the routing system will raise a RequestRedirect exception with the target for the redirect.

Keep in mind that the URL will be joined against the URL root of the script so don’t use a leading slash on the target URL unless you really mean root of that domain.

empty ()
Return an unbound copy of this rule. This can be useful if you want to reuse an already bound URL for another map.
bind (map)
Bind the url to a map and create a regular expression based on the information from the rule itself and the defaults from the map.

Rule Factories

class RuleFactory

As soon as you have more complex URL setups it’s a good idea to use rule factories to avoid repetitive tasks. Some of them are builtin, others can be added by subclassing RuleFactory and overriding get_rules.

get_rules (map)
Subclasses of RuleFactory have to override this method and return an iterable of rules.
class Subdomain

All URLs provided by this factory have the subdomain set to a specific domain. For example if you want to use the subdomain for the current language this can be a good setup:

url_map = Map([
    Rule('/', endpoint='#select_language'),
    Subdomain('<string(length=2):lang_code>', [
        Rule('/', endpoint='index'),
        Rule('/about', endpoint='about'),
        Rule('/help', endpoint='help')
    ])
])

All the rules except of the '#select_language' endpoint will now listen on a two letter long subdomain that helds the language code for the current request.

class Submount

Like Subdomain but prefixes the URL rule with a given string:

url_map = Map([
    Rule('/', endpoint='index'),
    Submount('/blog', [
        Rule('/', endpoint='blog/index'),
        Rule('/entry/<entry_slug>', endpoint='blog/show')
    ])
])

Now the rule 'blog/show' matches /blog/entry/<entry_slug>.

class EndpointPrefix

Prefixes all endpoints (which must be strings for this factory) with another string. This can be useful for sub applications:

url_map = Map([
    Rule('/', endpoint='index'),
    EndpointPrefix('blog/', [Submount('/blog', [
        Rule('/', endpoint='index'),
        Rule('/entry/<entry_slug>', endpoint='show')
    ])])
])

Rule Templates

class RuleTemplate

Returns copies of the rules wrapped and expands string templates in the endpoint, rule, defaults or subdomain sections.

Here a small example for such a rule template:

from werkzeug.routing import Map, Rule, RuleTemplate

resource = RuleTemplate([
    Rule('/$name/', endpoint='$name.list'),
    Rule('/$name/<int:id>', endpoint='$name.show')
])

url_map = Map([resource(name='user'), resource(name='page')])

When a rule template is called the keyword arguments are used to replace the placeholders in all the string parameters.

Custom Converters

You can easily add custom converters. The only thing you have to do is to subclass BaseConverter and pass that new converter to the url_map. A converter has to provide two public methods: to_python and to_url, as well as a member that represents a regular expression. Here is a small example:

from random import randrange
from werkzeug.routing import Rule, Map, BaseConverter, ValidationError

class BooleanConverter(BaseConverter):

    def __init__(self, url_map, randomify=False):
        super(BooleanConverter, self).__init__(url_map)
        self.randomify = randomify
        self.regex = '(?:yes|no|maybe)'

    def to_python(self, value):
        if value == 'maybe':
            if self.randomify:
                return not randrange(2)
            raise ValidationError()
        return value == 'yes'

    def to_url(self, value):
        return value and 'yes' or 'no'

url_map = Map([
    Rule('/vote/<bool:werkzeug_rocks>', endpoint='vote'),
    Rule('/vote/<bool(randomify=True):foo>', endpoint='foo')
], converters={'bool': BooleanConverter})

If you want that converter to be the default converter, name it 'default'.

PK܉88v v docs/script.html Management Script Utilities // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Management Script Utilities

Most of the time you have recurring tasks while writing an application such as starting up an interactive python interpreter with some prefilled imports, starting the development server, initializing the database or something similar.

For that purpose werkzeug provides the werkzeug.script module which helps you writing such scripts.

Basic Usage

The following snippet is roughly the same in every werkzeug script:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from werkzeug import script

# actions go here

if __name__ == '__main__':
    script.run()

Starting this script now does nothing because no actions are defined. An action is a function in the same module starting with "action_" which takes a number of arguments where every argument has a default. The type of the default value specifies the type of the argument.

Arguments can then be passed by position or using --name=value from the shell.

Because a runserver and shell command is pretty common there are two factory functions that create such commands:

def make_app():
    from yourapplication import YourApplication
    return YourApplication(...)

action_runserver = script.make_runserver(make_app, use_reloader=True)
action_shell = script.make_shell(lambda: {'app': make_app()})

Using The Scripts

The script from above can be used like this from the shell now:

$ ./manage.py --help
$ ./manage.py runserver localhost 8080 --debugger --no-reloader
$ ./manage.py runserver -p 4000
$ ./manage.py shell

As you can see it’s possible to pass parameters as positional arguments or as named parameters, pretty much like Python function calls.

Writing Actions

Writing new action functions is pretty straightforward. All you have to do is to name the function action_COMMAND and it will be available as ./manage.py COMMAND. The docstring of the function is used for the help screen and all arguments must have defaults which the run function can inspect. As a matter of fact you cannot use *args or **kwargs constructs.

An additional feature is the definition of tuples as defaults. The first item in the tuple could be a short name for the command and the second the default value:

def action_add_user(username=('u', ''), password=('p', '')):
    """Docstring goes here."""
    ...

Action Discovery

Per default, the run function looks up variables in the current locals. That means if no arguments are provided, it implicitly assumes this call:

script.run(locals(), 'action_')

If you don’t want to use an action discovery, you can set the prefix to an empty string and pass a dict with functions:

script.run(dict(
    runserver=script.make_runserver(make_app, use_reloader=True),
    shell=script.make_shell(lambda: {'app': make_app()}),
    initdb=on_initdb
), '')

Example Scripts

In the Werkzeug example folder there are some ./manage-APP.py scripts using werkzeug.script.

PK܉8docs/serving.html Serving WSGI Applications // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Serving WSGI Applications

There are many ways to serve a WSGI application. While you’re developing it, you usually don’t want to have a full-blown webserver like Apache up and running, but instead a simple standalone one. With Python 2.5 and onwards, there is the wsgiref server in the standard library. If you’re using older versions of Python you can download the package from the Cheeseshop.

However, there are some caveats. Sourcecode won’t reload itself when changed, and each time you kill the server using ^C you get a KeyboardInterrupt error. While the latter is easy to solve, the first one can be a pain in the ass in some situations.

Because of that Werkzeug ships a small wrapper over wsgiref that spawns the WSGI application in a subprocess and automatically reloads the application if a module was changed.

The easiest way is creating a small start-myproject.py file that runs the application:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from werkzeug import run_simple
from myproject import make_app

app = make_app(...)
run_simple('localhost', 8080, app, use_reloader=True)

You can also pass it the extra_files keyword argument with a list of additional files (like configuration files) you want to observe.

run_simple (hostname, port, application, use_reloader=False, use_debugger=False, use_evalex=True, extra_files=None, reloader_interval=1, threaded=False, processes=1, request_handler=None)

Start an application using wsgiref and with an optional reloader. This wraps wsgiref to fix the wrong default reporting of the multithreaded WSGI variable and adds optional multithreading and fork support.

Parameters

hostname: The host for the application. eg: 'localhost'

port: The port for the server. eg: 8080

application: the WSGI application to execute

use_reloader: should the server automatically restart the python process if modules were changed?

use_debugger: should the werkzeug debugging system be used?

use_evalex: should the exception evaluation feature be enabled?

extra_files: a list of files the reloader should listen for additionally to the modules. For example configuration files.

reloader_interval: the interval for the reloader in seconds.

threaded: should the process handle each request in a separate thread?

processes: number of processes to spawn.

request_handler: optional parameter that can be used to replace the default wsgiref request handler. Have a look at the werkzeug.serving sourcecode for more details.

PK܉8Y;docs/style.cssbody { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif; font-size: 14px; letter-spacing: -0.01em; line-height: 150%; text-align: center; background-color: #AFC1C4; color: black; margin: 0; padding: 0; } a { color: #CA7900; text-decoration: none; } a:hover { color: #2491CF; } pre { font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.85em; letter-spacing: 0.015em; padding: 0.5em; border: 1px solid #ccc; background-color: #f8f8f8; } cite, code, tt { font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.95em; letter-spacing: 0.01em; font-style: normal; } tt { background-color: #f2f2f2; border-bottom: 1px solid #ddd; color: #333; } tt.func-signature { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif; font-size: 0.85em; background-color: transparent; border-bottom: none; color: #555; } dt { margin-top: 0.8em; } dd p.first { margin-top: 0; } dd p.last { margin-bottom: 0; } pre { line-height: 150%; } pre a { color: inherit; text-decoration: underline; } div.syntax { background-color: transparent; } div.page { background-color: white; border: 1px solid #aaa; width: 740px; margin: 20px auto 20px auto; text-align: left; } div.header { background-image: url(header.png); height: 100px; border-bottom: 1px solid #aaa; } div.header h1 { float: right; position: absolute; margin: -30px 0 0 585px; height: 180px; width: 180px; } div.header h1 a { display: block; background-image: url(werkzeug.png); background-repeat: no-repeat; height: 180px; width: 180px; text-decoration: none; color: white!important; } div.header span { display: none; } div.header p { background-image: url(header_invert.png); margin: 0; padding: 10px; height: 80px; color: white; display: none; } ul.navigation { background-image: url(navigation.png); height: 2em; list-style: none; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 0; padding: 0; } ul.navigation li { margin: 0; padding: 0; height: 2em; float: left; } ul.navigation li a { margin: 0; padding: 0 10px 0 10px; line-height: 1.75em; color: #EE9816; } ul.navigation li a:hover { color: #3CA8E7; } ul.navigation li.active { background-image: url(navigation_active.png); } ul.navigation li.active a { color: black; } div.body { background-color: #ffffff; background-image: url(contents.png); background-repeat: repeat-x; margin: 0 20px 0 20px; padding: 0.5em 0 20px 0; } p { margin: 0.8em 0 0.5em 0; } h2 { margin: 0; padding: 0.7em 0 0.3em 0; font-size: 1.5em; color: #11557C; } h3 { margin: 1.3em 0 0.2em 0; font-size: 1.35em; padding: 0; } h4 { margin: 1em 0 -0.3em 0; } h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { color: black!important; } h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { display: none; margin: 0 0 0 0.3em; padding: 0 0.2em 0 0.2em; color: #aaa!important; } h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { display: inline; } h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, h5 a.anchor:hover, h6 a.anchor:hover { color: #777; background-color: #eee; } table { border-collapse: collapse; margin: 0 -0.5em 0 -0.5em; } table td, table th { padding: 0.2em 0.5em 0.2em 0.5em; } div.footer { background-color: #E3EFF1; color: #86989B; padding: 3px 8px 3px 0; clear: both; font-size: 0.8em; text-align: right; } div.footer a { color: #86989B; text-decoration: underline; } div.toc { float: right; background-color: white; border: 1px solid #86989B; padding: 0; margin: 0 0 1em 1em; width: 10em; } div.toc h4 { margin: 0; font-size: 0.9em; padding: 0.1em 0 0.1em 0.6em; margin: 0; color: white; border-bottom: 1px solid #86989B; background-color: #AFC1C4; } div.toc ul { margin: 1em 0 1em 0; padding: 0 0 0 1em; list-style: none; } div.toc ul li { margin: 0.5em 0 0.5em 0; font-size: 0.9em; line-height: 130%; } div.toc ul li p { margin: 0; padding: 0; } div.toc ul ul { margin: 0.2em 0 0.2em 0; padding: 0 0 0 1.8em; } div.toc ul ul li { padding: 0; } div.admonition, div.warning { font-size: 0.9em; margin: 1em 0 0 0; border: 1px solid #86989B; background-color: #f7f7f7; } div.admonition p, div.warning p { margin: 0.5em 1em 0.5em 1em; padding: 0; } div.admonition pre, div.warning pre { margin: 0.4em 1em 0.4em 1em; } div.admonition p.admonition-title, div.warning p.admonition-title { margin: 0; padding: 0.1em 0 0.1em 0.5em; color: white; border-bottom: 1px solid #86989B; font-weight: bold; background-color: #AFC1C4; } div.warning { border: 1px solid #940000; } div.warning p.admonition-title { background-color: #CF0000; border-bottom-color: #940000; } div.admonition ul, div.admonition ol, div.warning ul, div.warning ol { margin: 0.1em 0.5em 0.5em 3em; padding: 0; } PK܉8,5docs/templates.html Mini Templates // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Mini Templates

Werkzeug ships a minimal templating system which is useful for small scripts where you just want to generate some HTML and don’t want another dependency or full blown template engine system.

It it however not recommended to use this template system for anything else than simple content generation. The Template class can be directly imported from the werkzeug module.

This template engine recognizes ASP/PHP like blocks and executes the code in them:

t = Template('<% for u in users %>${u["username"]}\n<% endfor %>')
t.render(users=[{'username': 'John'},
                {'username': 'Jane'}])

would result in:

John
Jane

You can also create templates from files:

t = Template.from_file('test.html')

The syntax elements are a mixture of django, genshi text and mod_python templates and used internally in werkzeug components.

We do not recommend using this template engine in a real environment because is quite slow and does not provide any advanced features. For simple applications (cgi script like) this can however be sufficient.

Syntax Elements

Printing Variables:

$variable
$variable.attribute[item](some, function)(calls)
${expression} or <%py print expression %>

Keep in mind that the print statement adds a newline after the call or a whitespace if it ends with a comma.

For Loops:

<% for item in seq %>
    ...
<% endfor %>

While Loops:

<% while expression %>
    <%py break / continue %>
<% endwhile %>

If Conditions:

<% if expression %>
    ...
<% elif expression %>
    ...
<% else %>
    ...
<% endif %>

Python Expressions:

<%py
    ...
%>

<%python
    ...
%>

Note on python expressions: You cannot start a loop in a python block and continue it in another one. This example does not work:

<%python
    for item in seq:
%>
    ...

Comments:

<%#
    This is a comment
%>

Missing Variables

If you try to access a missing variable you will get back an Undefined object. You can iterate over such an object or print it and it won’t fail. However every other operation will raise an error. To test if a variable is undefined you can use this expression:

<% if variable is Undefined %>
    ...
<% endif %>

Python 2.3 Compatibility

Because of limitations in Python 2.3 it’s impossible to achieve the semi-silent variable lookup fallback. If a template relies on undefined variables it won’t execute under Python 2.3.

The Template Class

class Template

Represents a simple text based template. It’s a good idea to load such templates from files on the file system to get better debug output.

render (*args, **kwargs)
This function accepts either a dict or some keyword arguments which will then be the context the template is evaluated in. The return value will be the rendered template.
from_file (file, encoding='utf-8', errors='strict', unicode_mode=True)
Load a template from a file.

Besides the normal global functions and objects, the following functions are added to every namespace: escape, url_encode, url_quote, and url_quote_plus. You can change those by subclassing Template and overriding the default_context dict:

class MyTemplate(Template):
    default_namespace = {
        'ueber_func':       ueber_func
    }
    # Now add the old functions, too, because they are useful.
    default_namespace.update(Template.default_namespace)
PK܉8cJ docs/terms.html Important Terms // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Important Terms

Table of Contents

This page covers important terms used in the documentation and Werkzeug itself.

Response Object

For Werkzeug, a response object is an object that works like a WSGI application but does not do any request processing. Usually you have a view function or controller method that processes the request and assambles a response object.

A response object is not necessarily the BaseResponse object or a subclass thereof.

View Function

Often people speak of MVC (Model, View, Controller) when developing web applications. However, the Django framework coined MTV (Model, Template, View) which basically means the same but reduces the concept to the data model, a function that processes data from the request and the database and renders a template.

Werkzeug itself does not tell you how you should develop applications, but the documentation often speaks of view functions that work roughly the same. The idea of a view function is that it’s called with a request object (and optionally some parameters from an URL rule) and returns a response object.

PK܉8뀹**docs/test.html Test Utilities // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Test Utilities

Table of Contents

Quite often you want to unit-test your application or just check the output from an interactive Python session. In theory that is pretty simple because you can fake a WSGI environment and call the application with a dummy start_response and iterate over the application iterator, but there are argumentably better ways to interact with an application.

Diving In

Werkzeug provides a Client object which you can pass a WSGI application (and optionally a response wrapper) which you can use to send virtual requests to the application.

A response wrapper is a callable that takes three arguments: the application iterator, the status and finally a list of headers. The default response wrapper returns a tuple. Because response objects have the same signature, you can use them as response wrapper, ideally by subclassing them and hooking in test functionality.

>>> from werkzeug import Client, BaseResponse, test_app
>>> c = Client(test_app, BaseResponse)
>>> resp = c.get('/')
>>> resp.status_code
200
>>> resp.headers
Headers([('Content-Type', 'text/html; charset=utf-8')])
>>> resp.response_body.splitlines()[:2]
['<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"',
 '  "http://www.w3.org/TR/html4/loose.dtd">']

Or without a wrapper defined:

>>> c = Client(test_app)
>>> app_iter, status, headers = c.get('/')
>>> status
'200 OK'
>>> headers
[('Content-Type', 'text/html; charset=utf-8')]
>>> ''.join(app_iter).splitlines()[:2]
['<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"',
 '  "http://www.w3.org/TR/html4/loose.dtd">']

The Client

class Client

This class allows to send requests to a wrapped application.

__init__ (application, response_wrapper=None)

The response wrapper can be a class or factory function that takes three arguments: app_iter, status and headers. The default response wrapper just returns a tuple.

Example:

class ClientResponse(BaseResponse):
    ...

client = Client(MyApplication(), response_wrapper=ClientResponse)
open (path='/', base_url=None, query_string=None, method='GET', data=None, input_stream=None, content_type=None, content_length=0, errors_stream=None, multithread=False, multiprocess=False, run_once=False, environ_overrides=None, as_tuple=False)

Takes the same arguments as the create_environ function from the utility module with some additions.

The first parameter should be the path of the request which defaults to ‘/’. The second one can either be a absolute path (in that case the url host is localhost:80) or a full path to the request with scheme, netloc port and the path to the script.

If the path contains a query string it will be used, even if the query_string parameter was given. If it does not contain one the query_string parameter is used as querystring. In that case it can either be a dict, MultiDict or string.

The following options exist:

method
The request method. Defaults to GET
input_stream
The input stream. Defaults to an empty read only stream.
data

The data you want to transmit. You can set this to a string and define a content type instead of specifying an input stream. Additionally you can pass a dict with the form data. The values could then be strings (no unicode objects!) which are then url encoded or file objects.

A file object for this method is either a file descriptor with an additional name attribute (like a file descriptor returned by the open / file function), a tuple in the form (fd, filename, mimetype) (all arguments except fd optional) or as dict with those keys and values.

Additionally you can instanciate the werkzeug.test.File object (or a subclass of it) and pass it as value.

content_type
The content type for this request. Default is an empty content type.
content_length
The value for the content length header. Defaults to 0.
errors_stream
The wsgi.errors stream. Defaults to sys.stderr.
multithread
The multithreaded flag for the WSGI Environment. Defaults to False.
multiprocess
The multiprocess flag for the WSGI Environment. Defaults to False.
run_once
The run_once flag for the WSGI Environment. Defaults to False.
get (*args, **kw)
Like open but method is enforced to GET
head (*args, **kw)
Like open but method is enforced to HEAD
post (*args, **kw)
Like open but method is enforced to POST
put (*args, **kw)
Like open but method is enforced to PUT
delete (*args, **kw)
Like open but method is enforced to DELETE
PK܉8KKdocs/tutorial.html Werkzeug Tutorial // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Werkzeug Tutorial

Translations

This tutorial is available in the following languages:

Welcome to the Werkzeug 0.2 tutorial in which we will create a TinyURL clone that stores URLs in a database. The libraries we will use for this applications are Jinja for the templates, SQLAlchemy for the database layer and, of course, Werkzeug for the WSGI layer.

The reasons why we’ve decided on these libraries for the tutorial application is that we want to stick to some of the design decisions Django took in the past. One of them is using view functions instead of controller classes with action methods, which is common in Rails and Pylons, the other one is designer-friendly templates.

The Werkzeug example folder contains a couple of applications that use other template engines, too, so you may want to have a look at them. There is also the source code of this application.

You can use easy_install to install the required libraries:

sudo easy_install Jinja
sudo easy_install SQLAlchemy

If you’re on Windows, omit the “sudo” (and make sure, setuptools is installed); if you’re on OS X, you can check if the libraries are also available in port; or on Linux, you can check out your package manager for packages called python-jinja and python-sqlalchemy.

If you’re curious, check out the online demo of the application.

Small disclaimer: This tutorial requires Python 2.4 or later.

Part 0: The Folder Structure

Before we can get started we have to create a Python package for our Werkzeug application and the folders for the templates and static files.

This tutorial application is called shorty and the initial directory layout we will use looks like this:

manage.py
shorty/
    __init__.py
    templates/
    static/

The __init__.py and manage.py files should be empty for the time being. The first one makes shorty a Python package, the second one will hold our management utilities later.

Part 1: The WSGI Application

Unlike Django or other frameworks, Werkzeug operates directly on the WSGI layer. There is no fancy magic that implements the central WSGI application for you. As a result of that the first thing you will do every time you write a Werkzeug application is implementing this basic WSGI application object. This can now either be a function or, even better, a callable class.

A callable class has huge advantages over a function. For one you can pass it some configuration parameters and furthermore you can use inline WSGI middlewares. Inline WSGI middlewares are basically middlewares applied “inside” of our application object. This is a good idea for middlewares that are essential for the application (session middlewares, serving of media files etc.).

Here the initial code for our shorty/application.py file which implements the WSGI application:

from sqlalchemy import create_engine
from werkzeug import Request, ClosingIterator
from werkzeug.exceptions import HTTPException

from shorty.utils import session, metadata, local, local_manager, url_map
from shorty import views
import shorty.models


class Shorty(object):

    def __init__(self, db_uri):
        local.application = self
        self.database_engine = create_engine(db_uri, convert_unicode=True)

    def init_database(self):
        metadata.create_all(self.database_engine)

    def __call__(self, environ, start_response):
        local.application = self
        request = Request(environ)
        local.url_adapter = adapter = url_map.bind_to_environ(environ)
        try:
            endpoint, values = adapter.match()
            handler = getattr(views, endpoint)
            response = handler(request, **values)
        except HTTPException, e:
            response = e
        return ClosingIterator(response(environ, start_response),
                               [session.remove, local_manager.cleanup])

That’s a lot of code for the beginning! Let’s go through it step by step. First we have a couple of imports: From SQLAlchemy we import a factory function that creates a new database engine for us. A database engine holds a pool of connections for us and manages them. The next few imports pull some objects into the namespace Werkzeug provides: a request object, a special iterator class that helps us cleaning up stuff at the request end and finally the base class for all HTTP exceptions.

The next five imports are not working because we don’t have the utils module written yet. However we should cover some of the objects already. The session object pulled from there is not a PHP-like session object but a SQLAlchemy database session object. Basically a database session object keeps track of yet uncommited objects for the database. Unlike Django, an instantiated SQLAlchemy model is already tracked by the session! The metadata object is also an SQLAlchemy thing which is used to keep track of tables. We can use the metadata object to easily create all tables for the database and SQLAlchemy uses it to look up foreign keys and similar stuff.

The local object is basically a thread local object created in the utility module for us. Every attribute on this object is bound to the current request and we can use this to implicitly pass objects around in a thread-safe way.

The local_manager object ensures that all local objects it keeps track of are properly deleted at the end of the request.

The last thing we import from there is the URL map which holds the URL routing information. If you know Django you can compare that to the url patterns you specify in the urls.py module, if you have used PHP so far it’s comparable with some sort of built-in “mod_rewrite”.

We import our views module which holds the view functions and then we import the models module which holds all of our models. Even if it looks like we don’t use that import it’s there so that all the tables are registered on the metadata properly.

So let’s have a look at the application class. The constructor of this class takes a database URI which is basically the type of the database and the login credentials or location of the database. For SQLite this is for example 'sqlite:////tmp/shorty.db' (note that these are four slashes).

In the constructor we create a database engine for that database URI and use the convert_unicode parameter to tell SQLAlchemy that our strings are all unicode objects.

Another thing we do here is binding the application to the local object. This is not really required but useful if we want to play with the application in a python shell. On application instanciation we have it bound to the current thread and all the database functions will work as expected. If we don’t do that Werkzeug will complain that it’s unable to find the database when it’s creating a session for SQLAlchemy.

The init_database function defined below can be used to create all the tables we use.

And then comes the request dispatching function. In there we create a new request object by passing the environment to the Request constructor. Once again we bind the application to the local object, this time, however, we have to do this, otherwise things will break soon.

Then we create a new URL map adapter by binding the URL map to the current WSGI environment. This basically looks at the environment of the incoming request information and fetches the information from the environment it requires. This is for example the name of the server for external URLs, the location of the script so that it can generate absolute paths if we use the URL builder. We also bind the adapter to the local object so that we can use it for URL generation in the utils module.

After that we have a try/except that catches HTTP exceptions that could occur while matching or in the view function. When the adapter does not find a valid endpoint for our current request it will raise a NotFound exception which we can use like a response object. An endpoint is basically the name of the function we want to handle our request with. We just get the function with the name of the endpoint and pass it the request and the URL values.

At the end of the function we call the response object as WSGI application and pass the return value of this function (which will be an iterable) to the closing iterator class along with our cleanup callbacks (which remove the current SQLAlchemy session and clean up the data left in the local objects).

As next step create two empty files shorty/views.py and shorty/models.py so that our imports work. We will fill the modules with useful code later.

Part 2: The Utilities

Now we have basically finished the WSGI application itself but we have to add some more code into our utiliy module so that the imports work. For the time being we just add the objects which we need for the application to work. All the following code goes into the shorty/utils.py file:

from sqlalchemy import MetaData
from sqlalchemy.orm import create_session, scoped_session
from werkzeug import Local, LocalManager
from werkzeug.routing import Map, Rule

local = Local()
local_manager = LocalManager([local])
application = local('application')

metadata = MetaData()
session = scoped_session(lambda: create_session(application.database_engine,
                         transactional=True), local_manager.get_ident)

url_map = Map()
def expose(rule, **kw):
    def decorate(f):
        kw['endpoint'] = f.__name__
        url_map.add(Rule(rule, **kw))
        return f
    return decorate

def url_for(endpoint, _external=False, **values):
    return local.url_adapter.build(endpoint, values, force_external=_external)

First we again import a bunch of stuff, then we create the local objects and the local manager we already discussed in the section above. The new thing here is that calling a local object with a string returns a proxy object. This returned proxy object always points to the attribute with that name on the local object. For example application now points to local.application all the time. If you, however, try to do something with it and there is no object bound to local.application you will get a RuntimeError.

The next three lines are basically everything we need to get SQLAlchemy 0.4 or higher running in a Werkzeug application. We create a new metadata for all of our tables and then a new scoped session using the scoped_session factory function. This basically tells SQLAlchemy to use the same algorithm to determine the current context as werkzeug local does and use the database engine of the current application.

If we don’t plan to add support for multiple instances of the application in the same python interpreter we can also simplify that code by not looking up the application on the current local object but somewhere else. This approach is for example used by Django but makes it impossible to combine multiple such applications.

The rest of the module is code we will use in our views. Basically the idea there is to use decorators to specify the URL dispatching rule for a view function rather than a central urls.py file like you could do in Django or a .htaccess for URL rewrites like you would do in PHP. This is one way to do it and there are countless of other ways to handle rule definitions.

The url_for function, which we have there too, provides a simple way to generate URLs by endpoint. We will use it in the views and our model later.

Intermission: And Now For Something Completely Different

Now that we have finished the foundation for the application we could relax and do something completely different: management scripts. Most of the time you do similar tasks while developing. One of them is firing up a development server (If you’re used to PHP: Werkzeug does not rely on Apache for development, it’s perfectly fine and also recommended to use the wsgiref server that comes with python for development purposes), starting a python interpreter to play with the database models, initializing the database etc.

Werkzeug makes it incredible easy to write such management scripts. The following piece of code implements a fully featured management script. Put it into the manage.py file you have created in the beginning:

#!/usr/bin/env python
from werkzeug import script

def make_app():
    from shorty.application import Shorty
    return Shorty('sqlite:////tmp/shorty.db')

def make_shell():
    from shorty import models, utils
    application = make_app()
    return locals()

action_runserver = script.make_runserver(make_app, use_reloader=True)
action_shell = script.make_shell(make_shell)
action_initdb = lambda: make_app().init_database()

script.run()

werkzeug.script is explained in detail in the script documentation and we won’t cover it here, most of the code should be self explaining anyway.

What’s important is that you should be able to run python manage.py shell to get an interactive Python interpreter without traceback. If you get an exception check the line number and compare your code with the code we have in the code boxes above.

Now that the script system is running we can start writing our database models.

Part 3: Database Models

Now we can create the models. Because the application is pretty simple we just have one model and table:

from datetime import datetime
from sqlalchemy import Table, Column, String, Boolean, DateTime
from shorty.utils import session, metadata, url_for, get_random_uid

url_table = Table('urls', metadata,
    Column('uid', String(140), primary_key=True),
    Column('target', String(500)),
    Column('added', DateTime),
    Column('public', Boolean)
)

class URL(object):

    def __init__(self, target, public=True, uid=None, added=None):
        self.target = target
        self.public = public
        self.added = added or datetime.utcnow()
        if not uid:
            while True:
                uid = get_random_uid()
                if not URL.query.get(uid):
                    break
        self.uid = uid

    @property
    def short_url(self):
        return url_for('link', uid=self.uid, _external=True)

    def __repr__(self):
        return '<URL %r>' % self.uid

session.mapper(URL, url_table)

This module is pretty straightforward. We import all the stuff we need from SQLAlchemy and create a table. Then we add a class for this table and we map them both together. For detailed explanations regarding SQLAlchemy you should have a look at the excellent tutorial.

In the constructor we generate a unique ID until we find an id which is still free to use. What’s missing is the get_random_uid function we have to add to the utils module:

from random import sample, randrange

URL_CHARS = 'abcdefghijkmpqrstuvwxyzABCDEFGHIJKLMNPQRST23456789'

def get_random_uid():
    return ''.join(sample(URL_CHARS, randrange(3, 9)))

Once that is done we can use python manage.py initdb to initialize the database and play around with the stuff using python manage.py shell:

Interactive Werkzeug Shell
>>> from shorty.models import session, URL

Now we can add some URLs to the database:

>>> urls = [URL('http://example.org/'), URL('http://localhost:5000/')]
>>> URL.query.all()
[]
>>> session.commit()
>>> URL.query.all()
[<URL '5cFbsk'>, <URL 'mpugsT'>]

As you can see we have to commit in order to send the urls to the database. Let’s create a private item with a custom uid:

>>> URL('http://werkzeug.pocoo.org/', False, 'werkzeug-webpage')
>>> session.commit()

And query them all:

>>> URL.query.filter_by(public=False).all()
[<URL 'werkzeug-webpage'>]
>>> URL.query.filter_by(public=True).all()
[<URL '5cFbsk'>, <URL 'mpugsT'>]
>>> URL.query.get('werkzeug-webpage')
<URL 'werkzeug-webpage'>

Now that we have some data in the database and we are somewhat familiar with the way SQLAlchemy works, it’s time to create our views.

Part 4: The View Functions

Now after some playing with SQLAlchemy we can go back to Werkzeug and start creating our view functions. The term “view function” is derived from Django which also calls the functions that render templates “view functions”. So our example is MTV (Model, View, Template) and not MVC (Model, View, Controller). They are probably the same but it’s a lot easier to use the Django way of naming those things.

For the beginning we just create a view function for new URLs and a function that displays a message about a new link. All that code goes into our still empty views.py file:

from werkzeug import redirect
from werkzeug.exceptions import NotFound
from shorty.utils import session, render_template, expose, validate_url, \
     url_for
from shorty.models import URL

@expose('/')
def new(request):
    error = url = ''
    if request.method == 'POST':
        url = request.form.get('url')
        alias = request.form.get('alias')
        if not validate_url(url):
            error = "I'm sorry but you cannot shorten this URL."
        elif alias:
            if len(alias) > 140:
                error = 'Your alias is too long'
            elif '/' in alias:
                error = 'Your alias might not include a slash'
            elif URL.query.get(alias):
                error = 'The alias you have requested exists already'
        if not error:
            uid = URL(url, 'private' not in request.form, alias).uid
            session.commit()
            return redirect(url_for('display', uid=uid))
    return render_template('new.html', error=error, url=url)

@expose('/display/<uid>')
def display(request, uid):
    url = URL.query.get(uid)
    if not url:
        raise NotFound()
    return render_template('display.html', url=url)

@expose('/u/<uid>')
def link(request, uid):
    url = URL.query.get(uid)
    if not url:
        raise NotFound()
    return redirect(url.target, 301)

@expose('/list/', defaults={'page': 1})
@expose('/list/<int:page>')
def list(request, page):
    pass

Quite a lot of code again, but most of it is just plain old form validation. Basically we specify two functions: new and display and decorate them with our expose decorator from the utils. This decorator adds a new URL rule to the map by passing all parameters to the constructor of a rule object and setting the endpoint to the name of the function. So we can easily build URLs to those functions by using their name as endpoint.

Keep in mind that this is not necessarily a good idea for bigger applications. In such cases it’s encouraged to use the full import name with a common prefix as endpoint or something similar. Otherwise it becomes pretty confusing.

The form validation in the new method is pretty straightforward. We check if the current method is POST, if yes we get the data from the request and validate it. If there is no error we create a new URL object, commit it to the database and redirect to the display page.

The display function is not much more complex. The URL rule expects a parameter called uid, which the function accepts. Then we look up the URL rule with the given uid and render a template by passing the URL object to it.

If the URL does not exist we raise a NotFound exception which displays a generic “404 Page Not Found” page. We can later replace it by a custom error page by catching that exception before the generic HTTPException in our WSGI application.

The link view function is used by our models in the short_url property and is the short URL we provide. So if the URL uid is foobar the URL will be available as http://localhost:5000/u/foobar.

The list view function has not yet been written, we will do that later. But what’s important is that this function takes a URL parameter which is optional. The first decorator tells Werkzeug that if just /page/ is requested it will assume that the page equals 1. Even more important is the fact that Werkzeug also normalizes the URLs. So if you requested /page or /page/1, you will be redirected to /page/ in both cases. This makes Google happy and comes for free. If you don’t like that behavior, you can also disable it.

And again we have imported two objects from the utils module that don’t exist yet. One of those should render a jinja template into a response object, the other one validates a URL. So let’s add those to utils.py:

from os import path
from urlparse import urlparse
from werkzeug import Response
from jinja import Environment, FileSystemLoader

ALLOWED_SCHEMES = frozenset(['http', 'https', 'ftp', 'ftps'])
TEMPLATE_PATH = path.join(path.dirname(__file__), 'templates')
jinja_env = Environment(loader=FileSystemLoader(TEMPLATE_PATH))
jinja_env.globals['url_for'] = url_for

def render_template(template, **context):
    return Response(jinja_env.get_template(template).render(**context),
                    mimetype='text/html')

def validate_url(url):
    return urlparse(url)[0] in ALLOWED_SCHEMES

That’s it, basically. The validation function checks if our URL looks like an HTTP or FTP URL. We do this whitelisting to ensure nobody submits any dangerous JavaScript or similar URLs. The render_template function is not much more complicated either, it basically looks up a template on the file system in the templates folder and renders it as response.

Another thing we do here is passing the url_for function into the global template context so that we can build URLs in the templates too.

Now that we have our first two view functions it’s time to add the templates.

Part 5: The Templates

We have decided to use Jinja templates in this example. If you are used to Django templates you should feel at home, if you have worked with PHP so far you can compare the Jinja templates with smarty. If you have used PHP as templating language until now you should have a look at Mako for your next project.

Security Warning: We are using Jinja here which is a text based template engine. As a matter of fact, Jinja has no idea what it is dealing with, so if you want to create HTML template it’s your responsibility to escape all values that might include, at some point, any of the following characters: >, < or &. Inside attributes you also have to escape double quotes. You can use the jinja |e filter for basic escapes, if you pass it true as argument it will also escape quotes (|e(true)). As you can see from the examples below we don’t escape URLs. The reason is that we won’t have any ampersands in the URL and as such it’s safe to omit it.

For simplicity we will use HTML 4 in our templates. If you have already some experience with XHTML you can adopt the templates to XHTML. But keep in mind that the example stylesheet from below does not work with XHTML.

One of the cool things Jinja inherited from Django is template inheritance. Template inheritance means that we can put often used elements into a base template and fill in placeholders. For example all the doctype and HTML base frame goes into a file called templates/layout.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
  <title>Shorty</title>
</head>
<body>
  <h1><a href="{{ url_for('new') }}">Shorty</a></h1>
  <div class="body">{% block body %}{% endblock %}</div>
  <div class="footer">
    <a href="{{ url_for('new') }}">new</a> |
    <a href="{{ url_for('list') }}">list</a> |
    use shorty for good, not for evil
  </div>
</body>
</html>

And we can inherit from this base template in our templates/new.html:

{% extends 'layout.html' %}
{% block body %}
  <h2>Create a Shorty-URL!</h2>
  {% if error %}<div class="error">{{ error }}</div>{% endif -%}
  <form action="" method="post">
    <p>Enter the URL you want to shorten</p>
    <p><input type="text" name="url" id="url" value="{{ url|e(true) }}"></p>
    <p>Optionally you can give the URL a memorable name</p>
    <p><input type="text" id="alias" name="alias">{#
     #}<input type="submit" id="submit" value="Do!"></p>
    <p><input type="checkbox" name="private" id="private">
       <label for="private">make this URL private, so don't list it</label></p>
  </form>
{% endblock %}

If you’re wondering about the comment between the two input elements, this is a neat trick to keep the templates clean but not create whitespace between those two. We’ve prepared a stylesheet you can use which depends on not having a whitespace there.

And then a second template for the display page (templates/display.html):

{% extends 'layout.html' %}
{% block body %}
  <h2>Shortened URL</h2>
  <p>
    The URL {{ url.target|urlize(40, true) }}
    was shortened to {{ url.short_url|urlize }}.
  </p>
{% endblock %}

The urlize filter is provided by Jinja and translates a URL(s) in a text into clickable links. If you pass it an integer it will shorten the captions of those links to that number of characters, passing it true as second parameter adds a nofollow flag.

Now that we have our first two templates it’s time to fire up the server and look at those part of the application that work already: adding new URLs and getting redirected.

Intermission: Adding The Design

Now it’s time to do something different: adding a design. Design elements are usually in static CSS stylesheets so we have to put some static files somewhere. But that’s a little big tricky. If you have worked with PHP so far you have probably noticed that there is no such thing as translating the URL to filesystem paths and accessing static files right from the URL. You have to explicitly tell the webserver or our development server that some path holds static files.

Django even recommends a separate subdomain and standalone server for the static files which is a terribly good idea for heavily loaded environments but somewhat of an overkill for this simple application.

So here is the deal: We let our application host the static files, but in production mode you should probably tell the apache to serve those files by using an Alias directive in the apache config:

Alias /static /path/to/static/files

This will be a lot faster.

But how do we tell our application that we want it to share the static folder from our application package as /static?. Fortunately that’s pretty simple because Werkzeug provides a WSGI middleware for that. Now there are two ways to hook that middleware in. One way is to wrap the whole application in that middleware (we really don’t recommend this one) and the other is to just wrap the dispatching function (much better because we don’t lose the reference to the application object). So head back to application.py and do some code refactoring there.

First of all you have to add a new import and calculate the path to the static files:

from os import path
from werkzeug import SharedDataMiddleware

STATIC_PATH = path.join(path.dirname(__file__), 'static')

It may be better to put the path calculation into the utils.py file because we already calculate the path to the templates there. But it doesn’t really matter and for simplicity we can leave it in the application module.

So how do we wrap our dispatching function? In theory we just have to say self.__call__ = wrap(self.__call__) but unfortunately that doesn’t work in python. But it’s not much harder. Just rename __call__ to dispatch and add a new __call__ method:

def __call__(self, environ, start_response):
    return self.dispatch(environ, start_response)

Now we can go into our __init__ function and hook in the middleware by wrapping the dispatch method:

self.dispatch = SharedDataMiddleware(self.dispatch, {
    '/static':  STATIC_PATH
})

Now that wasn’t that hard. This way you can now hook in WSGI middlewares inside the application class!

Another good idea now is to tell our url_map in the utils module the location of our static files by adding a rule. This way we can generate URLs to the static files in the templates:

url_map = Map([Rule('/static/<file>', endpoint='static', build_only=True)])

Now we can open our templates/layout.html file again and add a link to the stylesheet style.css, which we are going to create afterwards:

<link rel="stylesheet" type="text/css" href="{{ url_for('static', file='style.css') }}">

This of course goes into the <head> tag where currently just the title is.

You can now design a nice layout for it or use the example stylesheet if you want. In both cases the file you have to create is called static/style.css

Part 6: Listing Public URLs

Now we want to list all of the public URLs on the list page. That shouldn’t be a big problem but we will have to do some sort of pagination. Because if we print all URLs at once we have sooner or later an endless page that takes minutes to load.

So let’s start by adding a Pagination class into our utils module:

from werkzeug import cached_property

class Pagination(object):

    def __init__(self, query, per_page, page, endpoint):
        self.query = query
        self.per_page = per_page
        self.page = page
        self.endpoint = endpoint

    @cached_property
    def count(self):
        return self.query.count()

    @cached_property
    def entries(self):
        return self.query.offset((self.page - 1) * self.per_page) \
                         .limit(self.per_page).all()

    has_previous = property(lambda x: x.page > 1)
    has_next = property(lambda x: x.page < x.pages)
    previous = property(lambda x: url_for(x.endpoint, page=x.page - 1))
    next = property(lambda x: url_for(x.endpoint, page=x.page + 1))
    pages = property(lambda x: max(0, x.count - 1) // x.per_page + 1)

This is a very simple class that does most of the pagination for us. We can pass at an unexecuted SQLAlchemy query, the number of items per page, the current page and the endpoint, which will be used for URL generation. The cached_property decorator you see works pretty much like the normal property decorator, just that it memorizes the result. We won’t cover that class in detail but basically the idea is that accessing pagination.entries returns the items for the current page and that the other properties return meaningful values so that we can use them in the template.

Now we can import the Pagination class into our views module and add some code to the list function:

from shorty.utils import Pagination

@expose('/list/', defaults={'page': 1})
@expose('/list/<int:page>')
def list(request, page):
    query = URL.query.filter_by(public=True)
    pagination = Pagination(query, 30, page, 'list')
    if pagination.page > 1 and not pagination.entries:
        raise NotFound()
    return render_template('list.html', pagination=pagination)

The if condition in this function basically ensures that status code 404 is returned if we are not on the first page and there aren’t any entries to display (Accessing something like /list/42 without entries on that page and not returning a 404 status code would be considered bad style.)

And finally the template:

{% extends 'layout.html' %}
{% block body %}
  <h2>List of URLs</h2>
  <ul>
  {%- for url in pagination.entries %}
    <li><a href="{{ url.short_url|e }}">{{ url.uid|e }}</a> &raquo;
        <small>{{ url.target|urlize(38, true) }}</small></li>
  {%- else %}
    <li><em>no URls shortened yet</em></li>
  {%- endfor %}
  </ul>
  <div class="pagination">
    {%- if pagination.has_previous %}<a href="{{ pagination.previous
        }}">&laquo; Previous</a>
    {%- else %}<span class="inactive">&laquo; Previous</span>{% endif %}
    | {{ pagination.page }} |
    {% if pagination.has_next %}<a href="{{ pagination.next }}">Next &raquo;</a>
    {%- else %}<span class="inactive">Next &raquo;</span>{% endif %}
  </div>
{% endblock %}

Bonus: Styling 404 Error Pages

Now that we’ve finished our application we can do some small improvements such as custom 404 error pages. That’s pretty simple. The first thing we have to do is creating a new function called not_found in the view that renders a template:

def not_found(request):
    return render_template('not_found.html')

Then we have to go into our application module and import the NotFound exception:

from werkzeug.exceptions import NotFound

Finally we have to catch it and translate it into a response. This except block goes right before the except block of the HTTPException:

try:
    # this stays the same
except NotFound, e:
    response = views.not_found(request)
    response.status_code = 404
except HTTPException, e:
    # this stays the same

Now add a template templates/not_found.html and you’re done:

{% extends 'layout.html' %}
{% block body %}
  <h2>Page Not Found</h2>
  <p>
    The page you have requested does not exist on this server.  What about
    <a href="{{ url_for('new') }}">adding a new URL</a>?
  </p>
{% endblock %}

Outro

This tutorial covers everything you need to get started with Werkzeug, SQLAlchemy and Jinja and should help you find the best solution for your application. For some more complex examples that also use different setups and ideas for dispatching have a look at the examples folder.

Have fun with Werkzeug!

PK܉8#Y#Ydocs/tutorial_de.html Werkzeug Tutorial // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Werkzeug Tutorial

Hinweis

Dies ist die deutsche Übersetzung des Tutorials. Die Entwicklung rund um Werkzeug steht nie still, und Verbesserungen an der Library wirken sich oft auch auf das Tutorial aus – deshalb ist die Originalversion möglicherweise aktueller.

Willkommen zum Tutorial für Werkzeug 0.2. Wir werden einen einfachen TinyURL-Klon programmieren, der die URLs in einer Datenbank speichert. Die Die verwendeten Bibliotheken für diese Anwendung sind Jinja für die Templates, SQLAlchemy für die Datenbank-Anbindung und natürlich Werkzeug für WSGI.

Wir haben uns hier für diese Komponenten entschieden, weil wir einen Django-ähnlichen Grundaufbau nachstellen wollen. Dazu zählen wir zum Beispiel View-Funktionen anstelle der in Rails und Pylons gängigen Controller-Klassen mit Action-Methoden, sowie designerfreundliche Templates.

In Werkzeugs Beispiel-Ordner befinden sich einige Anwendungen, die andere Konzepte verfolgen, Template-Engines einsetzen etc. Dort liegt auch der Quellcode der Anwendung, die wir in diesem Tutorial erstellen werden.

Du kannst easy_install verwenden, um Jinja und SQLAlchemy zu installieren, falls diese nicht bereits installiert sind:

sudo easy_install Jinja
sudo easy_install SQLAlchemy

Diese Befehle funktionieren auch auf einem Windows-System (mit Administratorrechten), sofern die setuptools installiert sind, allerdings musst du das sudo weglassen. Als OS X-Benutzer könntest du die Libraries auch via port installieren, Linux-Benutzer finden diese Pakete möglicherweise auch in ihrem Paketmanager.

Wenn du neugierig bist, kannst du dir auch die Online-Demo der Anwendung ansehen.

Noch ein kleiner Hinweis: Dieses Tutorial erfordert Python 2.4.

Teil 0: Die Ordnerstruktur

Bevor wir beginnen können, müssen wir ein Python-Paket für unsere Werkzeug-Anwendung erstellen. Dort werden wir die Anwendung, die Templates und die statischen Dateien ablegen.

Die Anwendung dieses Tutorials nennen wir shorty und die Struktur für unsere Anwendung sieht etwa so aus:

manage.py
shorty/
    __init__.py
    static/
    templates/

Die Dateien __init__.py und manage.py lassen wir für den Moment einmal leer. Die erste dieser Dateien macht aus dem Ordner shorty ein Python-Paket, die zweite werden wir später für unsere Verwaltungsfunktionen nutzen.

Teil 1: Die WSGI-Anwendung

Im Gegensatz zu Django oder ähnlichen Frameworks arbeitet Werkzeug direkt auf der WSGI-Schicht. Es gibt keine schicke Magie, die die zentrale WSGI-Anwendung für uns implementiert. Das bedeutet, dass wir als Erstes eben diese programmieren müssen. Eine WSGI-Anwendung ist eine Funktion oder, noch besser, eine Klasse mit einer Methode __call__.

Eine aufrufbare Klasse hat große Vorteile gegenüber einer Funktion: Zum Einen kann man Konfigurationsparameter direkt an den Konstruktor übergeben, zum Anderen können wir WSGI-Middlewares innerhalb der WSGI-Anwendung hinzufügen. Das ist nützlich für Middlewares, die entscheidend für die Funktion der Anwendung sind (z.B. eine Session-Middleware).

Hier zunächst einmal der Quellcode für unsere Datei shorty/application.py, in der wir die WSGI-Anwendung ablegen:

from sqlalchemy import create_engine
from werkzeug import Request, ClosingIterator
from werkzeug.exceptions import HTTPException

from shorty.utils import session, metadata, local, local_manager, url_map
from shorty import views
import shorty.models


class Shorty(object):

    def __init__(self, db_uri):
        local.application = self
        self.database_engine = create_engine(db_uri, convert_unicode=True)

    def init_database(self):
        metadata.create_all(self.database_engine)

    def __call__(self, environ, start_response):
        local.application = self
        request = Request(environ)
        local.url_adapter = adapter = url_map.bind_to_environ(environ)
        try:
            endpoint, values = adapter.match()
            handler = getattr(views, endpoint)
            response = handler(request, **values)
        except HTTPException, e:
            response = e
        return ClosingIterator(response(environ, start_response),
                               [session.remove, local_manager.cleanup])

Ziemlich viel für Code für den Anfang ... gehen wir ihn mal Schritt für Schritt durch. Zunächst sehen wir einige Imports: Aus dem Paket sqlalchemy holen wir uns eine Factory-Funktion, die eine neue Datenbank-Engine für uns erstellt, die wiederum einen Connection-Pool bereithält. Die nächsten Imports holen einige Objekte in den Namensraum, die uns Werkzeug zur Verfügung stellt: ein Request-Objekt; ein spezieller Iterator, der uns hilft am Ende eines Requests einige Dinge aufzuräumen; und schließlich die Basisklasse für alle HTTP-Exceptions.

Die nächsten fünf Imports funktionieren noch nicht, weil wir das utils-Modul noch nicht erstellt haben. Doch wir werden trotzdem schon ein wenig über diese Objekte sprechen. Das Objekt session ist kein aus PHP bekanntes Session-Array, sondern eine Datenbank-Session von SQLAlchemy. Alle Datenbank-Models, die im Kontext eines Requests erstellt werden, sind auf diesem Objekt zwischengespeichert, so dass man Änderungen mit einem Schlag zum Server senden kann. Im Gegensatz zu Django wird ein instantiiertes Datenbank-Model automatisch von der Session verwaltet und ist in dieser Session ein Singleton. Es kann also niemals zwei Instanzen des selben Datenbankeintrags in einer Session geben. Das Objekt metadata stammt ebenfalls aus SQLAlchemy und speichert Informationen über die Tabellen der Datenbank. Es stellt zum Beispiel eine Funktion bereit, die alle im Model definierten Tabellen in der Datenbank erstellt.

Das Objekt local ist ein kontext-lokales Objekt, erstellt vom Modul utility. Attributzugriffe auf dieses Objekt sind an den aktuellen Request gebunden, d.h. jeder Request bekommt ein anderes Objekt zurück und kann verschiedene Objekte ablegen, ohne Threadingprobleme zu bekommen. Der local_manager wird genutzt, um am Ende des Requests alle auf dem local-Objekt gespeicherten Daten wieder freizugeben.

Der letzte Import von dort ist die URL-Map, die alle URL-Routen verwaltet. Solltest du bereits mit Django gearbeitet haben, ist dies vergleichbar mit den regulären Ausdrücken in der jeweiligen urls.py. Kennst du PHP, ist die URL-Map ähnlich einem eingebauten mod_rewrite.

Zusätzlich importieren wir hier unser views-Modul, das die View-Funktionen enthält, sowie das models-Modul, in welchem unsere Models definiert sind. Auch wenn es so aussieht, als ob wir diesen Import nicht nutzen, ist er wichtig. Nur dadurch werden unsere Tabellen auf dem metadata-Objekt registriert.

Schauen wir auf die Anwendungsklasse. Der Konstruktor dieser Klasse nimmt die Datenbank-URI entgegen, die – einfach gesagt – den Typ der Datenbank und die Verbindungsdaten enthält. Für SQLite das wäre zum Beispiel sqlite:////tmp/shorty.db (die vier Slashes sind kein Tippfehler).

Im Konstruktor erstellen wir auch gleich eine Datenbank-Engine für diese URI und aktivieren das automatische Umwandeln von Bytestrings nach Unicode. Das ist nützlich, weil sowohl Jinja als auch Werkzeug intern nur Unicode verwenden.

Des Weiteren binden wir die Anwendung an das local-Objekt. Das ist eigentlich nicht nötig, aber nützlich, wenn wir mit der Anwendung in der Python-Shell spielen wollen. Damit werden direkt nach dem Instantiieren der Anwendung die Datenbankfunktionen testen. Wenn wir das nicht tun, wird der Python-Interpreter einen Fehler werfen, wenn außerhalb eines Requests versucht wird, eine SQLAlchemy-Session zu erstellen.

Die Methode init_database können wir später im Managementscript verwenden, um alle Tabellen zu erstellen, die wir definiert haben.

Nun zur eigentlichen WSGI-Anwendung, der __call__-Methode. Dort passiert das so genannte “Request Dispatching”, also das Weiterleiten von eingehenden Anfragen zu den richtigen Funktionen. Als Erstes erstellen wir dort ein neues Request-Objekt, um nicht direkt mit environ, dem Dictionary mit den Umgebungsvariablen, arbeiten zu müssen. Dann binden wir die Anwendung an das local-Objekt für den aktuellen Kontext.

Anschließend erstellen wir einen URL-Adapter, indem wir die URL-Map an die aktuelle WSGI-Umgebung binden. Der Adapter weiß dann, wie die aktuelle URL aussieht, wo die Anwendung eingebunden ist etc. Diesen Adapter können wir nutzen, um URLs zu erzeugen oder gegen den aktuellen Request zu matchen. Wir binden diesen Adapter auch an das local-Objekt, damit wir im utils-Modul auf ihn zugreifen können.

Danach kommt ein try/except-Konstrukt, das HTTP-Fehler abfängt, die während des Matchings oder in einer View-Funktion auftreten können. Wenn der Adapter keinen Endpoint für die aktuelle URL findet, wird er eine NotFound-Exception werfen, die wir wie ein Response Objekt aufrufen können. Der Endpoint ist in unserem Fall der Name der Funktion im views-Modul, die wir aufrufen möchten. Wir suchen uns einfach mit getattr die Funktion dem Namen nach heraus und rufen sie mit dem Request-Objekt und den URL-Werten auf.

Am Schluss rufen wir das gewonnene Response-Objekt (oder die Exception) als WSGI-Anwendung auf und übergeben den Rückgabewert dieser Funktion an den ClosingIterator, zusammen mit zwei Funktionen fürs Aufräumen. Dies schließt die SQLAlchemy-Session und leert das local-Objekt für diesen Request.

Nun müssen wir zwei leere Dateien shorty/views.py und shorty/models.py erstellen, damit die Imports nicht fehlschlagen. Den tatsächlichen Code für diese Module werden wir ein wenig später erstellen.

Teil 2: Die Utilities

Nun haben wir die eigentliche WSGI-Applikation fertig gestellt, aber wir müssen das Utility-Modul noch um Code ergänzen, damit die Imports klappen. Fürs Erste fügen wir nur die Objekte hinzu, die wir brauchen, damit die Applikation funktioniert. Der folgende Code landet in der Datei shorty/utils.py:

from sqlalchemy import MetaData
from sqlalchemy.orm import create_session, scoped_session
from werkzeug import Local, LocalManager
from werkzeug.routing import Map, Rule

local = Local()
local_manager = LocalManager([local])
application = local('application')

metadata = MetaData()
session = scoped_session(lambda: create_session(application.database_engine,
                         transactional=True), local_manager.get_ident)

url_map = Map()
def expose(rule, **kw):
    def decorate(f):
        kw['endpoint'] = f.__name__
        url_map.add(Rule(rule, **kw))
        return f
    return decorate

def url_for(endpoint, _external=False, **values):
    return local.url_adapter.build(endpoint, values, force_external=_external)

Zunächst importieren wir wieder eine Menge, dann erstellen wir das local-Objekt und den Manager dafür, wie bereits im vorherigen Abschnitt besprochen. Neu ist hier, dass der Aufruf eines local-Objekts mit einem String ein Proxy-Objekt zurück gibt. Dieses zeigt stets auf die gleichnamigen Attribute des local-Objekts. Beispielsweise verweist nun application dauerhaft auf local.application. Wenn du jedoch darauf zugreifst und kein Objekt an local.application gebunden ist, erhältst du einen RuntimeError.

Die folgenden drei Zeilen sind im Prinzip alles, um SQLAlchemy 0.4 oder höher in eine Werkzeug-Anwendung einzubinden. Wir erstellen ein Metadaten-Objekt für all unsere Tabellen sowie eine “scoped session” über die scoped_session-Factory-Funktion. Dadurch wird SQLAlchemy angewiesen, praktisch denselben Algorithmus zur Ermittlung des aktuellen Kontextes zu verwenden, wie es auch Werkzeug für die local-Objekte tut, und die Datenbank-Engine der aktuellen Applikation zu benutzen.

Wenn wir nicht vorhaben, mehrere Instanzen der Applikation in derselben Instanz des Python-Interpreters zu unterstützen, können wir den Code einfach halten, indem wir nicht über das aktuelle local-Objekt auf die Applikation zugreifen, sondern einen anderen Weg nehmen. Dieser Ansatz wird etwa von Django verfolgt, macht es allerdings unmöglich, mehrere solcher Applikationen zu kombinieren.

Der restliche Code des Moduls wird für unsere Views benutzt. Die Idee besteht darin, Dekoratoren zu benutzen, um die URL-Dispatching-Regeln für View-Funktionen festzulegen, anstatt ein zentrales Modul urls.py zu verwenden, wie es Django tut, oder über eine .htaccess-Datei URLs umzuschreiben, wie man es in PHP machen würde. Dies ist eine Möglichkeit, dies zu tun, und es gibt unzählige andere Wege der Handhabung von URL-Regeldefinitionen.

Die Funktion url_for, die wir ebenfalls definieren, bietet einen einfachen Weg, URLs anhand des Endpointes zu generieren. Wir werden sie später in den Views als auch unserem Model verwenden.

Unterbrechung: Und nun etwas komplett anderes

Da wir nun das Grundgerüst für unsere Anwendung fertig gestellt haben, können wir jetzt erst einmal relaxen und uns etwas komplett anderem zuwenden: den Verwaltungs-Scripts. Während der Entwicklung erledigt man häufig immer wiederkehrende Aufgaben, wie zum Beispiel das Starten eines Entwicklungs-Servers (im Gegensatz zu PHP benötigt Werkzeug keinen Apache-Server; der in Python integrierte wsgiref-Server ist völlig ausreichend und für die Entwicklung auf jeden Fall empfehlenswert), das Starten eines Python-Interpreters (um mit den Datenbankobjekten herumzuspielen oder die Datenbank zu initialisieren) etc.

Werkzeug macht es unglaublich einfach, solche Verwaltungs-Scripts zu schreiben. Der folgende Code implementiert ein voll funktionsfähiges Verwaltungs-Script und gehört in die manage.py-Datei, welche du am Anfang erstellt hast:

#!/usr/bin/env python
from werkzeug import script

def make_app():
    from shorty.application import Shorty
    return Shorty('sqlite:////tmp/shorty.db')

def make_shell():
    from shorty import models, utils
    application = make_app()
    return locals()

action_runserver = script.make_runserver(make_app, use_reloader=True)
action_shell = script.make_shell(make_shell)
action_initdb = lambda: make_app().init_database()

script.run()

werkzeug.script ist genauer in der Script-Dokumentation beschrieben, und da der Großteil des Codes verständlich sein sollte, werden wir hier nicht näher darauf eingehen.

Es ist aber wichtig, dass du python manage.py shell ausführen kannst, um eine interaktive Python-Shell zu starten. Solltest du einen Traceback bekommen, kontrolliere bitte die darin genannte Code-Zeile und vergleiche sie mit dem entsprechenden Code in dieser Anleitung.

Sobald das Script läuft, können wir mit dem Schreiben der Datenbank-Models beginnen.

Teil 3: Datenbank-Models

Jetzt können wir die Models erstellen. Da die Anwendung ziemlich einfach ist, haben wir nur ein Model und eine Tabelle:

from datetime import datetime
from sqlalchemy import Table, Column, String, Boolean, DateTime
from shorty.utils import session, metadata, url_for, get_random_uid

url_table = Table('urls', metadata,
    Column('uid', String(140), primary_key=True),
    Column('target', String(500)),
    Column('added', DateTime),
    Column('public', Boolean)
)

class URL(object):

    def __init__(self, target, public=True, uid=None, added=None):
        self.target = target
        self.public = public
        self.added = added or datetime.utcnow()
        if not uid:
            while True:
                uid = get_random_uid()
                if not URL.query.get(uid):
                    break
        self.uid = uid

    @property
    def short_url(self):
        return url_for('link', uid=self.uid, _external=True)

    def __repr__(self):
        return '<URL %r>' % self.uid

session.mapper(URL, url_table)

Dieses Modul ist gut überschaubar. Wir importieren alles, was wir von SQLAlchemy benötigen, und erstellen die Tabelle. Dann fügen wir eine Klasse für diese Tabelle hinzu und verbinden beide miteinander. Für eine detailliertere Erklärung bezüglich SQLAlchemy solltest du dir das exzellente Tutorial anschauen.

Im Konstruktor generieren wir solange eine eindeutige ID, bis wir eine finden, die noch nicht belegt ist. Die get_random_uid-Funktion fehlt – wir müssen sie noch in unser utils-Modul einfügen:

from random import sample, randrange

URL_CHARS = 'abcdefghijkmpqrstuvwxyzABCDEFGHIJKLMNPQRST23456789'

def get_random_uid():
    return ''.join(sample(URL_CHARS, randrange(3, 9)))

Wenn das getan ist, können wir python manage.py initdb ausführen, um die Datenbank zu erstellen und python manage.py shell, um damit herumzuspielen:

Interactive Werkzeug Shell
>>> from shorty.models import session, URL

Jetzt können wir einige URLs zu der Datenbank hinzufügen:

>>> urls = [URL('http://example.org/'), URL('http://localhost:5000/')]
>>> URL.query.all()
[]
>>> session.commit()
>>> URL.query.all()
[<URL '5cFbsk'>, <URL 'mpugsT'>]

Wie du sehen kannst, müssen wir session.commit() aufrufen, um die Änderungen in der Datenbank zu speichern. Nun erstellen wir ein privates Element mit einer eigenen UID:

>>> URL('http://werkzeug.pocoo.org/', False, 'werkzeug-webpage')
>>> session.commit()

Dann fragen wir alle ab:

>>> URL.query.filter_by(public=False).all()
[<URL 'werkzeug-webpage'>]
>>> URL.query.filter_by(public=True).all()
[<URL '5cFbsk'>, <URL 'mpugsT'>]
>>> URL.query.get('werkzeug-webpage')
<URL 'werkzeug-webpage'>

Jetzt haben wir einige Datensätze in der Datenbank und wissen ungefähr, auf welche Weise SQLAlchemy funktioniert. Zeit, unsere Views zu erstellen.

Teil 4: Die View-Funktionen

Nachdem wir mit SQLAlchemy herumgespielt haben, können wir zurück zu Werkzeug gehen und anfangen, unsere View-Funktionen zu erstellen. Der Begriff “View-Funktion” kommt von Django. Dort werden die Funktionen, die Templates befüllen und ausgeben, so genannt. Deshalb ist unser Beispiel eine Umsetzung von MVT (Model, View, Template) und nicht etwa MVC (Model, View, Controller). Die beiden Bezeichnungen bedeuten dasselbe, aber es ist viel einfacher, dieselbe Benennung wie Django zu nutzen.

Als Anfang erstellen wir einfach eine View-Funktion für neue URLs und eine Funktion, die eine Nachricht über einen neuen Link darstellt. Das wird der Inhalt unserer noch leeren views.py-Datei:

from werkzeug import redirect
from werkzeug.exceptions import NotFound
from shorty.utils import session, render_template, expose, validate_url, \
     url_for
from shorty.models import URL

@expose('/')
def new(request):
    error = url = ''
    if request.method == 'POST':
        url = request.form.get('url')
        alias = request.form.get('alias')
        if not validate_url(url):
            error = u"Entschuldigung, aber ich kann die angegebene " \
                    u"URL nicht kürzen."
        elif alias:
            if len(alias) > 140:
                error = 'Dein Alias ist zu lang'
            elif '/' in alias:
                error = 'Dein Alias darf keinen Slash beinhalten'
            elif URL.query.get(alias):
                error = 'Der angegeben Alias existiert bereits'
        if not error:
            uid = URL(url, 'private' not in request.form, alias).uid
            session.commit()
            return redirect(url_for('display', uid=uid))
    return render_template('new.html', error=error, url=url)

@expose('/display/<uid>')
def display(request, uid):
    url = URL.query.get(uid)
    if not url:
        raise NotFound()
    return render_template('display.html', url=url)

@expose('/u/<uid>')
def link(request, uid):
    url = URL.query.get(uid)
    if not url:
        raise NotFound()
    return redirect(url.target, 301)

@expose('/list/', defaults={'page': 1})
@expose('/list/<int:page>')
def list(request, page):
    pass

Wieder einmal ziemlich viel Code, aber das meiste ist normale Formularvalidierung. Wir erstellen zwei Funktionen, new und display, und dekorieren sie mit unserem expose-Dekorator aus dem utils-Modul. Dieser Dekorator fügt eine neue Regel zur URL-Map hinzu, indem er alle Parameter zum Konstruktor eines Rule-Objekts übergibt und den Endpoint auf den Namen der Funktion setzt. Damit können wir einfach URLs zu den Funktionen erzeugen – wir nutzen ihre Funktionsnamen als Endpoint.

Denke daran, dass dieser Code nicht unbedingt eine gute Idee für größere Anwendungen ist. In solchen Fällen ist es besser, den vollen Importnamen mit einem allgemeinen Prefix oder etwas Ähnlichem als Endpoint zu nutzen. Sonst wird es ziemlich verwirrend.

Die Formularvalidierung in der new-Methode ist ziemlich simpel. Wir kontrollieren, ob die aktuelle HTTP-Methode POST ist. Falls ja nehmen wir die Daten vom Request und validieren sie. Wenn dort kein Fehler auftritt, erstellen wir ein neues URL-Objekt, übergeben es der Datenbank und leiten auf die Anzeige-Seite um.

Die display-Funktion ist nicht viel komplizierter. Die URL-Rule erwartet einen uid-Parameter, welchen die Funktion entsprechend akzeptiert. Danach holen wir das URL-Objekt mit der angegebenen UID und geben das Template aus, dem wir wiederum das URL-Objekt übergeben.

Wenn die URL nicht existiert, werfen wir eine NotFound-Exception, welche eine statische “404 Seite nicht gefunden”-Seite anzeigt. Wir können diese später mit einer speziellen Fehlerseite ersetzen, indem wir die Exception auffangen, bevor die HTTPException geworfen wird.

Die View-Funktion link wird von unseren Models in der short_url-Eigenschaft genutzt und ist die kurze URL, die wir vermitteln. Wenn also die URL-UID foobar ist, wird die URL unter http://localhost:5000/u/foobar erreichbar sein.

Die View-Funktion list wurde noch nicht geschrieben, das machen wir später. Wichtig ist allerdings, dass die Funktion einen optionalen URL-Parameter akzeptiert. Der erste Dekorator sagt Werkzeug, dass für den Request-Pfad /page/ die erste Seite angezeigt wird (da der Parameter page standardmäßig auf 1 gesetzt wird). Wichtiger ist die Tatsache, dass Werkzeug URLs normalisiert. Wenn du also /page oder /page/1 aufrufst, wirst du in beiden Fällen zu /page/ umgeleitet. Das geschieht automatisch und macht Google glücklich. Wenn du dieses Verhalten nicht magst, kannst du es abschalten.

Und wieder einmal müssen wir zwei Objekte aus dem utils-Modul importieren, die jetzt noch nicht existieren. Eines von diesen soll ein Jinja-Template in ein Response-Objekt verwandeln, das andere prüft eine URL. Fügen wir also diese zu utils.py hinzu:

from os import path
from urlparse import urlparse
from werkzeug import Response
from jinja import Environment, FileSystemLoader

ALLOWED_SCHEMES = frozenset(['http', 'https', 'ftp', 'ftps'])
TEMPLATE_PATH = path.join(path.dirname(__file__), 'templates')
jinja_env = Environment(loader=FileSystemLoader(TEMPLATE_PATH))
jinja_env.globals['url_for'] = url_for

def render_template(template, **context):
    return Response(jinja_env.get_template(template).render(**context),
                    mimetype='text/html')

def validate_url(url):
    return urlparse(url)[0] in ALLOWED_SCHEMES

Im Grunde ist das alles. Die Validierungsfunktion prüft, ob deine URL wie eine HTTP- oder FTP-URL aussieht. Wir machen dies, um uns zu versichern, dass niemand potentiell gefährliches JavaScript oder ähnliche URLs abschickt. Die render_template-Funktion ist auch nicht viel komplizierter, sie schaut nach einem Template im Dateisystem im templates-Ordner und gibt es als Response aus.

Weiterhin fürgen wir die url_for-Funktion in den globalen Kontext des Templates ein, so dass wir auch in Templates URLs erzeugen können.

Da wir jetzt unsere beiden ersten View-Funktionen haben, ist es Zeit, die Templates hinzuzufügen.

Teil 5: Die Templates

Wir haben beschlossen, in diesem Beispiel Jinja-Templates zu nutzen. Wenn du weißt, wie man Django-Templates nutzt, sollte es dir bekannt vorkommen; wenn du bis jetzt mit PHP gearbeitet hast, kannst du Jinja-Templates mit Smarty vergleichen. Wenn du bis jetzt PHP als Templatesprache genutzt hast, solltest du für dein nächstes Projekt mal Mako ansehen.

Sicherheitswarnung: Wir nutzen hier Jinja, welches eine textbasierte Template-Engine ist. Da Jinja nicht weiß, womit es arbeitet, musst du, wenn du HTML-Templates erstellst, alle Werte maskieren, die irgendwann an irgendeinem Punkt irgendeines der folgenden Zeichen enthalten können: >, < oder &. Innerhalb von Attributen musst du außerdem Anführungszeichen maskieren. Du kannst Jinjas |e-Filter für normales Escaping benutzen. Wenn du true als Argument übergibst, maskiert es außerdem Anführungszeichen (|e(true)). Wie du in den Beispielen unterhalb sehen kannst, maskieren wir die URLs nicht. Der Grund dafür ist, dass wir keine & in den URLs haben und deshalb ist es sicher, auf Escaping zu verzichten.

Der Einfachheit halber werden wir HTML 4 in unseren Templates nutzen. Wenn du etwas Erfahrung mit XHTML hast, kannst du sie in XHTML schreiben. Aber beachte, dass das Beispiel-Stylesheet unten nicht mit XHTML funktioniert.

Eine coole Sache, die Jinja von Django übernommen hat, ist Templatevererbung. Das bedeutet, dass wir oft genutzte Stücke in ein Basistemplate auslagern und es mit Platzhaltern füllen können. Beispielsweise landen der Doctype und der HTML-Rahmen in der Datei templates/layout.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
  <title>Shorty</title>
</head>
<body>
  <h1><a href="{{ url_for('new') }}">Shorty</a></h1>
  <div class="body">{% block body %}{% endblock %}</div>
  <div class="footer">
    <a href="{{ url_for('new') }}">Neu</a> |
    <a href="{{ url_for('list') }}">Liste</a> |
    Benutze Shorty für Gutes, nicht für Böses
  </div>
</body>
</html>

Von diesem Template können wir in unserer templates/new.html erben:

{% extends 'layout.html' %}
{% block body %}
  <h2>Erstelle eine Shorty-URL!</h2>
  {% if error %}<div class="error">{{ error }}</div>{% endif -%}
  <form action="" method="post">
    <p>Gebe die URL an, die du kürzen willst</p>
    <p><input type="text" name="url" id="url" value="{{ url|e(true) }}"></p>
    <p>Optional kannst du der URL einen merkbaren Namen geben</p>
    <p><input type="text" id="alias" name="alias">{#
     #}<input type="submit" id="submit" value="Mach!"></p>
    <p><input type="checkbox" name="private" id="private">
       <label for="private">mache diese URL privat, also zeige sie
         nicht auf der Liste</label></p>
  </form>
{% endblock %}

Wenn du dich über den Kommentar zwischen den beiden input-Elementen wunderst, das ist ein sauberer Trick, die Templates sauber zu halten ohne Leerzeichen zwischen beide Elemente zu setzen. Wir haben ein Stylesheet vorbereitet, welches dort keine Leerzeichen erwartet.

Ein zweites Template für die Anzeige-Seite (templates/display.html):

{% extends 'layout.html' %}
{% block body %}
  <h2>Verkürzte URL</h2>
  <p>
    Die URL {{ url.target|urlize(40, true) }}
    wurde gekürzt zu {{ url.short_url|urlize }}.
  </p>
{% endblock %}

Jinjas urlize-Filter übersetzt eine URL in einem Text in einen klickbaren Link. Wenn du ihm einen Integer übergibst, wird er den angezeigten Text auf diese Anzahl Zeichen kürzen; wenn du true als zweiten Parameter übergibst, wird ein nofollow-Flag hinzugefügt.

Da wir jetzt unsere ersten beiden Templates fertig haben, ist es Zeit, den Server zu starten und auf den Teil der Anwendung zu schauen, der bereits funktioniert: neue URLs hinzufügen und weitergeleitet werden.

Zwischenschritt: Das Design hinzufügen

Jetzt ist es Zeit, etwas anderes zu machen: ein Design hinzufügen. Designelemente sind normalerweise in statischen CSS-Stylesheets definiert. Also müssen wir einige statische Dateien irgendwo ablegen – aber das ist ein wenig kompliziert. Wenn du mit bis jetzt mit PHP gearbeitet hast, wirst du gemerkt haben, dass es hier nichts gibt, was eine URL zum Dateisystempfad übersetzt und so direkt auf statische Dateien zugreift. Du musst dem Webserver oder unserem Entwicklungsserver explizit sagen, dass es einen Pfad gibt, der die statischen Dateien beinhaltet.

Django empfiehlt eine separate Subdomain und einen eigenen Server für die statischen Dateien, was eine sehr gute Idee für Umgebungen mit hoher Serverlast ist, aber zu viel des Guten für diese simple Anwendung.

Hier also folgendes Vorgehen: Wir lassen unsere Anwendung die statischen Dateien ausliefern, aber im Produktionsmodus solltest du dem Apachen mitteilen, dass er diese Dateien selbst ausliefern soll. Das geschieht mit Hilfe der Alias-Direktive in der Konfiguration von Apache:

Alias /static /path/to/static/files

Das ist um einiges schneller.

Und wie sagen wir unserer Anwendung, dass sie den Ordner mit statischen Dateien als /static verfügbar machen soll? Glücklicherweise ist das ziemlich einfach, da Werkzeug dafür eine WSGI-Middleware liefert. Es gibt zwei Möglichkeiten, diese zu integrieren: Entweder wrappen wir die ganze Anwendung in diese Middleware (diesen Weg empfehlen wir wirklich nicht) oder wir wrappen nur die Ausführungsfunktion (viel besser, weil wir die Referenz auf das Anwendungsobjekt nicht verlieren). Also gehen wir zurück zur application.py und passen den Code ein wenig an.

Als Erstes musst du einen neuen Import hinzufügen und den Pfad zu den statischen Dateien ermitteln:

from os import path
from werkzeug import SharedDataMiddleware

STATIC_PATH = path.join(path.dirname(__file__), 'static')

Es wäre besser, die Pfad-Manipulation in die utils.py-Datei zu verschieben, weil wir den Template-Pfad bereits dort ermittelt haben. Aber das ist nicht wirklich von Interesse, und wegen der Einfachheit können wir es im Anwendungsmodul lassen.

Wie können wir also die Ausführungsfunktion wrappen? Theoretisch müssen wir einfach self.__call__ = wrap(self.__call__) schreiben, doch leider klappt das so nicht in Python. Es ist aber nicht viel schwieriger: Benenne einfach __call__ in dispatch um und füge eine neue __call__-Methode hinzu:

def __call__(self, environ, start_response):
    return self.dispatch(environ, start_response)

Jetzt können wir in unsere __init__-Funktion gehen und die Middleware zuschalten, indem wir die dispatch-Methode einschieben:

self.dispatch = SharedDataMiddleware(self.dispatch, {
    '/static':  STATIC_PATH
})

Das war jetzt nicht schwer. Mit diesem Weg können wir WSGI-Middlewares in der Anwendungsklasse einhaken!

Eine andere gute Idee ist es, unserer url_map im utils-Modul den Ort unserer statischen Dateien mitzuteilen, indem wir eine Regel hinzufügen. Auf diesem Weg können wir URLs zu den statischen Dateien in den Templates generieren:

url_map = Map([Rule('/static/<file>', endpoint='static', build_only=True)])

Jetzt können wir unsere templates/layout.html-Datei wieder öffnen und einen Link zum style.css-Stylesheet hinzufügen, welches wir danach erstellen werden:

<link rel="stylesheet" type="text/css" href="{{ url_for('static',
  file='style.css') }}">

Das geht natürlich in den <head>-Tag, wo zur Zeit nur der Titel festgelegt ist.

Du kannst jetzt ein nettes Layout gestalten oder das Beispiel-Stylesheet nutzen. In beiden Fällen musst du es in die Datei static/style.css einfügen.

Teil 6: Öffentliche URLs auflisten

Jetzt wollen wir alle öffentlichen URLs auf der “List”-Seite auflisten. Das sollte kein großes Problem sein, aber wir wollen auch eine Art Seitenumbruch haben. Da wir alle URLs auf einmal ausgeben, haben wir früher oder später eine endlose Seite, die Minuten zum Laden benötigt.

Beginnen wir also mit dem Hinzufügen einer Pagination-Klasse zu unserem utils-Modul:

from werkzeug import cached_property

class Pagination(object):

    def __init__(self, query, per_page, page, endpoint):
        self.query = query
        self.per_page = per_page
        self.page = page
        self.endpoint = endpoint

    @cached_property
    def count(self):
        return self.query.count()

    @cached_property
    def entries(self):
        return self.query.offset((self.page - 1) * self.per_page) \
                         .limit(self.per_page).all()

    has_previous = property(lambda x: x.page > 1)
    has_next = property(lambda x: x.page < x.pages)
    previous = property(lambda x: url_for(x.endpoint, page=x.page - 1))
    next = property(lambda x: url_for(x.endpoint, page=x.page + 1))
    pages = property(lambda x: max(0, x.count - 1) // x.per_page + 1)

Dies ist eine sehr einfache Klasse, die das meiste der Seitenumbrüche für uns übernimmt. Wir können ihr eine unausgeführte SQLAlchemy-Abfrage (Query) übergeben, die Anzahl der Elemente pro Seite, die aktuelle Seite und den Endpoint, welcher für die URL-Generation benutzt wird. Der cached_property-Dekorator funktioniert, wie du siehst, fast genau so wie der normale property-Dekorator, mit der Ausnahme, dass er sich das Ergebnis merkt. Wir werden diese Klasse nicht genau besprechen, aber das Grundprinzip ist, dass ein Zugriff auf pagination.entries die Elemente für die aktuelle Seite ausgibt und dass die anderen Eigenschaften Werte zurückgeben, sodass wir sie im Template nutzen können.

Jetzt können wir die Pagination-Klasse in unser Views-Modul importieren und etwas Code zu der list-Funktion hinzufügen:

from shorty.utils import Pagination

@expose('/list/', defaults={'page': 1})
@expose('/list/<int:page>')
def list(request, page):
    query = URL.query.filter_by(public=True)
    pagination = Pagination(query, 30, page, 'list')
    if pagination.page > 1 and not pagination.entries:
        raise NotFound()
    return render_template('list.html', pagination=pagination)

Die If-Bedingung in dieser Funktion versichert, dass ein Statuscode 404 zurückgegeben wird, wenn wir nicht auf der ersten Seite sind und es keine Einträge zum Anzeigen gibt (einen Aufruf von /list/42 ohne Einträge auf dieser Seite nicht mit einem 404 zu quittieren, wäre schlechter Stil).

Und schließlich das Template:

{% extends 'layout.html' %}
{% block body %}
  <h2>URL Liste</h2>
  <ul>
  {%- for url in pagination.entries %}
    <li><a href="{{ url.short_url|e }}">{{ url.uid|e }}</a> &raquo;
        <small>{{ url.target|urlize(38, true) }}</small></li>
  {%- else %}
    <li><em>keine URLs bis jetzt gekürzt</em></li>
  {%- endfor %}
  </ul>
  <div class="pagination">
    {%- if pagination.has_previous %}<a href="{{ pagination.previous
        }}">&laquo; Vorherige</a>
    {%- else %}<span class="inactive">&laquo; Vorherige</span>{% endif %}
    | {{ pagination.page }} |
    {% if pagination.has_next %}<a href="{{ pagination.next
      }}">Nächste &raquo;</a>
    {%- else %}<span class="inactive">Nächste &raquo;</span>{% endif %}
  </div>
{% endblock %}

Bonus: 404-Fehlerseiten gestalten

Jetzt, da wir unsere Anwendung fertig gestellt haben, können wir kleine Verbesserungen vornehmen, zum Beispiel eigene 404-Fehlerseiten. Das ist ziemlich einfach. Das Erste, was wir machen müssen, ist eine neue Funktion namens not_found in den Views zu erstellen, welche ein Template ausgibt:

def not_found(request):
    return render_template('not_found.html')

Dann müssen wir in unser Anwendungsmodul wechseln und die NotFound-Exception importieren:

from werkzeug.exceptions import NotFound

Schließlich müssen wir sie auffangen und in eine Response umwandeln. Dieser except-Block kommt vor den except-Bock mit HTTPException:

try:
    # das bleibt das gleiche
except NotFound, e:
    response = views.not_found(request)
    response.status_code = 404
except HTTPException, e:
    # das bleibt das gleiche

Jetzt noch templates/not_found.html hinzufügen und du bist fertig:

{% extends 'layout.html' %}
{% block body %}
  <h2>Seite nicht gefunden</h2>
  <p>
    Die aufgerufene Seite existiert nicht auf dem Server.  Vielleicht
    willst du eine <a href="{{ url_for('new') }}">neue URL
    hinzufügen</a>?
  </p>
{% endblock %}

Abschluss

Dieses Tutorial behandelt alles, was du brauchst, um mit Werkzeug, SQLAlchemy und Jinja anzufangen und sollte dir helfen, die beste Lösung für deine Anwendung zu finden. Für einige größere Beispiele, die außerdem einen anderen Aufbau und Ideen zum Ausführen benutzen, solltest du mal einen Blick auf den Beispielordner werfen.

In diesem Sinne: Viel Spaß mit Werkzeug!

PK܉87f//docs/utils.html Utilities // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Utilities

Werkzeug comes with a bunch of utilties that can be useful for WSGI applications. Most of the classes provided by this module are used by the wrappers, you can however use them without the wrappers, too.

All the utilities can be directly imported from the werkzeug module.

Data Structures

class MultiDict

A MultiDict is a dictionary subclass customized to deal with multiple values for the same key which is for example used by the parsing functions in the wrappers. This is necessary because some HTML form elements pass multiple values for the same key.

MultiDict implements the all standard dictionary methods. Internally, it saves all values for a key as a list, but the standard dict access methods will only return the first value for a key. If you want to gain access to the other values too you have to use the list methods as explained below.

Basic Usage:

>>> d = MultiDict([('a', 'b'), ('a', 'c')])
>>> d
MultiDict([('a', 'b'), ('a', 'c')])
>>> d['a']
'b'
>>> d.getlist('a')
['b', 'c']
>>> 'a' in d
True

It behaves like a normal dict thus all dict functions will only return the first value when multiple values for one key are found.

From Werkzeug 0.3 onwards, the KeyError raised by this class is also a subclass of the BadRequest HTTP exception and will render a page for a 400 BAD REQUEST if catched in a catch-all for HTTP exceptions.

The following additional methods exist that are used to get or set the other values for a key or convert:

get (key, default=None, type=None)

Return the default value if the requested data doesn’t exist. If type is provided and is a callable it should convert the value, return it or raise a ValueError if that is not possible. In this case the function will return the default as if the value was not found.

Example:

>>> d = MultiDict(foo='42', bar='blub')
>>> d.get('foo', type=int)
42
>>> d.get('bar', -1, type=int)
-1
getlist (key, type=None)

Return the list of items for a given key. If that key is not in the MultiDict, the return value will be an empty list. Just as get getlist accepts a type parameter. All items will be converted with the callable defined there.

returns: list

setlist (key, new_list)

Remove the old values for a key and add new ones. Note that the list you pass the values in will be shallow-copied before it is inserted in the dictionary.

>>> multidict.setlist('foo', ['1', '2'])
>>> multidict['foo']
'1'
>>> multidict.getlist('foo')
['1', '2']
setlistdefault (key, default_list=())
Like setdefault but sets multiple values.

These functions work like the functions with the same name without list. Unlike the regular dict functions those operate on all the values as lists, not only on the first one:

lists, listvalues, iterlists, iterlistvalues, poplist, and popitemlist.

Also notable: update adds values and does not replace existing ones.

class CombinedMultiDict

A read only MultiDict decorator that you can pass multiple MultiDict instances as sequence and it will combine the return values of all wrapped dicts:

>>> from werkzeug import MultiDict, CombinedMultiDict
>>> post = MultiDict([('foo', 'bar')])
>>> get = MultiDict([('blub', 'blah')])
>>> combined = CombinedMultiDict([get, post])
>>> combined['foo']
'bar'
>>> combined['blub']
'blah'

This works for all read operations and will raise a TypeError for methods that usually change data which isn’t possible.

From Werkzeug 0.3 onwards, the KeyError raised by this class is also a subclass of the BadRequest HTTP exception and will render a page for a 400 BAD REQUEST if catched in a catch-all for HTTP exceptions.

class Headers

An object that stores some headers. It has a dict like interface but is ordered and can store keys multiple times.

This data structure is useful if you want a nicer way to handle WSGI headers which are stored as tuples in a list.

From Werkzeug 0.3 onwards, the KeyError raised by this class is also a subclass of the BadRequest HTTP exception and will render a page for a 400 BAD REQUEST if catched in a catch-all for HTTP exceptions.

__init__ (defaults=None, _list=None)
Create a new Headers object based on a list or dict of headers which are used as default values. This does not reuse the list passed to the constructor for internal usage. To create a Headers object that uses as internal storage the list or list-like object provided it’s possible to use the linked classmethod.
linked (headerlist)

Create a new Headers object that uses the list of headers passed as internal storage:

>>> headerlist = [('Content-Length', '40')]
>>> headers = Headers.linked(headerlist)
>>> headers.add('Content-Type', 'text/html')
>>> headerlist
[('Content-Length', '40'), ('Content-Type', 'text/html')]

returns: new linked Headers object.

get (key, default=None, type=None)

Return the default value if the requested data doesn’t exist. If type is provided and is a callable it should convert the value, return it or raise a ValueError if that is not possible. In this case the function will return the default as if the value was not found.

Example:

>>> d = Headers([('Content-Length', '42')])
>>> d.get('Content-Length', type=int)
42

If a headers object is bound you must notadd unicode strings because no encoding takes place.

getlist (key, type=None)

Return the list of items for a given key. If that key is not in the MultiDict, the return value will be an empty list. Just as get getlist accepts a type parameter. All items will be converted with the callable defined there.

returns: list

add (key, value)
add a new header tuple to the list
set (key, value)
remove all header tuples for key and add a new one
extend (iterable)
Extend the headers with a dict or an iterable yielding keys and values.
clear ()
clears all headers
to_list (charset='utf-8')

Convert the headers into a list and converts the unicode header items to the specified charset.

returns: list

__contains__ (key)
Check if a key is present.
__iter__ ()
Yield (key, value) tuples.

All the other dict functions such as iterkeys are available and work the same.

class EnvironHeaders

Read only version of the headers from a WSGI environment. This provides the same interface as Headers and is constructed from a WSGI environment.

From Werkzeug 0.3 onwards, the KeyError raised by this class is also a subclass of the BadRequest HTTP exception and will render a page for a 400 BAD REQUEST if catched in a catch-all for HTTP exceptions.

Working with HTTP Headers

parse_set_header (value, on_update=None)
Parse a set like header and return a HeaderSet object. The return value is an object that treats the items case insensitive and keeps the order of the items.
parse_list_header (value)

Parse lists as described by RFC 2068 Section 2.

In particular, parse comma-separated lists where the elements of the list may include quoted-strings. A quoted-string could contain a comma. A non-quoted string could have quotes in the middle. Quotes are removed automatically after parsing.

parse_dict_header (value)
Parse lists of key, value paits as described by RFC 2068 Section 2 and convert them into a python dict. If there is no value for a key it will be None.
dump_header (iterable, allow_token=True)

Dump an HTTP header again. This is the reversal of parse_list_header, parse_set_header and parse_dict_header. This also quotes strings that include an equals sign unless you pass it as dict of key, value pairs.

The allow_token parameter can be set to False to disallow tokens as values. If this is enabled all values are quoted.

class Accept

An Accept object is just a list subclass for lists of (value, quality) tuples. It is automatically sorted by quality.

provided
True if the Accept object was created from a list, False if the initial value was None. This is used by the request wrappers to keep the information if the header was present or not.
best
The best value (does not return a tuple!).
__getitem__ (key)
Beside index lookup (getting item n) you can also pass it a string to get the quality for the item. If the item is not in the list, the returned quality is 0.
find (key)
Get the position of an entry or return -1
values ()
Return a list of the values, not the qualities.
itervalues ()
Iterate over all values.
parse_accept_header (value)

Parses an HTTP Accept-* header. This does not implement a complete valid algorithm but one that supports at least value and quality extraction.

Returns a new Accept object (basicly a list of (value, quality) tuples sorted by the quality with some additional accessor methods).

class CacheControl

Subclass of a dict that stores values for a Cache-Control header. It has accesors for all the cache-control directives specified in RFC 2616. The class does not differentiate between request and response directives.

Because the cache-control directives in the HTTP header use dashes the python descriptors use underscores for that.

To get a header of the CacheControl object again you can convert the object into a string or call the to_header() function. If you plan to subclass it and add your own items have a look at the sourcecode for that class.

The following attributes are exposed:

no_cache, no_store, max_age, max_stale, min_fresh, no_transform, only_if_cached, public, private, must_revalidate, proxy_revalidate, and s_maxage

parse_cache_control_header (value, on_update=None)
Parse a cache control header. The RFC differs between response and request cache control, this method does not. It’s your responsibility to not use the wrong control statements.
class HeaderSet

Similar to the ETags class this implements a set like structure. Unlike ETags this is case insensitive and used for vary, allow, and content-language headers.

If not constructed using the parse_set_header function the instanciation works like this:

>>> hs = HeaderSet(['foo', 'bar', 'baz'])
>>> hs
HeaderSet(['foo', 'bar', 'baz'])
add (header)
Add a new header to the set.
remove (header)
Remove a layer from the set. This raises an IndexError if the header is not in the set.
update (iterable)
Add all the headers from the iterable to the set.
discard (header)
Like remove but ignores errors.
find (header)
Return the index of the header in the set or return -1 if not found.
index (header)
Return the index of the headerin the set or raise an IndexError.
clear ()
Clear the set.
as_set (preserve_casing=False)

Return the set as real python set structure. When calling this all the items are converted to lowercase and the ordering is lost.

If preserve_casing is True the items in the set returned will have the original case like in the HeaderSet, otherwise they will be lowercase.

to_header ()
Convert the header set into an HTTP header string.
class UserAgent

Represents a user agent. Pass it a WSGI environment or an user agent string and you can inspect some of the details from the user agent string via the attributes. The following attribute exist:

  • string, the raw user agent string
  • platform, the browser platform
  • browser, the name of the browser
  • version, the version of the browser
  • language, the language of the browser
parse_date (value)

Parse one of the following date formats into a datetime object:

Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format

If parsing fails the return value is None.

cookie_date (expires=None)

Formats the time to ensure compatibility with Netscape’s cookie standard.

Accepts a floating point number expressed in seconds since the epoc in, a datetime object or a timetuple. All times in UTC. The parse_date function in werkzeug.http can be used to parse such a date.

Outputs a string in the format Wdy, DD-Mon-YYYY HH:MM:SS GMT.

http_date (timestamp=None)

Formats the time to match the RFC1123 date format.

Accepts a floating point number expressed in seconds since the epoc in, a datetime object or a timetuple. All times in UTC. The parse_date function in werkzeug.http can be used to parse such a date.

Outputs a string in the format Wdy, DD Mon YYYY HH:MM:SS GMT.

parse_form_data (environ, stream_factory=None, charset='utf-8', errors='ignore')

Parse the form data in the environ and return it as tuple in the form (stream, form, files). You should only call this method if the transport method is POST or PUT.

If the mimetype of the data transmitted is multipart/form-data the files multidict will be filled with FileStorage objects. If the mimetype is unknow the input stream is wrapped and returned as first argument, else the stream is empty.

class ETags

A set that can be used to check if one etag is present in a collection of etags.

is_weak (etag)
Check if an etag is weak.
contains_weak (etag)
Check if an etag is part of the set including weak and strong tags.
contains (etag)
Check if an etag is part of the set ignoring weak tags.
contains_raw (etag)
When passed a quoted tag it will check if this tag is part of the set. If the tag is weak it is checked against weak and strong tags, otherwise weak only.
as_set (include_weak=False)
Convert the ETags object into a python set. Per default all the weak etags are not part of this set.
to_header ()
Convert the etags set into a HTTP header string.
parse_etags (value)
Parse and etag header. Returns an ETags object.
quote_etag (etag, weak=False)
Quote an etag.
unquote_etag (etag)
Unquote a single etag. Return a (etag, weak) tuple.
generate_etag (data)
Generate an etag for some data.
is_resource_modified (environ, etag=None, data=None, last_modified=None)
Convenience method for conditional requests.
class Authorization

Represents an Authorization header sent by the client. You should not create this kind of object yourself but use it when it’s returned by the parse_authorization_header function.

This object is a dict subclass and can be altered by setting dict items but it should be considered immutable as it’s returned by the client and not meant for modifications.

to_header ()
Convert the stored values into a WWW-Authenticate header.
username
The username transmitted. This is set for both basic and digest auth all the time.
password
When the authentication type is basic this is the password transmitted by the client, else None.
realm
This is the server realm send back for digest auth. For HTTP digest auth.
nonce
The nonce the server send for digest auth, send back by the client. A nonce should be unique for every 401 response for HTTP digest auth.
uri
The URI from Request-URI of the Request-Line; duplicated because proxies are allowed to change the Request-Line in transit. HTTP digest auth only.
nc
The nonce count value transmitted by clients if a qop-header is also transmitted. HTTP digest auth only.
cnonce
If the server sent a qop-header in the WWW-Authenticate header, the client has to provide this value for HTTP digest auth. See the RFC for more details.
response
A string of 32 hex digits computed as defined in RFC 2617, which proves that the user knows a password. Digest auth only.
opaque
The opaque header from the server returned unchanged by the client. It is recommended that this string be base64 or hexadecimal data. Digest auth only.
qop
Indicates what “quality of protection” the client has applied to the message for HTTP digest auth.
class WWWAuthenticate

Provides simple access to WWW-Authenticate headers.

set_basic (realm='authentication required')
Clear the auth info and enable basic auth.
set_digest (realm, nonce, qop=('auth',), opaque=None, algorithm=None, stale=False)
Clear the auth info and enable digest auth.
to_header ()
Convert the stored values into a WWW-Authenticate header.
type
The type of the auth machanism. HTTP currently specifies Basic and Digest.
realm
A string to be displayed to users so they know which username and password to use. This string should contain at least the name of the host performing the authentication and might additionally indicate the collection of users who might have access.
stale
A flag, indicating that the previous request from the client was rejected because the nonce value was stale.
domain
A list of URIs that define the protection space. If a URI is an absolte path, it is relative to the canonical root URL of the server being accessed.
nonce
A server-specified data string which should be uniquely generated each time a 401 response is made. It is recommended that this string be base64 or hexadecimal data.
opaque
A string of data, specified by the server, which should be returned by the client unchanged in the Authorization header of subsequent requests with URIs in the same protection space. It is recommended that this string be base64 or hexadecimal data.
algorithm
A string indicating a pair of algorithms used to produce the digest and a checksum. If this is not present it is assumed to be “MD5”. If the algorithm is not understood, the challenge should be ignored (and a different one used, if there is more than one).
qop
A set of quality-of-privacy modifies such as auth and auth-int.
parse_authorization_header (value)
Parse an HTTP basic/digest authorization header transmitted by the web browser. The return value is either None if the header was invalid or not given, otherwise an Authorization object.
parse_www_authenticate_header (value, on_update=None)
Parse an HTTP WWW-Authenticate header into a WWWAuthenticate object.
HTTP_STATUS_CODES
A dict of status code -> default status message pairs. This is used by the wrappers and other places where a integer status code is expanded to a string throughout Werkzeug.

URL Helpers

url_decode (s, charset='utf-8', decode_keys=False, include_empty=True, errors='ignore')

Parse a querystring and return it as MultiDict. Per default only values are decoded into unicode strings. If decode_keys is set to True the same will happen for keys.

Per default a missing value for a key will default to an empty key. If you don’t want that behavior you can set include_empty to False.

Per default encoding errors are ignore. If you want a different behavior you can set errors to 'replace' or 'strict'. In strict mode a HTTPUnicodeError is raised.

url_encode (obj, charset='utf-8', encode_keys=False)
URL encode a dict/MultiDict. If a value is None it will not appear in the result string. Per default only values are encoded into the target charset strings. If encode_keys is set to True unicode keys are supported too.
url_quote (s, charset='utf-8', safe='/:')
URL encode a single string with a given encoding.
url_quote_plus (s, charset='utf-8', safe='')
URL encode a single string with the given encoding and convert whitespace to “+”.
url_unquote (s, charset='utf-8', errors='ignore')

URL decode a single string with a given decoding.

Per default encoding errors are ignore. If you want a different behavior you can set errors to 'replace' or 'strict'. In strict mode a HTTPUnicodeError is raised.

url_unquote_plus (s, charset='utf-8', errors='ignore')

URL decode a single string with the given decoding and decode a “+” to whitespace.

Per default encoding errors are ignore. If you want a different behavior you can set errors to 'replace' or 'strict'. In strict mode a HTTPUnicodeError is raised.

url_fix (s, charset='utf-8')

Sometimes you get an URL by a user that just isn’t a real URL because it contains unsafe characters like ‘ ‘ and so on. This function can fix some of the problems in a similar way browsers handle data entered by the user:

>>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffsklärung)')
'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'
Parameters
charset: The target charset for the URL if the url was given as unicode string.
class Href

Implements a callable that constructs URLs with the given base. The function can be called with any number of positional and keyword arguments which than are used to assemble the URL. Works with URLs and posix paths.

Positional arguments are appended as individual segments to the path of the URL:

>>> href = Href('/foo')
>>> href('bar', 23)
'/foo/bar/23'
>>> href('foo', bar=23)
'/foo/foo?bar=23'

If any of the arguments (positional or keyword) evaluates to None it will be skipped. If no keyword arguments are given the last argument can be a dict or MultiDict (or any other dict subclass), otherwise the keyword arguments are used for the query parameters, cutting off the first trailing underscore of the parameter name:

>>> href(is_=42)
'/foo?is=42'

Accessing attributes on the href object creates a new href object with the attribute name as prefix:

>>> bar_href = href.bar
>>> bar_href("blub")
'/foo/bar/blub'

HTML Helpers

escape (s, quote=False)

Replace special characters “&”, “<” and “>” to HTML-safe sequences. If the optional flag quote is True, the quotation mark character (“) is also translated.

There is a special handling for None which escapes to an empty string.

unescape (s)
The reverse function of escape. This unescapes all the HTML entities, not only the XML entities inserted by escape.
class HTMLBuilder

Helper object for HTML generation.

Per default there are two instances of that class. The html one, and the xhtml one for those two dialects. The class uses keyword parameters and positional parameters to generate small snippets of HTML.

Keyword parameters are converted to XML/SGML attributes, positional arguments are used as children. Because Python accepts positional arguments before keyword arguments it’s a good idea to use a list with the star-syntax for some children:

>>> html.p(class_='foo', *[html.a('foo', href='foo.html'), ' ',
...                        html.a('bar', href='bar.html')])
'<p class="foo"><a href="foo.html">foo</a> <a href="bar.html">bar</a></p>'

This class works around some browser limitations and can not be used for arbitrary SGML/XML generation. For that purpose lxml and similar libraries exist.

Calling the builder escapes the string passed:

>>> html.p(html("<foo>"))
'<p>&lt;foo&gt;</p>'

WSGI Helpers

class ClosingIterator

The WSGI specification requires that all middlewares and gateways respect the close callback of an iterator. Because it is useful to add another close action to a returned iterator and adding a custom iterator is a boring task this class can be used for that:

return ClosingIterator(app(environ, start_response), [cleanup_session,
                                                      cleanup_locals])

If there is just one close function it can be bassed instead of the list.

A closing iterator is non needed if the application uses response objects and finishes the processing if the resonse is started:

try:
    return response(environ, start_response)
finally:
    cleanup_session()
    cleanup_locals()
class SharedDataMiddleware

A WSGI middleware that provides static content for development environments or simple server setups. Usage is quite simple:

import os
from werkzeug import SharedDataMiddleware

app = SharedDataMiddleware(app, {
    '/shared': os.path.join(os.path.dirname(__file__), 'shared')
})

The contents of the folder ./shared will now be available on http://example.com/shared/. This is pretty useful during development because a standalone media server is not required. One can also mount files on the root folder and still continue to use the application because the shared data middleware forwards all unhandled requests to the application, even if the requests are below one of the shared folders.

If pkg_resources is available you can also tell the middleware to serve files from package data:

app = SharedDataMiddleware(app, {
    '/shared': ('myapplication', 'shared_files')
})

This will then serve the shared_files folder in the myapplication python package.

class DispatcherMiddleware

Allows one to mount middlewares or application in a WSGI application. This is useful if you want to combine multiple WSGI applications:

app = DispatcherMiddleware(app, {
    '/app2':        app2,
    '/app3':        app3
})
class FileStorage

The FileStorage object is a thin wrapper over incoming files. It is used by the request object to represent uploaded files. All the attributes of the wrapper stream are proxied by the file storage so it’s possible to do storage.read() instead of the long form storage.stream.read().

name
The name of the form field which represents the data.
filename
The incoming filename.
content_type
The mimetype of the file. E.g. 'text/html'.
content_length / len()
The expected length of the file.
stream
Gives you access to the underlaying stream. Note that the exact methods of this stream and its type is not strictly specified. In most situations it will be a TemporaryFile object.
__init__ (stream=None, filename=None, name=None, content_type='application/octet-stream', content_length=-1)

Creates a new FileStorage object.

Parameters

stream: the input stream for uploaded file. Usually this points to a temporary file.

filename: The filename of the file on the client.

name: the name of the form field

content_type: the content type of the file

content_length: the content length of the file.

save (dst, buffer_size=16384)
Save the file to a destination path or file object. If the destination is a file object you have to close it yourself after the call. The buffer size is the number of bytes held in the memory during the copy process. It defaults to 16KB.
get_host (environ)
Return the real host for the given WSGI enviornment. This takes care of the X-Forwarded-Host header.
get_current_url (environ, root_only=False, strip_querystring=False, host_only=False)

A handy helper function that recreates the full URL for the current request or parts of it. Here an example:

>>> env = create_environ("/?param=foo", "http://localhost/script")
>>> get_current_url(env)
'http://localhost/script/?param=foo'
>>> get_current_url(env, root_only=True)
'http://localhost/script/'
>>> get_current_url(env, host_only=True)
'http://localhost/'
>>> get_current_url(env, strip_querystring=True)
'http://localhost/script/'
responder (f)

Marks a function as responder. Decorate a function with it and it will automatically call the return value as WSGI application.

Example:

@responder
def application(environ, start_response):
    return Response('Hello World!')
create_environ (path='/', base_url=None, query_string=None, method='GET', input_stream=None, content_type=None, content_length=0, errors_stream=None, multithread=False, multiprocess=False, run_once=False)

Create a new WSGI environ dict based on the values passed. The first parameter should be the path of the request which defaults to ‘/’. The second one can either be a absolute path (in that case the host is localhost:80) or a full path to the request with scheme, netloc port and the path to the script.

If the path contains a query string it will be used, even if the query_string parameter was given. If it does not contain one the query_string parameter is used as querystring. In that case it can either be a dict, MultiDict or string.

The following options exist:

method
The request method. Defaults to GET
input_stream
The input stream. Defaults to an empty read only stream.
content_type
The content type for this request. Default is an empty content type.
content_length
The value for the content length header. Defaults to 0.
errors_stream
The wsgi.errors stream. Defaults to sys.stderr.
multithread
The multithreaded flag for the WSGI Environment. Defaults to False.
multiprocess
The multiprocess flag for the WSGI Environment. Defaults to False.
run_once
The run_once flag for the WSGI Environment. Defaults to False.
run_wsgi_app (app, environ, buffered=False)

Return a tuple in the form (app_iter, status, headers) of the application output. This works best if you pass it an application that returns a iterator all the time.

Sometimes applications may use the write() callable returned by the start_response function. This tries to resolve such edge cases automatically. But if you don’t get the expected output you should set buffered to True which enforces buffering.

If passed an invalid WSGI application the behavior of this function is undefined. Never pass non-conforming WSGI applications to this function.

Helper Functions

cached_property (func, name=None, doc=None)

A decorator that converts a function into a lazy property. The function wrapped is called the first time to retrieve the result and than that calculated result is used the next time you access the value:

class Foo(object):

    @cached_property
    def foo(self):
        # calculate something important here
        return 42
environ_property (name, default=None, load_func=None, dump_func=None, read_only=None, doc=None)

Maps request attributes to environment variables. This works not only for the Werzeug request object, but also any other class with an environ attribute:

>>> class test_p(object):
...     environ = { 'test': 'test' }
...     test = environ_property('test')
>>> var = test_p()
>>> var.test
test

If you pass it a second value it’s used as default if the key does not exist, the third one can be a converter that takes a value and converts it. If it raises ValueError or TypeError the default value is used. If no default value is provided None is used.

Per default the property is read only. You have to explicitly enable it by passing read_only=False to the constructor.

header_property (name, default=None, load_func=None, dump_func=None, read_only=None, doc=None)
Like environ_property but for headers.
parse_cookie (header, charset='utf-8', errors='ignore')

Parse a cookie. Either from a string or WSGI environ.

Per default encoding errors are ignore. If you want a different behavior you can set errors to 'replace' or 'strict'. In strict mode a HTTPUnicodeError is raised.

dump_cookie (key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False, charset='utf-8', sync_expires=True)

Creates a new Set-Cookie header without the Set-Cookie prefix The parameters are the same as in the cookie Morsel object in the Python standard library but it accepts unicode data too.

Parameters

max_age: should be a number of seconds, or None (default) if the cookie should last only as long as the client’s browser session. Additionally timedelta objects are accepted too.

expires: should be a datetime object or unix timestamp.

path: limits the cookie to a given path, per default it will span the whole domain.

domain: Use this if you want to set a cross-domain cookie. For example, domain=".example.com" will set a cookie that is readable by the domain www.example.com, foo.example.com etc. Otherwise, a cookie will only be readable by the domain that set it.

secure: The cookie will only be available via HTTPS

httponly: disallow JavaScript to access the cookie. This is an extension to the cookie standard and probably not supported by all browsers.

charset: the encoding for unicode values.

sync_expires: automatically set expires if max_age is defined but expires not.

redirect (location, code=302)
Return a response object (a WSGI application) that, if called, redirects the client to the target location. Supported codes are 301, 302, 303, 305, and 307. 300 is not supported because it’s not a real redirect and 304 because it’s the answer for a request with a request with defined If-Modified-Since headers.
append_slash_redirect (environ, code=301)
Redirect to the same URL but with a slash appended. The behavior of this function is undefined if the path ends with a slash already.
import_string (import_name, silent=False)

Imports an object based on a string. This use useful if you want to use import paths as endpoints or something similar. An import path can be specified either in dotted notation (xml.sax.saxutils.escape) or with a colon as object delimiter (xml.sax.saxutils:escape).

If the silent is True the return value will be None if the import fails.

returns: imported object

find_modules (import_path, include_packages=False, recursive=False)

Find all the modules below a package. This can be useful to automatically import all views / controllers so that their metaclasses / function decorators have a chance to register themselves on the application.

Packages are not returned unless include_packages is True. This can also recursively list modules but in that case it will import all the packages to get the correct load path of that module.

returns: generator

validate_arguments (func, args, kwargs, drop_extra=True)

Check if the function accepts the arguments and keyword arguments. Returns a new (args, kwargs) tuple that can savely be passed to the function without causing a TypeError because the function signature is incompatible. If drop_extra is set to True (which is the default) any extra positional or keyword arguments are dropped automatically.

The exception raised provides three attributes:

missing
A set of argument names that the function expected but where missing.
extra
A dict of keyword arguments that the function can not handle but where provided.
extra_positional
A list of values that where given by positional argument but the function cannot accept.

This can be useful for decorators that forward user submitted data to a view function:

from werkzeug import ArgumentValidationError, validate_arguments

def sanitize(f):
    def proxy(request):
        data = request.values.to_dict()
        try:
            args, kwargs = validate_arguments(f, (request,), data)
        except ArgumentValidationError:
            raise BadRequest('The browser failed to transmit all '
                             'the data expected.')
        return f(*args, **kwargs)
    return proxy
bind_arguments (func, args, kwargs)
Bind the arguments provided into a dict. When passed a function, a tuple of arguments and a dict of keyword arguments bind_arguments returns a dict of names as the function would see it. This can be useful to implement a cache decorator that uses the function arguments to build the cache key based on the values of the arguments.
test_app (environ, start_response)
Simple test application that dumps the environment.
PK܉8=iȥJJdocs/werkzeug.pngPNG  IHDR=2sRGB pHYscS+tIME0 IDATxwT?llotQR[bbn4Mb-hDw*Jbleβ (e}ysv9= t5F]kt5F]kt5F]ktv _]{]g rs,vf(6y)i(B( ¶lKtAtGY nBW5ME6(⪪ؖn.(0dC3TU5EUEB_BGBXlq W.>0KUuCQUS5ͧ(OQSUU@ OBxAZe;ajZ)8!0뚮Dihc5M*+M=ۗ-\72.10tMS4iW+ *}Ñe[cʊR2j@]Pw}a6 =\2??~Qm.VWؽ y媮y 4!\@}.>%0jkޘh4 Z'P¢؄̴*Mӄ*!TE .O*`hn(둺Ś*K>o{ 03f_ SxcC]W[]p`iTU@ :nv_f]uSQF}xLҗg􏶽7 #7k"< y`%xaJ]_[]_WS(@c+5Х]@9UUzj_\SQS{{` cOB\7 9raPE8XiٙiG.+Qǘg$TT1l̳&? \p,_ uGMmTl]ׂy9aZU'M'4tAxLuM`˶uQ=C07v@K (GL.#7aSTDԢ .[UFMa^n<%F 1 Gsྰ~Tմ8(ۧW*2`I =a-jꪹ .֖Vah(<P8Dw,ϬMɮtCEuAq9JW=ˆ7-uݛu-a )mj¥a͛P^lZ;`|f6M=57 _de!̉y94|De%CͭPX%Eu55FVܒT fUF3wֿ9r¥WW$füg`8 w΅mppKG_f^N?2QE(B3jl08g$*)cƇ )SҕPZ+e%z=#'42:B5Pkg̦H\'u.Zt⒔̌s`Ԅ2-MkŽaE(pveYYt~}PEpFB0hѦό7}F?—C\Ths^3\2)QJ#`PRԮWZ#4"S•-43 fM7tSQTߧEh1ifHxۻ%a`\7v`CT\;v/4?g.ݣ "2S!DH(3fG`55eƷ]lVyIip3#90\2 vRYa8TUҳ3bbgRkğ(_*mFo$(HxۻCV/ZzYiQIz e6a0ez'RVG-s=846G^QtlL0q3u=TڷB10"Qh4|~#еnl:tݒ3d s)C0;!:fwg{Z~TUFfu-B( ų]WtvN34i"a6`ִn{?lϽ0!Ԧ2 xa ft?&aJyZyIiJeYytFNVit|\PBOBPkg>#!'K_~ڒ=_(s+PG%a>xg 6 3K s2,EP+ PkfS'BQc}~34nv?h3d0U\ssZK?ʨ9QqAE d5.a;. mD:;̪nD󛉚|w.`~+_+oP@mF3}>QtȐ OAVZ0zo&}~n  x&УM=Sl )?^PK= _,P֠.RغDQ--*N;|@ZJzzq\rbBlPkf4ty>_PT=Ȣ&7/yx<M |g7K> > >QU^\YVZh`lJu=f-qi$m'x}!<( oˋ;laK󣲬"8)-+$>155]'AhZ0#|>tW_qhϾ^^+0?rv{bZt |10my{K,-V\PUj":r[|$j̺Za>LGw˯^up^-?lbUbiaaRrZJYBrrL@xx#Cux0#p'd+Wܳwk~?Q^3"P@OJ  xj|0k6x~sK./)K9@fjfzQBRR]gZ0kiG٧E ߗw׮,/;3铯oڍǰa^՘hՐ1q0n8e&'G?cUS|43Z]nN5CDбm~x{YDŽOn%w\}=}]i#wYL!I(-a =se4ܹ@u5~ R/^;߇T&u0syT'۟^\(Ҧ(B)`sja;-m¢n5vXi %?{ɜWw➦ 10s|0}|Jsυag2k;.-Kd!X|l4}~mlGI*H,:t8=.1<%=FQUj]Up=!:̪EgF3?Kf鹞03 Uh2pH͕t\,\0F xr 9 vloÕ7̀8y Va0\Xv+ޏʪ´nIeɩ)5EO]S TH/Eq::̆aL3%E3lc;F~-ï@U@$=/_omoóˆJd|I_VJ Α,'m|TWǕ$$%VtKOQ@V9qZ7͆Gى`ᡧazz 8{Q {cLlM'^Ct⩃!S`-*ueEݢkҲ2UZ;0kfBD/:g /]_`[̸|shDDɤՍ[BeS[1<3"#AKFR)}d$uMLk ʊꖞZwN3~T^\c+f|᭙̀< 9aP"`pXo 3e=I/Lja՛їT;P+0x4d'‚-Ե51EiU{ Ch8ePkfEQE>#0ԓ+J{|uOv|053+/R8`D|PPP_& }sjB PNRW[S832*:#'RQUdCJ~=a$\V\zoq|UanuVH>2^=HcFEQC`@oc85M!?pN,X|XWSSxPVdTduzfFkU!\*J$q"N%̆iYk.A_"cC3a@4|,\.CI7vLXIᦛ2!'S@Hk[L J ""#js{(OW~N5̆a$UU,~ߗ靋V&7Άֻ$ c (J.Yz& O!4>IךB(5UqaFMjfzaBW[N+Y׵4|>_RuEy&uEm'_)_s~lּ 7_M=X3`!0`i2<y|] ` QRPnf0WRkjNRk'fU¬FzkoFrmUeWFfƍsCl22L13bFu-ly\1{ero uS`rH3cCHx?LMKV 6>YYpBU';A]o* k|>TW]~o6:Xa?!*>8`6(oZ*X{gAF$FF_,J1H܀j9zu`5i` QRX[隧P&P"ɀkU_۴`0 ^yb<6/M5J౿ÿ^kn|q.^5%࣏_@.")Tj9pGRU-uU@  Z;0cxLYx٤7N[qMJ=:`ۆyPX .b)ca(saj~&{`;6 }Vmu`|~Jz`P !@}}d3r pC Z90zn>T_]~I7mS[U"Ȫ # mWb,l pЍ7߮_OxgӠk0p.Y0s*RhtM.zn((iĄ&_>jYUf u-˭8c~}ClooDs.sSݸy\}mmt ?HX<"(oF$<8z'G` M@vB!7 (uMHJՅ`ptl6 p~_X 锍ځarUyD>p0WYn|U_ qcZ3}~#Ine+ sz¼ 9cwF' ~}ܣTyG=wB>BW@Qwi:Bވia@mj+hʊӳ"EU j2hrmӏCuY=fIN0j©k '] /?g>yVmrp/vl~[\O?: ZX+۴+`90| 5WK_f۶>b%L0AXBsaǶV"F;N5@uTToZ鱦߈@]][+W{g }M֞!0y/?o_~J}8Ǹ&4G!;DZe2iԅ 9! qN2C~rre*{1J 3vs{*R u=3o\-k7\}`sQ2 }dܧ!'Ƿ*3V D``ݛi+̜i'( ;ជdqt>&PѾ IDAT=` XǶ,_iQQf]M}ٞPlu]!DRcG05tEULU"|>=Ќxo& M۰tͫMi370+r­w65a/yS;qWd@t1<7:\3B Us(0au0?_RĽx0bX:TvTWTX= u=I1v^BUu]UUSTQkR-^67L 3D#>?l0iW|B@09E߅koH'أ&Yt`D|6Tؽ?=8͏j8ֿ&]vn~ֵ)f 3-f@B誦EI[֬ۛ&ֵX4JypgDl" :ե߂n꯲r'} wXVib®?@^(;f0fܐm1TB9'ea[V́/j*YY[oPtPECUU1.wf&ff,zrQ`bxlM rD.`?70ʗ iC!\r|eʟvn8xu-7:ZJVBBh(}>ܺmFk0 5g1&[V_`~3t ,UWo/YxAs=4p|**ڮv\X_[żLXRf 9 K+_gȇ4%? }=mwTUrn?j+O/}BUuRS.֏ 4 kA8CnNB-̟߼>hԑ* ${VL4qLB7PP 7]ol♝.˲)\`DRjʡ-xAyulsq2' BṞ4zpwޖVWg4"of^%N &O[OogCCv@Ob ᯫUOjb^>{hbXцoo!cMK\4zʤE }r+h:BX4T9;Z]~& *irR*ifjVf@xn}yqI F5^]{攦(#a&O 2UuP  -K+Ʉū!'9Lk:ƍYtEj@<\-Rpi󆌾h[]Nx] r|+֭cqG)+麖=RӴ@iQQv>)ԞKVAPIMp/oЎ z ~r|*ȺFQCN+ s[Lp;!5+m=rS%oEQD\BBلg4­+J,.wm :TM\vK?鈉;uG^A̲J!ܚ`Ы :?Z9*!꺎(]DfܲnI "v%ONn\0Ԇ|^!6OIK;GÐ2? > ;p]18W'3o~x{Vnɛu0-mod VgzɘMVe9#07`WD54: h#4]23|~_mYQqjmuMLa/]1&Kcۅ=t1g$¼dQhO? ,8\轢Dm!زF6M!sj&L69v_o|4Iec]dȘo{E@̲uBx/ s@Ҟ硪pe3sz"ܬʂ2jjO-CHi¢pխ-~SMcwo<76̀Km=9Crl.ؐkmpeVE'%^4eƍy `*s,m/ݕ͌&##P͡V5ˮGfĆׂjNGwޒ/aNY|{9'Oz; ]IχmSJ;YVb\:r¸Q+h9SNu._rdj"%-:*:0pf]Ms^T+*೽ǿf1-g=7m <rr.5d`%7= 0߆kN0;:ufLY4t荺avY k>0Z3473/"[rqIAAz]uMGƲ7@#CJr*{^Zt^1^~Nΐ@W+3Ыjw6|YZGaPL%o7?o}GSwkӠ[` \Y eNL(3EMAQB˶]Ne' =PyDNrJjMdtTuIAAjueUBּ-G SN&˂O)vb|Jٰ! Vʚ|}_@A<z넴ᚫ+W1#2:fKw񛪦XUjV 4-_ }JB)^V^^EZVfa55qKd[ G.PQ |\.w > q 2XVm} y}-Cx0ͦAv Ӿn5W>1+NF5+hs|CjM-."*8E -y?4?y.T/?#/nÞ`)X7^͚(ۦeaJy0kG-ndtTͨ>y2'`W8[ص'ܮQ)ԪyEOUS򲒒䪊pm 2.d ,ڃ~`;rfT3Qg723:{.Գ_gR/,[fCNH‚![&;,تacFqIkU<ɂ+PDԀHNKNNVZ8*0zv8~(f,L vV %|Qa6`bOK41^| >4nbcx}v\C΃= RژJ7o[5&OX㋌< eeW8[ut֔BxԚn%EEݪ+Õz(*BdӦ⡐-N|Fxސ֢cܼ:=dK'&n<f6GFdoZ6mǠk?h\o:bkf̩vhsT̉'RQ PTgDmzi v۳sW/X2k{{?~r <2X_֛ڢQOHs<ݰ  ˯ -mܨHx1xYx#=f,s4! -'-NLX2҉#bbV+lϩS S1?<Qӳ{~qAArueez~[]g?$)uCM2C6#_y ~m+] s 1 IomkEccƌ]FA0`:,\~:`>@B)P !DL|\0!)4$5|R5lz:vf:7OC m䆝z a3@n,4b؆ѓ'}|+h9]aۧt[P;5u驪%~ࡔD~¤m>Ot9d$Cr"4 fFAE)|53#vA&Ξ4":P0,VyV))}T]fPz"&1!VRVTPQZn~@eeLB~˲Jm۩plC|ʀ> Ԏ5ZE볺VV'{۬Ӈ0m踁_f:j=̃YpݲDIPA`a@o9ȣE(* 0U3#8`p} 0ۖ]`n?Oۢay߯GeF44SO.>xKf|붸ρřѻŔ=\4 upMpI` 7)'7~SP;Ms6W^/Jm.- 8)U6: M(B(2TEqI=/+*,+:N3l 2=2:Rs=0kVZt5ŗÕaɊrS֞Tw~/} z7os XeUXS8^]s;̧0Ԯ+4MB=!""̒ ޽zl'(Z|NV4r]RX/V3vn`Ybkȹ_Blt6فUל1eY\R>+`%U;N}Gօyk ^P^R[QZI2:ԺT߹qeoY͵ |ofX{ n]~=h{9;?[)a3hS()-mwg݊R7u+ B(("2&:]T[]//.IsP9om@Lr'ڔ@y i#.+`ZS8N5W[8 iXP OE"@Dszu/(-./i ðp!Ħtp^(G@J7NذC>Tœ ? $DvzI蝿Gު2;}}5W,HHMcK-۩mJXnM`GC}4_Ov[l ({^U4vpްY8'1 J <47*0{l*oΆ1h^uK(/>Y~[xV+/ث]wnhٳڀTHY sPO_dYZ]QQ|0S(–p,u4?u(##ϕke"oCa6~";] hoo(^s~p̩KҲ>viв˛\e[aP@jPitll9}kT۲p.QCV93|拆eP\fS&?%++~Ka`# {Ns|xn}))=uKUaN繵snԚ+P(⏈3r`~vbqW] ѩ_f^xnmhrI$C.Kϗ}bjvo?r5ѯGӮrn|佁@Բ v t[P{"\WU'h~k**|%iJ]R ρ^ICތ'~|-Ӑ_ c3E:NY0~B׵o?uoF~?~UsS2?R+h`0wXۂZiZUBP+0 /378P_fO J`:l$dqjFF>~<+Ai;µ߂_ xy_ ѱ5q0͌=?dYݻjYvəs(""*WRW]m;vs5d$ZJ)4?T8p~||ZGK߀\9^{{T7f8{o)_+wWk5WqJv*ẵP5ղ-!:!I@ӑlr@tT둺Ś>_3`0cemxe`Nn53o. <e(腳ZvU0 qμ \mlYs?_JJM KVT559(u;BOUR O0>7G^iN-:t8qյ~3LNR'/p#`>t4=ƨd3t/ԲcvV; džCQTyB(B_7Z(/.I f2xu\n@c ?Ýc$pǷwd6!JK e0yH )3m+_uZSy͸WsziYvI0pL sP yjUW7w+e%@bzуzBǩԪT> = kކ7!2@er=%=78 nc?[w.ض#mffsP{GFxy%vႬ` FsæV~_W-Iֆ۴]6 om}aًAۋ=!"v,\H7~ִ}qP'z IDAT̕g̝v@MR2pdYRmX􆼾_\=WlA.'\C,,˧Y >|C2zWbL_y+/sw8Wřt{VT0|>7obS7 3ꢚnXS->Y< ԁxYyc8ǐ q첽Q_#[2R3^~ />oerj8L-?*F੪f#~9|,ޞ8|E+ rsXm},&%A`fKZop`Vuq~ fjqh{A \:N㻄ߝ< >ф檮Wжԃ,jCڶߵ#idV* jޝvw~w"\G ~DwjSg+>˶o`#Ru{Guu}fvt/.D뗀r''~kyuzE6MϨ,ܽKrٹLR,uQ{o `tyf5@;U'纪@ZUVj0C-,AX"u{pn. Cp2ӏ݌`+i=Ա,杪s\Udl@ouTCMmQ@+g3J%<ne{b-3&snF,ȍL`D8nNHZ fQSoB{Kx,BoaaNG'f;zyrQEUL(6 -Q`!uӕR63߿|WmCj ?] #*=tW,˞s=/UUm`?sԒ41OnI(=٥lT(&@"0u!o~#YxuKҶ3彪W1׺`6܌ZHI}tOܱ\D߬SO/ҁW.zf,[j^wD˗͍37AR131 !(旲D)_H6OP~(?LgW4z#ӓXf0l@?4jb Sw'd~r9\SyckSf.eـ~l-ۆ43 D$ԕJԝBR(k9Zz~h{O}) P!q4,Tg<^&Z#3=c;ztuTE)m0?l.d C6-v(v!eV2,dmqijU)Z!UҺxm0Oj l;$hBe)!)Ck; y\"0DnH:8ŲC̶2$f" !4{DZI)=ꏲ>ġ^+Cm1%Hi ! l@?QEz6#6 'u3lt l@`׽6 {l„ &Ov̻GIENDB`PK܉8YBdocs/wrappers.html Wrappers // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

Wrappers

The wrappers are simple request and response objects which you can subclass to do whatever you want them to do. The request object contains the information transmitted by the client (webbrowser) and the response object contains all the information sent back to the browser.

An important detail is that the request object is created with the WSGI environ and will act as high-level proxy whereas the response object is an actual WSGI application.

Like everything else in Werkzeug these objects will work correctly with unicode data. Incoming form data parsed by the response object will be decoded into an unicode object if possible and if it makes sense.

You can import all these objects directly from werkzeug.

Base Wrappers

These objects implement a common set of operations. They are missing fancy addon functionality like user agent parsing or etag handling. These features are available by mixing in various mixin classes or using Request and Response.

class BaseRequest

Very basic request object. This does not implement advanced stuff like entity tag parsing or cache controls. The request object is created with the WSGI environment as first argument and will add itself to the WSGI environment as 'werkzeug.request' unless it’s created with populate_request set to False.

There are a couple of mixins available that add additional functionality to the request object, there is also a class called Request which subclasses BaseRequest and all the important mixins.

It’s a good idea to create a custom subclass of the BaseRequest and add missing functionality either via mixins or direct implementation. Here an example for such subclasses:

from werkzeug import BaseRequest, ETagRequestMixin

class Request(BaseRequest, ETagRequestMixin):
    pass

Request objects should be considered read only. Even though the object doesn’t enforce read only access everywhere you should never modify any data on the object itself unless you know exactly what you are doing.

Per default the request object will assume all the text data is utf-8 encoded. Please refer to the unicode chapter for more details about customizing the behavior.

Creating Request Objects

__init__ (environ, populate_request=True, shallow=False)

Per default the request object will be added to the WSGI enviornment as werkzeug.request to support the debugging system. If you don’t want that, set populate_request to False.

If shallow is True the environment is initialized as shallow object around the environ. Every operation that would modify the environ in any way (such as consuming form data) raises an exception unless the shallow attribute is explicitly set to False. This is useful for middlewares where you don’t want to consume the form data by accident. A shallow request is not populated to the WSGI environment.

from_values (path='/', base_url=None, query_string=None, **options)

Create a new request object based on the values provided. If environ is given missing values are filled from there. This method is useful for small scripts when you need to simulate a request from an URL. Do not use this method for unittesting, there is a full featured client object in werkzeug.test that allows to create multipart requests etc.

This accepts the same options as the create_environ function from the utils module and additionally an environ parameter that can contain values which will override the values from dict returned by create_environ.

Additionally a dict passed to query_string will be encoded in the request class charset.

returns: request object

application (f)

Decorate a function as responder that accepts the request as first argument. This works like the responder decorator but the function is passed the request object as first argument:

@Request.application
def my_wsgi_app(request):
    return Response('Hello World!')

Properties

path
The current path requested, relative to the position where the WSGI application is mounted (PATH_INFO). It will contain a leading slash and will be at least a string with a single slash when accessing the URL root.
script_root
The root path for the script (SCRIPT_NAME). Does not contain a trailing slash.
url
The full URL for the current request.
base_url
The current full URL without the query string.
url_root
The current URL up to the script root.
host_url
The current URL for the host.
host
The current hostname, without scheme.
is_secure
True if this is an HTTPS request.
is_multithread
True if this request was created in a multithreaded environment.
is_multiprocess
True if this request was created in a forking environment.
is_run_once
True if this request was created on the command line, a CGI script or a similar environment.
is_xhr
True if the request was triggered via an JavaScript XMLHttpRequest. This only works with libraries that support the X-Requested-With header and set it to “XMLHttpRequest”. Libraries that do that are prototype, jQuery and Mochikit and probably some more.
method
The request method. GET, POST etc.
args
A dictionary-like object containing all given HTTP GET parameters. See the MultiDict documentation in the utils section.
form

A dictionary-like object containing all given HTTP POST parameters. See the MultiDict documentation in the utils section.

This dict does not contain uploaded files, see files regarding that.

values
An immutable dictionary-like object containing both the args and form values. See the CombinedMultiDict documentation in the utils section.
cookies
A dictionary with the submitted cookie values.
files

A dictionary-like object containing all uploaded files. Each key in files is the name from the <input type="file" name="" />. Each value in files is a Werkzeug FileStorage object with the following members:

  • filename - The name of the uploaded file, as a Python string.
  • type - The content type of the uploaded file.
  • data - The raw content of the uploaded file.
  • read() - Read from the stream.

Note that files will only contain data if the request method was POST and the <form> that posted to the request had enctype="multipart/form-data". It will be empty otherwise.

See the MultiDict / FileStorage documentation in the utils section for more details about the used data structure.

environ
The WSGI environment used to create the request object.
stream
The buffered stream with incoming data from the webbrowser if the submitted data was not multipart or URL-encoded form data.
input_stream
The input stream provided by the client. Used internally by the form data parser. Reading from this stream must never be mixed with accessing stream, data, files, or form, because then it’s not guaranteed that more data is requested from the client than expected. Never read beyond environ['CONTENT_LENGTH'].
data
Accessing this the first time reads the whole stream and stores it. Keep in mind that this does not read the whole WSGI input stream like Django does.
remote_addr
The remote address for the user that created this request. If the class variable is_behind_proxy is set to True (either by subclassing the process or overriding this variable on the instance) it will try to get the value from the X_HTTP_FORWARDED_FOR header. Keep in mind that this is disabled by default because unless you are really behind a proxy this is a security problem.
access_route
If you are behind a proxy server this will list all the IP addresses that take place in the request. The end user IP address is the first one in the list, the last proxy server is the last item in the list. This also works if the is_behind_proxy class variable is set to False.
class BaseResponse

Base response class. The most important fact about a response object is that it’s a regular WSGI application. It’s initialized with a couple of response parameters (headers, body, status code etc.) and will start a valid WSGI response when called with the environ and start response callable.

Because it’s a WSGI application itself processing usually ends before the actual response is sent to the server. This helps debugging systems because they can catch all the exceptions before responses are started.

Here a small example WSGI application that takes advantage of the response objects:

from werkzeug import BaseResponse as Response

def index():
    return Response('Index page')

def application(environ, start_response):
    path = environ.get('PATH_INFO') or '/'
    if path == '/':
        response = index()
    else:
        response = Response('Not Found', status=404)
    return response(environ, start_response)

Like BaseRequest which object is lacking a lot of functionality implemented in mixins. This gives you a better control about the actual API of your response objects, so you can create subclasses and add custom functionality. A full featured response object is available as Response which implements a couple of useful mixins.

To enforce a new type of already existing responses you can use the force_type method. This is useful if you’re working with different subclasses of response objects and you want to post process them with a know interface.

Per default the request object will assume all the text data is utf-8 encoded. Please refer to the unicode chapter for more details about customizing the behavior.

Creating Response Objects

__init__ (response=None, status=None, headers=None, mimetype=None, content_type=None)

Response can be any kind of iterable or string. If it’s a string it’s considered being an iterable with one item which is the string passed. Headers can be a list of tuples or a Headers object.

Special note for mimetype and content_type. For most mime types mimetype and content_type work the same, the difference affects only ‘text’ mimetypes. If the mimetype passed with mimetype is a mimetype starting with text/ it becomes a charset parameter defined with the charset of the response object. In constrast the content_type parameter is always added as header unmodified.

force_type (response, environ=None)

Enforce that the WSGI response is a response object of the current type. Werkzeug will use the BaseResponse internally in many situations like the exceptions. If you call get_response on an exception you will get back a regular BaseResponse object, even if you are using a custom subclass.

This method can enforce a given response type, and it will also convert arbitrary WSGI callables into response objects if an environ is provided:

# convert a Werkzeug response object into an instance of the
# MyResponseClass subclass.
response = MyResponseClass.force_type(response)

# convert any WSGI application into a response object
response = MyResponseClass.force_type(response, environ)

This is especially useful if you want to post-process responses in the main dispatcher and use functionality provided by your subclass.

Keep in mind that this will modify response objects in place if possible!

from_app (app, environ, buffered=False)
Create a new response object from an application output. This works best if you pass it an application that returns a generator all the time. Sometimes applications may use the write() callable returned by the start_response function. This tries to resolve such edge cases automatically. But if you don’t get the expected output you should set buffered to True which enforces buffering.

Properties

response
The application iterator. If constructed from a string this will be a list, otherwise the object provided as application iterator.
headers
A Headers object representing the response headers.
status
The response status as string.
status_code
The response status as integer.
data
When accessed the response iterator is buffered and, encoded and returned as bytestring.
header_list
Read only list that contains the current list for the headers in the response encoding.
is_streamed

If the response is streamed (the response is not a sequence) this property is True. In this case streamed means that there is no information about the number of iterations. This is usully True if a generator is passed to the response object.

This is useful for checking before applying some sort of post filtering that should not take place for streamed responses.

Methods

iter_encoded (charset=None)
Iter the response encoded with the encoding specified. If no encoding is given the encoding from the class is used. Note that this does not encode data that is already a bytestring.
set_cookie (key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False)

Sets a cookie. The parameters are the same as in the cookie Morsel object in the Python standard library but it accepts unicode data too:

  • max_age should be a number of seconds, or None (default) if the

    cookie should last only as long as the client’s browser session.

  • expires should be a datetime object or UNIX timestamp.

  • Use domain if you want to set a cross-domain cookie. For example, domain=".example.com" will set a cookie that is readable by the domain www.example.com, foo.example.com etc. Otherwise, a cookie will only be readable by the domain that set it.

  • path limits the cookie to a given path, per default it will span the whole domain.

delete_cookie (key, path='/', domain=None)
Delete a cookie. Fails silently if key doesn’t exist.
fix_headers (environ)
This is automatically called right before the response is started and should fix common mistakes in headers. For example location headers are joined with the root URL here.
close ()
Close the wrapped response if possible.
freeze ()
Call this method if you want to make your response object ready for pickeling. This buffers the generator if there is one.
__call__ (environ, start_response)
Process this response as WSGI application.

Mixin Classes

Werkzeug also provides helper mixins for various HTTP related functionality such as etags, cache control, user agents etc. When subclassing you can mix those classes in to extend the functionality of the BaseRequest or BaseResponse object. Here a small example for a request object that parses accept headers:

from werkzeug import BaseRequest, AcceptMixin

class Request(BaseRequest, AcceptMixin):
    pass

The Request and Response classes subclass the BaseRequest and BaseResponse classes and implement all the mixins Werkzeug provides:

class Request

Full featured request object implementing the following mixins:

  • AcceptMixin for accept header parsing
  • ETagRequestMixin for etag and cache control handling
  • UserAgentMixin for user agent introspection
  • AuthorizationMixin for http auth handling
class Response

Full featured response object implementing the following mixins:

  • ETagResponseMixin for etag and cache control handling
  • ResponseStreamMixin to add support for the stream property
  • CommonResponseDescriptorsMixin for various HTTP descriptors
  • WWWAuthenticateMixin for HTTP authentication support
class AcceptMixin

A mixin for classes with an environ attribute to get and all the HTTP accept headers as Accept objects. This can be mixed in request objects or any other object that has a WSGI environ available as environ.

accept_mimetypes
List of mimetypes this client supports.
accept_charsets
List of charsets this client supports.
accept_encodings
List of encodings this client accepts. Encodings in a HTTP term are compression encodings such as gzip. For charsets have a look at accept_charset.
accept_languages
List of languages this client accepts.

All this properties store Accept objects as documented in the utils section.

class AuthorizationMixin

Adds an authorization property that represents the parsed value of the Authorization header as Authorization object.

authorization
The Authorization object in parsed form.
class ETagRequestMixin

Add entity tag and cache descriptors to a request object or object with an WSGI environment available as environ. This not only provides access to etags but also to the cache control header.

cache_control
A CacheControl object for the incoming cache control headers.
if_match
An object containing all the etags in the If-Match header.
if_none_match
An object containing all the etags in the If-None-Match header.
if_modified_since
The parsed If-Modified-Since header as datetime object.
if_unmodified_since
The parsed If-Unmodified-Since header as datetime object.

All the used data structures are documented in the utils section.

class ETagResponseMixin

Adds extra functionality to a response object for etag and cache handling. This mixin requires an object with at least a headers object that implements a dict like interface similar to Headers.

cache_control
The Cache-Control general-header field is used to specify directives that MUST be obeyed by all caching mechanisms along the request/response chain.
make_conditional (request_or_environ)

Make the response conditional to the request. This method works best if an etag was defined for the response already. The add_etag method can be used to do that. If called without etag just the date header is set.

This does nothing if the request method in the request or enviorn is anything but GET or HEAD.

It does not remove the body of the response because that’s something the __call__ function does for us automatically.

Returns self so that you can do return resp.make_conditional(req) but modifies the object in-place.

add_etag (overwrite=False, weak=False)
Add an etag for the current response if there is none yet.
set_etag (etag, weak=False)
Set the etag, and override the old one if there was one.
get_etag ()
Return a tuple in the form (etag, is_weak). If there is no ETag the return value is (None, None).
freeze (no_etag=False)
Call this method if you want to make your response object ready for pickeling. This buffers the generator if there is one. This also sets the etag unless no_etag is set to True.
class ResponseStreamMixin

Mixin for BaseRequest subclasses. Classes that inherit from this mixin will automatically get a stream property that provides a write-only interface to the response iterable.

stream
The response iterable as write-only stream.
class CommonResponseDescriptorsMixin

A mixin for BaseResponse subclasses. Response objects that mix this class in will automatically get descriptors for a couple of HTTP headers with automatic type conversion.

mimetype
The mimetype (content type without charset etc.)
location
The Location response-header field is used to redirect the recipient to a location other than the Request-URI for completion of the request or identification of a new resource.
age

The Age response-header field conveys the sender’s estimate of the amount of time since the response (or its revalidation) was generated at the origin server.

Age values are non-negative decimal integers, representing time in seconds.

content_type
The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient or, in the case of the HEAD method, the media type that would have been sent had the request been a GET.
content_length
The Content-Length entity-header field indicates the size of the entity-body, in decimal number of OCTETs, sent to the recipient or, in the case of the HEAD method, the size of the entity-body that would have been sent had the request been a GET.
content_location
The Content-Location entity-header field MAY be used to supply the resource location for the entity enclosed in the message when that entity is accessible from a location separate from the requested resource’s URI.
content_encoding
The Content-Encoding entity-header field is used as a modifier to the media-type. When present, its value indicates what additional content codings have been applied to the entity-body, and thus what decoding mechanisms must be applied in order to obtain the media-type referenced by the Content-Type header field.
content_language
The Content-Language entity-header field describes the natural language(s) of the intended audience for the enclosed entity. Note that this might not be equivalent to all the languages used within the entity-body.
content_md5
The Content-MD5 entity-header field, as defined in RFC 1864, is an MD5 digest of the entity-body for the purpose of providing an end-to-end message integrity check (MIC) of the entity-body. (Note: a MIC is good for detecting accidental modification of the entity-body in transit, but is not proof against malicious attacks.)
date
The Date general-header field represents the date and time at which the message was originated, having the same semantics as orig-date in RFC 822.
expires
The Expires entity-header field gives the date/time after which the response is considered stale. A stale cache entry may not normally be returned by a cache.
last_modified
The Last-Modified entity-header field indicates the date and time at which the origin server believes the variant was last modified.
retry_after

The Retry-After response-header field can be used with a 503 (Service Unavailable) response to indicate how long the service is expected to be unavailable to the requesting client.

Time in seconds until expiration or date.

vary
The Vary field value indicates the set of request-header fields that fully determines, while the response is fresh, whether a cache is permitted to use the response to reply to a subsequent request without revalidation.
allow
The Allow entity-header field lists the set of methods supported by the resource identified by the Request-URI. The purpose of this field is strictly to inform the recipient of valid methods associated with the resource. An Allow header field MUST be present in a 405 (Method Not Allowed) response.
class WWWAuthenticateMixin

Adds a www_authenticate property to a response object.

www_authenticate
The WWW-Authenticate header in a parsed form.

Note on File Uploads

File uploads are a tricky thing in general. Per default all the file uploads go into temporary files on the filesystem and not the memory of the current process to avoid high memory usage. You could also change that to store the data somewhere else by implementing an object that implements the python IO protocol (see StringIO) for both writing and reading.

Then you have to subclass a request object and override _get_file_stream:

_get_file_stream ()

Called to get a stream for the file upload.

This must provide a file-like class with read(), readline() and seek() methods that is both writeable and readable.

The default implementation returns a temporary file.

PK܉85Ri>>docs/wsgihowto.html How WSGI Works // Werkzeug Documentation

Werkzeug

The Swiss Army Knife For Python Web Developers

How WSGI Works

New to Werkzeug or even WSGI? This part of the documentation covers the low-level parts of Werkzeug that are in fact just plain WSGI.

What is WSGI?

Basically WSGI is an interface between web servers and web applications. We’ll explain the mechanics of WSGI below, but to sum it up WSGI lets you develop rich web application without the need to know about your server environment. A WSGI application runs on standalone servers, on any webserver out there, via mod_python, FastCGI, SCGI, CGI, basically everything that runs Python.

But there’s much more; WSGI is more than just HTTP! You and other developers can extend WSGI by adding new features to it, filtering input or output, adding rich debugging systems, automatically send e-mails to your mailer as soon as an unhandled application error occurs. You can add session features to it etc.

There are few things you cannot do with WSGI, and Werkzeug itself is just a tiny wrapper around WSGI, thus you don’t lose any functionally unlike some other implementations that abstract more. If you don’t require all the power of WSGI you can choose a higher-level implementation like Django.

Writing a WSGI Application

The first part is about how to use WSGI without Werkzeug. You can read the PEP (:pep:333) that defines it, but we’ll do a very brief summary:

  • A WSGI application is just a callable object that is passed an environ - a dict that contains request data, and a start_response function that is called to start sending the response.

    In order to send data to the server, all you have to do is to call start_response and return an iterable.

  • environ is a plain old CGI environment (contains values like PATH_INFO) in form of a Python dict with some extensions like wsgi.input that represent the input stream.

  • Middlewares (we’ll cover them later) can add additional data to the environ.

So, here’s a simple application:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ['Hello World!']

Now you have an application, but how can you start it? From Python 2.5, the stdlib contains a WSGI server called wsgiref; for Python 2.4 and lower you have to install that on your own. There are also other standalone implementations which you can find at wsgi.org.

To start the application as standalone server, all you have to do is adding this start hook to the file:

if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    srv = make_server('localhost', 5000, application)
    srv.serve_forever()

When you now start the application you will see Hello World! on http://localhost:5000/.

The __name__ hook is a good idea so that you can use the module for other modules like flup (provides a bridge to Apache and other webservers via FastCGI and similar protocols) without altering the code.

Adding Interactive Elements

Let’s extend the example from above so that we say Hello John if the user visits http://localhost:5000/?name=John:

from cgi import parse_qs, escape

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    query = parse_qs(environ.get('QUERY_STRING', ''))
    return ['Hello %s!' % escape(query.get('name', 'World'))]

Basically we just combined the python CGI module with our WSGI application.

That Sucks!

Indeed it does. It’s neither fun to work with nor does it look clean and simple. And that’s where utilities like Werkzeug come into play. Werkzeug, for example, lets you easily create unicode-aware applications (in fact it forces you to use unicode internally) and avoid repetitive work.

In this example we want to connect / with an index function and /about with an about function, both returning different content. To keep the example simple we just want to use a dict that maps to that and take advantage or some of the Werkzeug features:

from werkzeug import Request, Response, escape

def index(req):
    return Response(u'''<h1>Index Page</h1>
    <p>What\'s your name?</p>
    <form action="hello" method="post">
        <input type="text" name="name" value="My Name">
        <input type="submit" value="Greet Me!">
    </form>
    ''', mimetype='text/html')

def hello(req):
    name = req.form.get('name') or 'Nobody'
    return Response(u'<h1>Hello %s!</h1>' % escape(name),
                    mimetype='text/html')

def about(req):
    return Response(u'''<h1>About This Page</h1>
        <p>This page is just a small example page for Werkzeug</p>
    ''', mimetype='text/html')

def not_found(req):
    return Response(u'<h1>Page Not Found</h1>', status=404,
                    mimetype='text/html')

views = {
    '/':        index,
    '/hello':   hello,
    '/about':   about
}

def application(environ, start_response):
    req = Request(environ)
    if req.path not in views:
        resp = not_found(req)
    else:
        resp = views[req.path](req)
    return resp(environ, start_response)

if __name__ == '__main__':
    from werkzeug import run_simple
    run_simple('localhost', 5000, application)

Alright. That’s quite a lot of code but let’s go through it step-by-step.

The first thing we do is importing the stuff we use. In that example we need the escape function that XML/SGML escapes strings. This is required to avoid XSS attacks to the application. Then we need the Request and Response objects which ecapsulate the WSGI environ and responses.

Then we define a bunch of callback functions that just return HTML responses. One of them - not_found - is called when we don’t have a matching URL, the others are bound to urls in the views dict.

The WSGI application itself just creates a request object and looks up the callbacks or proceeds with the not_found function if there is no matching URL. The return value of the response is then called as WSGI application.

This is possible because Response objects behave like WSGI applications.

Of course, this example is still bad code; in real applications you would use one of the template engines that are available for Python. But for the simple example that should be enough.

Debugging It

You probably have had a typo in one of the examples above. In that case you don’t have to use the error output from the wsgiref server. There are some excellent debugging systems for WSGI, and one of them is shipped with Werkzeug and explained in the debug system docs.

PK[82EGG-INFO/dependency_links.txt PK82EGG-INFO/not-zip-safe PK[8H$x x EGG-INFO/PKG-INFOMetadata-Version: 1.0 Name: Werkzeug Version: 0.3.1 Summary: The Swiss Army knife of Python web development Home-page: http://werkzeug.pocoo.org/ Author: Armin Ronacher Author-email: armin.ronacher@active-4.com License: BSD Description: Werkzeug ======== Werkzeug started as simple collection of various utilities for WSGI applications and has become one of the most advanced WSGI utility modules. It includes a powerful debugger, full featured request and response objects, HTTP utilities to handle entity tags, cache control headers, HTTP dates, cookie handling, file uploads, a powerful URL routing system and a bunch of community contributed addon modules. Werkzeug is unicode aware and doesn't enforce a specific template engine, database adapter or anything else. It doesn't even enforce a specific way of handling requests and leaves all that up to the developer. It's most useful for end user applications which should work on as many server environments as possible (such as blogs, wikis, bulletin boards, etc.). Details and example applications are available on the `Werkzeug website `_. Features -------- - unicode awareness - request and response objects - various utility functions for dealing with HTTP headers such as `Accept` and `Cache-Control` headers. - thread local objects with proper cleanup at request end - an interactive debugger - wrapper around wsgiref that works around some of the limitations and bugs, adds threading and fork support for test environments and adds an automatic reloader. - a flexible URL routing system with REST support. - fully WSGI compatible Development Version ------------------- The `Werkzeug tip `_ is installable via `easy_install` with ``easy_install Werkzeug==dev``. Platform: any Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules PK[8o"//EGG-INFO/requires.txt [wsgiref] wsgiref [plugin] setuptools>=0.6a2PK[8EGG-INFO/SOURCES.txtAUTHORS CHANGES LICENSE MANIFEST.in Makefile THANKS ez_setup.py setup.cfg setup.py Werkzeug.egg-info/PKG-INFO Werkzeug.egg-info/SOURCES.txt Werkzeug.egg-info/dependency_links.txt Werkzeug.egg-info/not-zip-safe Werkzeug.egg-info/requires.txt Werkzeug.egg-info/top_level.txt artwork/logo.png artwork/logo.svg docs/build/README docs/build/api_stability.html docs/build/debug.html docs/build/deploying.html docs/build/exceptions.html docs/build/favicon.ico docs/build/header.png docs/build/index.html docs/build/installation.html docs/build/libraries.html docs/build/local.html docs/build/organizing.html docs/build/print.css docs/build/pygments.css docs/build/routing.html docs/build/script.html docs/build/serving.html docs/build/style.css docs/build/templates.html docs/build/terms.html docs/build/test.html docs/build/tutorial.html docs/build/tutorial_de.html docs/build/utils.html docs/build/werkzeug.png docs/build/wrappers.html docs/build/wsgihowto.html docs/src/api_stability.txt docs/src/debug.txt docs/src/deploying.txt docs/src/exceptions.txt docs/src/index.txt docs/src/installation.txt docs/src/libraries.txt docs/src/local.txt docs/src/organizing.txt docs/src/routing.txt docs/src/script.txt docs/src/serving.txt docs/src/templates.txt docs/src/terms.txt docs/src/test.txt docs/src/tutorial.txt docs/src/tutorial_de.txt docs/src/unicode.txt docs/src/utils.txt docs/src/wrappers.txt docs/src/wsgihowto.txt examples/README examples/httpbasicauth.py examples/manage-coolmagic.py examples/manage-couchy.py examples/manage-cupoftee.py examples/manage-i18nurls.py examples/manage-plnt.py examples/manage-shorty.py examples/manage-simplewiki.py examples/manage-webpylike.py examples/upload.py examples/contrib/README examples/contrib/securecookie.py examples/contrib/sessions.py examples/coolmagic/__init__.py examples/coolmagic/application.py examples/coolmagic/helpers.py examples/coolmagic/utils.py examples/coolmagic/public/style.css examples/coolmagic/templates/layout.html examples/coolmagic/templates/static/about.html examples/coolmagic/templates/static/index.html examples/coolmagic/templates/static/not_found.html examples/coolmagic/views/__init__.py examples/coolmagic/views/static.py examples/couchy/README examples/couchy/__init__.py examples/couchy/application.py examples/couchy/models.py examples/couchy/utils.py examples/couchy/views.py examples/couchy/static/style.css examples/couchy/templates/display.html examples/couchy/templates/layout.html examples/couchy/templates/list.html examples/couchy/templates/new.html examples/couchy/templates/not_found.html examples/cupoftee/__init__.py examples/cupoftee/application.py examples/cupoftee/db.py examples/cupoftee/network.py examples/cupoftee/pages.py examples/cupoftee/utils.py examples/cupoftee/shared/content.png examples/cupoftee/shared/down.png examples/cupoftee/shared/favicon.ico examples/cupoftee/shared/header.png examples/cupoftee/shared/logo.png examples/cupoftee/shared/style.css examples/cupoftee/shared/up.png examples/cupoftee/templates/layout.html examples/cupoftee/templates/missingpage.html examples/cupoftee/templates/search.html examples/cupoftee/templates/server.html examples/cupoftee/templates/serverlist.html examples/i18nurls/__init__.py examples/i18nurls/application.py examples/i18nurls/urls.py examples/i18nurls/views.py examples/i18nurls/templates/about.html examples/i18nurls/templates/blog.html examples/i18nurls/templates/index.html examples/i18nurls/templates/layout.html examples/partial/README examples/partial/complex_routing.py examples/plnt/__init__.py examples/plnt/database.py examples/plnt/sync.py examples/plnt/utils.py examples/plnt/views.py examples/plnt/webapp.py examples/plnt/shared/style.css examples/plnt/templates/about.html examples/plnt/templates/index.html examples/plnt/templates/layout.html examples/shorty/__init__.py examples/shorty/application.py examples/shorty/models.py examples/shorty/utils.py examples/shorty/views.py examples/shorty/static/style.css examples/shorty/templates/display.html examples/shorty/templates/layout.html examples/shorty/templates/list.html examples/shorty/templates/new.html examples/shorty/templates/not_found.html examples/simplewiki/__init__.py examples/simplewiki/actions.py examples/simplewiki/application.py examples/simplewiki/database.py examples/simplewiki/specialpages.py examples/simplewiki/utils.py examples/simplewiki/shared/style.css examples/simplewiki/templates/action_diff.html examples/simplewiki/templates/action_edit.html examples/simplewiki/templates/action_log.html examples/simplewiki/templates/action_revert.html examples/simplewiki/templates/action_show.html examples/simplewiki/templates/layout.html examples/simplewiki/templates/macros.xml examples/simplewiki/templates/missing_action.html examples/simplewiki/templates/page_index.html examples/simplewiki/templates/page_missing.html examples/simplewiki/templates/recent_changes.html examples/webpylike/example.py examples/webpylike/webpylike.py tests/conftest.py tests/test_http.py tests/test_routing.py tests/test_templates.py tests/test_utils.py tests/test_wrappers.py tests/contrib/test_testtools.py tests/res/test.txt werkzeug/__init__.py werkzeug/_internal.py werkzeug/exceptions.py werkzeug/http.py werkzeug/local.py werkzeug/routing.py werkzeug/script.py werkzeug/serving.py werkzeug/templates.py werkzeug/test.py werkzeug/testapp.py werkzeug/useragents.py werkzeug/utils.py werkzeug/wrappers.py werkzeug/contrib/__init__.py werkzeug/contrib/atom.py werkzeug/contrib/cache.py werkzeug/contrib/iterio.py werkzeug/contrib/jsrouting.py werkzeug/contrib/kickstart.py werkzeug/contrib/limiter.py werkzeug/contrib/profiler.py werkzeug/contrib/reporterstream.py werkzeug/contrib/securecookie.py werkzeug/contrib/sessions.py werkzeug/contrib/testtools.py werkzeug/debug/__init__.py werkzeug/debug/console.py werkzeug/debug/render.py werkzeug/debug/repr.py werkzeug/debug/tbtools.py werkzeug/debug/utils.py werkzeug/debug/shared/body.tmpl werkzeug/debug/shared/codetable.tmpl werkzeug/debug/shared/console.png werkzeug/debug/shared/debugger.js werkzeug/debug/shared/jquery.js werkzeug/debug/shared/less.png werkzeug/debug/shared/more.png werkzeug/debug/shared/source.png werkzeug/debug/shared/style.css werkzeug/debug/shared/vartable.tmpl werkzeug/debug/templates/console.html werkzeug/debug/templates/dump_object.html werkzeug/debug/templates/frame.html werkzeug/debug/templates/help_command.html werkzeug/debug/templates/source.html werkzeug/debug/templates/traceback_full.html werkzeug/debug/templates/traceback_plaintext.html werkzeug/debug/templates/traceback_summary.html PK[8lk EGG-INFO/top_level.txtwerkzeug PK݉8ecwerkzeug/__init__.py# -*- coding: utf-8 -*- """ werkzeug ~~~~~~~~ Werkzeug is the Swiss Army knife of Python web development. It provides useful classes and functions for any WSGI application to make the life of a python web developer much easier. All of the provided classes are independed from each other so you can mix it with any other library. :copyright: 2007-2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from types import ModuleType import sys all_by_module = { 'werkzeug.debug': ['DebuggedApplication'], 'werkzeug.local': ['Local', 'LocalManager', 'LocalProxy'], 'werkzeug.templates': ['Template'], 'werkzeug.serving': ['run_simple'], 'werkzeug.test': ['Client'], 'werkzeug.testapp': ['test_app'], 'werkzeug.exceptions': ['abort', 'Aborter'], 'werkzeug.utils': ['escape', 'create_environ', 'url_quote', 'environ_property', 'cookie_date', 'http_date', 'url_encode', 'url_quote_plus', 'Headers', 'EnvironHeaders', 'CombinedMultiDict', 'url_fix', 'run_wsgi_app', 'get_host', 'responder', 'SharedDataMiddleware', 'ClosingIterator', 'FileStorage', 'url_unquote_plus', 'url_decode', 'url_unquote', 'get_current_url', 'redirect', 'append_slash_redirect', 'cached_property', 'MultiDict', 'import_string', 'dump_cookie', 'parse_cookie', 'unescape', 'format_string', 'Href', 'DispatcherMiddleware', 'find_modules', 'header_property', 'html', 'xhtml', 'HTMLBuilder', 'parse_form_data', 'validate_arguments', 'ArgumentValidationError', 'bind_arguments'], 'werkzeug.useragents': ['UserAgent'], 'werkzeug.http': ['Accept', 'CacheControl', 'ETags', 'parse_etags', 'parse_date', 'parse_cache_control_header', 'is_resource_modified', 'parse_accept_header', 'parse_set_header', 'quote_etag', 'unquote_etag', 'generate_etag', 'dump_header', 'parse_list_header', 'parse_dict_header', 'HeaderSet', 'parse_authorization_header', 'parse_www_authenticate_header', 'WWWAuthenticate', 'Authorization', 'HTTP_STATUS_CODES'], 'werkzeug.wrappers': ['BaseResponse', 'BaseRequest', 'Request', 'Response', 'AcceptMixin', 'ETagRequestMixin', 'ETagResponseMixin', 'ResponseStreamMixin', 'CommonResponseDescriptorsMixin', 'UserAgentMixin', 'AuthorizationMixin', 'WWWAuthenticateMixin'], # the undocumented easteregg ;-) 'werkzeug._internal': ['_easteregg'] } attribute_modules = dict.fromkeys(['exceptions', 'routing', 'script']) object_origins = {} for module, items in all_by_module.iteritems(): for item in items: object_origins[item] = module class module(ModuleType): """Automatically import objects from the modules.""" def __getattr__(self, name): if name in object_origins: module = __import__(object_origins[name], None, None, [name]) for extra_name in all_by_module[module.__name__]: setattr(self, extra_name, getattr(module, extra_name)) return getattr(module, name) elif name in attribute_modules: __import__('werkzeug.' + name) return ModuleType.__getattribute__(self, name) # keep a reference to this module so that it's not garbage collected old_module = sys.modules['werkzeug'] # setup the new module and patch it into the dict of loaded modules new_module = sys.modules['werkzeug'] = module('werkzeug') new_module.__dict__.update({ '__file__': __file__, '__path__': __path__, '__doc__': __doc__, '__all__': tuple(object_origins) + tuple(attribute_modules) }) PK[8螦((werkzeug/__init__.pyc; raHc-@skdZdklZdkZhddg<ddddg<d d g<d d g<d dg<ddg<dddg<ddddddddddddd d!d"d#d$d%d&d'd(d)d*d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;d<d=d>g*<d?d@g<dAdBdCdDdEdFdGdHdIdJdKdLdMdNdOdPdQdRdSdTdUdVg<dWdXdYdZd[d\d]d^d_d`dadbdcg <dddeg' % ( self.__class__.__name__, self.name ) class _ExtendedCookie(BaseCookie): """Form of the base cookie that doesn't raise a `CookieError` for malformed keys. This has the advantage that broken cookies submitted by nonstandard browsers don't cause the cookie to be empty. """ def _BaseCookie__set(self, key, real_value, coded_value): morsel = self.get(key, _ExtendedMorsel()) try: morsel.set(key, real_value, coded_value) except CookieError: pass dict.__setitem__(self, key, morsel) class _DictAccessorProperty(object): """Baseclass for `environ_property` and `header_property`.""" read_only = False def __init__(self, name, default=None, load_func=None, dump_func=None, read_only=None, doc=None): self.name = name self.default = default self.load_func = load_func self.dump_func = dump_func if read_only is not None: self.read_only = read_only self.__doc__ = doc def __get__(self, obj, type=None): if obj is None: return self storage = self.lookup(obj) if self.name not in storage: return self.default rv = storage[self.name] if self.load_func is not None: try: rv = self.load_func(rv) except (ValueError, TypeError): rv = self.default return rv def __set__(self, obj, value): if self.read_only: raise AttributeError('read only property') if self.dump_func is not None: value = self.dump_func(value) self.lookup(obj)[self.name] = value def __delete__(self, obj): if self.read_only: raise AttributeError('read only property') self.lookup(obj).pop(self.name, None) def __repr__(self): return '<%s %s>' % ( self.__class__.__name__, self.name ) class _UpdateDict(dict): """A dict that calls `on_update` on modifications.""" def __init__(self, data, on_update): dict.__init__(self, data) self.on_update = on_update def calls_update(f): def oncall(self, *args, **kw): rv = f(self, *args, **kw) if self.on_update is not None: self.on_update(self) return rv return _patch_wrapper(f, oncall) __setitem__ = calls_update(dict.__setitem__) __delitem__ = calls_update(dict.__delitem__) clear = calls_update(dict.clear) pop = calls_update(dict.pop) popitem = calls_update(dict.popitem) setdefault = calls_update(dict.setdefault) update = calls_update(dict.update) def _easteregg(app): """Like the name says.""" gyver = '\n'.join([x + (77 - len(x)) * ' ' for x in ''' eJyFlzuOJDkMRP06xRjymKgDJCDQStBYT8BCgK4gTwfQ2fcFs2a2FzvZk+hvlcRvRJD148efHt9m 9Xz94dRY5hGt1nrYcXx7us9qlcP9HHNh28rz8dZj+q4rynVFFPdlY4zH873NKCexrDM6zxxRymzz 4QIxzK4bth1PV7+uHn6WXZ5C4ka/+prFzx3zWLMHAVZb8RRUxtFXI5DTQ2n3Hi2sNI+HK43AOWSY jmEzE4naFp58PdzhPMdslLVWHTGUVpSxImw+pS/D+JhzLfdS1j7PzUMxij+mc2U0I9zcbZ/HcZxc q1QjvvcThMYFnp93agEx392ZdLJWXbi/Ca4Oivl4h/Y1ErEqP+lrg7Xa4qnUKu5UE9UUA4xeqLJ5 jWlPKJvR2yhRI7xFPdzPuc6adXu6ovwXwRPXXnZHxlPtkSkqWHilsOrGrvcVWXgGP3daXomCj317 8P2UOw/NnA0OOikZyFf3zZ76eN9QXNwYdD8f8/LdBRFg0BO3bB+Pe/+G8er8tDJv83XTkj7WeMBJ v/rnAfdO51d6sFglfi8U7zbnr0u9tyJHhFZNXYfH8Iafv2Oa+DT6l8u9UYlajV/hcEgk1x8E8L/r XJXl2SK+GJCxtnyhVKv6GFCEB1OO3f9YWAIEbwcRWv/6RPpsEzOkXURMN37J0PoCSYeBnJQd9Giu LxYQJNlYPSo/iTQwgaihbART7Fcyem2tTSCcwNCs85MOOpJtXhXDe0E7zgZJkcxWTar/zEjdIVCk iXy87FW6j5aGZhttDBoAZ3vnmlkx4q4mMmCdLtnHkBXFMCReqthSGkQ+MDXLLCpXwBs0t+sIhsDI tjBB8MwqYQpLygZ56rRHHpw+OAVyGgaGRHWy2QfXez+ZQQTTBkmRXdV/A9LwH6XGZpEAZU8rs4pE 1R4FQ3Uwt8RKEtRc0/CrANUoes3EzM6WYcFyskGZ6UTHJWenBDS7h163Eo2bpzqxNE9aVgEM2CqI GAJe9Yra4P5qKmta27VjzYdR04Vc7KHeY4vs61C0nbywFmcSXYjzBHdiEjraS7PGG2jHHTpJUMxN Jlxr3pUuFvlBWLJGE3GcA1/1xxLcHmlO+LAXbhrXah1tD6Ze+uqFGdZa5FM+3eHcKNaEarutAQ0A QMAZHV+ve6LxAwWnXbbSXEG2DmCX5ijeLCKj5lhVFBrMm+ryOttCAeFpUdZyQLAQkA06RLs56rzG 8MID55vqr/g64Qr/wqwlE0TVxgoiZhHrbY2h1iuuyUVg1nlkpDrQ7Vm1xIkI5XRKLedN9EjzVchu jQhXcVkjVdgP2O99QShpdvXWoSwkp5uMwyjt3jiWCqWGSiaaPAzohjPanXVLbM3x0dNskJsaCEyz DTKIs+7WKJD4ZcJGfMhLFBf6hlbnNkLEePF8Cx2o2kwmYF4+MzAxa6i+6xIQkswOqGO+3x9NaZX8 MrZRaFZpLeVTYI9F/djY6DDVVs340nZGmwrDqTCiiqD5luj3OzwpmQCiQhdRYowUYEA3i1WWGwL4 GCtSoO4XbIPFeKGU13XPkDf5IdimLpAvi2kVDVQbzOOa4KAXMFlpi/hV8F6IDe0Y2reg3PuNKT3i RYhZqtkQZqSB2Qm0SGtjAw7RDwaM1roESC8HWiPxkoOy0lLTRFG39kvbLZbU9gFKFRvixDZBJmpi Xyq3RE5lW00EJjaqwp/v3EByMSpVZYsEIJ4APaHmVtpGSieV5CALOtNUAzTBiw81GLgC0quyzf6c NlWknzJeCsJ5fup2R4d8CYGN77mu5vnO1UqbfElZ9E6cR6zbHjgsr9ly18fXjZoPeDjPuzlWbFwS pdvPkhntFvkc13qb9094LL5NrA3NIq3r9eNnop9DizWOqCEbyRBFJTHn6Tt3CG1o8a4HevYh0XiJ sR0AVVHuGuMOIfbuQ/OKBkGRC6NJ4u7sbPX8bG/n5sNIOQ6/Y/BX3IwRlTSabtZpYLB85lYtkkgm p1qXK3Du2mnr5INXmT/78KI12n11EFBkJHHp0wJyLe9MvPNUGYsf+170maayRoy2lURGHAIapSpQ krEDuNoJCHNlZYhKpvw4mspVWxqo415n8cD62N9+EfHrAvqQnINStetek7RY2Urv8nxsnGaZfRr/ nhXbJ6m/yl1LzYqscDZA9QHLNbdaSTTr+kFg3bC0iYbX/eQy0Bv3h4B50/SGYzKAXkCeOLI3bcAt mj2Z/FM1vQWgDynsRwNvrWnJHlespkrp8+vO1jNaibm+PhqXPPv30YwDZ6jApe3wUjFQobghvW9p 7f2zLkGNv8b191cD/3vs9Q833z8t'''.decode('base64').decode('zlib').splitlines()]) def easteregged(environ, start_response): def injecting_start_response(status, headers, exc_info=None): headers.append(('X-Powered-By', 'Werkzeug')) return start_response(status, headers, exc_info) if environ.get('QUERY_STRING') != 'macgybarchakku': return app(environ, injecting_start_response) injecting_start_response('200 OK', [('Content-Type', 'text/html')]) return [''' About Werkzeug</> <style type="text/css"> body { font: 15px Georgia, serif; text-align: center; } a { color: #333; text-decoration: none; } h1 { font-size: 30px; margin: 20px 0 10px 0; } p { margin: 0 0 30px 0; } pre { font: 11px 'Consolas', 'Monaco', monospace; line-height: 0.95; } </style> <h1><a href="http://werkzeug.pocoo.org/">Werkzeug</a></h1> <p>the Swiss Army knife of Python web development. <pre>%s\n\n\n</>''' % gyver] return easteregged PK�����[8N��N�����werkzeug/_internal.pyc; raHc�����������@���s��d��Z��d�k�Z�d�k�Z�d�k�l�Z�d�k�l�Z�d�k�l�Z�l �Z �l �Z �d�k �l �Z �l �Z �l �Z �d�k�l�Z�e�a�e�d��Z�e���Z�h��d�d �<d �d �<d �d �<d�d�<d�d�<d�d�<d�d�<d�d�<d�d�<d�d�<d�d�<d�d�<d �d!�<d"�d#�<d$�d%�<d&�d'�<d(�d)�<d*�d+�<d,�d-�<d.�d/�<d0�d1�<d2�d3�<d4�d5�<d6�d7�<d8�d9�<d:�d;�<d<�d=�<d>�d?�<d@�dA�<dB�dC�<dD�dE�<dF�dG�<dH�dI�<dJ�dK�<dL�dM�<dN�dO�<dP�dQ�<dR�dS�<dT�dU�<dV�dW�<dX�dY�<dZ�d[�<d\�d]�<d^�d_�<d`�da�<db�dc�<dd�de�<df�dg�<dh�di�<dj�dk�<Z�dl���Z�dm���Z�dn���Z�do���Z�dp���Z�dq���Z�dr�e �f�ds�����YZ�dt�e�i�f�du�����YZ�dv�e�f�dw�����YZ�dx�e�f�dy�����YZ�dz�e �f�d{�����YZ!�d|���Z"�d�S(}���s��� werkzeug._internal ~~~~~~~~~~~~~~~~~~ This module provides internally used helpers and constants. :copyright: Copyright 2008 by Armin Ronacher. :license: GNU GPL. N(���s���WeakKeyDictionary(���s���StringIO(���s ���BaseCookies���Morsels ���CookieError(���s���asctimes���gmtimes���time(���s���datetimes����id���s���Continueie���s���Switching Protocolsif���s ���Processingi���s���OKi���s���Createdi���s���Acceptedi���s���Non Authoritative Informationi���s ���No Contenti���s ���Reset Contenti���s���Partial Contenti���s ���Multi Statusi���s���IM Usedi,��s���Multiple Choicesi-��s���Moved Permanentlyi.��s���Foundi/��s ���See Otheri0��s ���Not Modifiedi1��s ���Use Proxyi3��s���Temporary Redirecti��s ���Bad Requesti��s ���Unauthorizedi��s���Payment Requiredi��s ���Forbiddeni��s ���Not Foundi��s���Method Not Allowedi��s���Not Acceptablei��s���Proxy Authentication Requiredi��s���Request Timeouti��s���Conflicti��s���Gonei��s���Length Requiredi��s���Precondition Failedi��s���Request Entity Too Largei��s���Request URI Too Longi��s���Unsupported Media Typei��s���Requested Range Not Satisfiablei��s���Expectation Failedi��s���Unprocessable Entityi��s���Lockedi��s���Failed Dependencyi��s���Upgrade Requiredi��s ���Retry Withi��s���Internal Server Errori��s���Not Implementedi��s ���Bad Gatewayi��s���Service Unavailablei��s���Gateway Timeouti��s���HTTP Version Not Supportedi��s���Insufficient Storagei��s ���Not Extendedc���������O���su���t��t�j�oE�d�k�}�|�i���}�|�i�d��a��t��i�|��t��i�|�i��n�t �t��|���|�i ���|�|��d�S(���s&���Log into the internal werkzeug logger.Ns���werkzeug(���s���_loggers���Nones���loggings ���StreamHandlers���handlers ���getLoggers ���addHandlers���setLevels���INFOs���getattrs���types���messages���rstrips���argss���kwargs(���s���types���messages���argss���kwargss���loggings���handler(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���_logO���s������    c��� ���������s>��t��|��d��o �|��i�}��n�t�i�|���}�|�t�j �o�|�Sn�t�i�|���\�}����} �| �p�f��} �t �|���g���x�t�|��D]�\�}�}�t�|�t��o�t�d���n�y�| �|��}�Wn"�t�j �o�|�t�t�f�}�n�X|�t�|�f�}��i�|��q�Wt���������d���}�|�t�|��<|�Sd�S(���s+���Return a signature object for the function.s���im_funcsC���cannot parse functions that unpack tuples in the function signaturec��� ���������sV��g��}�g��} �h��}�x�t���D]�\�}�\�}�}�}�y�|�i �|��|��Wnh�t �j �o\�y�|�i �|�i �|���Wq�t�j �o,�|�o�|�i �|��q�| �i �|��q�Xq�X|�|�j�o�|�i �|��|�|�<q�q�W|����}��t�j �o�|�i�|��f��}�n�|�o ��t�j � o�|�i�|��h��}�n�|�|�| �|�|����f�Sd��S(���N(���s���new_argss���missings���extras ���enumerates ���argumentss���idxs���names ���has_defaults���defaults���appends���argss ���IndexErrors���kwargss���pops���KeyErrors ���arg_counts���extra_positionals ���vararg_vars���Nones���extends ���kwarg_vars���update( ���s���argss���kwargss ���has_defaults���names���idxs���extras���defaults���new_argss���extra_positionals���missing(���s ���arg_counts ���vararg_vars ���argumentss ���kwarg_var(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���parsew���s0���� �       N(���s���hasattrs���funcs���im_funcs���_signature_caches���gets���parses���Nones���inspects ���getargspecs ���positionals ���vararg_vars ���kwarg_vars���defaultss���lens ���arg_counts ���argumentss ���enumerates���idxs���names ���isinstances���lists ���TypeErrors���defaults ���IndexErrors���Falses���params���Trues���appends���tuple( ���s���funcs ���vararg_vars���parses���params ���positionals ���argumentss���names���idxs���defaults ���arg_counts���defaultss ���kwarg_var(����(���s ���vararg_vars ���argumentss ���arg_counts ���kwarg_vars8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���_parse_signature[���s0�����     �   c���������C���sF���y4�|��i�|�_�|��i�|�_�|��i�|�_�|��i�|�_�Wn�n�X|�Sd�S(���sU���Helper function that forwards all the function details to the decorated function.N(���s���olds���__name__s���news ���__module__s���__doc__s���__dict__(���s���olds���new(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���_patch_wrapper���s�����   c���������C���s���t��}�|�i�d��o�|�d�}�d�}�n�y�|��i�|�|��SWnU�t�j �oI�}�|�t��j �o�|��i�|�d��Sn�d�k �l �}�|�t �|����n�Xd�S(���se���Like the regular decode function but this one raises an `HTTPUnicodeError` if errors is `strict`.s ���fallback:i ���s���stricts���ignore(���s���HTTPUnicodeErrorN( ���s���Nones���fallbacks���errorss ���startswiths���values���decodes���charsets ���UnicodeErrors���es���werkzeug.exceptionss���HTTPUnicodeErrors���str(���s���values���charsets���errorss���es���HTTPUnicodeErrors���fallback(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���_decode_unicode���s�����    c��� ������c���s��d�k��}�t�|�d��o5�x*�|�i�|���D]�\�}�}�} �|�| �f�Vq)�Wd�Sn�d�k�l�}�d�k �l �}�t ���}�x�|��D]�}��x~�t �i�|���D]m�}�t �i�i�|��|��}�|�|��}�|�o �|�d�j�o2�|�|�j�o!�|�i�|��|�|�|��f�Vq�q�q�Wqx�Wd�S(���s&���Iterate over all modules in a package.Ns ���iter_modules(���s ���getmodulename(���s ���ispackages���__init__(���s���pkgutils���hasattrs ���iter_moduless���paths���importers���modnames���ispkgs���inspects ���getmodulenames���pydocs ���ispackages���sets���founds���oss���listdirs���filenames���joins���ps���add( ���s���paths ���ispackages���modnames���pkgutils ���getmodulenames���filenames���ps���importers���founds���ispkg(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys ���_iter_modules���s(����� �   ��   c���������C���s���|��t�j�o �t���}��nJ�t�|��t��o�|��i���}��n*�t�|��t�t�t�f��o�t�|���}��n�d�d�d�d�d�d�d�d�f�|��i �|��i �|�d �d �d �d �d �d�d�d�d�d�d�d�f �|��i �d�|�t �|��i��|��i�|��i�|��i�f �Sd�S(���s'���Used for `http_date` and `cookie_date`.s#���%s, %02d%s%s%s%s %02d:%02d:%02d GMTs���Mons���Tues���Weds���Thus���Fris���Sats���Suns���Jans���Febs���Mars���Aprs���Mays���Juns���Juls���Augs���Seps���Octs���Novs���Deci���N(���s���ds���Nones���gmtimes ���isinstances���datetimes ���utctimetuples���ints���longs���floats���tm_wdays���tm_mdays���delims���tm_mons���strs���tm_years���tm_hours���tm_mins���tm_sec(���s���ds���delim(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys ���_dump_date���s�����  s���_ExtendedMorselc�����������B���sB���t��Z�h��d�d�<Z�e�i�e�i��e�e�d��Z�e�d��Z�RS(���Ns���httponlys���HttpOnlyc���������C���s5���t��i�|���|�t�j �o�|��i�|�|�|��n�d��S(���N(���s���Morsels���__init__s���selfs���names���Nones���sets���value(���s���selfs���names���value(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���__init__���s����  c���������C���sJ���|��i�d�t��}�t�i�|��|��i�d��}�|�o�|�d�7}�n�|�Sd��S(���Ns���httponlys��� ;s ���; HttpOnly( ���s���selfs���pops���Falses���httponlys���Morsels ���OutputStrings���attrss���rstrips���result(���s���selfs���attrss���httponlys���result(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys ���OutputString���s ����(���s���__name__s ���__module__s ���_reserveds���updates���Morsels���Nones���__init__s ���OutputString(����(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���_ExtendedMorsel���s���s���_StorageHelperc�����������B���s)���t��Z�d��Z�e�i�Z�d���Z�d���Z�RS(���s���Helper class used by `parse_form_data` to parse submitted file and form data. Don't use this class directly. This also defines a simple repr that prints just the filename as the default repr reads the complete data of the stream. c������ ������st�����t�j �o�t���d��|��_�n�t�i�i�|��d�|�d�d�h��d�|�d�<d�|�d�<d�|�d�<d�t�d��S( ���Nc������������s�������S(���N(���s���stream_factory(���s���binary(���s���stream_factory(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���<lambda>���s����s���fps ���wsgi.inputs���environs���REQUEST_METHODs ���CONTENT_TYPEs���CONTENT_LENGTHs���keep_blank_values( ���s���stream_factorys���Nones���selfs ���make_files���cgis ���FieldStorages���__init__s���environs���True(���s���selfs���environs���stream_factory(����(���s���stream_factorys8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���__init__���s ����   -c���������C���s���d�|��i�i�|��i�f�Sd��S(���Ns���<%s %r>(���s���selfs ���__class__s���__name__s���name(���s���self(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���__repr__��s����(���s���__name__s ���__module__s���__doc__s���cgis ���FieldStorages���FieldStorageClasss���__init__s���__repr__(����(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���_StorageHelper���s��� �  s���_ExtendedCookiec�����������B���s���t��Z�d��Z�d���Z�RS(���s���Form of the base cookie that doesn't raise a `CookieError` for malformed keys. This has the advantage that broken cookies submitted by nonstandard browsers don't cause the cookie to be empty. c���������C���sY���|��i�|�t����}�y�|�i�|�|�|��Wn�t�j �o�n�Xt �i �|��|�|��d��S(���N( ���s���selfs���gets���keys���_ExtendedMorsels���morsels���sets ���real_values ���coded_values ���CookieErrors���dicts ���__setitem__(���s���selfs���keys ���real_values ���coded_values���morsel(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���_BaseCookie__set��s ����(���s���__name__s ���__module__s���__doc__s���_BaseCookie__set(����(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���_ExtendedCookie ��s��� �s���_DictAccessorPropertyc�����������B���sS���t��Z�d��Z�e�Z�e�e�e�e�e�d��Z�e�d��Z�d���Z�d���Z �d���Z �RS(���s7���Baseclass for `environ_property` and `header_property`.c���������C���sK���|�|��_��|�|��_�|�|��_�|�|��_�|�t�j �o �|�|��_�n�|�|��_�d��S(���N( ���s���names���selfs���defaults ���load_funcs ���dump_funcs ���read_onlys���Nones���docs���__doc__(���s���selfs���names���defaults ���load_funcs ���dump_funcs ���read_onlys���doc(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���__init__��s����      c���������C���s���|�t�j�o�|��Sn�|��i�|��}�|��i�|�j�o �|��i�Sn�|�|��i�}�|��i�t�j �o<�y�|��i�|��}�Wq�t �t �f�j �o�|��i�}�q�Xn�|�Sd��S(���N( ���s���objs���Nones���selfs���lookups���storages���names���defaults���rvs ���load_funcs ���ValueErrors ���TypeError(���s���selfs���objs���types���storages���rv(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���__get__(��s����   c���������C���sW���|��i�o�t�d���n�|��i�t�j �o�|��i�|��}�n�|�|��i�|��|��i�<d��S(���Ns���read only property( ���s���selfs ���read_onlys���AttributeErrors ���dump_funcs���Nones���values���lookups���objs���name(���s���selfs���objs���value(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���__set__6��s ���� c���������C���s:���|��i�o�t�d���n�|��i�|��i�|��i�t��d��S(���Ns���read only property(���s���selfs ���read_onlys���AttributeErrors���lookups���objs���pops���names���None(���s���selfs���obj(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys ���__delete__=��s���� c���������C���s���d�|��i�i�|��i�f�Sd��S(���Ns���<%s %s>(���s���selfs ���__class__s���__name__s���name(���s���self(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���__repr__B��s����( ���s���__name__s ���__module__s���__doc__s���Falses ���read_onlys���Nones���__init__s���__get__s���__set__s ���__delete__s���__repr__(����(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���_DictAccessorProperty��s��� �   s ���_UpdateDictc�����������B���s���t��Z�d��Z�d���Z�d���Z�e�e�i��Z�e�e�i��Z�e�e�i��Z�e�e�i ��Z �e�e�i ��Z �e�e�i ��Z �e�e�i ��Z �RS(���s/���A dict that calls `on_update` on modifications.c���������C���s���t��i�|��|��|�|��_�d��S(���N(���s���dicts���__init__s���selfs���datas ���on_update(���s���selfs���datas ���on_update(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���__init__L��s����c������������s�����d���}�t���|��Sd��S(���Nc������������s;�����|��|�|��}�|��i�t�j �o�|��i�|���n�|�Sd��S(���N(���s���fs���selfs���argss���kws���rvs ���on_updates���None(���s���selfs���argss���kws���rv(���s���f(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���oncallQ��s����(���s���oncalls���_patch_wrappers���f(���s���fs���oncall(����(���s���fs8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys ���calls_updateP��s���� ( ���s���__name__s ���__module__s���__doc__s���__init__s ���calls_updates���dicts ���__setitem__s ���__delitem__s���clears���pops���popitems ���setdefaults���update(����(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys ���_UpdateDictI��s��� �  c������������sq���d�i��g��i�}�d�i�d��i�d��i���D]"�}�|�|�d�t�|��d��q,�~������d���}�|�Sd�S( ���s���Like the name says.s��� s# �� eJyFlzuOJDkMRP06xRjymKgDJCDQStBYT8BCgK4gTwfQ2fcFs2a2FzvZk+hvlcRvRJD148efHt9m 9Xz94dRY5hGt1nrYcXx7us9qlcP9HHNh28rz8dZj+q4rynVFFPdlY4zH873NKCexrDM6zxxRymzz 4QIxzK4bth1PV7+uHn6WXZ5C4ka/+prFzx3zWLMHAVZb8RRUxtFXI5DTQ2n3Hi2sNI+HK43AOWSY jmEzE4naFp58PdzhPMdslLVWHTGUVpSxImw+pS/D+JhzLfdS1j7PzUMxij+mc2U0I9zcbZ/HcZxc q1QjvvcThMYFnp93agEx392ZdLJWXbi/Ca4Oivl4h/Y1ErEqP+lrg7Xa4qnUKu5UE9UUA4xeqLJ5 jWlPKJvR2yhRI7xFPdzPuc6adXu6ovwXwRPXXnZHxlPtkSkqWHilsOrGrvcVWXgGP3daXomCj317 8P2UOw/NnA0OOikZyFf3zZ76eN9QXNwYdD8f8/LdBRFg0BO3bB+Pe/+G8er8tDJv83XTkj7WeMBJ v/rnAfdO51d6sFglfi8U7zbnr0u9tyJHhFZNXYfH8Iafv2Oa+DT6l8u9UYlajV/hcEgk1x8E8L/r XJXl2SK+GJCxtnyhVKv6GFCEB1OO3f9YWAIEbwcRWv/6RPpsEzOkXURMN37J0PoCSYeBnJQd9Giu LxYQJNlYPSo/iTQwgaihbART7Fcyem2tTSCcwNCs85MOOpJtXhXDe0E7zgZJkcxWTar/zEjdIVCk iXy87FW6j5aGZhttDBoAZ3vnmlkx4q4mMmCdLtnHkBXFMCReqthSGkQ+MDXLLCpXwBs0t+sIhsDI tjBB8MwqYQpLygZ56rRHHpw+OAVyGgaGRHWy2QfXez+ZQQTTBkmRXdV/A9LwH6XGZpEAZU8rs4pE 1R4FQ3Uwt8RKEtRc0/CrANUoes3EzM6WYcFyskGZ6UTHJWenBDS7h163Eo2bpzqxNE9aVgEM2CqI GAJe9Yra4P5qKmta27VjzYdR04Vc7KHeY4vs61C0nbywFmcSXYjzBHdiEjraS7PGG2jHHTpJUMxN Jlxr3pUuFvlBWLJGE3GcA1/1xxLcHmlO+LAXbhrXah1tD6Ze+uqFGdZa5FM+3eHcKNaEarutAQ0A QMAZHV+ve6LxAwWnXbbSXEG2DmCX5ijeLCKj5lhVFBrMm+ryOttCAeFpUdZyQLAQkA06RLs56rzG 8MID55vqr/g64Qr/wqwlE0TVxgoiZhHrbY2h1iuuyUVg1nlkpDrQ7Vm1xIkI5XRKLedN9EjzVchu jQhXcVkjVdgP2O99QShpdvXWoSwkp5uMwyjt3jiWCqWGSiaaPAzohjPanXVLbM3x0dNskJsaCEyz DTKIs+7WKJD4ZcJGfMhLFBf6hlbnNkLEePF8Cx2o2kwmYF4+MzAxa6i+6xIQkswOqGO+3x9NaZX8 MrZRaFZpLeVTYI9F/djY6DDVVs340nZGmwrDqTCiiqD5luj3OzwpmQCiQhdRYowUYEA3i1WWGwL4 GCtSoO4XbIPFeKGU13XPkDf5IdimLpAvi2kVDVQbzOOa4KAXMFlpi/hV8F6IDe0Y2reg3PuNKT3i RYhZqtkQZqSB2Qm0SGtjAw7RDwaM1roESC8HWiPxkoOy0lLTRFG39kvbLZbU9gFKFRvixDZBJmpi Xyq3RE5lW00EJjaqwp/v3EByMSpVZYsEIJ4APaHmVtpGSieV5CALOtNUAzTBiw81GLgC0quyzf6c NlWknzJeCsJ5fup2R4d8CYGN77mu5vnO1UqbfElZ9E6cR6zbHjgsr9ly18fXjZoPeDjPuzlWbFwS pdvPkhntFvkc13qb9094LL5NrA3NIq3r9eNnop9DizWOqCEbyRBFJTHn6Tt3CG1o8a4HevYh0XiJ sR0AVVHuGuMOIfbuQ/OKBkGRC6NJ4u7sbPX8bG/n5sNIOQ6/Y/BX3IwRlTSabtZpYLB85lYtkkgm p1qXK3Du2mnr5INXmT/78KI12n11EFBkJHHp0wJyLe9MvPNUGYsf+170maayRoy2lURGHAIapSpQ krEDuNoJCHNlZYhKpvw4mspVWxqo415n8cD62N9+EfHrAvqQnINStetek7RY2Urv8nxsnGaZfRr/ nhXbJ6m/yl1LzYqscDZA9QHLNbdaSTTr+kFg3bC0iYbX/eQy0Bv3h4B50/SGYzKAXkCeOLI3bcAt mj2Z/FM1vQWgDynsRwNvrWnJHlespkrp8+vO1jNaibm+PhqXPPv30YwDZ6jApe3wUjFQobghvW9p 7f2zLkGNv8b191cD/3vs9Q833z8ts���base64s���zlibiM���s��� c������������s[���t����d��}�|��i�d��d�j�o��|��|��Sn�|�d�d�d�f�g��d��g�Sd��S(���Nc������������s'���|�i�d�d�f����|��|�|��Sd��S(���Ns ���X-Powered-Bys���Werkzeug(���s���headerss���appends���start_responses���statuss���exc_info(���s���statuss���headerss���exc_info(���s���start_response(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���injecting_start_response��s����s ���QUERY_STRINGs���macgybarchakkus���200 OKs ���Content-Types ���text/htmls��<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <title>About Werkzeug</> <style type="text/css"> body { font: 15px Georgia, serif; text-align: center; } a { color: #333; text-decoration: none; } h1 { font-size: 30px; margin: 20px 0 10px 0; } p { margin: 0 0 30px 0; } pre { font: 11px 'Consolas', 'Monaco', monospace; line-height: 0.95; } </style> <h1><a href="http://werkzeug.pocoo.org/">Werkzeug</a></h1> <p>the Swiss Army knife of Python web development. <pre>%s </>(���s���Nones���injecting_start_responses���environs���gets���apps���gyver(���s���environs���start_responses���injecting_start_response(���s���apps���gyver(���s���start_responses8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys ���easteregged��s ����N( ���s���joins���appends���_[1]s���decodes ���splitliness���xs���lens���gyvers ���easteregged(���s���apps ���eastereggeds���_[1]s���gyvers���x(����(���s���apps���gyvers8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys ���_eastereggb��s�����Z (#���s���__doc__s���cgis���inspects���weakrefs���WeakKeyDictionarys ���cStringIOs���StringIOs���Cookies ���BaseCookies���Morsels ���CookieErrors���times���asctimes���gmtimes���datetimes���Nones���_loggers ���_empty_streams���_signature_caches���HTTP_STATUS_CODESs���_logs���_parse_signatures���_patch_wrappers���_decode_unicodes ���_iter_moduless ���_dump_dates���_ExtendedMorsels ���FieldStorages���_StorageHelpers���_ExtendedCookies���objects���_DictAccessorPropertys���dicts ���_UpdateDicts ���_easteregg(���s ���_UpdateDicts���_patch_wrappers���_decode_unicodes���datetimes���_StorageHelpers���_signature_caches���WeakKeyDictionarys ���_empty_streams���asctimes���HTTP_STATUS_CODESs���cgis���_DictAccessorPropertys ���_iter_moduless���inspects���gmtimes���_logs ���BaseCookies���_ExtendedMorsels ���_eastereggs���_ExtendedCookies���StringIOs���Morsels���_parse_signatures ���_dump_dates���times ���CookieError(����(����s8���build/bdist.darwin-8.11.1-i386/egg/werkzeug/_internal.pys���? ���s0���       �6 @   /PK�����݉8C?4��4�����werkzeug/exceptions.py# -*- coding: utf-8 -*- """ werkzeug.exceptions ~~~~~~~~~~~~~~~~~~~ This module implements a number of Python exceptions you can raise from within your views to trigger a standard non 200 response. Usage Example ------------- :: from werkzeug import BaseRequest, responder from werkzeug.exceptions import HTTPException, NotFound def view(request): raise NotFound() @responder def application(environ, start_response): request = BaseRequest(environ) try: return view(request) except HTTPException, e: return e As you can see from this example those exceptions are callable WSGI applications. Because of Python 2.3 / 2.4 compatibility those do not extend from the response objects but only from the python exception class. As a matter of fact they are not Werkzeug response objects. However you can get a response object by calling ``get_response()`` on a HTTP exception. Keep in mind that you have to pass an environment to ``get_response()`` because some errors fetch additional information from the WSGI environment. If you want to hook in a different exception page to say, an 404 status code, you can add a second except for a specific subclass of an error:: @responder def application(environ, start_response): request = BaseRequest(environ) try: return view(request) except NotFound, e: return not_found(request) except HTTPException, e: return e :copyright: 2007-2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import sys from werkzeug._internal import HTTP_STATUS_CODES class HTTPException(Exception): """ Baseclass for all HTTP exceptions. This exception can be called as WSGI application to render a default error page or you can catch the subclasses of it independently and render nicer error messages. """ code = None description = None def __init__(self, description=None): Exception.__init__(self, '%d %s' % (self.code, self.name)) if description is not None: self.description = description def wrap(cls, exception, name=None): """ This method returns a new subclass of the exception provided that also is a subclass of `BadRequest`. """ class newcls(cls, exception): def __init__(self, arg=None, description=None): cls.__init__(self, description) exception.__init__(self, arg) newcls.__module__ = sys._getframe(1).f_globals.get('__name__') newcls.__name__ = name or cls.__name__ + exception.__name__ return newcls wrap = classmethod(wrap) def name(self): """The status name.""" return HTTP_STATUS_CODES[self.code] name = property(name, doc=name.__doc__) def get_description(self, environ): """Get the description.""" return self.description def get_body(self, environ): """Get the HTML body.""" return ( '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n' '<title>%(code)s %(name)s\n' '

%(name)s

\n' '%(description)s\n' ) % { 'code': self.code, 'name': escape(self.name), 'description': self.get_description(environ) } def get_headers(self, environ): """Get a list of headers.""" return [('Content-Type', 'text/html')] def get_response(self, environ): """Get a response object.""" # lazyly imported for various reasons. For one can use the exceptions # with custom responses (testing exception instances against types) and # so we don't ever have to import the wrappers, but also because there # are ciruclar dependencies when bootstrapping the module. from werkzeug.wrappers import BaseResponse headers = self.get_headers(environ) return BaseResponse(self.get_body(environ), self.code, headers) def __call__(self, environ, start_response): """Call the exception as WSGI application.""" response = self.get_response(environ) return response(environ, start_response) class _ProxyException(HTTPException): """ An HTTP exception that expands renders a WSGI application on error. """ def __init__(self, response): Exception.__init__(self, 'proxy exception for %r' % response) self.response = response def get_response(self, environ): return self.response class BadRequest(HTTPException): """ *400* `Bad Request` Raise if the browser send something to the application the application or server cannot handle. """ code = 400 description = ( '

The browser (or proxy) sent a request that this server could ' 'not understand.

' ) class Unauthorized(HTTPException): """ *401* `Unauthorized` Raise if the user is not authorized. Also used if you want to use HTTP basic auth. """ code = 401 description = ( '

The server could not verify that you are authorized to access ' 'the URL requested. You either supplied the wrong credentials (e.g.' ', bad password), or your browser doesn\'t understand how to supply ' 'the credentials required.

In case you are allowed to request ' 'the document, please check your user-id and password and try ' 'again.

' ) class Forbidden(HTTPException): """ *403* `Forbidden` Raise if the user doesn't have the permission for the requested resource but was authenticated. """ code = 403 description = ( '

You don\'t have the permission to access the requested resource. ' 'It is either read-protected or not readable by the server.

' ) class NotFound(HTTPException): """ *404* `Not Found` Raise if a resource does not exist and never existed. """ code = 404 description = ( '

The requested URL was not found on the server.

' '

If you entered the URL manually please check your spelling and ' 'try again.

' ) class MethodNotAllowed(HTTPException): """ *405* `Method Not Allowed` Raise if the server used a method the resource does not handle. For example `POST` if the resource is view only. Especially useful for REST. The first argument for this exception should be a list of allowed methods. Strictly speaking the response would be invalid if you don't provide valid methods in the header which you can do with that list. """ code = 405 def __init__(self, valid_methods=None, description=None): """Takes an optional list of valid http methods starting with werkzeug 0.3 the list will be mandatory.""" HTTPException.__init__(self, description) self.valid_methods = valid_methods def get_headers(self, environ): headers = HTTPException.get_headers(self, environ) if self.valid_methods: headers.append(('Allow', ', '.join(self.valid_methods))) return headers def get_description(self, environ): m = escape(environ.get('REQUEST_METHOD', 'GET')) return '

The method %s is not allowed for the requested URL.

' % m class NotAcceptable(HTTPException): """ *406* `Not Acceptable` Raise if the server cant return any content conforming to the `Accept` headers of the client. """ code = 406 description = ( '

The resource identified by the request is only capable of ' 'generating response entities which have content characteristics ' 'not acceptable according to the accept headers sent in the ' 'request.

' ) class RequestTimeout(HTTPException): """ *408* `Request Timeout` Raise to signalize a timeout. """ code = 408 description = ( '

The server closed the network connection because the browser ' 'didn\'t finish the request within the specified time.

' ) class Gone(HTTPException): """ *410* `Gone` Raise if a resource existed previously and went away without new location. """ code = 410 description = ( '

The requested URL is no longer available on this server and ' 'there is no forwarding address.

If you followed a link ' 'from a foreign page, please contact the author of this page.' ) class LengthRequired(HTTPException): """ *411* `Length Required` Raise if the browser submitted data but no ``Content-Length`` header which is required for the kind of processing the server does. """ code = 411 description = ( '

A request with this method requires a valid Content-' 'Lenght header.

' ) class PreconditionFailed(HTTPException): """ *412* `Precondition Failed` Status code used in combination with ``If-Match``, ``If-None-Match``, or ``If-Unmodified-Since``. """ code = 412 description = ( '

The precondition on the request for the URL failed positive ' 'evaluation.

' ) class RequestEntityTooLarge(HTTPException): """ *413* `Request Entity Too Large` The status code one should return if the data submitted exceeded a given limit. """ code = 413 description = ( '

The data value transmitted exceed the capacity limit.

' ) class RequestURITooLarge(HTTPException): """ *414* `Request URI Too Large` Like *413* but for too long URLs. """ code = 414 description = ( '

The length of the requested URL exceeds the capacity limit ' 'for this server. The request cannot be processed.

' ) class UnsupportedMediaType(HTTPException): """ *415* `Unsupported Media Type` The status code returned if the server is unable to handle the media type the client transmitted. """ code = 415 description = ( '

The server does not support the media type transmitted in ' 'the request.

' ) class InternalServerError(HTTPException): """ *500* `Internal Server Error` Raise if an internal server error occoured. This is a good fallback if an unknown error occoured in the dispatcher. """ code = 500 description = ( '

The server encountered an internal error and was unable to ' 'complete your request. Either the server is overloaded or there ' 'is an error in the application.

' ) class NotImplemented(HTTPException): """ *501* `Not Implemented` Raise if the application does not support the action requested by the browser. """ code = 501 description = ( '

The server does not support the action requested by the ' 'browser.

' ) class BadGateway(HTTPException): """ *502* `Bad Gateway` If you do proxing in your application you should return this status code if you received an invalid response from the upstream server it accessed in attempting to fulfill the request. """ code = 502 description = ( '

The proxy server received an invalid response from an upstream ' 'server.

' ) class ServiceUnavailable(HTTPException): """ *503* `Service Unavailable` Status code you should return if a service is temporarily unavailable. """ code = 503 description = ( '

The server is temporarily unable to service your request due to ' 'maintenance downtime or capacity problems. Please try again ' 'later.

' ) default_exceptions = {} __all__ = ['HTTPException'] def _find_exceptions(): for name, obj in globals().iteritems(): try: if getattr(obj, 'code', None) is not None: default_exceptions[obj.code] = obj __all__.append(obj.__name__) except TypeError: continue _find_exceptions() del _find_exceptions #: raised by the request functions if they were unable to decode the #: incomding data properly. HTTPUnicodeError = BadRequest.wrap(UnicodeError, 'HTTPUnicodeError') class Aborter(object): """ When passed a dict of code -> exception items it can be used as callable that raises exceptions. If the first argument to the callable is a integer it will be looked up in the mapping, if it's a WSGI application it will be raised in a proxy exception. The rest of the arguments are forwarded to the exception constructor. """ def __init__(self, mapping=None, extra=None): if mapping is None: mapping = default_exceptions self.mapping = dict(mapping) if extra is not None: self.mapping.update(extra) def __call__(self, code, *args, **kwargs): if not args and not kwargs and not isinstance(code, (int, long)): raise _ProxyException(code) if code not in self.mapping: raise LookupError('no exception for %r' % code) raise self.mapping[code](*args, **kwargs) abort = Aborter() # imported here because of circular dependencies of werkzeug.utils from werkzeug.utils import escape PK[8EPPwerkzeug/exceptions.pyc; raHc@s"dZdkZdklZdefdYZdefdYZdefdYZd efd YZd efd YZ d efdYZ defdYZ defdYZ defdYZ defdYZdefdYZdefdYZdefdYZdefdYZdefd YZd!efd"YZd#efd$YZd%efd&YZd'efd(YZhZdgZd)Ze[eied*Zd+efd,YZeZ d-k!l"Z"dS(.s werkzeug.exceptions ~~~~~~~~~~~~~~~~~~~ This module implements a number of Python exceptions you can raise from within your views to trigger a standard non 200 response. Usage Example ------------- :: from werkzeug import BaseRequest, responder from werkzeug.exceptions import HTTPException, NotFound def view(request): raise NotFound() @responder def application(environ, start_response): request = BaseRequest(environ) try: return view(request) except HTTPException, e: return e As you can see from this example those exceptions are callable WSGI applications. Because of Python 2.3 / 2.4 compatibility those do not extend from the response objects but only from the python exception class. As a matter of fact they are not Werkzeug response objects. However you can get a response object by calling ``get_response()`` on a HTTP exception. Keep in mind that you have to pass an environment to ``get_response()`` because some errors fetch additional information from the WSGI environment. If you want to hook in a different exception page to say, an 404 status code, you can add a second except for a specific subclass of an error:: @responder def application(environ, start_response): request = BaseRequest(environ) try: return view(request) except NotFound, e: return not_found(request) except HTTPException, e: return e :copyright: 2007-2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. N(sHTTP_STATUS_CODESs HTTPExceptioncBstZdZeZeZedZedZeeZdZ e e de iZ dZ dZ dZ dZd ZRS( s Baseclass for all HTTP exceptions. This exception can be called as WSGI application to render a default error page or you can catch the subclasses of it independently and render nicer error messages. cCs>ti|d|i|if|tj o ||_ndS(Ns%d %s(s Exceptions__init__sselfscodesnames descriptionsNone(sselfs description((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys__init__Js  cs_dfdY}tidiid|_|pi i |_ |SdS(s This method returns a new subclass of the exception provided that also is a subclass of `BadRequest`. snewclscstZeedZRS(Ncs$i||i||dS(N(sclss__init__sselfs descriptions exceptionsarg(sselfsargs description(s exceptionscls(s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys__init__Us(s__name__s __module__sNones__init__((s exceptionscls(s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysnewclsTsis__name__N( sclss exceptionsnewclsssyss _getframes f_globalssgets __module__snames__name__(sclss exceptionsnamesnewcls((sclss exceptions9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pyswrapOs cCst|iSdS(sThe status name.N(sHTTP_STATUS_CODESsselfscode(sself((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysname]ssdoccCs |iSdS(sGet the description.N(sselfs description(sselfsenviron((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysget_descriptionbscCs<dhd|i<dt|i<d|i|<SdS(sGet the HTML body.s{ %(code)s %(name)s

%(name)s

%(description)s scodesnames descriptionN(sselfscodesescapesnamesget_descriptionsenviron(sselfsenviron((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysget_bodyfscCsddfgSdS(sGet a list of headers.s Content-Types text/htmlN((sselfsenviron((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys get_headerssscCs<dkl}|i|}||i||i|SdS(sGet a response object.(s BaseResponseN(swerkzeug.wrapperss BaseResponsesselfs get_headerssenvironsheaderssget_bodyscode(sselfsenvironsheaderss BaseResponse((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys get_responsews cCs |i|}|||SdS(s'Call the exception as WSGI application.N(sselfs get_responsesenvironsresponsesstart_response(sselfsenvironsstart_responsesresponse((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys__call__s(s__name__s __module__s__doc__sNonescodes descriptions__init__swraps classmethodsnamespropertysget_descriptionsget_bodys get_headerss get_responses__call__(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys HTTPException@s       s_ProxyExceptioncBs tZdZdZdZRS(sM An HTTP exception that expands renders a WSGI application on error. cCs!ti|d|||_dS(Nsproxy exception for %r(s Exceptions__init__sselfsresponse(sselfsresponse((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys__init__scCs |iSdS(N(sselfsresponse(sselfsenviron((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys get_responses(s__name__s __module__s__doc__s__init__s get_response(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys_ProxyExceptions  s BadRequestcBstZdZdZdZRS(s *400* `Bad Request` Raise if the browser send something to the application the application or server cannot handle. isS

The browser (or proxy) sent a request that this server could not understand.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys BadRequests s UnauthorizedcBstZdZdZdZRS(s{ *401* `Unauthorized` Raise if the user is not authorized. Also used if you want to use HTTP basic auth. isP

The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.

In case you are allowed to request the document, please check your user-id and password and try again.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys Unauthorizeds s ForbiddencBstZdZdZdZRS(s *403* `Forbidden` Raise if the user doesn't have the permission for the requested resource but was authenticated. is

You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys Forbiddens sNotFoundcBstZdZdZdZRS(sV *404* `Not Found` Raise if a resource does not exist and never existed. is

The requested URL was not found on the server.

If you entered the URL manually please check your spelling and try again.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysNotFounds sMethodNotAllowedcBs5tZdZdZeedZdZdZRS(s *405* `Method Not Allowed` Raise if the server used a method the resource does not handle. For example `POST` if the resource is view only. Especially useful for REST. The first argument for this exception should be a list of allowed methods. Strictly speaking the response would be invalid if you don't provide valid methods in the header which you can do with that list. icCsti||||_dS(skTakes an optional list of valid http methods starting with werkzeug 0.3 the list will be mandatory.N(s HTTPExceptions__init__sselfs descriptions valid_methods(sselfs valid_methodss description((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys__init__scCsGti||}|io#|iddi|ifn|SdS(NsAllows, (s HTTPExceptions get_headerssselfsenvironsheaderss valid_methodssappendsjoin(sselfsenvironsheaders((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys get_headerss #cCs$t|idd}d|SdS(NsREQUEST_METHODsGETs:

The method %s is not allowed for the requested URL.

(sescapesenvironsgetsm(sselfsenvironsm((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysget_descriptions(s__name__s __module__s__doc__scodesNones__init__s get_headerssget_description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysMethodNotAlloweds  s NotAcceptablecBstZdZdZdZRS(s *406* `Not Acceptable` Raise if the server cant return any content conforming to the `Accept` headers of the client. is

The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys NotAcceptables sRequestTimeoutcBstZdZdZdZRS(sD *408* `Request Timeout` Raise to signalize a timeout. isx

The server closed the network connection because the browser didn't finish the request within the specified time.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysRequestTimeouts sGonecBstZdZdZdZRS(sf *410* `Gone` Raise if a resource existed previously and went away without new location. is

The requested URL is no longer available on this server and there is no forwarding address.

If you followed a link from a foreign page, please contact the author of this page.(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysGone s sLengthRequiredcBstZdZdZdZRS(s *411* `Length Required` Raise if the browser submitted data but no ``Content-Length`` header which is required for the kind of processing the server does. isV

A request with this method requires a valid Content-Lenght header.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysLengthRequireds sPreconditionFailedcBstZdZdZdZRS(s *412* `Precondition Failed` Status code used in combination with ``If-Match``, ``If-None-Match``, or ``If-Unmodified-Since``. isN

The precondition on the request for the URL failed positive evaluation.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysPreconditionFailed(s sRequestEntityTooLargecBstZdZdZdZRS(s *413* `Request Entity Too Large` The status code one should return if the data submitted exceeded a given limit. is<

The data value transmitted exceed the capacity limit.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysRequestEntityTooLarge6s sRequestURITooLargecBstZdZdZdZRS(sN *414* `Request URI Too Large` Like *413* but for too long URLs. ist

The length of the requested URL exceeds the capacity limit for this server. The request cannot be processed.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysRequestURITooLargeCs sUnsupportedMediaTypecBstZdZdZdZRS(s *415* `Unsupported Media Type` The status code returned if the server is unable to handle the media type the client transmitted. isM

The server does not support the media type transmitted in the request.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysUnsupportedMediaTypePs sInternalServerErrorcBstZdZdZdZRS(s *500* `Internal Server Error` Raise if an internal server error occoured. This is a good fallback if an unknown error occoured in the dispatcher. is

The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysInternalServerError^s sNotImplementedcBstZdZdZdZRS(sy *501* `Not Implemented` Raise if the application does not support the action requested by the browser. isG

The server does not support the action requested by the browser.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysNotImplementedms s BadGatewaycBstZdZdZdZRS(s *502* `Bad Gateway` If you do proxing in your application you should return this status code if you received an invalid response from the upstream server it accessed in attempting to fulfill the request. isM

The proxy server received an invalid response from an upstream server.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys BadGateway{s sServiceUnavailablecBstZdZdZdZRS(sq *503* `Service Unavailable` Status code you should return if a service is temporarily unavailable. is

The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

(s__name__s __module__s__doc__scodes description(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysServiceUnavailables cCs{xttiD]c\}}y>t|dttj o!|t|i exception items it can be used as callable that raises exceptions. If the first argument to the callable is a integer it will be looked up in the mapping, if it's a WSGI application it will be raised in a proxy exception. The rest of the arguments are forwarded to the exception constructor. cCsK|tjo t}nt||_|tj o|ii|ndS(N(smappingsNonesdefault_exceptionssdictsselfsextrasupdate(sselfsmappingsextra((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys__init__s    cOsu| o| ot|ttf ot|n||ijot d|n|i|||dS(Nsno exception for %r( sargsskwargss isinstancescodesintslongs_ProxyExceptionsselfsmappings LookupError(sselfscodesargsskwargs((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys__call__s '(s__name__s __module__s__doc__sNones__init__s__call__(((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pysAborters (sescape(#s__doc__ssysswerkzeug._internalsHTTP_STATUS_CODESs Exceptions HTTPExceptions_ProxyExceptions BadRequests Unauthorizeds ForbiddensNotFoundsMethodNotAlloweds NotAcceptablesRequestTimeoutsGonesLengthRequiredsPreconditionFailedsRequestEntityTooLargesRequestURITooLargesUnsupportedMediaTypesInternalServerErrorsNotImplementeds BadGatewaysServiceUnavailablesdefault_exceptionss__all__s_find_exceptionsswraps UnicodeErrorsHTTPUnicodeErrorsobjectsAbortersabortswerkzeug.utilssescape(sRequestTimeoutsescapesRequestEntityTooLargesServiceUnavailablesabortsUnsupportedMediaTypes BadGatewaysRequestURITooLargesAborters_ProxyExceptions HTTPExceptions__all__s NotAcceptables BadRequestsHTTPUnicodeErrorsNotImplementeds UnauthorizedsGonesPreconditionFailedssyssInternalServerErrorsMethodNotAllowedsNotFoundsLengthRequireds Forbiddensdefault_exceptionss_find_exceptionssHTTP_STATUS_CODES((s9build/bdist.darwin-8.11.1-i386/egg/werkzeug/exceptions.pys?;s<  G       PK݉8Lqqwerkzeug/http.py# -*- coding: utf-8 -*- """ werkzeug.http ~~~~~~~~~~~~~ Werkzeug comes with a bunch of utilties that help Werkzeug to deal with HTTP data. Most of the classes and functions provided by this module are used by the wrappers, but they are useful on their own too, especially if the response and request objects are not used. This covers some of the more HTTP centric features of WSGI, some other utilities such as cookie handling are documented in the `werkzeug.utils` module. :copyright: 2007-2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import re import rfc822 from urllib2 import parse_http_list as _parse_list_header from datetime import datetime try: from hashlib import md5 except ImportError: from md5 import new as md5 try: set = set frozenset = frozenset except NameError: from sets import Set as set, ImmutableSet as frozenset from werkzeug._internal import _patch_wrapper, _UpdateDict, HTTP_STATUS_CODES _accept_re = re.compile(r'([^\s;,]+)(?:[^,]*?;\s*q=(\d*(?:\.\d+)?))?') _token_chars = frozenset("!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" '^_`abcdefghijklmnopqrstuvwxyz|~') _etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)') class Accept(list): """An `Accept` object is just a list subclass for lists of ``(value, quality)`` tuples. It is automatically sorted by quality. """ def __init__(self, values=()): if values is None: list.__init__(self) self.provided = False else: self.provided = True values = [(a, b) for b, a in values] values.sort() values.reverse() list.__init__(self, [(a, b) for b, a in values]) def __getitem__(self, key): """Beside index lookup (getting item n) you can also pass it a string to get the quality for the item. If the item is not in the list, the returned quality is ``0``. """ if isinstance(key, basestring): for value in self: if value[0] == key: return value[1] return 0 return list.__getitem__(self, key) def __contains__(self, key): return self.find(key) > -1 def __repr__(self): return '%s(%s)' % ( self.__class__.__name__, list.__repr__(self) ) def index(self, key): """Get the position of en entry or raise `IndexError`.""" rv = self.find(key) if rv < 0: raise IndexError(key) return key def find(self, key): """Get the position of an entry or return -1""" if isinstance(key, basestring): for idx, value in enumerate(self): if value[0] == key: return idx return -1 return list.find(self, key) def values(self): """Return a list of the values, not the qualities.""" return [x[0] for x in self] def itervalues(self): """Iterate over all values.""" for item in self: yield item[0] def best(self): """The best match as value.""" return self and self[0][0] or None best = property(best) class HeaderSet(object): """Similar to the `ETags` class this implements a set like structure. Unlike `ETags` this is case insensitive and used for vary, allow, and content-language headers. If not constructed using the `parse_set_header` function the instanciation works like this: >>> hs = HeaderSet(['foo', 'bar', 'baz']) >>> hs HeaderSet(['foo', 'bar', 'baz']) """ def __init__(self, headers=None, on_update=None): self._headers = list(headers or ()) self._set = set([x.lower() for x in self._headers]) self.on_update = on_update def add(self, header): """Add a new header to the set.""" self.update((header,)) def remove(self, header): """Remove a layer from the set. This raises an `IndexError` if the header is not in the set.""" key = header.lower() if key not in self._set: raise IndexError(header) self._set.remove(key) for idx, key in enumerate(self._headers): if key.lower() == header: del self._headers[idx] break if self.on_update is not None: self.on_update(self) def update(self, iterable): """Add all the headers from the iterable to the set.""" inserted_any = False for header in iterable: key = header.lower() if key not in self._set: self._headers.append(header) self._set.add(key) inserted_any = True if inserted_any and self.on_update is not None: self.on_update(self) def discard(self, header): """Like remove but ignores errors.""" try: return self.remove(header) except IndexError: pass def find(self, header): """Return the index of the header in the set or return -1 if not found.""" header = header.lower() for idx, item in enumerate(self._headers): if item.lower() == header: return idx return -1 def index(self, header): """Return the index of the headerin the set or raise an `IndexError`.""" rv = self.find(header) if rv < 0: raise IndexError(header) return rv def clear(self): """Clear the set.""" self._set.clear() del self._headers[:] if self.on_update is not None: self.on_update(self) def as_set(self, preserve_casing=False): """Return the set as real python set structure. When calling this all the items are converted to lowercase and the ordering is lost. If `preserve_casing` is `True` the items in the set returned will have the original case like in the `HeaderSet`, otherwise they will be lowercase. """ if preserve_casing: return set(self._headers) return set(self._set) def to_header(self): """Convert the header set into an HTTP header string.""" return ', '.join(map(quote_header_value, self._headers)) def __getitem__(self, idx): return self._headers[idx] def __delitem__(self, idx): rv = self._headers.pop(idx) self._set.remove(rv.lower()) if self.on_update is not None: self.on_update(self) def __setitem__(self, idx, value): old = self._headers[idx] self._set.remove(old.lower()) self._headers[idx] = value self._set.add(value.lower()) if self.on_update is not None: self.on_update(self) def __contains__(self, header): return header.lower() in self._set def __len__(self): return len(self._set) def __iter__(self): return iter(self._headers) def __nonzero__(self): return bool(self._set) def __str__(self): return self.to_header() def __repr__(self): return '%s(%r)' % ( self.__class__.__name__, self._headers ) class CacheControl(_UpdateDict): """Subclass of a dict that stores values for a Cache-Control header. It has accesors for all the cache-control directives specified in RFC 2616. The class does not differentiate between request and response directives. Because the cache-control directives in the HTTP header use dashes the python descriptors use underscores for that. To get a header of the `CacheControl` object again you can convert the object into a string or call the `to_header()` function. If you plan to subclass it and add your own items have a look at the sourcecode for that class. The following attributes are exposed: `no_cache`, `no_store`, `max_age`, `max_stale`, `min_fresh`, `no_transform`, `only_if_cached`, `public`, `private`, `must_revalidate`, `proxy_revalidate`, and `s_maxage`""" def cache_property(key, default, type): """Return a new property object for a cache header. Useful if you want to add support for a cache extension in a subclass.""" return property(lambda x: x._get_cache_value(key, default, type), lambda x, v: x._set_cache_value(key, v, type), 'accessor for %r' % key) no_cache = cache_property('no-cache', '*', bool) no_store = cache_property('no-store', None, bool) max_age = cache_property('max-age', -1, int) max_stale = cache_property('max-stale', '*', int) min_fresh = cache_property('min-fresh', '*', int) no_transform = cache_property('no-transform', None, None) only_if_cached = cache_property('only-if-cached', None, bool) public = cache_property('public', None, bool) private = cache_property('private', '*', None) must_revalidate = cache_property('must-revalidate', None, bool) proxy_revalidate = cache_property('proxy-revalidate', None, bool) s_maxage = cache_property('s-maxage', None, None) def __init__(self, values=(), on_update=None): _UpdateDict.__init__(self, values or (), on_update) self.provided = values is not None def _get_cache_value(self, key, default, type): """Used internally be the accessor properties.""" if type is bool: return key in self if key in self: value = self[key] if value is None: return default elif type is not None: try: value = type(value) except ValueError: pass return value def _set_cache_value(self, key, value, type): """Used internally be the accessor properties.""" if type is bool: if value: self[key] = None else: self.pop(key, None) else: if value is not None: self[key] = value else: self.pop(key, None) def to_header(self): """Convert the stored values into a cache control header.""" return dump_header(self) def __str__(self): return self.to_header() def __repr__(self): return '<%s %r>' % ( self.__class__.__name__, self.to_header() ) # make cache_property a staticmethod so that subclasses of # `CacheControl` can use it for new properties. cache_property = staticmethod(cache_property) class ETags(object): """A set that can be used to check if one etag is present in a collection of etags. """ def __init__(self, strong_etags=None, weak_etags=None, star_tag=False): self._strong = frozenset(not star_tag and strong_etags or ()) self._weak = frozenset(weak_etags or ()) self.star_tag = star_tag def as_set(self, include_weak=False): """Convert the `ETags` object into a python set. Per default all the weak etags are not part of this set.""" rv = set(self._strong) if include_weak: rv.update(self._weak) return rv def is_weak(self, etag): """Check if an etag is weak.""" return etag in self._weak def contains_weak(self, etag): """Check if an etag is part of the set including weak and strong tags.""" return self.is_weak(etag) or self.contains(etag) def contains(self, etag): """Check if an etag is part of the set ignoring weak tags.""" if self.star_tag: return True return etag in self._strong def contains_raw(self, etag): """When passed a quoted tag it will check if this tag is part of the set. If the tag is weak it is checked against weak and strong tags, otherwise weak only.""" etag, weak = unquote_etag(etag) if weak: return self.contains_weak(etag) return self.contains(etag) def to_header(self): """Convert the etags set into a HTTP header string.""" if self.star_tag: return '*' return ', '.join( ['"%s"' % x for x in self._strong] + ['w/"%s"' % x for x in self._weak] ) def __call__(self, etag=None, data=None, include_weak=False): if [etag, data].count(None) != 1: raise TypeError('either tag or data required, but at least one') if etag is None: etag = generate_etag(data) if include_weak: if etag in self._weak: return True return etag in self._strong def __nonzero__(self): return bool(self.star_tag or self._strong) def __str__(self): return self.to_header() def __iter__(self): return iter(self._strong) def __contains__(self, etag): return self.contains(etag) def __repr__(self): return '<%s %r>' % (self.__class__.__name__, str(self)) class Authorization(dict): """Represents an `Authorization` header sent by the client. You should not create this kind of object yourself but use it when it's returned by the `parse_authorization_header` function. This object is a dict subclass and can be altered by setting dict items but it should be considered immutable as it's returned by the client and not meant for modifications. """ def __init__(self, auth_type, data=None): dict.__init__(self, data or {}) self.type = auth_type username = property(lambda x: x.get('username'), doc=''' The username transmitted. This is set for both basic and digest auth all the time.''') password = property(lambda x: x.get('password'), doc=''' When the authentication type is basic this is the password transmitted by the client, else `None`.''') realm = property(lambda x: x.get('realm'), doc=''' This is the server realm send back for digest auth. For HTTP digest auth.''') nonce = property(lambda x: x.get('nonce'), doc=''' The nonce the server send for digest auth, send back by the client. A nonce should be unique for every 401 response for HTTP digest auth.''') uri = property(lambda x: x.get('uri'), doc=''' The URI from Request-URI of the Request-Line; duplicated because proxies are allowed to change the Request-Line in transit. HTTP digest auth only.''') nc = property(lambda x: x.get('nc'), doc=''' The nonce count value transmitted by clients if a qop-header is also transmitted. HTTP digest auth only.''') cnonce = property(lambda x: x.get('cnonce'), doc=''' If the server sent a qop-header in the ``WWW-Authenticate`` header, the client has to provide this value for HTTP digest auth. See the RFC for more details.''') response = property(lambda x: x.get('response'), doc=''' A string of 32 hex digits computed as defined in RFC 2617, which proves that the user knows a password. Digest auth only.''') opaque = property(lambda x: x.get('opaque'), doc=''' The opaque header from the server returned unchanged by the client. It is recommended that this string be base64 or hexadecimal data. Digest auth only.''') def qop(self): """Indicates what "quality of protection" the client has applied to the message for HTTP digest auth.""" def on_update(header_set): if not header_set and name in self: del self['qop'] elif header_set: self['qop'] = header_set.to_header() return parse_set_header(self.get('qop'), on_update) qop = property(qop, doc=qop.__doc__) class WWWAuthenticate(_UpdateDict): """Provides simple access to `WWW-Authenticate` headers.""" #: list of keys that require quoting in the generated header _require_quoting = frozenset(['domain', 'nonce', 'opaque', 'realm']) def __init__(self, auth_type=None, values=None, on_update=None): _UpdateDict.__init__(self, values or (), on_update) if auth_type: self['__auth_type__'] = auth_type def set_basic(self, realm='authentication required'): """Clear the auth info and enable basic auth.""" dict.clear(self) dict.update(self, {'__auth_type__': 'basic', 'realm': realm}) if self.on_update: self.on_update(self) def set_digest(self, realm, nonce, qop=('auth',), opaque=None, algorithm=None, stale=False): """Clear the auth info and enable digest auth.""" d = { '__auth_type__': 'digest', 'realm': realm, 'nonce': nonce, 'qop': dump_header(qop) } if stale: d['stale'] = 'TRUE' if opaque is not None: d['opaque'] = opaque if algorithm is not None: d['algorithm'] = algorithm dict.clear(self) dict.update(self, d) if self.on_update: self.on_update(self) def to_header(self): """Convert the stored values into a WWW-Authenticate header.""" d = dict(self) auth_type = d.pop('__auth_type__', None) or 'basic' return '%s %s' % (auth_type.title(), ', '.join([ '%s=%s' % (key, quote_header_value(value, allow_token=key not in self._require_quoting)) for key, value in d.iteritems() ])) def __str__(self): return self.to_header() def __repr__(self): return '<%s %r>' % ( self.__class__.__name__, self.to_header() ) def auth_property(name, doc=None): def _set_value(self, value): if value is None: self.pop(name, None) else: self[name] = str(value) return property(lambda x: x.get(name), _set_value, doc=doc) def _set_property(name, doc=None): def fget(self): def on_update(header_set): if not header_set and name in self: del self[name] elif header_set: self[name] = header_set.to_header() return parse_set_header(self.get(name), on_update) return property(fget, doc=doc) type = auth_property('__auth_type__', doc=''' The type of the auth machanism. HTTP currently specifies `Basic` and `Digest`.''') realm = auth_property('realm', doc=''' A string to be displayed to users so they know which username and password to use. This string should contain at least the name of the host performing the authentication and might additionally indicate the collection of users who might have access.''') domain = _set_property('domain', doc=''' A list of URIs that define the protection space. If a URI is an absolte path, it is relative to the canonical root URL of the server being accessed.''') nonce = auth_property('nonce', doc=''' A server-specified data string which should be uniquely generated each time a 401 response is made. It is recommended that this string be base64 or hexadecimal data.''') opaque = auth_property('opaque', doc=''' A string of data, specified by the server, which should be returned by the client unchanged in the Authorization header of subsequent requests with URIs in the same protection space. It is recommended that this string be base64 or hexadecimal data.''') algorithm = auth_property('algorithm', doc=''' A string indicating a pair of algorithms used to produce the digest and a checksum. If this is not present it is assumed to be "MD5". If the algorithm is not understood, the challenge should be ignored (and a different one used, if there is more than one).''') qop = _set_property('qop', doc=''' A set of quality-of-privacy modifies such as auth and auth-int.''') def _get_stale(self): val = self.get('stale') if val is not None: return val.lower() == 'true' def _set_stale(self, value): if value is None: self.pop('stale', None) else: self['stale'] = value and 'TRUE' or 'FALSE' stale = property(_get_stale, _set_stale, doc=''' A flag, indicating that the previous request from the client was rejected because the nonce value was stale.''') del _get_stale, _set_stale # make auth_property a staticmethod so that subclasses of # `WWWAuthenticate` can use it for new properties. auth_property = staticmethod(auth_property) del _set_property def quote_header_value(value, extra_chars='', allow_token=True): """Quote a header value if necessary.""" value = str(value) if allow_token: token_chars = _token_chars | set(extra_chars) if set(value).issubset(token_chars): return value return '"%s"' % value.replace('\\', '\\\\').replace('"', '\\"') def dump_header(iterable, allow_token=True): """Dump an HTTP header again. This is the reversal of `parse_list_header`, `parse_set_header` and `parse_dict_header`. This also quotes strings that include an equals sign unless you pass it as dict of key, value pairs. The `allow_token` parameter can be set to `False` to disallow tokens as values. If this is enabled all values are quoted. """ if isinstance(iterable, dict): items = [] for key, value in iterable.iteritems(): if value is None: items.append(key) else: items.append('%s=%s' % ( key, quote_header_value(value, allow_token=allow_token) )) else: items = [quote_header_value(x, allow_token=allow_token) for x in iterable] return ', '.join(items) def parse_list_header(value): """Parse lists as described by RFC 2068 Section 2. In particular, parse comma-separated lists where the elements of the list may include quoted-strings. A quoted-string could contain a comma. A non-quoted string could have quotes in the middle. Quotes are removed automatically after parsing. """ result = [] for item in _parse_list_header(value): if item[:1] == item[-1:] == '"': item = item[1:-1] result.append(item) return result def parse_dict_header(value): """Parse lists of key, value paits as described by RFC 2068 Section 2 and convert them into a python dict. If there is no value for a key it will be `None`. """ result = {} for item in _parse_list_header(value): if '=' not in item: result[item] = None continue name, value = item.split('=', 1) if value[:1] == value[-1:] == '"': value = value[1:-1] result[name] = value return result def parse_accept_header(value): """Parses an HTTP Accept-* header. This does not implement a complete valid algorithm but one that supports at least value and quality extraction. Returns a new `Accept` object (basicly a list of ``(value, quality)`` tuples sorted by the quality with some additional accessor methods). """ if not value: return Accept(None) result = [] for match in _accept_re.finditer(value): quality = match.group(2) if not quality: quality = 1 else: quality = max(min(float(quality), 1), 0) result.append((match.group(1), quality)) return Accept(result) def parse_cache_control_header(value, on_update=None): """Parse a cache control header. The RFC differs between response and request cache control, this method does not. It's your responsibility to not use the wrong control statements. """ if not value: return CacheControl(None, on_update) return CacheControl(parse_dict_header(value), on_update) def parse_set_header(value, on_update=None): """Parse a set like header and return a `HeaderSet` object. The return value is an object that treats the items case insensitive and keeps the order of the items. """ if not value: return HeaderSet(None, on_update) return HeaderSet(parse_list_header(value), on_update) def parse_authorization_header(value): """Parse an HTTP basic/digest authorization header transmitted by the web browser. The return value is either `None` if the header was invalid or not given, otherwise an `Authorization` object. """ if not value: return try: auth_type, auth_info = value.split(None, 1) auth_type = auth_type.lower() except ValueError: return if auth_type == 'basic': try: username, password = auth_info.decode('base64').split(':', 1) except Exception, e: return return Authorization('basic', {'username': username, 'password': password}) elif auth_type == 'digest': auth_map = parse_dict_header(auth_info) for key in 'username', 'realm', 'nonce', 'uri', 'nc', 'cnonce', \ 'response': if not key in auth_map: return return Authorization('digest', auth_map) def parse_www_authenticate_header(value, on_update=None): """Parse an HTTP WWW-Authenticate header into a `WWWAuthenticate` object.""" if not value: return WWWAuthenticate(on_update=on_update) try: auth_type, auth_info = value.split(None, 1) auth_type = auth_type.lower() except (ValueError, AttributeError): return WWWAuthenticate(value.lower(), on_update=on_update) return WWWAuthenticate(auth_type, parse_dict_header(auth_info), on_update) def quote_etag(etag, weak=False): """Quote an etag.""" if '"' in etag: raise ValueError('invalid etag') etag = '"%s"' % etag if weak: etag = 'w/' + etag return etag def unquote_etag(etag): """Unquote a single etag. Return a ``(etag, weak)`` tuple.""" if not etag: return None, None etag = etag.strip() weak = False if etag[:2] in ('w/', 'W/'): weak = True etag = etag[2:] if etag[:1] == etag[-1:] == '"': etag = etag[1:-1] return etag, weak def parse_etags(value): """Parse and etag header. Returns an `ETags` object.""" if not value: return ETags() strong = [] weak = [] end = len(value) pos = 0 while pos < end: match = _etag_re.match(value, pos) if match is None: break is_weak, quoted, raw = match.groups() if raw == '*': return ETags(star_tag=True) elif quoted: raw = quoted if is_weak: weak.append(raw) else: strong.append(raw) pos = match.end() return ETags(strong, weak) def generate_etag(data): """Generate an etag for some data.""" return md5(data).hexdigest() def parse_date(value): """Parse one of the following date formats into a datetime object: .. sourcecode:: text Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format If parsing fails the return value is `None`. """ if value: t = rfc822.parsedate_tz(value.strip()) if t is not None: # if no timezone is part of the string we assume UTC if t[-1] is None: t = t[:-1] + (0,) return datetime.utcfromtimestamp(rfc822.mktime_tz(t)) def is_resource_modified(environ, etag=None, data=None, last_modified=None): """Convenience method for conditional requests.""" if etag is None and data is not None: etag = generate_etag(data) elif data is not None: raise TypeError('both data and etag given') if environ['REQUEST_METHOD'] not in ('GET', 'HEAD'): return False unmodified = False if isinstance(last_modified, basestring): last_modified = parse_date(last_modified) modified_since = parse_date(environ.get('HTTP_IF_MODIFIED_SINCE')) if modified_since and last_modified and last_modified <= modified_since: unmodified = True if etag: if_none_match = parse_etags(environ.get('HTTP_IF_NONE_MATCH')) if if_none_match: unmodified = if_none_match.contains_raw(etag) return not unmodified PK[8ííwerkzeug/http.pyc; raHc@sdZdkZdkZdklZdklZydklZWn e j odkl ZnXye Z e Z Wn&e j odklZ lZ nXdklZlZlZeidZe d Zeid Zd efd YZd efdYZdefdYZdefdYZdefdYZ defdYZ!de"dZ#e"dZ$dZ%dZ&dZ'e(dZ)e(dZ*dZ+e(d Z,e-d!Z.d"Z/d#Z0d$Z1d%Z2e(e(e(d&Z3dS('sF werkzeug.http ~~~~~~~~~~~~~ Werkzeug comes with a bunch of utilties that help Werkzeug to deal with HTTP data. Most of the classes and functions provided by this module are used by the wrappers, but they are useful on their own too, especially if the response and request objects are not used. This covers some of the more HTTP centric features of WSGI, some other utilities such as cookie handling are documented in the `werkzeug.utils` module. :copyright: 2007-2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. N(sparse_http_list(sdatetime(smd5(snew(sSets ImmutableSet(s_patch_wrappers _UpdateDictsHTTP_STATUS_CODESs*([^\s;,]+)(?:[^,]*?;\s*q=(\d*(?:\.\d+)?))?sM!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~s&([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)sAcceptcBsntZdZfdZdZdZdZdZdZdZ dZ d Z e e Z RS( sAn `Accept` object is just a list subclass for lists of ``(value, quality)`` tuples. It is automatically sorted by quality. cCs|tjoti|t|_nt|_gi}|D]\}}|||fq>~}|i |i ti|gi}|D]\}}|||fq~dS(N(svaluessNoneslists__init__sselfsFalsesprovidedsTruesappends_[1]sbsassortsreverse(sselfsvaluessasbs_[1]((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__init__.s    3  cCsZt|to6x+|D]#}|d|jo |dSqqWdSnti||SdS(sBeside index lookup (getting item n) you can also pass it a string to get the quality for the item. If the item is not in the list, the returned quality is ``0``. iiN(s isinstanceskeys basestringsselfsvalueslists __getitem__(sselfskeysvalue((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys __getitem__9scCs|i|djSdS(Ni(sselfsfindskey(sselfskey((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys __contains__EscCs!d|iiti|fSdS(Ns%s(%s)(sselfs __class__s__name__slists__repr__(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__repr__HscCs4|i|}|djot|n|SdS(s3Get the position of en entry or raise `IndexError`.iN(sselfsfindskeysrvs IndexError(sselfskeysrv((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysindexNs  cCsbt|to>x3t|D]%\}}|d|jo|SqqWdSnti||SdS(s)Get the position of an entry or return -1iiN( s isinstanceskeys basestrings enumeratesselfsidxsvalueslistsfind(sselfskeysidxsvalue((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysfindUs  cCs-gi}|D]}||dq~SdS(s/Return a list of the values, not the qualities.iN(sappends_[1]sselfsx(sselfs_[1]sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysvalues^sccsx|D]}|dVqWdS(sIterate over all values.iN(sselfsitem(sselfsitem((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys itervaluesbscCs|o |ddptSdS(sThe best match as value.iN(sselfsNone(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysbestgs( s__name__s __module__s__doc__s__init__s __getitem__s __contains__s__repr__sindexsfindsvaluess itervaluessbestsproperty(((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysAccept)s       s HeaderSetcBstZdZeedZdZdZdZdZdZ dZ dZ e d Z d Zd Zd Zd ZdZdZdZdZdZdZRS(ssSimilar to the `ETags` class this implements a set like structure. Unlike `ETags` this is case insensitive and used for vary, allow, and content-language headers. If not constructed using the `parse_set_header` function the instanciation works like this: >>> hs = HeaderSet(['foo', 'bar', 'baz']) >>> hs HeaderSet(['foo', 'bar', 'baz']) cCs\t|pf|_tgi}|iD]}||iq*~|_ ||_ dS(N( slistsheaderssselfs_headersssetsappends_[1]sxslowers_sets on_update(sselfsheaderss on_updates_[1]sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__init__zs9cCs|i|fdS(sAdd a new header to the set.N(sselfsupdatesheader(sselfsheader((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysaddscCs|i}||ijot|n|ii|x?t|iD].\}}|i|jo|i|=PqLqLW|i t j o|i |ndS(sbRemove a layer from the set. This raises an `IndexError` if the header is not in the set.N( sheaderslowerskeysselfs_sets IndexErrorsremoves enumerates_headerssidxs on_updatesNone(sselfsheadersidxskey((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysremoves    cCst}xT|D]L}|i}||ijo*|ii ||ii |t }q q W|o |i t j o|i |ndS(s1Add all the headers from the iterable to the set.N(sFalses inserted_anysiterablesheaderslowerskeysselfs_sets_headerssappendsaddsTrues on_updatesNone(sselfsiterablesheaderskeys inserted_any((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysupdates cCs+y|i|SWntj onXdS(sLike remove but ignores errors.N(sselfsremovesheaders IndexError(sselfsheader((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysdiscards cCsO|i}x8t|iD]'\}}|i|jo|SqqWdSdS(sDReturn the index of the header in the set or return -1 if not found.iN(sheaderslowers enumeratesselfs_headerssidxsitem(sselfsheadersidxsitem((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysfinds   cCs4|i|}|djot|n|SdS(sBReturn the index of the headerin the set or raise an `IndexError`.iN(sselfsfindsheadersrvs IndexError(sselfsheadersrv((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysindexs  cCs9|ii|i2|itj o|i|ndS(sClear the set.N(sselfs_setsclears_headerss on_updatesNone(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysclears  cCs)|ot|iSnt|iSdS(s@Return the set as real python set structure. When calling this all the items are converted to lowercase and the ordering is lost. If `preserve_casing` is `True` the items in the set returned will have the original case like in the `HeaderSet`, otherwise they will be lowercase. N(spreserve_casingssetsselfs_headerss_set(sselfspreserve_casing((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysas_setscCsditt|iSdS(s2Convert the header set into an HTTP header string.s, N(sjoinsmapsquote_header_valuesselfs_headers(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys to_headerscCs|i|SdS(N(sselfs_headerssidx(sselfsidx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys __getitem__scCsM|ii|}|ii|i|it j o|i|ndS(N( sselfs_headersspopsidxsrvs_setsremoveslowers on_updatesNone(sselfsidxsrv((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys __delitem__scCsk|i|}|ii|i||i|<|ii|i|i t j o|i |ndS(N( sselfs_headerssidxsolds_setsremoveslowersvaluesadds on_updatesNone(sselfsidxsvaluesold((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys __setitem__s   cCs|i|ijSdS(N(sheaderslowersselfs_set(sselfsheader((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys __contains__scCst|iSdS(N(slensselfs_set(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__len__scCst|iSdS(N(sitersselfs_headers(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__iter__scCst|iSdS(N(sboolsselfs_set(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys __nonzero__scCs|iSdS(N(sselfs to_header(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__str__scCsd|ii|ifSdS(Ns%s(%r)(sselfs __class__s__name__s_headers(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__repr__s(s__name__s __module__s__doc__sNones__init__saddsremovesupdatesdiscardsfindsindexsclearsFalsesas_sets to_headers __getitem__s __delitem__s __setitem__s __contains__s__len__s__iter__s __nonzero__s__str__s__repr__(((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys HeaderSetms(                s CacheControlcBs7tZdZdZeddeZedeeZeddeZ eddeZ eddeZ ed eeZ ed eeZ ed eeZed deZed eeZedeeZedeeZfedZdZdZdZdZdZeeZRS(s1Subclass of a dict that stores values for a Cache-Control header. It has accesors for all the cache-control directives specified in RFC 2616. The class does not differentiate between request and response directives. Because the cache-control directives in the HTTP header use dashes the python descriptors use underscores for that. To get a header of the `CacheControl` object again you can convert the object into a string or call the `to_header()` function. If you plan to subclass it and add your own items have a look at the sourcecode for that class. The following attributes are exposed: `no_cache`, `no_store`, `max_age`, `max_stale`, `min_fresh`, `no_transform`, `only_if_cached`, `public`, `private`, `must_revalidate`, `proxy_revalidate`, and `s_maxage`cs-tdddSdS(sReturn a new property object for a cache header. Useful if you want to add support for a cache extension in a subclass.cs|iS(N(sxs_get_cache_valueskeysdefaultstype(sx(sdefaultstypeskey(s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysscs|i|S(N(sxs_set_cache_valueskeysvstype(sxsv(stypeskey(s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysssaccessor for %rN(spropertyskey(skeysdefaultstype((skeysdefaultstypes3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pyscache_propertys sno-caches*sno-storesmax-ageis max-stales min-freshs no-transformsonly-if-cachedspublicsprivatesmust-revalidatesproxy-revalidatess-maxagecCs-ti||pf||tj |_dS(N(s _UpdateDicts__init__sselfsvaluess on_updatesNonesprovided(sselfsvaluess on_update((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__init__scCs|tjo||jSn||jo^||}|tjo|Sn8|tj o*y||}Wq~tj oq~Xn|SndS(s+Used internally be the accessor properties.N(stypesboolskeysselfsvaluesNonesdefaults ValueError(sselfskeysdefaultstypesvalue((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys_get_cache_values      cCse|tjo)|ot||(sselfs __class__s__name__s to_header(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__repr__?s(s__name__s __module__s__doc__scache_propertysboolsno_cachesNonesno_storesintsmax_ages max_stales min_freshs no_transformsonly_if_cachedspublicsprivatesmust_revalidatesproxy_revalidatess_maxages__init__s_get_cache_values_set_cache_values to_headers__str__s__repr__s staticmethod(((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys CacheControls*      sETagscBstZdZeeedZedZdZdZdZ dZ dZ eeedZ d Z d Zd Zd Zd ZRS(sYA set that can be used to check if one etag is present in a collection of etags. cCsAt| o|pf|_t|pf|_||_dS(N(s frozensetsstar_tags strong_etagssselfs_strongs weak_etagss_weak(sselfs strong_etagss weak_etagssstar_tag((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__init__OscCs2t|i}|o|i|in|SdS(soConvert the `ETags` object into a python set. Per default all the weak etags are not part of this set.N(ssetsselfs_strongsrvs include_weaksupdates_weak(sselfs include_weaksrv((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysas_setTs cCs||ijSdS(sCheck if an etag is weak.N(setagsselfs_weak(sselfsetag((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysis_weak\scCs!|i|p |i|SdS(sCCheck if an etag is part of the set including weak and strong tags.N(sselfsis_weaksetagscontains(sselfsetag((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys contains_weak`scCs#|iotSn||ijSdS(s7Check if an etag is part of the set ignoring weak tags.N(sselfsstar_tagsTruesetags_strong(sselfsetag((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pyscontainsds cCs;t|\}}|o|i|Sn|i|SdS(sWhen passed a quoted tag it will check if this tag is part of the set. If the tag is weak it is checked against weak and strong tags, otherwise weak only.N(s unquote_etagsetagsweaksselfs contains_weakscontains(sselfsetagsweak((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys contains_rawjs cCsw|iodSndigi}|iD]}|d|q)~gi}|iD]}|d|qT~SdS(s0Convert the etags set into a HTTP header string.s*s, s"%s"sw/"%s"N(sselfsstar_tagsjoinsappends_[1]s_strongsxs_weak(sselfs_[1]sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys to_headerss cCs}||gitdjotdn|tjot|}n|o||ijot Sqln||i jSdS(Nis-either tag or data required, but at least one( setagsdatascountsNones TypeErrors generate_etags include_weaksselfs_weaksTrues_strong(sselfsetagsdatas include_weak((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__call__|s  cCst|ip|iSdS(N(sboolsselfsstar_tags_strong(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys __nonzero__scCs|iSdS(N(sselfs to_header(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__str__scCst|iSdS(N(sitersselfs_strong(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__iter__scCs|i|SdS(N(sselfscontainssetag(sselfsetag((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys __contains__scCsd|iit|fSdS(Ns<%s %r>(sselfs __class__s__name__sstr(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__repr__s(s__name__s __module__s__doc__sNonesFalses__init__sas_setsis_weaks contains_weakscontainss contains_raws to_headers__call__s __nonzero__s__str__s__iter__s __contains__s__repr__(((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysETagsJs          s AuthorizationcBstZdZedZedddZedddZedddZed dd Z ed dd Z ed ddZ edddZ edddZ edddZdZeedeiZRS(sRepresents an `Authorization` header sent by the client. You should not create this kind of object yourself but use it when it's returned by the `parse_authorization_header` function. This object is a dict subclass and can be altered by setting dict items but it should be considered immutable as it's returned by the client and not meant for modifications. cCs$ti||ph||_dS(N(sdicts__init__sselfsdatas auth_typestype(sselfs auth_typesdata((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__init__scCs |idS(Nsusername(sxsget(sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysssdocsd The username transmitted. This is set for both basic and digest auth all the time.cCs |idS(Nspassword(sxsget(sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pyssss When the authentication type is basic this is the password transmitted by the client, else `None`.cCs |idS(Nsrealm(sxsget(sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysss[ This is the server realm send back for digest auth. For HTTP digest auth.cCs |idS(Nsnonce(sxsget(sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysss The nonce the server send for digest auth, send back by the client. A nonce should be unique for every 401 response for HTTP digest auth.cCs |idS(Nsuri(sxsget(sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysss The URI from Request-URI of the Request-Line; duplicated because proxies are allowed to change the Request-Line in transit. HTTP digest auth only.cCs |idS(Nsnc(sxsget(sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysssz The nonce count value transmitted by clients if a qop-header is also transmitted. HTTP digest auth only.cCs |idS(Nscnonce(sxsget(sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysss If the server sent a qop-header in the ``WWW-Authenticate`` header, the client has to provide this value for HTTP digest auth. See the RFC for more details.cCs |idS(Nsresponse(sxsget(sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysss A string of 32 hex digits computed as defined in RFC 2617, which proves that the user knows a password. Digest auth only.cCs |idS(Nsopaque(sxsget(sx((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pysss The opaque header from the server returned unchanged by the client. It is recommended that this string be base64 or hexadecimal data. Digest auth only.cs&d}tid|SdS(sjIndicates what "quality of protection" the client has applied to the message for HTTP digest auth.cs?| o tjo d=n|o|id(sselfs __class__s__name__s to_header(sself((s3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys__repr__scs)d}td|d|SdS(Ncs5|tjo|itnt||ssdoc(s _set_valuespropertysdoc(snamesdocs _set_value((snames3build/bdist.darwin-8.11.1-i386/egg/werkzeug/http.pys auth_property s cs d}t|d|SdS(Ncs)d}ti|SdS(Ncs?| o jo =n|o|i' % ( self.__class__.__name__, len(self.locals) ) class LocalProxy(object): """Acts as a proxy for a werkzeug local. Forwards all operations to a proxied object. The only operations not supported for forwarding are right handed operands and any kind of assignment. Example usage:: from werkzeug import Local l = Local() request = l('request') user = l('user') Whenever something is bound to l.user / l.request the proxy objects will forward all operations. If no object is bound a `RuntimeError` will be raised. """ __slots__ = ('__local', '__dict__', '__name__') def __init__(self, local, name): object.__setattr__(self, '_LocalProxy__local', local) object.__setattr__(self, '__name__', name) def _get_current_object(self): """Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context. """ try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError('no object bound to %s' % self.__name__) __current_object = property(_get_current_object) def __dict__(self): try: return self.__current_object.__dict__ except RuntimeError: return AttributeError('__dict__') __dict__ = property(__dict__) def __repr__(self): try: obj = self.__current_object except RuntimeError: return '<%s unbound>' % self.__class__.__name__ return repr(obj) def __nonzero__(self): try: return bool(self.__current_object) except RuntimeError: return False def __unicode__(self): try: return unicode(self.__current_oject) except RuntimeError: return repr(self) def __dir__(self): try: return dir(self.__current_object) except RuntimeError: return [] def __getattr__(self, name): if name == '__members__': return dir(self.__current_object) return getattr(self.__current_object, name) def __setitem__(self, key, value): self.__current_object[key] = value def __delitem__(self, key): del self.__current_object[key] def __setslice__(self, i, j, seq): self.__current_object[i:j] = seq def __delslice__(self, i, j): del self.__current_object[i:j] __setattr__ = lambda x, n, v: setattr(x.__current_object, n, v) __delattr__ = lambda x, n: delattr(x.__current_object, n) __str__ = lambda x: str(x.__current_object) __lt__ = lambda x, o: x.__current_object < o __le__ = lambda x, o: x.__current_object <= o __eq__ = lambda x, o: x.__current_object == o __ne__ = lambda x, o: x.__current_object != o __gt__ = lambda x, o: x.__current_object > o __ge__ = lambda x, o: x.__current_object >= o __cmp__ = lambda x, o: cmp(x.__current_object, o) __hash__ = lambda x: hash(x.__current_object) __call__ = lambda x, *a, **kw: x.__current_object(*a, **kw) __len__ = lambda x: len(x.__current_object) __getitem__ = lambda x, i: x.__current_object[i] __iter__ = lambda x: iter(x.__current_object) __contains__ = lambda x, i: i in x.__current_object __getslice__ = lambda x, i, j: x.__current_object[i:j] __add__ = lambda x, o: x.__current_object + o __sub__ = lambda x, o: x.__current_object - o __mul__ = lambda x, o: x.__current_object * o __floordiv__ = lambda x, o: x.__current_object // o __mod__ = lambda x, o: x.__current_object % o __divmod__ = lambda x, o: x.__current_object.__divmod__(o) __pow__ = lambda x, o: x.__current_object ** o __lshift__ = lambda x, o: x.__current_object << o __rshift__ = lambda x, o: x.__current_object >> o __and__ = lambda x, o: x.__current_object & o __xor__ = lambda x, o: x.__current_object ^ o __or__ = lambda x, o: x.__current_object | o __div__ = lambda x, o: x.__current_object.__div__(o) __truediv__ = lambda x, o: x.__current_object.__truediv__(o) __neg__ = lambda x: -(x.__current_object) __pos__ = lambda x: +(x.__current_object) __abs__ = lambda x: abs(x.__current_object) __invert__ = lambda x: ~(x.__current_object) __complex__ = lambda x: complex(x.__current_object) __int__ = lambda x: int(x.__current_object) __long__ = lambda x: long(x.__current_object) __float__ = lambda x: float(x.__current_object) __oct__ = lambda x: oct(x.__current_object) __hex__ = lambda x: hex(x.__current_object) __index__ = lambda x: x.__current_object.__index__() __coerce__ = lambda x, o: x.__coerce__(x, o) __enter__ = lambda x: x.__enter__() __exit__ = lambda x, *a, **kw: x.__exit__(*a, **kw) PK[8h .^^werkzeug/local.pyc; raHc@sdZydklZeiZ[Wneefj o eZnXydkl Z l Z Wn&ej odk l Z l Z nXdk lZdklZeejo e Z n dZ defdYZdefd YZd efd YZd S( s werkzeug.local ~~~~~~~~~~~~~~ Sooner or later you have some things you want to have in every single view or helper function or whatever. In PHP the way to go are global variables. However that is not possible in WSGI applications without a major drawback: As soon as you operate on the global namespace your application is not thread safe any longer. The python standard library comes with a utility called "thread locals". A thread local is a global object where you can put stuff on and get back later in a thread safe way. That means whenever you set or get an object to / from a thread local object the thread local object checks in which thread you are and delivers the correct value. This however has a few disadvantages. For example beside threads there are other ways to handle concurrency in Python. A very popular approach are greenlets. Also, whether every request gets its own thread is not guaranteed in WSGI. It could be that a request is reusing a thread from before and data is left in the thread local object. Nutshell -------- Here a simple example how you can use werkzeug.local:: from werkzeug import Local, LocalManager local = Local() local_manager = LocalManager([local]) def application(environ, start_response): local.request = request = Request(environ) ... application = local_manager.make_middleware(application) Now what this code does is binding request to `local.request`. Every other piece of code executed after this assignment in the same context can safely access local.request and will get the same request object. The `make_middleware` method on the local manager ensures that everything is cleaned up after the request. The same context means the same greenlet (if you're using greenlets) in the same thread and same process. If a request object is not yet set on the local object and you try to access it you will get an `AttributeError`. You can use `getattr` to avoid that:: def get_request(): return getattr(local, 'request', None) This will try to get the request or return `None` if the request is not (yet?) available. Note that local objects cannot manage themselves, for that you need a local manager. You can pass a local manager multiple locals or add additionals later by appending them to `manager.locals` and everytime the manager cleans up it will clean up all the data left in the locals for this context. :copyright: 2007-2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. (sgreenlet(s get_idents allocate_lock(sClosingIterator(s_patch_wrappercCstttfS(N(shashsget_current_threadsget_current_greenlet(((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysZssLocalcBsJtZddfZdZdZdZdZdZdZRS( Ns __storage__s__lock__cCs-ti|dhti|dtdS(Ns __storage__s__lock__(sobjects __setattr__sselfs allocate_lock(sself((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys__init__`scCs|iiSdS(N(sselfs __storage__s iteritems(sself((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys__iter__dscCst||SdS(sCreate a proxy for a name.N(s LocalProxysselfsproxy(sselfsproxy((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys__call__gscCs^|iiz<y|it|SWntj ot|nXWd|iiXdS(N( sselfs__lock__sacquires __storage__s get_identsnamesKeyErrorsAttributeErrorsrelease(sselfsname((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys __getattr__ks cCsj|iizHt}|i}||jo||||(sselfs __class__s__name__slenslocals(sself((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys__repr__s( s__name__s __module__s__doc__sNones__init__s get_identscleanupsmake_middlewares middlewares__repr__(((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys LocalManagers     s LocalProxycBs6tZdZdddfZdZdZeeZdZeeZdZ dZ d Z d Z d Z d Zd ZdZdZdZdZdZdZdZdZdZdZdZdZdZdZdZdZdZ dZ!d Z"d!Z#d"Z$d#Z%d$Z&d%Z'd&Z(d'Z)d(Z*d)Z+d*Z,d+Z-d,Z.d-Z/d.Z0d/Z1d0Z2d1Z3d2Z4d3Z5d4Z6d5Z7d6Z8d7Z9d8Z:d9Z;d:Z<d;Z=d<Z>RS(=sActs as a proxy for a werkzeug local. Forwards all operations to a proxied object. The only operations not supported for forwarding are right handed operands and any kind of assignment. Example usage:: from werkzeug import Local l = Local() request = l('request') user = l('user') Whenever something is bound to l.user / l.request the proxy objects will forward all operations. If no object is bound a `RuntimeError` will be raised. s__locals__dict__s__name__cCs*ti|d|ti|d|dS(Ns_LocalProxy__locals__name__(sobjects __setattr__sselfslocalsname(sselfslocalsname((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys__init__scCsDyt|i|iSWn&tj otd|inXdS(sReturn the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context. sno object bound to %sN(sgetattrsselfs_LocalProxy__locals__name__sAttributeErrors RuntimeError(sself((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys_get_current_objects cCs2y|iiSWntj otdSnXdS(Ns__dict__(sselfs_LocalProxy__current_objects__dict__s RuntimeErrorsAttributeError(sself((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys__dict__scCs?y |i}Wn!tj od|iiSnXt|SdS(Ns <%s unbound>(sselfs_LocalProxy__current_objectsobjs RuntimeErrors __class__s__name__srepr(sselfsobj((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys__repr__s  cCs/yt|iSWntj o tSnXdS(N(sboolsselfs_LocalProxy__current_objects RuntimeErrorsFalse(sself((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys __nonzero__scCs5yt|iSWntj ot|SnXdS(N(sunicodesselfs_LocalProxy__current_ojects RuntimeErrorsrepr(sself((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys __unicode__scCs/yt|iSWntj o gSnXdS(N(sdirsselfs_LocalProxy__current_objects RuntimeError(sself((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys__dir__scCs2|djot|iSnt|i|SdS(Ns __members__(snamesdirsselfs_LocalProxy__current_objectsgetattr(sselfsname((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys __getattr__ s cCs||i|scCst|i|S(N(sdelattrsxs_LocalProxy__current_objectsn(sxsn((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysscCs t|iS(N(sstrsxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysscCs |i|jS(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysscCs |i|jS(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys scCs |i|jS(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys!scCs |i|jS(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys"scCs |i|jS(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys#scCs |i|jS(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys$scCst|i|S(N(scmpsxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys%scCs t|iS(N(shashsxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys&scOs|i||S(N(sxs_LocalProxy__current_objectsaskw(sxsaskw((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys'scCs t|iS(N(slensxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys(scCs |i|S(N(sxs_LocalProxy__current_objectsi(sxsi((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys)scCs t|iS(N(sitersxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys*scCs ||ijS(N(sisxs_LocalProxy__current_object(sxsi((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys+scCs|i||!S(N(sxs_LocalProxy__current_objectsisj(sxsisj((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys,scCs |i|S(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys-scCs |i|S(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys.scCs |i|S(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys/scCs |i|S(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys0scCs |i|S(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys1scCs|ii|S(N(sxs_LocalProxy__current_objects __divmod__so(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys2scCs |i|S(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys3scCs |i|>S(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys4scCs |i|?S(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys5scCs |i|@S(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys6scCs |i|AS(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys7scCs |i|BS(N(sxs_LocalProxy__current_objectso(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys8scCs|ii|S(N(sxs_LocalProxy__current_objects__div__so(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys9scCs|ii|S(N(sxs_LocalProxy__current_objects __truediv__so(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys:scCs|i S(N(sxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys;scCs|i S(N(sxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys<scCs t|iS(N(sabssxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys=scCs|iS(N(sxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys>scCs t|iS(N(scomplexsxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys?scCs t|iS(N(sintsxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys@scCs t|iS(N(slongsxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysAscCs t|iS(N(sfloatsxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysBscCs t|iS(N(soctsxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysCscCs t|iS(N(shexsxs_LocalProxy__current_object(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysDscCs |iiS(N(sxs_LocalProxy__current_objects __index__(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysEscCs|i||S(N(sxs __coerce__so(sxso((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysFscCs |iS(N(sxs __enter__(sx((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysGscOs|i||S(N(sxs__exit__saskw(sxsaskw((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pysHs(?s__name__s __module__s__doc__s __slots__s__init__s_get_current_objectspropertys_LocalProxy__current_objects__dict__s__repr__s __nonzero__s __unicode__s__dir__s __getattr__s __setitem__s __delitem__s __setslice__s __delslice__s __setattr__s __delattr__s__str__s__lt__s__le__s__eq__s__ne__s__gt__s__ge__s__cmp__s__hash__s__call__s__len__s __getitem__s__iter__s __contains__s __getslice__s__add__s__sub__s__mul__s __floordiv__s__mod__s __divmod__s__pow__s __lshift__s __rshift__s__and__s__xor__s__or__s__div__s __truediv__s__neg__s__pos__s__abs__s __invert__s __complex__s__int__s__long__s __float__s__oct__s__hex__s __index__s __coerce__s __enter__s__exit__(((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys LocalProxysz                                                          N(s__doc__spy.magicsgreenlets getcurrentsget_current_greenlets RuntimeErrors ImportErrorsintsthreads get_identsget_current_threads allocate_locks dummy_threadswerkzeug.utilssClosingIteratorswerkzeug._internals_patch_wrappersobjectsLocals LocalManagers LocalProxy( s_patch_wrappersget_current_threads allocate_locks LocalManagers LocalProxysClosingIterators get_identsgreenletsget_current_greenletsLocal((s4build/bdist.darwin-8.11.1-i386/egg/werkzeug/local.pys?Es$        />PK݉8ywerkzeug/routing.py# -*- coding: utf-8 -*- """ werkzeug.routing ~~~~~~~~~~~~~~~~ When it comes to combining multiple controller or view functions (however you want to call them) you need a dispatcher. A simple way would be applying regular expression tests on the ``PATH_INFO`` and call registered callback functions that return the value then. This module implements a much more powerful system than simple regular expression matching because it can also convert values in the URLs and build URLs. Here a simple example that creates an URL map for an application with two subdomains (www and kb) and some URL rules:: m = Map([ # Static URLs Rule('/', endpoint='static/index'), Rule('/about', endpoint='static/about'), Rule('/help', endpoint='static/help'), # Knowledge Base Subdomain('kb', [ Rule('/', endpoint='kb/index'), Rule('/browse/', endpoint='kb/browse'), Rule('/browse//', endpoint='kb/browse'), Rule('/browse//', endpoint='kb/browse') ]) ], default_subdomain='www') If the application doesn't use subdomains it's perfectly fine to not set the default subdomain and use the `Subdomain` rule factory. The endpoint in the rules can be anything, for example import paths or unique identifiers. The WSGI application can use those endpoints to get the handler for that URL. It doesn't have to be a string at all but it's recommended. Now it's possible to create a URL adapter for one of the subdomains and build URLs: >>> c = m.bind('example.com') >>> c.build("kb/browse", dict(id=42)) 'http://kb.example.com/browse/42/' >>> c.build("kb/browse", dict()) 'http://kb.example.com/browse/' >>> c.build("kb/browse", dict(id=42, page=3)) 'http://kb.example.com/browse/42/3' >>> c.build("static/about") u'/about' >>> c.build("static/about", subdomain="kb") 'http://www.example.com/about' >>> c.build("static/index", force_external=True) 'http://www.example.com/' The first argument to bind is the server name *without* the subdomain. Per default it will assume that the script is mounted on the root, but often that's not the case so you can provide the real mount point as second argument: >>> c = m.bind('example.com', '/applications/example') The third argument can be the subdomain, if not given the default subdomain is used. For more details about binding have a look at the documentation of the `MapAdapter`. And here is how you can match URLs: >>> c = m.bind('example.com') >>> c.match("/") ('static/index', {}) >>> c.match("/about") ('static/about', {}) >>> c = m.bind('example.com', '/', 'kb') >>> c.match("/") ('kb/index', {}) >>> c.match("/browse/42/23") ('kb/browse', {'id': 42, 'page': 23}) If matching fails you get a `NotFound` exception, if the rule thinks it's a good idea to redirect (for example because the URL was defined to have a slash at the end but the request was missing that slash) it will raise a `RequestRedirect` exception. Both are subclasses of the `HTTPException` so you can use those errors as responses in the application. If matching succeeded but the URL rule was incompatible to the given method (for example there were only rules for `GET` and `HEAD` and routing system tried to match a `POST` request) a `MethodNotAllowed` method is raised. :copyright: 2007-2008 by Armin Ronacher, Leif K-Brooks, Thomas Johansson. :license: BSD, see LICENSE for more details. """ import sys import re from urlparse import urljoin from itertools import izip from werkzeug.utils import url_encode, url_quote, redirect, format_string from werkzeug.exceptions import HTTPException, NotFound, MethodNotAllowed try: set except NameError: from sets import Set as set _rule_re = re.compile(r''' (?P[^<]*) # static rule data < (?: (?P[a-zA-Z_][a-zA-Z0-9_]*) # converter name (?:\((?P.*?)\))? # converter arguments \: # variable delimiter )? (?P[a-zA-Z][a-zA-Z0-9_]*) # variable name > ''', re.VERBOSE) _simple_rule_re = re.compile(r'<([^>]+)>') def parse_rule(rule): """Parse a rule and return it as generator. Each iteration yields tuples in the form ``(converter, arguments, variable)``. If the converter is `None` it's a static url part, otherwise it's a dynamic one. :internal: """ pos = 0 end = len(rule) do_match = _rule_re.match used_names = set() while pos < end: m = do_match(rule, pos) if m is None: break data = m.groupdict() if data['static']: yield None, None, data['static'] variable = data['variable'] converter = data['converter'] or 'default' if variable in used_names: raise ValueError('variable name %r used twice.' % variable) used_names.add(variable) yield converter, data['args'] or None, variable pos = m.end() if pos < end: remaining = rule[pos:] if '>' in remaining or '<' in remaining: raise ValueError('malformed url rule: %r' % rule) yield None, None, remaining def get_converter(map, name, args): """Create a new converter for the given arguments or raise exception if the converter does not exist. :internal: """ if not name in map.converters: raise LookupError('the converter %r does not exist' % name) if args: storage = type('_Storage', (), {'__getitem__': lambda s, x: x})() args, kwargs = eval(u'(lambda *a, **kw: (a, kw))(%s)' % args, {}, storage) else: args = () kwargs = {} return map.converters[name](map, *args, **kwargs) class RoutingException(Exception): """Special exceptions that require the application to redirect, notifies him about missing urls etc. :internal: """ class RequestRedirect(HTTPException, RoutingException): """Raise if the map requests a redirect. This is for example the case if `strict_slashes` are activated and an url that requires a leading slash. The attribute `new_url` contains the absolute desitination url. """ code = 301 def __init__(self, new_url): RoutingException.__init__(self, new_url) self.new_url = new_url def get_response(self, environ): return redirect(self.new_url, 301) class RequestSlash(RoutingException): """Internal exception.""" class BuildError(RoutingException, LookupError): """Raised if the build system cannot find a URL for an endpoint with the values provided. """ def __init__(self, endpoint, values, method): LookupError.__init__(self, endpoint, values, method) self.endpoint = endpoint self.values = values self.method = method class ValidationError(ValueError): """Validation error. If a rule converter raises this exception the rule does not match the current URL and the next URL is tried. """ class RuleFactory(object): """As soon as you have more complex URL setups it's a good idea to use rule factories to avoid repetitive tasks. Some of them are builtin, others can be added by subclassing `RuleFactory` and overriding `get_rules`. """ def get_rules(self, map): """Subclasses of `RuleFactory` have to override this method and return an iterable of rules.""" raise NotImplementedError() class Subdomain(RuleFactory): """All URLs provided by this factory have the subdomain set to a specific domain. For example if you want to use the subdomain for the current language this can be a good setup:: url_map = Map([ Rule('/', endpoint='#select_language'), Subdomain('', [ Rule('/', endpoint='index'), Rule('/about', endpoint='about'), Rule('/help', endpoint='help') ]) ]) All the rules except of the ``'#select_language'`` endpoint will now listen on a two letter long subdomain that helds the language code for the current request. """ def __init__(self, subdomain, rules): self.subdomain = subdomain self.rules = rules def get_rules(self, map): for rulefactory in self.rules: for rule in rulefactory.get_rules(map): rule.subdomain = self.subdomain yield rule class Submount(RuleFactory): """Like `Subdomain` but prefixes the URL rule with a given string:: url_map = Map([ Rule('/', endpoint='index'), Submount('/blog', [ Rule('/', endpoint='blog/index'), Rule('/entry/', endpoint='blog/show') ]) ]) Now the rule ``'blog/show'`` matches ``/blog/entry/``. """ def __init__(self, path, rules): self.path = path.rstrip('/') self.rules = rules def get_rules(self, map): for rulefactory in self.rules: for rule in rulefactory.get_rules(map): rule.rule = self.path + rule.rule yield rule class EndpointPrefix(RuleFactory): """Prefixes all endpoints (which must be strings for this factory) with another string. This can be useful for sub applications:: url_map = Map([ Rule('/', endpoint='index'), EndpointPrefix('blog/', [Submount('/blog', [ Rule('/', endpoint='index'), Rule('/entry/', endpoint='show') ])]) ]) """ def __init__(self, prefix, rules): self.prefix = prefix self.rules = rules def get_rules(self, map): for rulefactory in self.rules: for rule in rulefactory.get_rules(map): rule.endpoint = self.prefix + rule.endpoint yield rule class RuleTemplate(object): """Returns copies of the rules wrapped and expands string templates in the endpoint, rule, defaults or subdomain sections. Here a small example for such a rule template:: from werkzeug.routing import Map, Rule, RuleTemplate resource = RuleTemplate([ Rule('/$name/', endpoint='$name.list'), Rule('/$name/', endpoint='$name.show') ]) url_map = Map([resource(name='user'), resource(name='page')]) When a rule template is called the keyword arguments are used to replace the placeholders in all the string parameters. """ def __init__(self, rules): self.rules = list(rules) def __call__(self, *args, **kwargs): return RuleTemplateFactory(self.rules, dict(*args, **kwargs)) class RuleTemplateFactory(RuleFactory): """A factory that fills in template variables into rules. Used by `RuleTemplate` internally. :internal: """ def __init__(self, rules, context): self.rules = rules self.context = context def get_rules(self, map): for rulefactory in self.rules: for rule in rulefactory.get_rules(map): new_defaults = subdomain = None if rule.defaults is not None: new_defaults = {} for key, value in rule.defaults.iteritems(): if isinstance(value, basestring): value = format_string(value, self.context) new_defaults[key] = value if rule.subdomain is not None: subdomain = format_string(rule.subdomain, self.context) new_endpoint = rule.endpoint if isinstance(new_endpoint, basestring): new_endpoint = format_string(new_endpoint, self.context) yield Rule( format_string(rule.rule, self.context), new_defaults, subdomain, rule.methods, rule.build_only, new_endpoint, rule.strict_slashes ) class Rule(RuleFactory): """A Rule represents one URL pattern. There are some options for `Rule` that change the way it behaves and are passed to the `Rule` constructor. Note that beside the rule-string all arguments *must* be keyword arguments in order to not break the application on Werkzeug upgrades. `string` Rule strings basically are just normal URL paths with placeholders in the format ```` where the converter and the arguments are optional. If no converter is defined the `default` converter is used which means `string` in the normal configuration. URL rules that end with a slash are branch URLs, others are leaves. If you have `strict_slashes` enabled (which is the default), all branch URLs that are matched without a trailing slash will trigger a redirect to the same URL with the missing slash appended. The converters are defined on the `Map`. `endpoint` The endpoint for this rule. This can be anything. A reference to a function, a string, a number etc. The preferred way is using a string as because the endpoint is used for URL generation. `defaults` An optional dict with defaults for other rules with the same endpoint. This is a bit tricky but useful if you want to have unique URLs:: url_map = Map([ Rule('/all/', defaults={'page': 1}, endpoint='all_entries'), Rule('/all/page/', endpoint='all_entries') ]) If a user now visits ``http://example.com/all/page/1`` he will be redirected to ``http://example.com/all/``. If `redirect_defaults` is disabled on the `Map` instance this will only affect the URL generation. `subdomain` The subdomain rule string for this rule. If not specified the rule only matches for the `default_subdomain` of the map. If the map is not bound to a subdomain this feature is disabled. Can be useful if you want to have user profiles on different subdomains and all subdomains are forwarded to your application:: url_map = Map([ Rule('/', subdomain='', endpoint='user/homepage'), Rule('/stats', subdomain='', endpoint='user/stats') ]) `methods` A sequence of http methods this rule applies to. If not specified, all methods are allowed. For example this can be useful if you want different endpoints for `POST` and `GET`. If methods are defined and the path matches but the method matched against is not in this list or in the list of another rule for that path the error raised is of the type `MethodNotAllowed` rather than `NotFound`. `strict_slashes` Override the `Map` setting for `strict_slashes` only for this rule. If not specified the `Map` setting is used. `build_only` Set this to true and the rule will never match but will create a URL that can be build. This is useful if you have resources on a subdomain or folder that are not handled by the WSGI application (like static data) `redirect_to` If given this must be either a string or callable. In case of a callable it's called with the url adapter that triggered the match and the values of the URL as keyword arguments and has to return the target for the redirect, otherwise it has to be a string with placeholders in rule syntax:: def foo_with_slug(adapter, id): # ask the database for the slug for the old id. this of # course has nothing to do with werkzeug. return 'foo/' + Foo.get_slug_for_id(id) url_map = Map([ Rule('/foo/', endpoint='foo'), Rule('/some/old/url/', redirect_to='foo/'), Rule('/other/old/url/', redirect_to=foo_with_slug) ]) When the rule is matched the routing system will raise a `RequestRedirect` exception with the target for the redirect. Keep in mind that the URL will be joined against the URL root of the script so don't use a leading slash on the target URL unless you really mean root of that domain. """ def __init__(self, string, defaults=None, subdomain=None, methods=None, build_only=False, endpoint=None, strict_slashes=None, redirect_to=None): if not string.startswith('/'): raise ValueError('urls must start with a leading slash') self.rule = string self.is_leaf = not string.endswith('/') self.map = None self.strict_slashes = strict_slashes self.subdomain = subdomain self.defaults = defaults self.build_only = build_only if methods is None: self.methods = None else: self.methods = set([x.upper() for x in methods]) self.endpoint = endpoint self.greediness = 0 self.redirect_to = redirect_to self._trace = [] if defaults is not None: self.arguments = set(map(str, defaults)) else: self.arguments = set() self._converters = {} self._regex = None self._weights = [] def empty(self): """Return an unbound copy of this rule. This can be useful if you want to reuse an already bound URL for another map.""" return Rule(self.rule, self.defaults, self.subdomain, self.methods, self.build_only, self.endpoint, self.strict_slashes, self.redirect_to) def get_rules(self, map): yield self def bind(self, map): """Bind the url to a map and create a regular expression based on the information from the rule itself and the defaults from the map. :internal: """ if self.map is not None: raise RuntimeError('url rule %r already bound to map %r' % (self, self.map)) self.map = map if self.strict_slashes is None: self.strict_slashes = map.strict_slashes if self.subdomain is None: self.subdomain = map.default_subdomain rule = self.subdomain + '|' + (self.is_leaf and self.rule or self.rule.rstrip('/')) regex_parts = [] for converter, arguments, variable in parse_rule(rule): if converter is None: regex_parts.append(re.escape(variable)) self._trace.append((False, variable)) self._weights.append(len(variable)) else: convobj = get_converter(map, converter, arguments) regex_parts.append('(?P<%s>%s)' % (variable, convobj.regex)) self._converters[variable] = convobj self._trace.append((True, variable)) self._weights.append(convobj.weight) self.arguments.add(str(variable)) if convobj.is_greedy: self.greediness += 1 if not self.is_leaf: self._trace.append((False, '/')) if not self.build_only: regex = r'^%s%s$' % ( u''.join(regex_parts), (not self.is_leaf or not self.strict_slashes) and \ '(?/?)' or '' ) self._regex = re.compile(regex, re.UNICODE) def match(self, path): """Check if the rule matches a given path. Path is a string in the form ``"subdomain|/path(method)"`` and is assembled by the map. If the rule matches a dict with the converted values is returned, otherwise the return value is `None`. :internal: """ if not self.build_only: m = self._regex.search(path) if m is not None: groups = m.groupdict() # we have a folder like part of the url without a trailing # slash and strict slashes enabled. raise an exception that # tells the map to redirect to the same url but with a # trailing slash if self.strict_slashes and not self.is_leaf and \ not groups.pop('__suffix__'): raise RequestSlash() # if we are not in strict slashes mode we have to remove # a __suffix__ elif not self.strict_slashes: del groups['__suffix__'] result = {} for name, value in groups.iteritems(): try: value = self._converters[name].to_python(value) except ValidationError: return result[str(name)] = value if self.defaults is not None: result.update(self.defaults) return result def build(self, values): """Assembles the relative url for that rule and the subdomain. If building doesn't work for some reasons `None` is returned. :internal: """ tmp = [] add = tmp.append processed = set(self.arguments) for is_dynamic, data in self._trace: if is_dynamic: try: add(self._converters[data].to_url(values[data])) except ValidationError: return processed.add(data) else: add(data) subdomain, url = (u''.join(tmp)).split('|', 1) query_vars = {} for key in set(values) - processed: query_vars[key] = unicode(values[key]) if query_vars: url += '?' + url_encode(query_vars, self.map.charset) return subdomain, url def provides_defaults_for(self, rule): """Check if this rule has defaults for a given rule. :internal: """ return not self.build_only and self.defaults is not None and \ self.endpoint == rule.endpoint and self != rule and \ self.arguments == rule.arguments def suitable_for(self, values, method): """Check if the dict of values has enough data for url generation. :internal: """ if self.methods is not None and method not in self.methods: return False valueset = set(values) for key in self.arguments - set(self.defaults or ()): if key not in values: return False if self.arguments.issubset(valueset): if self.defaults is None: return True for key, value in self.defaults.iteritems(): if value != values[key]: return False return True def match_compare(self, other): """Compare this object with another one for matching. :internal: """ for sw, ow in izip(self._weights, other._weights): if sw > ow: return -1 elif sw < ow: return 1 if len(self._weights) > len(other._weights): return -1 if len(self._weights) < len(other._weights): return 1 if not other.arguments and self.arguments: return 1 elif other.arguments and not self.arguments: return -1 elif other.defaults is None and self.defaults is not None: return 1 elif other.defaults is not None and self.defaults is None: return -1 elif self.greediness > other.greediness: return -1 elif self.greediness < other.greediness: return 1 elif len(self.arguments) > len(other.arguments): return 1 elif len(self.arguments) < len(other.arguments): return -1 return 1 def build_compare(self, other): """Compare this object with another one for building. :internal: """ if not other.arguments and self.arguments: return -1 elif other.arguments and not self.arguments: return 1 elif other.defaults is None and self.defaults is not None: return -1 elif other.defaults is not None and self.defaults is None: return 1 elif self.provides_defaults_for(other): return -1 elif other.provides_defaults_for(self): return 1 elif self.greediness > other.greediness: return -1 elif self.greediness < other.greediness: return 1 elif len(self.arguments) > len(other.arguments): return -1 elif len(self.arguments) < len(other.arguments): return 1 return -1 def __eq__(self, other): return self.__class__ is other.__class__ and \ self._trace == other._trace def __ne__(self, other): return not self.__eq__(other) def __unicode__(self): return self.rule def __str__(self): charset = self.map is not None and self.map.charset or 'utf-8' return unicode(self).encode(charset) def __repr__(self): if self.map is None: return '<%s (unbound)>' % self.__class__.__name__ charset = self.map is not None and self.map.charset or 'utf-8' tmp = [] for is_dynamic, data in self._trace: if is_dynamic: tmp.append('<%s>' % data) else: tmp.append(data) return '<%s %r%s -> %s>' % ( self.__class__.__name__, (u''.join(tmp).encode(charset)).lstrip('|'), self.methods is not None and ' (%s)' % \ ', '.join(self.methods) or '', self.endpoint ) class BaseConverter(object): """Base class for all converters.""" regex = '[^/]+' is_greedy = False weight = 100 def __init__(self, map): self.map = map def to_python(self, value): return value def to_url(self, value): return url_quote(value, self.map.charset) class UnicodeConverter(BaseConverter): """This converter is the default converter and accepts any string but only one one path segment. Thus the string can not include a slash. Supported arguments: - `minlength` - the minimum length of the string. must be greater than 1. - `maxlength` - the maximum length of the string. - `length` - the exact length of that string. """ def __init__(self, map, minlength=1, maxlength=None, length=None): BaseConverter.__init__(self, map) if length is not None: length = '{%d}' % int(length) else: if maxlength is None: maxlength = '' else: maxlength = int(maxlength) length = '{%s,%s}' % ( int(minlength), maxlength ) self.regex = '[^/]' + length class AnyConverter(BaseConverter): """Matches one of the items provided. Items can either be Python identifiers or unicode strings:: Rule('/') """ def __init__(self, map, *items): BaseConverter.__init__(self, map) self.regex = '(?:%s)' % '|'.join([re.escape(x) for x in items]) class PathConverter(BaseConverter): """Like the default string converter, but it also matches slashes.""" regex = '[^/].*?' is_greedy = True weight = 50 class NumberConverter(BaseConverter): """Baseclass for `IntegerConverter` and `FloatConverter`. :internal: """ def __init__(self, map, fixed_digits=0, min=None, max=None): BaseConverter.__init__(self, map) self.fixed_digits = fixed_digits self.min = min self.max = max def to_python(self, value): if (self.fixed_digits and len(value) != self.fixed_digits): raise ValidationError() value = self.num_convert(value) if (self.min is not None and value < self.min) or \ (self.max is not None and value > self.max): raise ValidationError() return value def to_url(self, value): value = self.num_convert(value) if self.fixed_digits: value = ('%%0%sd' % self.fixed_digits) % value return str(value) class IntegerConverter(NumberConverter): """This converter only accepts integer values:: Rule('/page/') Supported arguments: - `fixed_digits` - the number of fixed digits in the URL. If you set this to ``4`` for example, the application will only match if the url looks like ``/0001/``. The default is variable length. - `min` - the minimal value. - `max` - the maximal value. """ regex = r'\d+' num_convert = int class FloatConverter(NumberConverter): """This converter only accepts floating point values:: Rule('/probability/') Supported arguments: - `min` - the minimal value. - `max` - the maximal value. """ regex = r'\d+\.\d+' num_convert = float def __init__(self, map, min=None, max=None): NumberConverter.__init__(self, map, 0, min, max) class Map(object): """The map class stores all the URL rules and some configuration parameters. Some of the configuration values are only stored on the `Map` instance since those affect all rules, others are just defaults and can be overridden for each rule. Note that you have to specify all arguments beside the `rules` as keywords arguments! """ def __init__(self, rules=None, default_subdomain='', charset='utf-8', strict_slashes=True, redirect_defaults=True, converters=None): """Initializes the new URL map. :param rules: sequence of url rules for this map. :param default_subdomain: The default subdomain for rules without a subdomain defined. :param charset: charset of the url. defaults to ``"utf-8"`` :param strict_slashes: Take care of trailing slashes. :param redirect_defaults: This will redirect to the default rule if it wasn't visited that way. This helps creating unique URLs. :param converters: A dict of converters that adds additional converters to the list of converters. If you redefine one converter this will override the original one. """ self._rules = [] self._rules_by_endpoint = {} self._remap = True self.default_subdomain = default_subdomain self.charset = charset self.strict_slashes = strict_slashes self.redirect_defaults = redirect_defaults self.converters = DEFAULT_CONVERTERS.copy() if converters: self.converters.update(converters) for rulefactory in rules or (): self.add(rulefactory) def is_endpoint_expecting(self, endpoint, *arguments): """Iterate over all rules and check if the endpoint expects the arguments provided. This is for example useful if you have some URLs that expect a language code and others that do not and you want to wrap the builder a bit so that the current language code is automatically added if not provided but endpoints expect it. """ self.update() arguments = set(arguments) for rule in self._rules_by_endpoint[endpoint]: if arguments.issubset(rule.arguments): return True return False def iter_rules(self, endpoint=None): """Iterate over all rules or the rules of an endpoint.""" if endpoint is not None: return iter(self._rules_by_endpoint[endpoint]) return iter(self._rules) def add(self, rulefactory): """Add a new rule or factory to the map and bind it. Requires that the rule is not bound to another map. """ for rule in rulefactory.get_rules(self): rule.bind(self) self._rules.append(rule) self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule) self._remap = True def add_rule(self, rule): from warnings import warn warn(DeprecationWarning('use map.add instead of map.add_rule now')) return self.add(rule) def bind(self, server_name, script_name=None, subdomain=None, url_scheme='http', default_method='GET', path_info=None): """Return a new `MapAdapter` with the details specified to the call. Note that `script_name` will default to ``'/'`` if not further specified or `None`. The `server_name` at least is a requirement because the HTTP RFC requires absolute URLs for redirects and so all redirect exceptions raised by Werkzeug will contain the full canonical URL. If no path_info is passed to match() it will use the default path info passed to bind. While this doesn't really make sense for manual bind calls, it's useful if you bind a map to a WSGI environment which already contains the path info. `subdomain` will default to the `default_subdomain` for this map if no defined. If there is no `default_subdomain` you cannot use the subdomain feature. """ if subdomain is None: subdomain = self.default_subdomain if script_name is None: script_name = '/' return MapAdapter(self, server_name, script_name, subdomain, url_scheme, path_info, default_method) def bind_to_environ(self, environ, server_name=None, subdomain=None, calculate_subdomain=False): """Like `bind` but you can pass it an WSGI environment and it will fetch the information from that directory. Note that because of limitations in the protocol there is no way to get the current subdomain and real `server_name` from the environment. If you don't provide it, Werkzeug will use `SERVER_NAME` and `SERVER_PORT` (or `HTTP_HOST` if provided) as used `server_name` with disabled subdomain feature. If `subdomain` is `None` but an environment and a server name is provided it will calculate the current subdomain automatically. Example: `server_name` is ``'example.com'`` and the `SERVER_NAME` in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated subdomain will be ``'staging.dev'``. If the object passed as environ as an environ attribute, the value of this attribute is used instead. This allows you to pass request objects. Additionally `PATH_INFO` added as a default ot the `MapAdapter` so that you don't have to pass the path info to the match method. """ if hasattr(environ, 'environ'): environ = environ.environ if server_name is None: if 'HTTP_HOST' in environ: server_name = environ['HTTP_HOST'] else: server_name = environ['SERVER_NAME'] if (environ['wsgi.url_scheme'], environ['SERVER_PORT']) not \ in (('https', '443'), ('http', '80')): server_name += ':' + environ['SERVER_PORT'] elif subdomain is None: cur_server_name = environ['SERVER_NAME'].split('.') real_server_name = server_name.split(':', 1)[0].split('.') offset = -len(real_server_name) if cur_server_name[offset:] != real_server_name: raise ValueError('the server name provided (%r) does not ' 'match the server name from the WSGI ' 'environment (%r)' % (environ['SERVER_NAME'], server_name)) subdomain = '.'.join(filter(None, cur_server_name[:offset])) return Map.bind(self, server_name, environ.get('SCRIPT_NAME'), subdomain, environ['wsgi.url_scheme'], environ['REQUEST_METHOD'], environ.get('PATH_INFO')) def update(self): """Called before matching and building to keep the compiled rules in the correct order after things changed. """ if self._remap: self._rules.sort(lambda a, b: a.match_compare(b)) for rules in self._rules_by_endpoint.itervalues(): rules.sort(lambda a, b: a.build_compare(b)) self._remap = False class MapAdapter(object): """Retured by `Map.bind` or `Map.bind_to_environ` and does the URL matching and building based on runtime information. """ def __init__(self, map, server_name, script_name, subdomain, url_scheme, path_info, default_method): self.map = map self.server_name = server_name if not script_name.endswith('/'): script_name += '/' self.script_name = script_name self.subdomain = subdomain self.url_scheme = url_scheme self.path_info = path_info or u'' self.default_method = default_method def dispatch(self, view_func, path_info=None, method=None, catch_http_exceptions=False): """Does the complete dispatching process. `view_func` is called with the endpoint and a dict with the values for the view. It should look up the view function, call it, and return a response object or WSGI application. http exceptions are not catched by default so that applications can display nicer error messages by just catching them by hand. If you want to stick with the default error messages you can pass it ``catch_http_exceptions=True`` and it will catch the http exceptions. Here a small example for the dispatch usage:: from werkzeug import Request, Response, responder from werkzeug.routing import Map, Rule def on_index(request): return Response('Hello from the index') url_map = Map([Rule('/', endpoint='index')]) views = {'index': on_index} @responder def application(environ, start_response): request = Request(environ) urls = url_map.bind_to_environ(environ) return urls.dispatch(lambda e, v: views[e](request, **v), catch_http_exceptions=True) Keep in mind that this method might return exception objects too, so use `Response.force_type` to get a response object. """ try: try: endpoint, args = self.match(path_info, method) except RequestRedirect, e: return e return view_func(endpoint, args) except HTTPException, e: if catch_http_exceptions: return e raise def match(self, path_info=None, method=None): """The usage is simple: you just pass the match method the current path info as well as the method (which defaults to `GET`). The following things can then happen: - you receive a `NotFound` exception that indicates that no URL is matching. A `NotFound` exception is also a WSGI application you can call to get a default page not found page (happens to be the same object as `werkzeug.exceptions.NotFound`) - you receive a `MethodNotAllowed` exception that indicates that there is a match for this URL but non for the current request method. This is useful for RESTful applications. - you receive a `RequestRedirect` exception with a `new_url` attribute. This exception is used to notify you about a request Werkzeug requests by your WSGI application. This is for example the case if you request ``/foo`` although the correct URL is ``/foo/`` You can use the `RequestRedirect` instance as response-like object similar to all other subclasses of `HTTPException`. - you get a tuple in the form ``(endpoint, arguments)`` when there is a match. If the path info is not passed to the match method the default path info of the map is used (defaults to the root URL if not defined explicitly). All of the exceptions raised are subclasses of `HTTPException` so they can be used as WSGI responses. The will all render generic error or redirect pages. Here is a small example for matching: >>> from werkzeug.routing import Map, Rule >>> m = Map([ ... Rule('/', endpoint='index'), ... Rule('/downloads/', endpoint='downloads/index'), ... Rule('/downloads/', endpoint='downloads/show') ... ]) >>> urls = m.bind("example.com", "/") >>> urls.match("/", "GET") ('index', {}) >>> urls.match("/downloads/42") ('downloads/show', {'id': 42}) And here is what happens on redirect and missing URLs: >>> urls.match("/downloads") Traceback (most recent call last): ... werkzeug.routing.RequestRedirect: http://example.com/downloads/ >>> urls.match("/missing") Traceback (most recent call last): ... werkzeug.routing.NotFound: /missing """ self.map.update() if path_info is None: path_info = self.path_info if not isinstance(path_info, unicode): path_info = path_info.decode(self.map.charset, 'ignore') method = (method or self.default_method).upper() path = u'%s|/%s' % (self.subdomain, path_info.lstrip('/')) have_match_for = set() for rule in self.map._rules: try: rv = rule.match(path) except RequestSlash: raise RequestRedirect(str('%s://%s%s%s/%s/' % ( self.url_scheme, self.subdomain and self.subdomain + '.' or '', self.server_name, self.script_name[:-1], path_info.lstrip('/') ))) if rv is None: continue if rule.methods is not None and method not in rule.methods: have_match_for.update(rule.methods) continue if self.map.redirect_defaults: for r in self.map._rules_by_endpoint[rule.endpoint]: if r.provides_defaults_for(rule) and \ r.suitable_for(rv, method): rv.update(r.defaults) subdomain, path = r.build(rv) raise RequestRedirect(str('%s://%s%s%s/%s' % ( self.url_scheme, subdomain and subdomain + '.' or '', self.server_name, self.script_name[:-1], path.lstrip('/') ))) if rule.redirect_to is not None: if isinstance(rule.redirect_to, basestring): def _handle_match(match): value = rv[match.group(1)] return rule._converters[match.group(1)].to_url(value) redirect_url = _simple_rule_re.sub(_handle_match, rule.redirect_to) else: redirect_url = rule.redirect_to(self, **rv) raise RequestRedirect(str(urljoin('%s://%s%s%s' % ( self.url_scheme, self.subdomain and self.subdomain + '.' or '', self.server_name, self.script_name ), redirect_url))) return rule.endpoint, rv if have_match_for: raise MethodNotAllowed(valid_methods=list(have_match_for)) raise NotFound() def test(self, path_info=None, method=None): """Test if a rule would match. Works like `match` but returns `True` if the URL matches, or `False` if it does not exist. """ try: self.match(path_info, method) except RequestRedirect: pass except NotFound: return False return True def build(self, endpoint, values=None, method=None, force_external=False): """Building URLs works pretty much the other way round. Instead of `match` you call `build` and pass it the endpoint and a dict of arguments for the placeholders. The `build` function also accepts an argument called `force_external` which, if you set it to `True` will force external URLs. Per default external URLs (include the server name) will only be used if the target URL is on a different subdomain. With the same map as in the example above this code generates some target URLs: >>> urls.build("index", {}) '/' >>> urls.build("downloads/show", {'id': 42}) '/downloads/42' >>> urls.build("downloads/show", {'id': 42}, force_external=True) 'http://example.com/downloads/42' Because URLs cannot contain non ASCII data you will always get bytestrings back. Non ASCII characters are urlencoded with the charset defined on the map instance. Additional values are converted to unicode and appended to the URL as URL querystring parameters: >>> urls.build("index", {'q': 'My Searchstring'}) '/?q=My+Searchstring' If a rule does not exist when building a `BuildError` exception is raised. The build method accepts an argument called `method` which allows you to specify the method you want to have an URL builded for if you have different methods for the same endpoint specified. """ self.map.update() method = method or self.default_method if values: values = dict([(k, v) for k, v in values.items() if v is not None]) else: values = {} for rule in self.map._rules_by_endpoint.get(endpoint, ()): if rule.suitable_for(values, method): rv = rule.build(values) if rv is not None: break else: raise BuildError(endpoint, values, method) subdomain, path = rv if not force_external and subdomain == self.subdomain: return str(urljoin(self.script_name, path.lstrip('/'))) return str('%s://%s%s%s/%s' % ( self.url_scheme, subdomain and subdomain + '.' or '', self.server_name, self.script_name[:-1], path.lstrip('/') )) #: the default converter mapping for the map. DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter } PK[8JFBBwerkzeug/routing.pyc; raHc@sdZdkZdkZdklZdklZdklZl Z l Z l Z dk l Z lZlZyeWn ej odklZnXeideiZeidZd Zd Zd efd YZd e efdYZdefdYZdeefdYZde fdYZ!de"fdYZ#de#fdYZ$de#fdYZ%de#fdYZ&de"fdYZ'de#fd YZ(d!e#fd"YZ)d#e"fd$YZ*d%e*fd&YZ+d'e*fd(YZ,d)e*fd*YZ-d+e*fd,YZ.d-e.fd.YZ/d/e.fd0YZ0d1e"fd2YZ1d3e"fd4YZ2hd5e+<d6e+<d7e,<d8e-<d9e/<d:e0/', endpoint='kb/browse'), Rule('/browse//', endpoint='kb/browse') ]) ], default_subdomain='www') If the application doesn't use subdomains it's perfectly fine to not set the default subdomain and use the `Subdomain` rule factory. The endpoint in the rules can be anything, for example import paths or unique identifiers. The WSGI application can use those endpoints to get the handler for that URL. It doesn't have to be a string at all but it's recommended. Now it's possible to create a URL adapter for one of the subdomains and build URLs: >>> c = m.bind('example.com') >>> c.build("kb/browse", dict(id=42)) 'http://kb.example.com/browse/42/' >>> c.build("kb/browse", dict()) 'http://kb.example.com/browse/' >>> c.build("kb/browse", dict(id=42, page=3)) 'http://kb.example.com/browse/42/3' >>> c.build("static/about") u'/about' >>> c.build("static/about", subdomain="kb") 'http://www.example.com/about' >>> c.build("static/index", force_external=True) 'http://www.example.com/' The first argument to bind is the server name *without* the subdomain. Per default it will assume that the script is mounted on the root, but often that's not the case so you can provide the real mount point as second argument: >>> c = m.bind('example.com', '/applications/example') The third argument can be the subdomain, if not given the default subdomain is used. For more details about binding have a look at the documentation of the `MapAdapter`. And here is how you can match URLs: >>> c = m.bind('example.com') >>> c.match("/") ('static/index', {}) >>> c.match("/about") ('static/about', {}) >>> c = m.bind('example.com', '/', 'kb') >>> c.match("/") ('kb/index', {}) >>> c.match("/browse/42/23") ('kb/browse', {'id': 42, 'page': 23}) If matching fails you get a `NotFound` exception, if the rule thinks it's a good idea to redirect (for example because the URL was defined to have a slash at the end but the request was missing that slash) it will raise a `RequestRedirect` exception. Both are subclasses of the `HTTPException` so you can use those errors as responses in the application. If matching succeeded but the URL rule was incompatible to the given method (for example there were only rules for `GET` and `HEAD` and routing system tried to match a `POST` request) a `MethodNotAllowed` method is raised. :copyright: 2007-2008 by Armin Ronacher, Leif K-Brooks, Thomas Johansson. :license: BSD, see LICENSE for more details. N(surljoin(sizip(s url_encodes url_quotesredirects format_string(s HTTPExceptionsNotFoundsMethodNotAllowed(sSetsk (?P[^<]*) # static rule data < (?: (?P[a-zA-Z_][a-zA-Z0-9_]*) # converter name (?:\((?P.*?)\))? # converter arguments \: # variable delimiter )? (?P[a-zA-Z][a-zA-Z0-9_]*) # variable name > s <([^>]+)>c csMd}t|}ti}t}x||jo|||}|t joPn|i } | dot t | dfVn| d}| dpd}||jotd|n|i||| dpt |fV|i}q'W||joI||}d|jp d |jotd |nt t |fVnd S( sParse a rule and return it as generator. Each iteration yields tuples in the form ``(converter, arguments, variable)``. If the converter is `None` it's a static url part, otherwise it's a dynamic one. :internal: isstaticsvariables convertersdefaultsvariable name %r used twice.sargss>ssu(lambda *a, **kw: (a, kw))(%s)N( snamesmaps converterss LookupErrorsargsstypesstoragesevalskwargs(smapsnamesargssstorageskwargs((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys get_converters! sRoutingExceptioncBstZdZRS(szSpecial exceptions that require the application to redirect, notifies him about missing urls etc. :internal: (s__name__s __module__s__doc__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysRoutingExceptions sRequestRedirectcBs&tZdZdZdZdZRS(sRaise if the map requests a redirect. This is for example the case if `strict_slashes` are activated and an url that requires a leading slash. The attribute `new_url` contains the absolute desitination url. i-cCsti||||_dS(N(sRoutingExceptions__init__sselfsnew_url(sselfsnew_url((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__scCst|idSdS(Ni-(sredirectsselfsnew_url(sselfsenviron((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys get_responses(s__name__s __module__s__doc__scodes__init__s get_response(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysRequestRedirects  s RequestSlashcBstZdZRS(sInternal exception.(s__name__s __module__s__doc__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys RequestSlashs s BuildErrorcBstZdZdZRS(s_Raised if the build system cannot find a URL for an endpoint with the values provided. cCs5ti||||||_||_||_dS(N(s LookupErrors__init__sselfsendpointsvaluessmethod(sselfsendpointsvaluessmethod((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__s  (s__name__s __module__s__doc__s__init__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys BuildErrors sValidationErrorcBstZdZRS(sValidation error. If a rule converter raises this exception the rule does not match the current URL and the next URL is tried. (s__name__s __module__s__doc__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysValidationErrors s RuleFactorycBstZdZdZRS(sAs soon as you have more complex URL setups it's a good idea to use rule factories to avoid repetitive tasks. Some of them are builtin, others can be added by subclassing `RuleFactory` and overriding `get_rules`. cCs tdS(saSubclasses of `RuleFactory` have to override this method and return an iterable of rules.N(sNotImplementedError(sselfsmap((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys get_ruless(s__name__s __module__s__doc__s get_rules(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys RuleFactorys s SubdomaincBs tZdZdZdZRS(sAll URLs provided by this factory have the subdomain set to a specific domain. For example if you want to use the subdomain for the current language this can be a good setup:: url_map = Map([ Rule('/', endpoint='#select_language'), Subdomain('', [ Rule('/', endpoint='index'), Rule('/about', endpoint='about'), Rule('/help', endpoint='help') ]) ]) All the rules except of the ``'#select_language'`` endpoint will now listen on a two letter long subdomain that helds the language code for the current request. cCs||_||_dS(N(s subdomainsselfsrules(sselfs subdomainsrules((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__s ccsBx;|iD]0}x'|i|D]}|i|_|Vq Wq WdS(N(sselfsruless rulefactorys get_rulessmapsrules subdomain(sselfsmaps rulefactorysrule((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys get_ruless   (s__name__s __module__s__doc__s__init__s get_rules(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys Subdomains  sSubmountcBs tZdZdZdZRS(s}Like `Subdomain` but prefixes the URL rule with a given string:: url_map = Map([ Rule('/', endpoint='index'), Submount('/blog', [ Rule('/', endpoint='blog/index'), Rule('/entry/', endpoint='blog/show') ]) ]) Now the rule ``'blog/show'`` matches ``/blog/entry/``. cCs|id|_||_dS(Ns/(spathsrstripsselfsrules(sselfspathsrules((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__sccsIxB|iD]7}x.|i|D]}|i|i|_|Vq Wq WdS(N(sselfsruless rulefactorys get_rulessmapsrulespath(sselfsmaps rulefactorysrule((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys get_ruless  (s__name__s __module__s__doc__s__init__s get_rules(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysSubmounts  sEndpointPrefixcBs tZdZdZdZRS(sPrefixes all endpoints (which must be strings for this factory) with another string. This can be useful for sub applications:: url_map = Map([ Rule('/', endpoint='index'), EndpointPrefix('blog/', [Submount('/blog', [ Rule('/', endpoint='index'), Rule('/entry/', endpoint='show') ])]) ]) cCs||_||_dS(N(sprefixsselfsrules(sselfsprefixsrules((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__,s ccsIxB|iD]7}x.|i|D]}|i|i|_|Vq Wq WdS(N(sselfsruless rulefactorys get_rulessmapsrulesprefixsendpoint(sselfsmaps rulefactorysrule((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys get_rules0s  (s__name__s __module__s__doc__s__init__s get_rules(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysEndpointPrefixs  s RuleTemplatecBs tZdZdZdZRS(sXReturns copies of the rules wrapped and expands string templates in the endpoint, rule, defaults or subdomain sections. Here a small example for such a rule template:: from werkzeug.routing import Map, Rule, RuleTemplate resource = RuleTemplate([ Rule('/$name/', endpoint='$name.list'), Rule('/$name/', endpoint='$name.show') ]) url_map = Map([resource(name='user'), resource(name='page')]) When a rule template is called the keyword arguments are used to replace the placeholders in all the string parameters. cCst||_dS(N(slistsrulessself(sselfsrules((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__JscOst|it||SdS(N(sRuleTemplateFactorysselfsrulessdictsargsskwargs(sselfsargsskwargs((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__call__Ms(s__name__s __module__s__doc__s__init__s__call__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys RuleTemplate7s  sRuleTemplateFactorycBs tZdZdZdZRS(ssA factory that fills in template variables into rules. Used by `RuleTemplate` internally. :internal: cCs||_||_dS(N(srulessselfscontext(sselfsrulesscontext((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__Xs c cs2x+|iD] }x|i|D]}t}}|i tj oZh}xQ|i i D]<\}}t |tot||i}n|||`` where the converter and the arguments are optional. If no converter is defined the `default` converter is used which means `string` in the normal configuration. URL rules that end with a slash are branch URLs, others are leaves. If you have `strict_slashes` enabled (which is the default), all branch URLs that are matched without a trailing slash will trigger a redirect to the same URL with the missing slash appended. The converters are defined on the `Map`. `endpoint` The endpoint for this rule. This can be anything. A reference to a function, a string, a number etc. The preferred way is using a string as because the endpoint is used for URL generation. `defaults` An optional dict with defaults for other rules with the same endpoint. This is a bit tricky but useful if you want to have unique URLs:: url_map = Map([ Rule('/all/', defaults={'page': 1}, endpoint='all_entries'), Rule('/all/page/', endpoint='all_entries') ]) If a user now visits ``http://example.com/all/page/1`` he will be redirected to ``http://example.com/all/``. If `redirect_defaults` is disabled on the `Map` instance this will only affect the URL generation. `subdomain` The subdomain rule string for this rule. If not specified the rule only matches for the `default_subdomain` of the map. If the map is not bound to a subdomain this feature is disabled. Can be useful if you want to have user profiles on different subdomains and all subdomains are forwarded to your application:: url_map = Map([ Rule('/', subdomain='', endpoint='user/homepage'), Rule('/stats', subdomain='', endpoint='user/stats') ]) `methods` A sequence of http methods this rule applies to. If not specified, all methods are allowed. For example this can be useful if you want different endpoints for `POST` and `GET`. If methods are defined and the path matches but the method matched against is not in this list or in the list of another rule for that path the error raised is of the type `MethodNotAllowed` rather than `NotFound`. `strict_slashes` Override the `Map` setting for `strict_slashes` only for this rule. If not specified the `Map` setting is used. `build_only` Set this to true and the rule will never match but will create a URL that can be build. This is useful if you have resources on a subdomain or folder that are not handled by the WSGI application (like static data) `redirect_to` If given this must be either a string or callable. In case of a callable it's called with the url adapter that triggered the match and the values of the URL as keyword arguments and has to return the target for the redirect, otherwise it has to be a string with placeholders in rule syntax:: def foo_with_slug(adapter, id): # ask the database for the slug for the old id. this of # course has nothing to do with werkzeug. return 'foo/' + Foo.get_slug_for_id(id) url_map = Map([ Rule('/foo/', endpoint='foo'), Rule('/some/old/url/', redirect_to='foo/'), Rule('/other/old/url/', redirect_to=foo_with_slug) ]) When the rule is matched the routing system will raise a `RequestRedirect` exception with the target for the redirect. Keep in mind that the URL will be joined against the URL root of the script so don't use a leading slash on the target URL unless you really mean root of that domain. c Cs2|id otdn||_|id |_t|_||_ ||_ ||_ ||_ |tjo t|_ n7tgi} |D]} | | iq~ |_ ||_d|_||_g|_|tj ottt||_n t|_h|_t|_g|_dS(Ns/s$urls must start with a leading slashi(sstrings startswiths ValueErrorsselfsrulesendswithsis_leafsNonesmapsstrict_slashess subdomainsdefaultss build_onlysmethodsssetsappends_[1]sxsuppersendpoints greedinesss redirect_tos_tracesstrs argumentss _converterss_regexs_weights( sselfsstringsdefaultss subdomainsmethodss build_onlysendpointsstrict_slashess redirect_tos_[1]sx((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__s,        6        c Cs;t|i|i|i|i|i|i|i|i SdS(s{Return an unbound copy of this rule. This can be useful if you want to reuse an already bound URL for another map.N( sRulesselfsrulesdefaultss subdomainsmethodss build_onlysendpointsstrict_slashess redirect_to(sself((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysemptysccs|VdS(N(sself(sselfsmap((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys get_rulessc Cs;|itj otd||ifn||_|itjo|i|_n|itjo|i|_n|id|io|ip|ii d}g}xt |D]\}}}|tjoF|iti||iit|f|iit|qt|||}|id||if||i|<|iit|f|ii|i|i it||io|id7_qqW|i o|iitdfn|i oRddi!||i p|i odpd f}ti"|ti#|_$nd S( sBind the url to a map and create a regular expression based on the information from the rule itself and the defaults from the map. :internal: s#url rule %r already bound to map %rs|s/s (?P<%s>%s)is^%s%s$us(?/?)sN(%sselfsmapsNones RuntimeErrorsstrict_slashess subdomainsdefault_subdomainsis_leafsrulesrstrips regex_partss parse_rules converters argumentssvariablesappendsresescapes_tracesFalses_weightsslens get_convertersconvobjsregexs _converterssTruesweightsaddsstrs is_greedys greedinesss build_onlysjoinscompilesUNICODEs_regex( sselfsmaps convertersconvobjs regex_partssrulesregexs argumentssvariable((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysbinds: 1      6cCs|i o |ii|}|tj o|i}|i o|i o|i d o t n|i o |d=nh}x^|iD]P\}}y|i|i|}Wntj o dSnX||t|sutf-8s<%s>s<%s %r%s -> %s>us|s (%s)s, s(sselfsmapsNones __class__s__name__scharsetstmps_traces is_dynamicsdatasappendsjoinsencodeslstripsmethodssendpoint(sselfstmpscharsets is_dynamicsdata((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__repr__s# (s__name__s __module__s__doc__sNonesFalses__init__semptys get_rulessbindsmatchsbuildsprovides_defaults_fors suitable_fors match_compares build_compares__eq__s__ne__s __unicode__s__str__s__repr__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysRulevs \   , $       s BaseConvertercBs;tZdZdZeZdZdZdZdZ RS(sBase class for all converters.s[^/]+idcCs ||_dS(N(smapsself(sselfsmap((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__scCs|SdS(N(svalue(sselfsvalue((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys to_pythonscCst||iiSdS(N(s url_quotesvaluesselfsmapscharset(sselfsvalue((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysto_urls( s__name__s __module__s__doc__sregexsFalses is_greedysweights__init__s to_pythonsto_url(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys BaseConverters   sUnicodeConvertercBs tZdZdeedZRS(sgThis converter is the default converter and accepts any string but only one one path segment. Thus the string can not include a slash. Supported arguments: - `minlength` - the minimum length of the string. must be greater than 1. - `maxlength` - the maximum length of the string. - `length` - the exact length of that string. icCs{ti|||tj odt|}n:|tjo d}n t|}dt||f}d||_ dS(Ns{%d}ss{%s,%s}s[^/]( s BaseConverters__init__sselfsmapslengthsNonesints maxlengths minlengthsregex(sselfsmaps minlengths maxlengthslength((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__s    (s__name__s __module__s__doc__sNones__init__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysUnicodeConverters s AnyConvertercBstZdZdZRS(sMatches one of the items provided. Items can either be Python identifiers or unicode strings:: Rule('/') cGsTti||ddigi}|D]}|t i |q'~|_ dS(Ns(?:%s)s|( s BaseConverters__init__sselfsmapsjoinsappends_[1]sitemssxsresescapesregex(sselfsmapsitemss_[1]sx((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__s(s__name__s __module__s__doc__s__init__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys AnyConverters s PathConvertercBs tZdZdZeZdZRS(s?Like the default string converter, but it also matches slashes.s[^/].*?i2(s__name__s __module__s__doc__sregexsTrues is_greedysweight(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys PathConverters sNumberConvertercBs2tZdZdeedZdZdZRS(sKBaseclass for `IntegerConverter` and `FloatConverter`. :internal: icCs/ti||||_||_||_dS(N(s BaseConverters__init__sselfsmaps fixed_digitssminsmax(sselfsmaps fixed_digitssminsmax((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__)s  cCs|iot||ijo tn|i|}|itj o ||ijp|itj o ||ijo tn|SdS(N( sselfs fixed_digitsslensvaluesValidationErrors num_convertsminsNonesmax(sselfsvalue((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys to_python/s   @ cCs<|i|}|iod|i|}nt|SdS(Ns%%0%sd(sselfs num_convertsvalues fixed_digitssstr(sselfsvalue((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysto_url8s (s__name__s __module__s__doc__sNones__init__s to_pythonsto_url(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysNumberConverter#s  sIntegerConvertercBstZdZdZeZRS(sThis converter only accepts integer values:: Rule('/page/') Supported arguments: - `fixed_digits` - the number of fixed digits in the URL. If you set this to ``4`` for example, the application will only match if the url looks like ``/0001/``. The default is variable length. - `min` - the minimal value. - `max` - the maximal value. s\d+(s__name__s __module__s__doc__sregexsints num_convert(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysIntegerConverter?s sFloatConvertercBs)tZdZdZeZeedZRS(sThis converter only accepts floating point values:: Rule('/probability/') Supported arguments: - `min` - the minimal value. - `max` - the maximal value. s\d+\.\d+cCsti||d||dS(Ni(sNumberConverters__init__sselfsmapsminsmax(sselfsmapsminsmax((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__^s(s__name__s __module__s__doc__sregexsfloats num_convertsNones__init__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysFloatConverterQs sMapcBstZdZeddeeedZdZedZdZdZ eedd ed Z eee d Z d Z RS( sYThe map class stores all the URL rules and some configuration parameters. Some of the configuration values are only stored on the `Map` instance since those affect all rules, others are just defaults and can be overridden for each rule. Note that you have to specify all arguments beside the `rules` as keywords arguments! ssutf-8cCsg|_h|_t|_||_||_||_||_t i |_ |o|i i |nx"|pfD]}|i|qwWdS(sInitializes the new URL map. :param rules: sequence of url rules for this map. :param default_subdomain: The default subdomain for rules without a subdomain defined. :param charset: charset of the url. defaults to ``"utf-8"`` :param strict_slashes: Take care of trailing slashes. :param redirect_defaults: This will redirect to the default rule if it wasn't visited that way. This helps creating unique URLs. :param converters: A dict of converters that adds additional converters to the list of converters. If you redefine one converter this will override the original one. N(sselfs_ruless_rules_by_endpointsTrues_remapsdefault_subdomainscharsetsstrict_slashessredirect_defaultssDEFAULT_CONVERTERSscopys converterssupdatesruless rulefactorysadd(sselfsrulessdefault_subdomainscharsetsstrict_slashessredirect_defaultss converterss rulefactory((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__js       cGsQ|it|}x0|i|D]!}|i|iotSq$q$Wt SdS(soIterate over all rules and check if the endpoint expects the arguments provided. This is for example useful if you have some URLs that expect a language code and others that do not and you want to wrap the builder a bit so that the current language code is automatically added if not provided but endpoints expect it. N( sselfsupdatessets argumentss_rules_by_endpointsendpointsrulesissubsetsTruesFalse(sselfsendpoints argumentssrule((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysis_endpoint_expectings   cCs3|tj ot|i|Snt|iSdS(s3Iterate over all rules or the rules of an endpoint.N(sendpointsNonesitersselfs_rules_by_endpoints_rules(sselfsendpoint((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys iter_ruless cCscxS|i|D]B}|i||ii||ii|i gi|qWt |_ dS(swAdd a new rule or factory to the map and bind it. Requires that the rule is not bound to another map. N( s rulefactorys get_rulessselfsrulesbinds_rulessappends_rules_by_endpoints setdefaultsendpointsTrues_remap(sselfs rulefactorysrule((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysadds #cCs.dkl}|td|i|SdS(N(swarns'use map.add instead of map.add_rule now(swarningsswarnsDeprecationWarningsselfsaddsrule(sselfsruleswarn((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysadd_rules shttpsGETcCsQ|tjo |i}n|tjo d}nt|||||||SdS(sEReturn a new `MapAdapter` with the details specified to the call. Note that `script_name` will default to ``'/'`` if not further specified or `None`. The `server_name` at least is a requirement because the HTTP RFC requires absolute URLs for redirects and so all redirect exceptions raised by Werkzeug will contain the full canonical URL. If no path_info is passed to match() it will use the default path info passed to bind. While this doesn't really make sense for manual bind calls, it's useful if you bind a map to a WSGI environment which already contains the path info. `subdomain` will default to the `default_subdomain` for this map if no defined. If there is no `default_subdomain` you cannot use the subdomain feature. s/N( s subdomainsNonesselfsdefault_subdomains script_names MapAdapters server_names url_schemes path_infosdefault_method(sselfs server_names script_names subdomains url_schemesdefault_methods path_info((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysbinds    c Csnt|do |i}n|tjold|jo|d}q1|d}|d|dfddfdd ffjo|d |d7}q1n|tjo|did }|id d d id }t| }|||jot d|d|fnd i t t|| }nt i|||id||d|d|idSdS(sbLike `bind` but you can pass it an WSGI environment and it will fetch the information from that directory. Note that because of limitations in the protocol there is no way to get the current subdomain and real `server_name` from the environment. If you don't provide it, Werkzeug will use `SERVER_NAME` and `SERVER_PORT` (or `HTTP_HOST` if provided) as used `server_name` with disabled subdomain feature. If `subdomain` is `None` but an environment and a server name is provided it will calculate the current subdomain automatically. Example: `server_name` is ``'example.com'`` and the `SERVER_NAME` in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated subdomain will be ``'staging.dev'``. If the object passed as environ as an environ attribute, the value of this attribute is used instead. This allows you to pass request objects. Additionally `PATH_INFO` added as a default ot the `MapAdapter` so that you don't have to pass the path info to the match method. senvirons HTTP_HOSTs SERVER_NAMEswsgi.url_schemes SERVER_PORTshttpss443shttps80s:s.iis[the server name provided (%r) does not match the server name from the WSGI environment (%r)s SCRIPT_NAMEsREQUEST_METHODs PATH_INFON(shasattrsenvirons server_namesNones subdomainssplitscur_server_namesreal_server_nameslensoffsets ValueErrorsjoinsfiltersMapsbindsselfsget(sselfsenvirons server_names subdomainscalculate_subdomainscur_server_namesoffsetsreal_server_name((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysbind_to_environs&    -    cCsX|ioJ|iidx'|iiD]}|idq-Wt|_ndS(szCalled before matching and building to keep the compiled rules in the correct order after things changed. cCs |i|S(N(sas match_comparesb(sasb((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysscCs |i|S(N(sas build_comparesb(sasb((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pyssN(sselfs_remaps_rulesssorts_rules_by_endpoints itervaluessrulessFalse(sselfsrules((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysupdates (s__name__s __module__s__doc__sNonesTrues__init__sis_endpoint_expectings iter_rulessaddsadd_rulesbindsFalsesbind_to_environsupdate(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysMapbs !   .s MapAdaptercBsYtZdZdZeeedZeedZeedZeeedZ RS(s|Retured by `Map.bind` or `Map.bind_to_environ` and does the URL matching and building based on runtime information. cCsi||_||_|id o|d7}n||_||_||_|pd|_||_dS(Ns/u( smapsselfs server_names script_namesendswiths subdomains url_schemes path_infosdefault_method(sselfsmaps server_names script_names subdomains url_schemes path_infosdefault_method((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys__init__s     cCswyIy|i||\}}Wntj o }|SnX|||SWn't j o}|o|SnnXdS(sTDoes the complete dispatching process. `view_func` is called with the endpoint and a dict with the values for the view. It should look up the view function, call it, and return a response object or WSGI application. http exceptions are not catched by default so that applications can display nicer error messages by just catching them by hand. If you want to stick with the default error messages you can pass it ``catch_http_exceptions=True`` and it will catch the http exceptions. Here a small example for the dispatch usage:: from werkzeug import Request, Response, responder from werkzeug.routing import Map, Rule def on_index(request): return Response('Hello from the index') url_map = Map([Rule('/', endpoint='index')]) views = {'index': on_index} @responder def application(environ, start_response): request = Request(environ) urls = url_map.bind_to_environ(environ) return urls.dispatch(lambda e, v: views[e](request, **v), catch_http_exceptions=True) Keep in mind that this method might return exception objects too, so use `Response.force_type` to get a response object. N( sselfsmatchs path_infosmethodsendpointsargssRequestRedirectses view_funcs HTTPExceptionscatch_http_exceptions(sselfs view_funcs path_infosmethodscatch_http_exceptionssendpointsesargs((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysdispatchs c s|ii|tjo |i}nt|t o|i|iid}n|p|i i }d|i |i df}t}x]|iiD]Oyi|Wnftj oZttd|i|i o |i dpd|i|id |i dfnXtjoqnitj o |ijo|iiqn|iiox|iiiD]} | i o| i!|ovi| i"| i#\}}ttd|i|o|dpd|i|id |i dfqqWni$tj oti$t%o(d }t'i(|i$}ni$|}ttt*d |i|i o |i dpd|i|if|nifSqW|ot+d t,|nt-d S( s The usage is simple: you just pass the match method the current path info as well as the method (which defaults to `GET`). The following things can then happen: - you receive a `NotFound` exception that indicates that no URL is matching. A `NotFound` exception is also a WSGI application you can call to get a default page not found page (happens to be the same object as `werkzeug.exceptions.NotFound`) - you receive a `MethodNotAllowed` exception that indicates that there is a match for this URL but non for the current request method. This is useful for RESTful applications. - you receive a `RequestRedirect` exception with a `new_url` attribute. This exception is used to notify you about a request Werkzeug requests by your WSGI application. This is for example the case if you request ``/foo`` although the correct URL is ``/foo/`` You can use the `RequestRedirect` instance as response-like object similar to all other subclasses of `HTTPException`. - you get a tuple in the form ``(endpoint, arguments)`` when there is a match. If the path info is not passed to the match method the default path info of the map is used (defaults to the root URL if not defined explicitly). All of the exceptions raised are subclasses of `HTTPException` so they can be used as WSGI responses. The will all render generic error or redirect pages. Here is a small example for matching: >>> from werkzeug.routing import Map, Rule >>> m = Map([ ... Rule('/', endpoint='index'), ... Rule('/downloads/', endpoint='downloads/index'), ... Rule('/downloads/', endpoint='downloads/show') ... ]) >>> urls = m.bind("example.com", "/") >>> urls.match("/", "GET") ('index', {}) >>> urls.match("/downloads/42") ('downloads/show', {'id': 42}) And here is what happens on redirect and missing URLs: >>> urls.match("/downloads") Traceback (most recent call last): ... werkzeug.routing.RequestRedirect: http://example.com/downloads/ >>> urls.match("/missing") Traceback (most recent call last): ... werkzeug.routing.NotFound: /missing signoreu%s|/%ss/s%s://%s%s%s/%s/s.sis%s://%s%s%s/%scs4|id}i|idi|SdS(Ni(srvsmatchsgroupsvaluesrules _converterssto_url(smatchsvalue(srulesrv(s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys _handle_matchss %s://%s%s%ss valid_methodsN(.sselfsmapsupdates path_infosNones isinstancesunicodesdecodescharsetsmethodsdefault_methodsuppers subdomainslstripspathssetshave_match_fors_rulessrulesmatchsrvs RequestSlashsRequestRedirectsstrs url_schemes server_names script_namesmethodssredirect_defaultss_rules_by_endpointsendpointsrsprovides_defaults_fors suitable_forsdefaultssbuilds redirect_tos basestrings _handle_matchs_simple_rule_ressubs redirect_urlsurljoinsMethodNotAllowedslistsNotFound( sselfs path_infosmethodshave_match_fors _handle_matchs subdomains redirect_urlsrvspathsrulesr((srvsrules6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysmatch=sN8     X   #Y =cCsHy|i||Wn)tj ontj o tSnXtSdS(sTest if a rule would match. Works like `match` but returns `True` if the URL matches, or `False` if it does not exist. N(sselfsmatchs path_infosmethodsRequestRedirectsNotFoundsFalsesTrue(sselfs path_infosmethod((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pystests c Cs{|ii|p|i}|oTtgi}|i D]-\} }|t j o|| |fq;q;~}nh}xj|ii i|fD]>}|i||o%|i|}|t j oPqqqWt||||\} } | o | |ijo#tt|i| idSntd|i| o| dpd|i|id | idfSdS(sBuilding URLs works pretty much the other way round. Instead of `match` you call `build` and pass it the endpoint and a dict of arguments for the placeholders. The `build` function also accepts an argument called `force_external` which, if you set it to `True` will force external URLs. Per default external URLs (include the server name) will only be used if the target URL is on a different subdomain. With the same map as in the example above this code generates some target URLs: >>> urls.build("index", {}) '/' >>> urls.build("downloads/show", {'id': 42}) '/downloads/42' >>> urls.build("downloads/show", {'id': 42}, force_external=True) 'http://example.com/downloads/42' Because URLs cannot contain non ASCII data you will always get bytestrings back. Non ASCII characters are urlencoded with the charset defined on the map instance. Additional values are converted to unicode and appended to the URL as URL querystring parameters: >>> urls.build("index", {'q': 'My Searchstring'}) '/?q=My+Searchstring' If a rule does not exist when building a `BuildError` exception is raised. The build method accepts an argument called `method` which allows you to specify the method you want to have an URL builded for if you have different methods for the same endpoint specified. s/s%s://%s%s%s/%ss.siN(sselfsmapsupdatesmethodsdefault_methodsvaluessdictsappends_[1]sitemssksvsNones_rules_by_endpointsgetsendpointsrules suitable_forsbuildsrvs BuildErrors subdomainspathsforce_externalsstrsurljoins script_nameslstrips url_schemes server_name( sselfsendpointsvaluessmethodsforce_externalsrvsrules_[1]svspaths subdomainsk((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pysbuilds"% T   #( s__name__s __module__s__doc__s__init__sNonesFalsesdispatchsmatchstestsbuild(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys MapAdapters  +r sdefaultsstringsanyspathsintsfloat(4s__doc__ssyssresurlparsesurljoins itertoolssizipswerkzeug.utilss url_encodes url_quotesredirects format_stringswerkzeug.exceptionss HTTPExceptionsNotFoundsMethodNotAllowedssets NameErrorssetssSetscompilesVERBOSEs_rule_res_simple_rule_res parse_rules get_converters ExceptionsRoutingExceptionsRequestRedirects RequestSlashs LookupErrors BuildErrors ValueErrorsValidationErrorsobjects RuleFactorys SubdomainsSubmountsEndpointPrefixs RuleTemplatesRuleTemplateFactorysRules BaseConvertersUnicodeConverters AnyConverters PathConvertersNumberConvertersIntegerConvertersFloatConvertersMaps MapAdaptersDEFAULT_CONVERTERS(&s parse_rulessetsizips PathConvertersredirectsMaps RuleFactorys get_convertersValidationErrorsres BuildErrorsEndpointPrefixs format_strings url_quotes RuleTemplates_simple_rule_resRoutingExceptions url_encodes Subdomainsurljoins HTTPExceptionsNumberConvertersSubmountsRulessyssFloatConvertersMethodNotAllowedsRequestRedirectsNotFoundsDEFAULT_CONVERTERSs MapAdaptersUnicodeConvertersIntegerConverters_rule_res BaseConvertersRuleTemplateFactorys AnyConverters RequestSlash((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/routing.pys?asJ        %o PK݉8n9""werkzeug/script.py# -*- coding: utf-8 -*- r''' werkzeug.script ~~~~~~~~~~~~~~~ Most of the time you have recurring tasks while writing an application such as starting up an interactive python interpreter with some prefilled imports, starting the development server, initializing the database or something similar. For that purpose werkzeug provides the `werkzeug.script` module which helps you writing such scripts. Basic Usage ----------- The following snippet is roughly the same in every werkzeug script:: #!/usr/bin/env python # -*- coding: utf-8 -*- from werkzeug import script # actions go here if __name__ == '__main__': script.run() Starting this script now does nothing because no actions are defined. An action is a function in the same module starting with ``"action_"`` which takes a number of arguments where every argument has a default. The type of the default value specifies the type of the argument. Arguments can then be passed by position or using ``--name=value`` from the shell. Because a runserver and shell command is pretty common there are two factory functions that create such commands:: def make_app(): from yourapplication import YourApplication return YourApplication(...) action_runserver = script.make_runserver(make_app, use_reloader=True) action_shell = script.make_shell(lambda: {'app': make_app()}) Using The Scripts ----------------- The script from above can be used like this from the shell now: .. sourcecode:: text $ ./manage.py --help $ ./manage.py runserver localhost 8080 --debugger --no-reloader $ ./manage.py runserver -p 4000 $ ./manage.py shell As you can see it's possible to pass parameters as positional arguments or as named parameters, pretty much like Python function calls. :copyright: 2007-2008 by Armin Ronacher, Thomas Johansson. :license: BSD, see LICENSE for more details. ''' import sys import inspect import getopt from os.path import basename try: set = set except NameError: from sets import Set as set argument_types = { bool: 'boolean', str: 'string', int: 'integer', float: 'float' } converters = { 'boolean': lambda x: x.lower() in ('1', 'true', 'yes', 'on'), 'string': str, 'integer': int, 'float': float } def run(namespace=None, action_prefix='action_', args=None): """Run the script. Participating actions are looked up in the callers namespace if no namespace is given, otherwise in the dict provided. Only items that start with action_prefix are processed as actions. If you want to use all items in the namespace provided as actions set action_prefix to an empty string.""" if namespace is None: namespace = sys._getframe(1).f_locals actions = find_actions(namespace, action_prefix) if args is None: args = sys.argv[1:] if not args or args[0] in ('-h', '--help'): return print_usage(actions) elif args[0] not in actions: fail('Unknown action \'%s\'' % args[0]) arguments = {} conv = {} key_to_arg = {} long_options = [] formatstring = '' func, doc, arg_def = actions[args.pop(0)] for idx, (arg, shortcut, default, option_type) in enumerate(arg_def): real_arg = arg.replace('-', '_') converter = converters[option_type] if shortcut: formatstring += shortcut if not isinstance(default, bool): formatstring += ':' key_to_arg['-' + shortcut] = real_arg long_options.append(isinstance(default, bool) and arg or arg + '=') key_to_arg['--' + arg] = real_arg key_to_arg[idx] = real_arg conv[real_arg] = converter arguments[real_arg] = default try: optlist, posargs = getopt.gnu_getopt(args, formatstring, long_options) except getopt.GetoptError, e: fail(str(e)) specified_arguments = set() for key, value in enumerate(posargs): try: arg = key_to_arg[key] except IndexError: fail('Too many parameters') specified_arguments.add(arg) try: arguments[arg] = conv[arg](value) except ValueError: fail('Invalid value for argument %s (%s): %s' % (key, arg, value)) for key, value in optlist: arg = key_to_arg[key] if arg in specified_arguments: fail('Argument \'%s\' is specified twice' % arg) if arg.startswith('no_'): value = 'no' elif not value: value = 'yes' try: arguments[arg] = conv[arg](value) except ValueError: fail('Invalid value for \'%s\': %s' % (key, value)) newargs = {} for k, v in arguments.iteritems(): newargs[k.startswith('no_') and k[3:] or k] = v arguments = newargs return func(**arguments) def fail(message, code=-1): """Fail with an error.""" print >> sys.stderr, 'Error:', message sys.exit(code) def find_actions(namespace, action_prefix): """Find all the actions in the namespace.""" actions = {} for key, value in namespace.iteritems(): if key.startswith(action_prefix): actions[key[len(action_prefix):]] = analyse_action(value) return actions def print_usage(actions): """Print the usage information. (Help screen)""" actions = actions.items() actions.sort() print 'usage: %s []' % basename(sys.argv[0]) print ' %s --help' % basename(sys.argv[0]) print print 'actions:' for name, (func, doc, arguments) in actions: print ' %s:' % name for line in doc.splitlines(): print ' %s' % line if arguments: print for arg, shortcut, default, argtype in arguments: if isinstance(default, bool): print ' %s' % ( (shortcut and '-%s, ' % shortcut or '') + '--' + arg ) else: print ' %-30s%-10s%s' % ( (shortcut and '-%s, ' % shortcut or '') + '--' + arg, argtype, default ) print def analyse_action(func): """Analyse a function.""" description = inspect.getdoc(func) or 'undocumented action' arguments = [] args, varargs, kwargs, defaults = inspect.getargspec(func) if varargs or kwargs: raise TypeError('variable length arguments for action not allowed.') if len(args) != len(defaults or ()): raise TypeError('not all arguments have proper definitions') for idx, (arg, definition) in enumerate(zip(args, defaults or ())): if arg.startswith('_'): raise TypeError('arguments may not start with an underscore') if not isinstance(definition, tuple): shortcut = None default = definition else: shortcut, default = definition argument_type = argument_types[type(default)] if isinstance(default, bool) and default is True: arg = 'no-' + arg arguments.append((arg.replace('_', '-'), shortcut, default, argument_type)) return func, description, arguments def make_shell(init_func=lambda: {}, banner=None, use_ipython=True): """Returns an action callback that spawns a new interactive python shell.""" if banner is None: banner = 'Interactive Werkzeug Shell' def action(ipython=use_ipython): """Start a new interactive python session.""" namespace = init_func() if ipython: try: import IPython except ImportError: pass else: sh = IPython.Shell.IPShellEmbed(banner=banner) sh(global_ns={}, local_ns=namespace) return from code import interact interact(banner, local=namespace) return action def make_runserver(app_factory, hostname='localhost', port=5000, use_reloader=False, use_debugger=False, use_evalex=True, threaded=False, processes=1): """Returns an action callback that spawns a new wsgiref server.""" def action(hostname=('h', hostname), port=('p', port), reloader=use_reloader, debugger=use_debugger, evalex=use_evalex, threaded=threaded, processes=processes): """Start a new development server.""" from werkzeug.serving import run_simple app = app_factory() run_simple(hostname, port, app, reloader, debugger, evalex, None, 1, threaded, processes) return action PK[8T's,,werkzeug/script.pyc; raHc@s"dZdkZdkZdkZdklZy eZWn ej odkl ZnXhe d<e d<e d<e dVssaction_cCs|tjotidi}nt||}|tjoti d}n| p|dddfjot |Sn(|d|jot d|dnh} h}h}g}d}||id\}}}xt|D]\}\} } }}| idd} t|}| o;|| 7}t|t  o|d 7}n| |d|  []is %s --helpsactions:s %s:s %ss-%s, ss--s %-30s%-10s%sN(sactionssitemsssortsbasenamessyssargvsnamesfuncsdocs argumentss splitlinesslinesargsshortcutsdefaultsargtypes isinstancesbool( sactionssargtypesnamesdefaultsdocs argumentssfuncsargslinesshortcut((s5build/bdist.darwin-8.11.1-i386/egg/werkzeug/script.pys print_usages*     '0c Csvti|pd}g}ti|\}}}} |p|ot dnt |t | pfjot dnxt t || pfD]\} \}} |idot dnt| t ot}| } n | \}} tt| }t| to | tjod|}n|i|idd|| |fqW|||fSdS( sAnalyse a function.sundocumented actions1variable length arguments for action not allowed.s)not all arguments have proper definitionss_s*arguments may not start with an underscoresno-s-N(sinspectsgetdocsfuncs descriptions argumentss getargspecsargssvarargsskwargssdefaultss TypeErrorslens enumerateszipsidxsargs definitions startswiths isinstancestuplesNonesshortcutsdefaultsargument_typesstypes argument_typesboolsTruesappendsreplace( sfuncsargs argument_typesshortcuts argumentsskwargssargss descriptionsvarargss definitionsidxsdefaultsdefaults((s5build/bdist.darwin-8.11.1-i386/egg/werkzeug/script.pysanalyse_actions*   )cCshS(N((((s5build/bdist.darwin-8.11.1-i386/egg/werkzeug/script.pysscs1tjo dn|d}|SdS(sJReturns an action callback that spawns a new interactive python shell.sInteractive Werkzeug Shellcs}|oSy dk}Wntj oqcX|iid}|dhd|dSndk l }|d|dS(s'Start a new interactive python session.Nsbanners global_nsslocal_ns(sinteractslocal( s init_funcs namespacesipythonsIPythons ImportErrorsShells IPShellEmbedsbannersshscodesinteract(sipythonsinteracts namespacesIPythonssh(sbanners init_func(s5build/bdist.darwin-8.11.1-i386/egg/werkzeug/script.pysactions   N(sbannersNones use_ipythonsaction(s init_funcsbanners use_ipythonsaction((s init_funcsbanners5build/bdist.darwin-8.11.1-i386/egg/werkzeug/script.pys make_shells   s localhostiic s5d|fd|f|||||d}|SdS(s<Returns an action callback that spawns a new wsgiref server.shspc s?dkl}}|||||||t d|| dS(sStart a new development server.(s run_simpleiN( swerkzeug.servings run_simples app_factorysappshostnamesportsreloadersdebuggersevalexsNonesthreadeds processes( shostnamesportsreloadersdebuggersevalexsthreadeds processess run_simplesapp(s app_factory(s5build/bdist.darwin-8.11.1-i386/egg/werkzeug/script.pysactions   N(shostnamesports use_reloaders use_debuggers use_evalexsthreadeds processessaction( s app_factoryshostnamesports use_reloaders use_debuggers use_evalexsthreadeds processessaction((s app_factorys5build/bdist.darwin-8.11.1-i386/egg/werkzeug/script.pysmake_runservers-(s__doc__ssyssinspectsgetoptsos.pathsbasenamessets NameErrorssetssSetsboolsstrsintsfloatsargument_typess converterssNonesrunsfails find_actionss print_usagesanalyse_actionsTrues make_shellsFalsesmake_runserver(ssetsargument_typessbasenamesfailsmake_runserversgetopts converterssrunsinspectssyss find_actionssanalyse_actions make_shells print_usage((s5build/bdist.darwin-8.11.1-i386/egg/werkzeug/script.pys?Bs"     *-J   PK݉8%:ב%%werkzeug/serving.py# -*- coding: utf-8 -*- """ werkzeug.serving ~~~~~~~~~~~~~~~~ There are many ways to serve a WSGI application. While you're developing it you usually don't want a full blown webserver like Apache but a simple standalone one. With Python 2.5 onwards there is the `wsgiref`_ server in the standard library. If you're using older versions of Python you can download the package from the cheeseshop. However there are some caveats. Sourcecode won't reload itself when changed and each time you kill the server using ``^C`` you get an `KeyboardInterrupt` error. While the latter is easy to solve the first one can be a pain in the ass in some situations. Because of that Werkzeug ships a small wrapper over `wsgiref` that spawns the WSGI application in a subprocess and automatically reloads the application if a module was changed. The easiest way is creating a small ``start-myproject.py`` that runs the application:: #!/usr/bin/env python # -*- coding: utf-8 -*- from myproject import make_app from werkzeug import run_simple app = make_app(...) run_simple('localhost', 8080, app, use_reloader=True) You can also pass it a `extra_files` keyword argument with a list of additional files (like configuration files) you want to observe. For bigger applications you should consider using `werkzeug.script` instead of a simple start file. .. _wsgiref: http://cheeseshop.python.org/pypi/wsgiref :copyright: 2007-2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import os import socket import sys import time import thread from itertools import chain try: from wsgiref.simple_server import ServerHandler, WSGIRequestHandler, \ WSGIServer have_wsgiref = True except ImportError: have_wsgiref = False from SocketServer import ThreadingMixIn, ForkingMixIn from werkzeug._internal import _log if have_wsgiref: class BaseRequestHandler(WSGIRequestHandler): """ Subclass of the normal request handler that thinks it is threaded or something like that. The default wsgiref handler has wrong information so we need this class. """ multithreaded = False multiprocess = False _handler_class = None def get_handler(self): handler = self._handler_class if handler is None: class handler(ServerHandler): wsgi_multithread = self.multithreaded wsgi_multiprocess = self.multiprocess self._handler_class = handler rv = handler(self.rfile, self.wfile, self.get_stderr(), self.get_environ()) rv.request_handler = self return rv def handle(self): self.raw_requestline = self.rfile.readline() if self.parse_request(): self.get_handler().run(self.server.get_app()) def log_request(self, code='-', size='-'): _log('info', '%s -- [%s] %s %s', self.address_string(), self.requestline, code, size ) def log_error(self, format, *args): _log('error', 'Error: %s', format % args) def log_message(self, format, *args): _log('info', format, args) def make_server(host, port, app=None, threaded=False, processes=1, request_handler=None): """Create a new wsgiref server that is either threaded, or forks or just processes one request after another. """ if not have_wsgiref: raise RuntimeError('All the Werkzeug serving features require ' 'an installed wsgiref library.') request_handler = request_handler or BaseRequestHandler if threaded and processes > 1: raise ValueError("cannot have a multithreaded and " "multi process server.") elif threaded: class request_handler(request_handler): multithreaded = True class server(ThreadingMixIn, WSGIServer): pass elif processes > 1: class request_handler(request_handler): multiprocess = True class server(ForkingMixIn, WSGIServer): max_children = processes - 1 else: server = WSGIServer srv = server((host, port), request_handler) srv.set_app(app) return srv def reloader_loop(extra_files=None, interval=1): """When this function is run from the main thread, it will force other threads to exit when any modules currently loaded change. Copyright notice. This function is based on the autoreload.py from the CherryPy trac which originated from WSGIKit which is now dead. :param extra_files: a list of additional files it should watch. """ def iter_module_files(): for module in sys.modules.values(): filename = getattr(module, '__file__', None) if filename: while not os.path.isfile(filename): filename = os.path.dirname(filename) if not filename: break else: if filename[-4:] in ('.pyc', '.pyo'): filename = filename[:-1] yield filename mtimes = {} while 1: for filename in chain(iter_module_files(), extra_files or ()): try: mtime = os.stat(filename).st_mtime except OSError: continue old_time = mtimes.get(filename) if old_time is None: mtimes[filename] = mtime continue elif mtime > old_time: _log('info', ' * Detected change in %r, reloading' % filename) sys.exit(3) time.sleep(interval) def restart_with_reloader(): """Spawn a new Python interpreter with the same arguments as this one, but running the reloader thread. """ while 1: _log('info', ' * Restarting with reloader...') args = [sys.executable] + sys.argv if sys.platform == 'win32': args = ['"%s"' % arg for arg in args] new_environ = os.environ.copy() new_environ['WERKZEUG_RUN_MAIN'] = 'true' exit_code = os.spawnve(os.P_WAIT, sys.executable, args, new_environ) if exit_code != 3: return exit_code def run_with_reloader(main_func, extra_files=None, interval=1): """Run the given function in an independent python interpreter.""" if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': thread.start_new_thread(main_func, ()) try: reloader_loop(extra_files, interval) except KeyboardInterrupt: return try: sys.exit(restart_with_reloader()) except KeyboardInterrupt: pass def run_simple(hostname, port, application, use_reloader=False, use_debugger=False, use_evalex=True, extra_files=None, reloader_interval=1, threaded=False, processes=1, request_handler=None): """Start an application using wsgiref and with an optional reloader. This wraps `wsgiref` to fix the wrong default reporting of the multithreaded WSGI variable and adds optional multithreading and fork support. :param hostname: The host for the application. eg: ``'localhost'`` :param port: The port for the server. eg: ``8080`` :param application: the WSGI application to execute :param use_reloader: should the server automatically restart the python process if modules were changed? :param use_debugger: should the werkzeug debugging system be used? :param use_evalex: should the exception evaluation feature be enabled? :param extra_files: a list of files the reloader should listen for additionally to the modules. For example configuration files. :param reloader_interval: the interval for the reloader in seconds. :param threaded: should the process handle each request in a separate thread? :param processes: number of processes to spawn. :param request_handler: optional parameter that can be used to replace the default wsgiref request handler. Have a look at the `werkzeug.serving` sourcecode for more details. """ if use_debugger: from werkzeug.debug import DebuggedApplication application = DebuggedApplication(application, use_evalex) def inner(): srv = make_server(hostname, port, application, threaded, processes, request_handler) try: srv.serve_forever() except KeyboardInterrupt: pass if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': display_hostname = hostname or '127.0.0.1' _log('info', ' * Running on http://%s:%d/', display_hostname, port) if use_reloader: # Create and destroy a socket so that any exceptions are raised before # we spawn a separate Python interpreter and loose this ability. test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) test_socket.bind((hostname, port)) test_socket.close() run_with_reloader(inner, extra_files, reloader_interval) else: inner() PK[8@w 4 4werkzeug/serving.pyc; raHc @s!dZdkZdkZdkZdkZdkZdklZy#dkl Z l Z l Z e Z Wnej o eZ nXdklZlZdklZe ode fdYZneeded Zedd Zd Zedd Zeee ededed ZdS(sA werkzeug.serving ~~~~~~~~~~~~~~~~ There are many ways to serve a WSGI application. While you're developing it you usually don't want a full blown webserver like Apache but a simple standalone one. With Python 2.5 onwards there is the `wsgiref`_ server in the standard library. If you're using older versions of Python you can download the package from the cheeseshop. However there are some caveats. Sourcecode won't reload itself when changed and each time you kill the server using ``^C`` you get an `KeyboardInterrupt` error. While the latter is easy to solve the first one can be a pain in the ass in some situations. Because of that Werkzeug ships a small wrapper over `wsgiref` that spawns the WSGI application in a subprocess and automatically reloads the application if a module was changed. The easiest way is creating a small ``start-myproject.py`` that runs the application:: #!/usr/bin/env python # -*- coding: utf-8 -*- from myproject import make_app from werkzeug import run_simple app = make_app(...) run_simple('localhost', 8080, app, use_reloader=True) You can also pass it a `extra_files` keyword argument with a list of additional files (like configuration files) you want to observe. For bigger applications you should consider using `werkzeug.script` instead of a simple start file. .. _wsgiref: http://cheeseshop.python.org/pypi/wsgiref :copyright: 2007-2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. N(schain(s ServerHandlersWSGIRequestHandlers WSGIServer(sThreadingMixIns ForkingMixIn(s_logsBaseRequestHandlercBsStZdZeZeZeZdZdZ dddZ dZ dZ RS(s Subclass of the normal request handler that thinks it is threaded or something like that. The default wsgiref handler has wrong information so we need this class. csti}|tjo&dtfdY}|_n|iiii}|_ |SdS(NshandlercstZiZiZRS(N(s__name__s __module__sselfs multithreadedswsgi_multithreads multiprocessswsgi_multiprocess((sself(s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pyshandlerJs ( sselfs_handler_classshandlersNones ServerHandlersrfileswfiles get_stderrs get_environsrvsrequest_handler(sselfshandlersrv((sselfs6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pys get_handlerGs    cCsC|ii|_|io |ii|iindS(N( sselfsrfilesreadlinesraw_requestlines parse_requests get_handlersrunsserversget_app(sself((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pyshandleTs s-cCs&tdd|i|i||dS(Nsinfos%s -- [%s] %s %s(s_logsselfsaddress_strings requestlinescodessize(sselfscodessize((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pys log_requestYs   cGstdd||dS(Nserrors Error: %s(s_logsformatsargs(sselfsformatsargs((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pys log_errorascGstd||dS(Nsinfo(s_logsformatsargs(sselfsformatsargs((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pys log_messageds( s__name__s __module__s__doc__sFalses multithreadeds multiprocesssNones_handler_classs get_handlershandles log_requests log_errors log_message(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pysBaseRequestHandler=s   icst otdn|pt}|o djotdn|o3d|fdY}dttfdY}nJdjo6d|fdY}dt tfd Y}nt}|||f|}|i||Sd S( ssCreate a new wsgiref server that is either threaded, or forks or just processes one request after another. sGAll the Werkzeug serving features require an installed wsgiref library.is5cannot have a multithreaded and multi process server.srequest_handlercBstZeZRS(N(s__name__s __module__sTrues multithreaded(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pysrequest_handlerussservercBstZRS(N(s__name__s __module__(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pysserverwscBstZeZRS(N(s__name__s __module__sTrues multiprocess(((s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pysrequest_handlerzscstZdZRS(Ni(s__name__s __module__s processess max_children((s processes(s6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pysserver|sN(s have_wsgirefs RuntimeErrorsrequest_handlersBaseRequestHandlersthreadeds processess ValueErrorsThreadingMixIns WSGIServersservers ForkingMixInshostsportssrvsset_appsapp(shostsportsappsthreadeds processessrequest_handlerssrvsserver((s processess6build/bdist.darwin-8.11.1-i386/egg/werkzeug/serving.pys make_serverhs     cCsd}h}xnoxt||pfD]}yti|i}Wnt j o q3nX|i |}|t jo|||>werkzeug/templates.py# -*- coding: utf-8 -*- r""" werkzeug.templates ~~~~~~~~~~~~~~~~~~ This template engine recognizes ASP/PHP like blocks and executes the code in them:: t = Template('<% for u in users %>${u["username"]}\n<% endfor %>') t.render(users=[{'username': 'John'}, {'username': 'Jane'}]) would result in:: John Jane You can also create templates from files:: t = Template.from_file('test.html') The syntax elements are a mixture of django, genshi text and mod_python templates and used internally in werkzeug components. We do not recommend using this template engine in a real environment because is quite slow and does not provide any advanced features. For simple applications (cgi script like) this can however be sufficient. Syntax Elements --------------- Printing Variables: .. sourcecode:: text $variable $variable.attribute[item](some, function)(calls) ${expression} or <%py print expression %> Keep in mind that the print statement adds a newline after the call or a whitespace if it ends with a comma. For Loops: .. sourcecode:: text <% for item in seq %> ... <% endfor %> While Loops: .. sourcecode:: text <% while expression %> <%py break / continue %> <% endwhile %> If Conditions: .. sourcecode:: text <% if expression %> ... <% elif expression %> ... <% else %> ... <% endif %> Python Expressions: .. sourcecode:: text <%py ... %> <%python ... %> Note on python expressions: You cannot start a loop in a python block and continue it in another one. This example does *not* work: .. sourcecode:: text <%python for item in seq: %> ... Comments: .. sourcecode:: text <%# This is a comment %> Missing Variables ----------------- If you try to access a missing variable you will get back an `Undefined` object. You can iterate over such an object or print it and it won't fail. However every other operation will raise an error. To test if a variable is undefined you can use this expression: .. sourcecode:: text <% if variable is Undefined %> ... <% endif %> Python 2.3 Compatibility ------------------------ Because of limitations in Python 2.3 it's impossible to achieve the semi-silent variable lookup fallback. If a template relies on undefined variables it won't execute under Python 2.3. :copyright: 2006-2008 by Armin Ronacher, Ka-Ping Yee. :license: BSD License. """ import sys import re import __builtin__ as builtins from compiler import ast, parse from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL from compiler.pycodegen import ModuleCodeGenerator from tokenize import PseudoToken from werkzeug import utils from werkzeug._internal import _decode_unicode # Anything older than Python 2.4 if sys.version_info < (2, 4): class AstMangler(object): def __getattr__(self, key): class_ = getattr(_ast, key) def wrapper(*args, **kw): lineno = kw.pop('lineno', None) obj = class_(*args, **kw) obj.lineno = lineno return obj return wrapper _ast = ast ast = AstMangler() # Copyright notice: The `parse_data` method uses the string interpolation # algorithm by Ka-Ping Yee which originally was part of `ltpl20.py`_ # # .. _ltipl20.py: http://lfw.org/python/Itpl20.py token_re = re.compile('%s|%s(?s)' % ( r'[uU]?[rR]?("""|\'\'\')((?\n?(?s)') escape_re = re.compile(r'\\\n|\\(\\|<%)') namestart_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_' undefined = type('UndefinedType', (object,), { '__iter__': lambda x: iter(()), '__repr__': lambda x: 'Undefined', '__str__': lambda x: '' })() runtime_vars = dict.fromkeys(('Undefined', '__to_unicode', '__context', '__write', '__write_many')) def call_stmt(func, args, lineno): return ast.CallFunc(ast.Name(func, lineno=lineno), args, lineno=lineno) def tokenize(source, filename): escape = escape_re.sub escape_repl = lambda m: m.group(1) or '' lineno = 1 pos = 0 for match in directive_re.finditer(source): start, end = match.span() if start > pos: data = source[pos:start] yield lineno, 'data', escape(escape_repl, data) lineno += data.count('\n') is_comment, is_code, cmd, args = match.groups() if is_code: yield lineno, 'code', args elif not is_comment: yield lineno, 'cmd', (cmd, args) lineno += source[start:end].count('\n') pos = end if pos < len(source): yield lineno, 'data', escape(escape_repl, source[pos:]) def transform(node, filename): root = ast.Module(None, node, lineno=1) nodes = [root] while nodes: node = nodes.pop() node.filename = filename if node.__class__ in (ast.Printnl, ast.Print): node.dest = ast.Name('__context') elif node.__class__ is ast.Const and isinstance(node.value, str): try: node.value.decode('ascii') except UnicodeError: node.value = node.value.decode('utf-8') nodes.extend(node.getChildNodes()) return root class TemplateSyntaxError(SyntaxError): def __init__(self, msg, filename, lineno): from linecache import getline l = getline(filename, lineno) SyntaxError.__init__(self, msg, (filename, lineno, len(l) or 1, l)) class Parser(object): def __init__(self, gen, filename): self.gen = gen self.filename = filename self.lineno = 1 def fail(self, msg): raise TemplateSyntaxError(msg, self.filename, self.lineno) def parse_python(self, expr, type='exec'): if isinstance(expr, unicode): expr = '\xef\xbb\xbf' + expr.encode('utf-8') try: node = parse(expr, type) except SyntaxError, e: raise TemplateSyntaxError(str(e), self.filename, self.lineno + e.lineno - 1) nodes = [node] while nodes: n = nodes.pop() if hasattr(n, 'lineno'): n.lineno = (n.lineno or 1) + self.lineno - 1 nodes.extend(n.getChildNodes()) return node.node def parse(self, needle=()): start_lineno = self.lineno result = [] add = result.append for self.lineno, token, value in self.gen: if token == 'data': add(self.parse_data(value)) elif token == 'code': add(self.parse_code(value.splitlines())) elif token == 'cmd': name, args = value if name in needle: return name, args, ast.Stmt(result, lineno=start_lineno) if name in ('for', 'while'): add(self.parse_loop(args, name)) elif name == 'if': add(self.parse_if(args)) else: self.fail('unknown directive %s' % name) if needle: self.fail('unexpected end of template') return ast.Stmt(result, lineno=start_lineno) def parse_loop(self, args, type): rv = self.parse_python('%s %s: pass' % (type, args), 'exec').nodes[0] tag, value, rv.body = self.parse(('end' + type, 'else')) if value: self.fail('unexpected data after ' + tag) if tag == 'else': tag, value, rv.else_ = self.parse(('end' + type,)) if value: self.fail('unexpected data after else') return rv def parse_if(self, args): cond = self.parse_python('if %s: pass' % args).nodes[0] tag, value, body = self.parse(('else', 'elif', 'endif')) cond.tests[0] = (cond.tests[0][0], body) while 1: if tag == 'else': if value: self.fail('unexpected data after else') tag, value, cond.else_ = self.parse(('endif',)) elif tag == 'elif': expr = self.parse_python(value, 'eval') tag, value, body = self.parse(('else', 'elif', 'endif')) cond.tests.append((expr, body)) continue break if value: self.fail('unexpected data after endif') return cond def parse_code(self, lines): margin = sys.maxint for line in lines[1:]: content = len(line.lstrip()) if content: indent = len(line) - content margin = min(margin, indent) if lines: lines[0] = lines[0].lstrip() if margin < sys.maxint: for i in xrange(1, len(lines)): lines[i] = lines[i][margin:] while lines and not lines[-1]: lines.pop() while lines and not lines[0]: lines.pop(0) return self.parse_python('\n'.join(lines)) def parse_data(self, text): start_lineno = lineno = self.lineno pos = 0 end = len(text) nodes = [] def match_or_fail(pos): match = token_re.match(text, pos) if match is None: self.fail('invalid syntax') return match.group().strip(), match.end() def write_expr(code): node = self.parse_python(code, 'eval') nodes.append(call_stmt('__to_unicode', [node], lineno)) return code.count('\n') def write_data(value): if value: nodes.append(ast.Const(value, lineno=lineno)) return value.count('\n') return 0 while 1: offset = text.find('$', pos) if offset < 0: break next = text[offset + 1] if next == '{': lineno += write_data(text[pos:offset]) pos = offset + 2 level = 1 while level: token, pos = match_or_fail(pos) if token in ('{', '}'): level += token == '{' and 1 or -1 lineno += write_expr(text[offset + 2:pos - 1]) elif next in namestart_chars: lineno += write_data(text[pos:offset]) token, pos = match_or_fail(offset + 1) while pos < end: if text[pos] == '.' and pos + 1 < end and \ text[pos + 1] in namestart_chars: token, pos = match_or_fail(pos + 1) elif text[pos] in '([': pos += 1 level = 1 while level: token, pos = match_or_fail(pos) if token in ('(', ')', '[', ']'): level += token in '([' and 1 or -1 else: break lineno += write_expr(text[offset + 1:pos]) else: lineno += write_data(text[pos:offset + 1]) pos = offset + 1 + (next == '$') write_data(text[pos:]) return ast.Discard(call_stmt(len(nodes) == 1 and '__write' or '__write_many', nodes, start_lineno), lineno=start_lineno) class Context(object): def __init__(self, namespace, encoding, errors): self.encoding = encoding self.errors = errors self._namespace = namespace self._buffer = [] self._write = self._buffer.append _extend = self._buffer.extend self.runtime = dict( Undefined=undefined, __to_unicode=self.to_unicode, __context=self, __write=self._write, __write_many=lambda *a: _extend(a) ) def write(self, value): self._write(self.to_unicode(value)) def to_unicode(self, value): if isinstance(value, str): return _decode_unicode(value, self.encoding, self.errors) return unicode(value) def get_value(self, as_unicode=True): rv = u''.join(self._buffer) if not as_unicode: return rv.encode(self.encoding, self.errors) return rv def __getitem__(self, key, default=undefined): try: return self._namespace[key] except KeyError: return getattr(builtins, key, default) def get(self, key, default=None): return self.__getitem__(key, default) def __setitem__(self, key, value): self._namespace[key] = value def __delitem__(self, key): del self._namespace[key] class TemplateCodeGenerator(ModuleCodeGenerator): def __init__(self, node, filename): ModuleCodeGenerator.__init__(self, transform(node, filename)) def _nameOp(self, prefix, name): if name in runtime_vars: return self.emit(prefix + '_GLOBAL', name) return ModuleCodeGenerator._nameOp(self, prefix, name) class Template(object): """Represents a simple text based template. It's a good idea to load such templates from files on the file system to get better debug output. """ default_context = { 'escape': utils.escape, 'url_quote': utils.url_quote, 'url_quote_plus': utils.url_quote_plus, 'url_encode': utils.url_encode } def __init__(self, source, filename='