docs: def template2regex(template, ranges=None):
      """Convert a URL template to a regular expression.
  
      Converts a template, such as /{name}/ to a regular expression, e.g.
      /(?P<name>[^/]+)/ and a list of the named parameters found in the template
      (e.g. ['name']). Ranges are given after a colon in a template name to
      indicate a restriction on the characters that can appear there. For
      example, in the template:
  
          "/user/{id:alpha}"
  
      The `id` must contain only characters from a-zA-Z. Other characters there
      will cause the pattern not to match.
  
      The ranges parameter is an optional dictionary that maps range names to
      regular expressions. New range names can be added, or old range names can
      be redefined using this parameter.
  
      Example:
  
      >>> import rhino.mapper
      >>> rhino.mapper.template2regex("{fred}")
      ('^(?P<fred>[^/]+)$', ['fred'])
  
      """
      if len(template) and -1 < template.find('|') < len(template) - 1:
          raise InvalidTemplateError("'|' may only appear at the end, found at position %d in %s" % (template.find('|'), template))
      if ranges is None:
          ranges = DEFAULT_RANGES
      anchor = True
      state = S_PATH
      if len(template) and template[-1] == '|':
          anchor = False
      params = []
  
      bracketdepth = 0
      result = ['^']
      name = ""
      pattern = "[^/]+"
      rangename = None
      for c in template_splitter.split(template):
          if state == S_PATH:
              if len(c) > 1:
                  result.append(re.escape(c))
              elif c == '[':
                  result.append("(")
                  bracketdepth += 1
              elif c == ']':
                  bracketdepth -= 1
                  if bracketdepth < 0:
                      raise InvalidTemplateError("Mismatched brackets in %s" % template)
                  result.append(")?")
              elif c == '{':
                  name = ""
                  state = S_TEMPLATE
              elif c == '}':
                  raise InvalidTemplateError("Mismatched braces in %s" % template)
              elif c == '|':
                  pass
              else:
                  result.append(re.escape(c))
          else:
              if c == '}':
                  if rangename and rangename in ranges:
                      result.append("(?P<%s>%s)" % (name, ranges[rangename]))
                  else:
                      result.append("(?P<%s>%s)" % (name, pattern))
                  params.append(name)
                  state = S_PATH
                  rangename = None
              else:
                  name = c
                  if name.find(":") > -1:
                      name, rangename = name.split(":")
      if bracketdepth != 0:
          raise InvalidTemplateError("Mismatched brackets in %s" % template)
      if state == S_TEMPLATE:
          raise InvalidTemplateError("Mismatched braces in %s" % template)
      if anchor:
          result.append('$')
      return "".join(result), params