Skip to content

handler

Revit-aware event handler.

Attributes

mlogger = get_logger(__name__) module-attribute

ARGS_REQUEST = 'request' module-attribute

ARGS_UIAPP = 'uiapp' module-attribute

ARGS_UIDOC = 'uidoc' module-attribute

ARGS_DOC = 'doc' module-attribute

RESERVED_VAR_NAMES = [ARGS_REQUEST, ARGS_UIAPP] module-attribute

Classes

RequestHandler

Bases: IExternalEventHandler

Revit external event handler type.

Attributes

request property writable

Get registered request.

handler property writable

Get registered handler.

response property

Get registered response.

done property

Check if execution of handler is completed and response is set.

Functions

reset()

Reset internals for new execution.

Source code in pyrevitlib/pyrevit/routes/server/handler.py
def reset(self):
    """Reset internals for new execution."""
    with self._lock: #pylint: disable=not-context-manager
        self._response = None
        self._done = False
join()

Allow other threads to call this method and wait for completion.

Source code in pyrevitlib/pyrevit/routes/server/handler.py
def join(self):
    """Allow other threads to call this method and wait for completion."""
    # wait until handler signals completion
    while True:
        with self._lock:
            if self._done:
                return
run_handler(handler, kwargs) staticmethod

Execute the handler function and return base.Response.

Source code in pyrevitlib/pyrevit/routes/server/handler.py
@staticmethod
def run_handler(handler, kwargs):
    """Execute the handler function and return base.Response."""
    response = None
    kwargs = kwargs or {}
    if handler and callable(handler):
        try:
            # now call handler, and save response
            response = handler(**kwargs) #pylint: disable=not-callable
        except Exception as hndlr_ex:
            # grab original CLS exception
            clsx = hndlr_ex.clsException #pylint: disable=no-member
            # get exception info
            sys.exc_type, sys.exc_value, sys.exc_traceback = \
                sys.exc_info()
            # go back one frame to grab exception stack from handler
            # and grab traceback lines
            tb_report = ''.join(
                traceback.format_tb(sys.exc_traceback)[1:]
            )
            # wrap all the exception info
            response = excp.RouteHandlerException(
                message=str(hndlr_ex),
                exception_type=sys.exc_type,
                exception_traceback=tb_report,
                clsx_message=clsx.Message,
                clsx_source=clsx.Source,
                clsx_stacktrace=clsx.StackTrace,
                clsx_targetsite=clsx.TargetSite.ToString()
                )
    else:
        response = \
            excp.RouteHandlerIsNotCallableException(handler.__name__)
    return response
make_callback(callback_url, response) staticmethod

Prepare request from base.Response and submit to callback url.

Source code in pyrevitlib/pyrevit/routes/server/handler.py
@staticmethod
def make_callback(callback_url, response):
    """Prepare request from base.Response and submit to callback url."""
    # parse response object
    r = RequestHandler.parse_response(response)
    # prepare and submit request
    make_request(url=callback_url, headers=r.headers, data=r.data)
wants_api_context(handler) staticmethod

Check if handler needs host api context.

Source code in pyrevitlib/pyrevit/routes/server/handler.py
@staticmethod
def wants_api_context(handler):
    """Check if handler needs host api context."""
    return modutils.has_any_arguments(
        function_obj=handler,
        arg_name_list=[
            ARGS_UIAPP,
            ARGS_UIDOC,
            ARGS_DOC
        ])
prepare_handler_kwargs(request, handler, uiapp=None) staticmethod

Prepare call arguments for handler function.

Source code in pyrevitlib/pyrevit/routes/server/handler.py
@staticmethod
def prepare_handler_kwargs(request, handler, uiapp=None):
    """Prepare call arguments for handler function."""
    uidoc = doc = None
    if uiapp:
        uidoc = getattr(uiapp, 'ActiveUIDocument', None)
        if uidoc:
            doc = getattr(uidoc, 'Document', None)

    kwargs = {}
    kwargs[ARGS_REQUEST] = request
    # if route pattern has parameter, provide those as well
    if request.params:
        kwargs.update({x.key:x.value for x in request.params})
    # add host api context params
    kwargs[ARGS_UIAPP] = uiapp
    kwargs[ARGS_UIDOC] = uidoc
    kwargs[ARGS_DOC] = doc

    return modutils.filter_kwargs(handler, kwargs)
parse_response(response) staticmethod

Parse any given response data and return Response object.

Source code in pyrevitlib/pyrevit/routes/server/handler.py
@staticmethod
def parse_response(response):
    """Parse any given response data and return Response object."""
    status = base.OK
    headers = {}
    data = None

    # can not directly check for isinstance(x, Response)
    # this module is executed on a different Engine than the
    # script that registered the request handler function, thus
    # the Response in script engine does not match Response
    # registered when this module was loaded
    #
    # now process reponse based on obj type
    # it is an exception is has .message
    # write the exeption to output and return
    if hasattr(response, 'message'):
        status = \
            response.status if hasattr(response, 'status') \
                else base.INTERNAL_SERVER_ERROR
        headers = {'Content-Type': 'application/json'}
        data = json.dumps(
            {
                "exception": {
                    "source": response.source
                              if hasattr(response, 'source')
                              else base.DEFAULT_SOURCE,
                    "message": str(response)
                }
            }
        )

    # plain text response
    elif isinstance(response, str):
        # keey default status
        headers['Content-Type'] = 'text/html'
        data = json.dumps(response)

    # any obj that has .status and .data, OR
    # any json serializable object
    # serialize before sending results
    # in case exceptions happen in serialization,
    # there are no double status in response header
    else:
        # determine status
        status = getattr(response, 'status', base.OK)

        # determine headers
        headers.update(
            getattr(response, 'headers', {})
            )

        # determine data, or dump the response object
        data = getattr(response, 'data', response)

        # serialize data
        if data is not None:
            data = json.dumps(data)
            headers['Content-Type'] = 'application/json'

    return base.Response(status=status, data=data, headers=headers)
Execute(uiapp)

This method is called to handle the external event.

Source code in pyrevitlib/pyrevit/routes/server/handler.py
def Execute(self, uiapp):
    """This method is called to handle the external event."""
    # grab data. getters are thread-safe
    handler = self.handler
    request = self.request
    response = None

    try:
        # process necessary arguments for the handler
        kwargs = RequestHandler.prepare_handler_kwargs(
            request,
            handler,
            uiapp=uiapp
            )
        # run handler with prepared arguments, and grab the response
        response = self.run_handler(handler, kwargs)
    except Exception as exec_ex:
        # create exception response
        response = excp.RouteHandlerExecException(message=str(exec_ex))
    finally:
        # send response to callback url if requested
        if request.callback_url:
            RequestHandler.make_callback(request.callback_url, response)
        # or set the response to be picked up by http request handler
        else:
            self._set_response(response)
GetName()

String identification of the event handler.

Source code in pyrevitlib/pyrevit/routes/server/handler.py
def GetName(self):
    """String identification of the event handler."""
    return self.__class__.__name__

Functions