docs: def get_module_routes(module_name, custom_routes=None, exclusions=None,
                        arg_pattern=r'(?P<{}>[a-zA-Z0-9_\-]+)'):
      """Create and return routes for module_name
  
      Routes are (url, RequestHandler) tuples
  
      :returns: list of routes for ``module_name`` with respect to ``exclusions``
          and ``custom_routes``. Returned routes are with URLs formatted such
          that they are forward-slash-separated by module/class level
          and end with the lowercase name of the RequestHandler (it will also
          remove 'handler' from the end of the name of the handler).
          For example, a requesthandler with the name
          ``helloworld.api.HelloWorldHandler`` would be assigned the url
          ``/api/helloworld``.
          Additionally, if a method has extra arguments aside from ``self`` in
          its signature, routes with URL patterns will be generated to
          match ``r"(?P<{}>[a-zA-Z0-9_\-]+)".format(argname)`` for each
          argument. The aforementioned regex will match ONLY values
          with alphanumeric, hyphen and underscore characters. You can provide
          your own pattern by setting a ``arg_pattern`` param.
      :rtype: [(url, RequestHandler), ... ]
      :type  module_name: str
      :param module_name: Name of the module to get routes for
      :type  custom_routes: [(str, RequestHandler), ... ]
      :param custom_routes: List of routes that have custom URLs and therefore
          should be automagically generated
      :type  exclusions: [str, str, ...]
      :param exclusions: List of RequestHandler names that routes should not be
          generated for
      :type  arg_pattern: str
      :param arg_pattern: Default pattern for extra arguments of any method
      """
      def has_method(module, cls_name, method_name):
          return all([
              method_name in vars(getattr(module, cls_name)),
              is_method(reduce(getattr, [module, cls_name, method_name]))
          ])
  
      def yield_args(module, cls_name, method_name):
          """Get signature of ``module.cls_name.method_name``
  
          Confession: This function doesn't actually ``yield`` the arguments,
              just returns a list. Trust me, it's better that way.
  
          :returns: List of arg names from method_name except ``self``
          :rtype: list
          """
          wrapped_method = reduce(getattr, [module, cls_name, method_name])
          method = extract_method(wrapped_method)
  
          # If using tornado_json.gen.coroutine, original args are annotated...
          argspec_args = getattr(method, "__argspec_args",
                                 # otherwise just grab them from the method
                                 inspect.getargspec(method).args)
  
          return [a for a in argspec_args if a not in ["self"]]
  
      def generate_auto_route(module, module_name, cls_name, method_name, url_name):
          """Generate URL for auto_route
  
          :rtype: str
          :returns: Constructed URL based on given arguments
          """
          def get_handler_name():
              """Get handler identifier for URL
  
              For the special case where ``url_name`` is
              ``__self__``, the handler is named a lowercase
              value of its own name with 'handler' removed
              from the ending if give; otherwise, we
              simply use the provided ``url_name``
              """
              if url_name == "__self__":
                  if cls_name.lower().endswith('handler'):
                      return cls_name.lower().replace('handler', '', 1)
                  return cls_name.lower()
              else:
                  return url_name
  
          def get_arg_route():
              """Get remainder of URL determined by method argspec
  
              :returns: Remainder of URL which matches `\w+` regex
                  with groups named by the method's argument spec.
                  If there are no arguments given, returns ``""``.
              :rtype: str
              """
              if yield_args(module, cls_name, method_name):
                  return "/{}/?$".format("/".join(
                      [arg_pattern.format(argname) for argname
                       in yield_args(module, cls_name, method_name)]
                  ))
              return r"/?"
  
          return "/{}/{}{}".format(
              "/".join(module_name.split(".")[1:]),
              get_handler_name(),
              get_arg_route()
          )
  
      if not custom_routes:
          custom_routes = []
      if not exclusions:
          exclusions = []
  
      # Import module so we can get its request handlers
      module = importlib.import_module(module_name)
  
      # Generate list of RequestHandler names in custom_routes
      custom_routes_s = [c.__name__ for r, c in custom_routes]
  
      rhs = {cls_name: cls for (cls_name, cls) in
             inspect.getmembers(module, inspect.isclass)}
  
      # You better believe this is a list comprehension
      auto_routes = list(chain(*[
          list(set(chain(*[
              # Generate a route for each "name" specified in the
              #   __url_names__ attribute of the handler
              [
                  # URL, requesthandler tuple
                  (
                      generate_auto_route(
                          module, module_name, cls_name, method_name, url_name
                      ),
                      getattr(module, cls_name)
                  ) for url_name in getattr(module, cls_name).__url_names__
                  # Add routes for each custom URL specified in the
                  #   __urls__ attribute of the handler
              ] + [
                  (
                      url,
                      getattr(module, cls_name)
                  ) for url in getattr(module, cls_name).__urls__
              ]
              # We create a route for each HTTP method in the handler
              #   so that we catch all possible routes if different
              #   HTTP methods have different argspecs and are expecting
              #   to catch different routes. Any duplicate routes
              #   are removed from the set() comparison.
              for method_name in HTTP_METHODS if has_method(
                  module, cls_name, method_name)
          ])))
          # foreach classname, pyclbr.Class in rhs
          for cls_name, cls in rhs.items()
          # Only add the pair to auto_routes if:
          #    * the superclass is in the list of supers we want
          #    * the requesthandler isn't already paired in custom_routes
          #    * the requesthandler isn't manually excluded
          if is_handler_subclass(cls)
          and cls_name not in (custom_routes_s + exclusions)
      ]))
  
      routes = auto_routes + custom_routes
      return routes