Skip to content

Getting the templated path that led to an handler #3616

@xrmx

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions