K i4b dZddlZddlmZddlmZddlmZddlm Z m Z m Z ddl m Z ddlmZmZmZmZdd lmZmZmZmZmZmZmZmZmZmZGd d ej<ZGd d eZ GddejBZ"GddejBZ#eedeeeee$dfefeee$dfeee$effeee$dfeee$efe$ffZ%GddeZ&Gdde e&Z'GddZ(GddZ)Gdde)Z*Gdde)Z+Gdde)Z,Gd d!e)Z-Gd"d#e(Z.ed$e$d%e/fd&Z0ed)d'Z0d$ee$d%ee/fd(Z0y)*apFlexible routing implementation. Tornado routes HTTP requests to appropriate handlers using `Router` class implementations. The `tornado.web.Application` class is a `Router` implementation and may be used directly, or the classes in this module may be used for additional flexibility. The `RuleRouter` class can match on more criteria than `.Application`, or the `Router` interface can be subclassed for maximum customization. `Router` interface extends `~.httputil.HTTPServerConnectionDelegate` to provide additional routing capabilities. This also means that any `Router` implementation can be used directly as a ``request_callback`` for `~.httpserver.HTTPServer` constructor. `Router` subclass must implement a ``find_handler`` method to provide a suitable `~.httputil.HTTPMessageDelegate` instance to handle the request: .. code-block:: python class CustomRouter(Router): def find_handler(self, request, **kwargs): # some routing logic providing a suitable HTTPMessageDelegate instance return MessageDelegate(request.connection) class MessageDelegate(HTTPMessageDelegate): def __init__(self, connection): self.connection = connection def finish(self): self.connection.write_headers( ResponseStartLine("HTTP/1.1", 200, "OK"), HTTPHeaders({"Content-Length": "2"}), b"OK") self.connection.finish() router = CustomRouter() server = HTTPServer(router) The main responsibility of `Router` implementation is to provide a mapping from a request to `~.httputil.HTTPMessageDelegate` instance that will handle this request. In the example above we can see that routing is possible even without instantiating an `~.web.Application`. For routing to `~.web.RequestHandler` implementations we need an `~.web.Application` instance. `~.web.Application.get_handler_delegate` provides a convenient way to create `~.httputil.HTTPMessageDelegate` for a given request and `~.web.RequestHandler`. Here is a simple example of how we can we route to `~.web.RequestHandler` subclasses by HTTP method: .. code-block:: python resources = {} class GetResource(RequestHandler): def get(self, path): if path not in resources: raise HTTPError(404) self.finish(resources[path]) class PostResource(RequestHandler): def post(self, path): resources[path] = self.request.body class HTTPMethodRouter(Router): def __init__(self, app): self.app = app def find_handler(self, request, **kwargs): handler = GetResource if request.method == "GET" else PostResource return self.app.get_handler_delegate(request, handler, path_args=[request.path]) router = HTTPMethodRouter(Application()) server = HTTPServer(router) `ReversibleRouter` interface adds the ability to distinguish between the routes and reverse them to the original urls using route's name and additional arguments. `~.web.Application` is itself an implementation of `ReversibleRouter` class. `RuleRouter` and `ReversibleRuleRouter` are implementations of `Router` and `ReversibleRouter` interfaces and can be used for creating rule-based routing configurations. Rules are instances of `Rule` class. They contain a `Matcher`, which provides the logic for determining whether the rule is a match for a particular request and a target, which can be one of the following. 1) An instance of `~.httputil.HTTPServerConnectionDelegate`: .. code-block:: python router = RuleRouter([ Rule(PathMatches("/handler"), ConnectionDelegate()), # ... more rules ]) class ConnectionDelegate(HTTPServerConnectionDelegate): def start_request(self, server_conn, request_conn): return MessageDelegate(request_conn) 2) A callable accepting a single argument of `~.httputil.HTTPServerRequest` type: .. code-block:: python router = RuleRouter([ Rule(PathMatches("/callable"), request_callable) ]) def request_callable(request): request.write(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK") request.finish() 3) Another `Router` instance: .. code-block:: python router = RuleRouter([ Rule(PathMatches("/router.*"), CustomRouter()) ]) Of course a nested `RuleRouter` or a `~.web.Application` is allowed: .. code-block:: python router = RuleRouter([ Rule(HostMatches("example.com"), RuleRouter([ Rule(PathMatches("/app1/.*"), Application([(r"/app1/handler", Handler)])), ])) ]) server = HTTPServer(router) In the example below `RuleRouter` is used to route between applications: .. code-block:: python app1 = Application([ (r"/app1/handler", Handler1), # other handlers ... ]) app2 = Application([ (r"/app2/handler", Handler2), # other handlers ... ]) router = RuleRouter([ Rule(PathMatches("/app1.*"), app1), Rule(PathMatches("/app2.*"), app2) ]) server = HTTPServer(router) For more information on application-level routing see docs for `~.web.Application`. .. versionadded:: 4.5 N)partial)httputil)_CallableAdapter) url_escape url_unescapeutf8)app_log)basestring_type import_object re_unescape unicode_type) AnyUnionOptional AwaitableListDictPatternTupleoverloadSequenceceZdZdZdej dedeejfdZ de dejdejfdZ y ) RouterzAbstract router interface.requestkwargsreturnc t)aMust be implemented to return an appropriate instance of `~.httputil.HTTPMessageDelegate` that can serve the request. Routing implementations may pass additional kwargs to extend the routing logic. :arg httputil.HTTPServerRequest request: current HTTP request. :arg kwargs: additional keyword arguments passed by routing implementation. :returns: an instance of `~.httputil.HTTPMessageDelegate` that will be used to process the request. NotImplementedError)selfrrs U/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/tornado/routing.py find_handlerzRouter.find_handlers "## server_conn request_connct|||SN)_RoutingDelegate)r r$r%s r! start_requestzRouter.start_requests k<@@r#N) __name__ __module__ __qualname____doc__rHTTPServerRequestrrHTTPMessageDelegater"objectHTTPConnectionr)r#r!rrs`$ $11 $=@ $ (.. / $A!A191H1HA  % %Ar#rc*eZdZdZdededeefdZy)ReversibleRouterzxAbstract router interface for routers that can handle named routes and support reversing them to original urls. nameargsrct)aReturns url string for a given route name and arguments or ``None`` if no match is found. :arg str name: route name. :arg args: url parameters. :returns: parametrized url string for a given route name (or ``None``). r)r r5r6s r! reverse_urlzReversibleRouter.reverse_urls "##r#N)r*r+r,r-strrrr8r2r#r!r4r4s%$$C$HSM$r#r4ceZdZdededej ddfdZdeejejfdejde e dfd Zd ede e dfd Zdd Zdd Zy)r(routerr$r%rNc<||_||_d|_||_yr')r$r%delegater;)r r;r$r%s r!__init__z_RoutingDelegate.__init__s"'(  r# start_lineheadersct|tjsJtj|j|j ||}|j j||_|jEtjd|j|jt|j|_|jj||S)N) connectionserver_connectionr?r@z$Delegate for %s %s request not found) isinstancerRequestStartLiner.r%r$r;r"r=r debugmethodpath_DefaultMessageDelegateheaders_received)r r?r@rs r!rJz!_RoutingDelegate.headers_receiveds *h&?&?@@@,,(("..!   009 == MM6!!  4D4E4EFDM}}--j'BBr#chunkcT|jJ|jj|Sr')r= data_received)r rKs r!rMz_RoutingDelegate.data_receiveds'}}(((}}**511r#cT|jJ|jjyr')r=finishr s r!rOz_RoutingDelegate.finishs"}}((( r#cR|j|jjyyr')r=on_connection_closerPs r!rRz$_RoutingDelegate.on_connection_closes! == $ MM - - / %r#rN)r*r+r,rr0rr1r>rrEResponseStartLine HTTPHeadersrrrJbytesrMrOrRr2r#r!r(r(s+1AIAXAX C(33X5O5OOPC%%C )D/ " C0252Xio-F20r#r(c8eZdZdejddfdZddZy)rIrBrNc||_yr')rB)r rBs r!r>z _DefaultMessageDelegate.__init__s $r#c|jjtjdddtj|jj y)NzHTTP/1.1iz Not Found)rB write_headersrrTrUrOrPs r!rOz_DefaultMessageDelegate.finish"sD %%  & &z3 D  "   r#rS)r*r+r,rr1r>rOr2r#r!rIrIs %8#:#:%t%!r#rIRuleMatcherc eZdZdZddeeddfdZdeddfdZddZde jd e dee jfd Z d e de jd e dee jfd Zy) RuleRouterz!Rule-based router implementation.Nrulesrc:g|_|r|j|yy)aIConstructs a router from an ordered list of rules:: RuleRouter([ Rule(PathMatches("/handler"), Target), # ... more rules ]) You can also omit explicit `Rule` constructor and use tuples of arguments:: RuleRouter([ (PathMatches("/handler"), Target), ]) `PathMatches` is a default matcher, so the example above can be simplified:: RuleRouter([ ("/handler", Target), ]) In the examples above, ``Target`` can be a nested `Router` instance, an instance of `~.httputil.HTTPServerConnectionDelegate` or an old-style callable, accepting a request argument. :arg rules: a list of `Rule` instances or tuples of `Rule` constructor arguments. N)r_ add_rules)r r_s r!r>zRuleRouter.__init__:s6  NN5 ! r#c|D]}t|ttfrFt|dvsJt|dtrt t |dg|dd}nt |}|jj|j|y)zAppends new rules to the router. :arg rules: a list of Rule instances (or tuples of arguments, which are passed to Rule constructor). )rN) rDtuplelistlenr r[ PathMatchesr_append process_rule)r r_rules r!razRuleRouter.add_rulesYs  7D$ .4yI---d1g7 DG 4@tABx@D;D JJ  d//5 6 7r#c|S)zOverride this method for additional preprocessing of each rule. :arg Rule rule: a rule to be processed. :returns: the same or modified Rule instance. r2)r rms r!rlzRuleRouter.process_ruleis  r#rrc |jD]_}|jj|}|!|jr|j|d<|j|j |fi|}|]|cSy)N target_kwargs)r_matchermatchrpget_target_delegatetarget)r rrrm target_paramsr=s r!r"zRuleRouter.find_handlerqsJJ $D LL..w7M(%%595G5GM/23433KK,9'#O $r#rtruc Xt|tr|j|fi|St|tjr4|j J|j |j|j St|r.|j Jtt|fi||j Sy)aReturns an instance of `~.httputil.HTTPMessageDelegate` for a Rule's target. This method is called by `~.find_handler` and can be extended to provide additional target types. :arg target: a Rule's target. :arg httputil.HTTPServerRequest request: current request. :arg target_params: additional parameters that can be useful for `~.httputil.HTTPMessageDelegate` creation. N) rDrr"rHTTPServerConnectionDelegaterBr)rCcallablerr)r rtrrus r!rszRuleRouter.get_target_delegates ff %&6&&w@-@ @  E E F%%1 11''(A(A7CUCUV V f %%1 11#0-0'2D2D r#r'rmr[rr[)r*r+r,r-r _RuleListr>rarlrr.rr/r"rsr2r#r!r^r^7s+"hy1"T">7y7T7 11=@ (.. /$$,$>$>QT (.. /r#r^cZeZdZdZd deeddffd Zd fd Zdede deefd Z xZ S) ReversibleRuleRouteraA rule-based router that implements ``reverse_url`` method. Each rule added to this router may have a ``name`` attribute that can be used to reconstruct an original uri. The actual reconstruction takes place in a rule's matcher (see `Matcher.reverse`). Nr_rc2i|_t| |yr') named_rulessuperr>)r r_ __class__s r!r>zReversibleRuleRouter.__init__s r#ct||}|jrQ|j|jvr t j d|j||j|j<|S)Nz4Multiple handlers named %s; replacing previous value)rrlr5r~r warning)r rmrs r!rlz!ReversibleRuleRouter.process_rules[w#D) 99yyD,,,JDII+/D  TYY ' r#r5r6c ||jvr&|j|jj|S|jD]@}t |j t s|j j|g|}|>|cSyr')r~rqreverser_rDrtr4r8)r r5r6rm reversed_urls r!r8z ReversibleRuleRouter.reverse_urls 4## #94##D)11994@ @JJ (D$++'786t{{66tCdC +''  ( r#r'ry) r*r+r,r-rrzr>rlr9rr8 __classcell__rs@r!r|r|sC hy1 T    C HSM r#r|c jeZdZdZ d dddedeeeefdeeddf d Zd edeefd Z defd Z y)r[zA routing rule.Nrqr\rtrpr5rczt|tr t|}||_||_|r|ni|_||_y)adConstructs a Rule instance. :arg Matcher matcher: a `Matcher` instance used for determining whether the rule should be considered a match for a specific request. :arg target: a Rule's target (typically a ``RequestHandler`` or `~.httputil.HTTPServerConnectionDelegate` subclass or even a nested `Router`, depending on routing implementation). :arg dict target_kwargs: a dict of parameters that can be useful at the moment of target instantiation (for example, ``status_code`` for a ``RequestHandler`` subclass). They end up in ``target_params['target_kwargs']`` of `RuleRouter.get_target_delegate` method. :arg str name: the name of the rule that can be used to find it in `ReversibleRouter.reverse_url` implementation. N)rDr9r rqrtrpr5)r rqrtrpr5s r!r>z Rule.__init__s;. fc "#6*F  .;] r#r6c4|jj|Sr')rqrr r6s r!rz Rule.reverses#t||##T**r#cdj|jj|j|j|j |j SNz${}({!r}, {}, kwargs={!r}, name={!r}))formatrr*rqrtrpr5rPs r!__repr__z Rule.__repr__s@5<< NN # # LL KK    II   r#NN) r*r+r,r-rrrr9r>rrr2r#r!r[r[sw 37"  S#X/  sm   B+S+Xc]+ # r#cZeZdZdZdej deeee ffdZ de deefdZ y)r\z*Represents a matcher for request features.rrct)a1Matches current instance against the request. :arg httputil.HTTPServerRequest request: current HTTP request :returns: a dict of parameters to be passed to the target handler (for example, ``handler_kwargs``, ``path_args``, ``path_kwargs`` can be passed for proper `~.web.RequestHandler` instantiation). An empty dict is a valid (and common) return value to indicate a match when the argument-passing features are not used. ``None`` must be returned to indicate that there is no match.rr rs r!rrz Matcher.matchs "##r#r6cy)zEReconstructs full url from matcher instance and additional arguments.Nr2rs r!rzMatcher.reversesr#N) r*r+r,r-rr.rrr9rrrrr2r#r!r\r\sB4 $X77 $HT#s(^zHostMatches.__init__s= lO 4((-# " < 8D  ,D r#rcR|jj|jriSyr')rrr host_namers r!rrzHostMatches.matchs$    " "7#4#4 5Ir#)r*r+r,r-rr9rr>rr.rrrrrr2r#r!rrsGJ-U3<%8-T-X77HT#s(^zDefaultHostMatches.__init__'s&(r#rcd|jvr1|jj|jjriSy)Nz X-Real-Ip)r@rrrr default_hostrs r!rrzDefaultHostMatches.match+s6 goo -  &&t'7'7'D'DE r#) r*r+r,r-rrr>rr.rrr9rrr2r#r!rr"sG)C)w)4)X77HT#s(^zPathMatches.__init__6s lO 4((-# L1DJ%DJ4::(()a1B1B-CC  #zz11 2 C )-(9(9(;% D%r#rc|jj|j}|y|jjsiSg}i}|jjrD|j j Dcic]\}}t|t|}}}n&|jDcgc] }t|}}t||Scc}}wcc}w)N) path_args path_kwargs) rrrrHrr groupdictitemsr9_unquote_or_nonedict)r rrrrrkvss r!rrzPathMatches.matchEs   . =zz  I  :: :?//:K:Q:Q:S06AA(++K7|jd}|d k\s t||dzd}|jd |zE t|}|j|cd j||jjfS#t$rYywxYw#t$rYywxYw) zReturns a tuple (reverse string, group count) for a url. For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method would return ('/%s/%s/', 2). ^rfNr(r)rz%s) rr startswithrrcountsplitindexr rrkjoin)r rpiecesfragment paren_locunescaped_fragments r!rzPathMatches._find_groupsks6 **$$   c "abkG   C crlG ::   c 2 2 c* 2Hh$NN3/ >,-8)a-/9R-S* MM$);";<()4X)>&  01% 2(wwv 1 111&, ,,"(((s$D  D DD D'&D')r*r+r,r-rr9rr>rr.rrrrrrrintrr2r#r!rjrj3sJ rrt handler_classr)r rrrr5rqrs r!r>zURLSpec.__init__s@0g& '648]] ![[ r#cdj|jj|jj|j |j |jSr)rrr*rrrrr5rPs r!rzURLSpec.__repr__sF5<< NN # # JJ      KK II   r#r) r*r+r,r-rr9rrrrr>rrrs@r!rrsk,0" sG|$c3h(  sm   > # r#rrrcyr'r2rs r!rrr#cyr'r2rs r!rrrr#c&||St|ddS)zNone-safe wrapper around url_unescape to handle unmatched optional groups correctly. Note that args are passed as bytes so the handler can decide what encoding to use. NF)encodingr)rrs r!rrs y Du 55r#)rNrN)1r-r functoolsrtornadortornado.httpserverrtornado.escaperrr tornado.logr tornado.utilr r r r typingrrrrrrrrrrrwrr4r/r(rIr9rzr^r|r[r\rrrrjrrVrr2r#r!rsaF /99RR   AX 2 2A. $v $ +0x33+0\ !h:: !   S  eCN#S() eCN#S$sCx.89 eCN#S$sCx.#=>  @  eeP"+Z"J. . b('$"^2'^2B. d. b           6 6(5/ 6r#