Skip to content

Getting the templated path that led to an handler #3616

Description

@xrmx

For observability purpose in the tornado OpenTelemetry instrumentation we would like to get a templated path that led to an handler, e.g. with the following code:

class StoryHandler(RequestHandler):
    def get(self, story_id):
        self.write("this is story %s" % story_id)

app = Application([
    url(r"/story/([0-9]+)", StoryHandler, name="story")
    ])

We would like to being able to build something like /story/<story_id> starting from an handler instance, but let's ignore the regexp pattern to name problem and just think on how to get the rule. ATM I've drafted the following code:

from __future__ import annotations
from typing import Any
from tornado.routing import Router
from tornado.web import RequestHandler
from tornado import httputil

# Distinguishes "keep scanning sibling rules" from "matched a branch we can't
# inspect further, so stop and report no reliable result".
_NOT_FOUND = object()

def find_matched_rule(handler: RequestHandler):
  result = _find_rule(
      handler.application.default_router,
      handler.request,
      handler.__class__,
  )
  return None if result is _NOT_FOUND else result

def _find_rule(router: Any, request: httputil.HTTPServerRequest, handler_class: type[RequestHandler]):
  rules = getattr(router, "rules", None)
  if rules is None:
      # Opaque custom router; cannot inspect reliably.
      return _NOT_FOUND

  for rule in rules:
      params = rule.matcher.match(request)
      if params is None:
          continue
      target = getattr(rule, "target", None)
      if _is_handler_target(target):
          if target is handler_class:
              return rule
          # A different handler matched first, so Tornado would stop here too.
          return _NOT_FOUND
      if hasattr(target, "rules"):
          nested = _find_rule(target, request, handler_class)
          if nested is None:
              # Nested router did not resolve anything; keep scanning siblings.
              continue
          return nested
      if isinstance(target, Router):
          # Custom nested router matched, but we cannot see inside it.
          return _NOT_FOUND
      # Callable / connection delegate / other terminal target.
      return _NOT_FOUND
  return None

def _is_handler_target(target: Any) -> bool:
  return isinstance(target, type) and issubclass(target, RequestHandler)

So wondering if we're missing something or maybe we can get some help from tornado itself like a reference to the matched url in the handler. Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions