Skip to content

routes

HTTP API framework similar to flask.

Attributes

OK = 200 module-attribute

ACCEPTED = 202 module-attribute

INTERNAL_SERVER_ERROR = 500 module-attribute

NO_CONTENT = 204 module-attribute

Classes

Request(path='/', method='GET', data=None, params=None, query_params=None)

Bases: object

Request wrapper object.

Source code in pyrevitlib/pyrevit/routes/server/base.py
def __init__(
    self, path="/", method="GET", data=None, params=None, query_params=None
):
    self.path = path
    self.method = method
    self.data = data
    self._headers = {}
    self._params = params or []
    self._query_params = query_params or {}

Attributes

path = path instance-attribute
method = method instance-attribute
data = data instance-attribute
headers property

Request headers dict.

params property

Request parameters.

query_params property

Request query string parameters as a dictionary.

callback_url property

Request callback url, if provided in payload.

Methods:

add_header(key, value)

Add new header key:value.

Source code in pyrevitlib/pyrevit/routes/server/base.py
def add_header(self, key, value):
    """Add new header key:value."""
    self._headers[key] = value

Response(status=200, data=None, headers=None)

Bases: object

Response wrapper object.

Source code in pyrevitlib/pyrevit/routes/server/base.py
def __init__(self, status=200, data=None, headers=None):
    self.status = status
    self.data = data
    self._headers = headers or {}

Attributes

status = status instance-attribute
data = data instance-attribute
headers property

Response headers dict.

Methods:

add_header(key, value)

Add new header key:value.

Source code in pyrevitlib/pyrevit/routes/server/base.py
def add_header(self, key, value):
    """Add new header key:value."""
    self._headers[key] = value

API(name)

Bases: object

API root object.

Parameters:

Name Type Description Default
name str

URL-safe unique root name of the API

required

Examples:

from pyrevit import routes
api = routes.API("pyrevit-core")
@api.route('/sessions/', methods=['POST'])
def reload_pyrevit(uiapp):
    new_session_id = sessionmgr.reload_pyrevit()
    return {"session_id": new_session_id}
Source code in pyrevitlib/pyrevit/routes/__init__.py
def __init__(self, name):
    self.name = name

Attributes

name = name instance-attribute

Methods:

route(pattern, methods=['GET'])

Define a new route on this API.

Source code in pyrevitlib/pyrevit/routes/__init__.py
def route(self, pattern, methods=["GET"]):
    """Define a new route on this API."""

    def __func_wrapper__(f):
        for method in methods:
            add_route(
                api_name=self.name, pattern=pattern, method=method, handler_func=f
            )
        return f

    return __func_wrapper__

Functions:

init()

Initialize routes. Reset all registered routes and shutdown servers.

Source code in pyrevitlib/pyrevit/routes/server/__init__.py
def init():
    """Initialize routes. Reset all registered routes and shutdown servers."""
    # clear all routes
    router.reset_routes()
    # stop existing server
    deactivate_server()

activate_server()

Activate routes server for this host instance.

The server module is imported on first activation, so that sessions with the routes server disabled never import the HTTP stack.

Important

That module creates a Revit external event as it is imported, and the host prohibits creating one off its main thread. Activation must run on the main thread.

Activation is idempotent within a session: an already active server serves the routes that are registered now, so handing it back is correct. It is not how a server survives a reload - init() still stops the old one on every session load.

Idempotency is only claimed of a server that is still accepting. A server whose accept loop has exited is torn down and replaced, because handing that one back is the same dead registration that #3473 was reported for, reached from the other end.

Returns:

Type Description
RoutesServer

the active server, or None if it could not be started.

Source code in pyrevitlib/pyrevit/routes/server/__init__.py
def activate_server():
    """Activate routes server for this host instance.

    The server module is imported on first activation, so that sessions with
    the routes server disabled never import the HTTP stack.

    Important:
        That module creates a Revit external event as it is imported, and the
        host prohibits creating one off its main thread. Activation must run
        on the main thread.

        Activation is idempotent within a session: an already active server
        serves the routes that are registered now, so handing it back is
        correct. It is not how a server survives a reload - init() still stops
        the old one on every session load.

        Idempotency is only claimed of a server that is still accepting. A
        server whose accept loop has exited is torn down and replaced, because
        handing that one back is the same dead registration that #3473 was
        reported for, reached from the other end.

    Returns:
        (RoutesServer): the active server, or None if it could not be started.
    """
    routes_server = envvars.get_pyrevit_env_var(envvars.ROUTES_SERVER)
    if routes_server:
        if routes_server.is_running:
            mlogger.debug("Routes server already active | %s", routes_server)
            return routes_server
        mlogger.debug("Routes server registered but not accepting | %s", routes_server)
        deactivate_server()
    try:
        from pyrevit.routes.server import server

        rsinfo = serverinfo.register()
        routes_server = server.RoutesServer(
            host=rsinfo.server_host, port=rsinfo.server_port
        )
        envvars.set_pyrevit_env_var(envvars.ROUTES_SERVER, routes_server)
        mlogger.debug("Routes server activated | %s", routes_server)
        return routes_server
    except Exception as rs_ex:
        mlogger.error("Error starting Routes server | %s", str(rs_ex), exc_info=True)
        try:
            serverinfo.unregister()
        except Exception as unreg_ex:
            mlogger.error(
                "Error unregistering Routes server | %s", str(unreg_ex), exc_info=True
            )
        return None

deactivate_server()

Deactivate the active routes server for this host instance.

Deregisters whether or not the stop succeeded (#3473). A server that is no longer listening but is still in the env var made the next activation hand that dead server back instead of binding the port, so a reload silently lost routes.

Every step is contained, including the one that clears the env var: a failure there must not skip the deregistration, which is the step that keeps the next session from being handed this server.

Source code in pyrevitlib/pyrevit/routes/server/__init__.py
def deactivate_server():
    """Deactivate the active routes server for this host instance.

    Deregisters whether or not the stop succeeded (#3473). A server that is no
    longer listening but is still in the env var made the next activation hand
    that dead server back instead of binding the port, so a reload silently
    lost routes.

    Every step is contained, including the one that clears the env var: a
    failure there must not skip the deregistration, which is the step that keeps
    the next session from being handed this server.
    """
    routes_server = envvars.get_pyrevit_env_var(envvars.ROUTES_SERVER)
    if not routes_server:
        return
    try:
        routes_server.stop()
    except Exception as rs_ex:
        mlogger.error("Error stopping Routes server | %s", str(rs_ex), exc_info=True)
    finally:
        try:
            envvars.set_pyrevit_env_var(envvars.ROUTES_SERVER, None)
        except Exception as env_ex:
            mlogger.error(
                "Error clearing Routes server env var | %s", str(env_ex), exc_info=True
            )
        try:
            serverinfo.unregister()
        except Exception as unreg_ex:
            mlogger.error(
                "Error unregistering Routes server | %s", str(unreg_ex), exc_info=True
            )
    mlogger.debug("Routes server deactivated | %s", routes_server)

get_active_server()

Get active routes server for this host instance.

Source code in pyrevitlib/pyrevit/routes/server/__init__.py
def get_active_server():
    """Get active routes server for this host instance."""
    return envvars.get_pyrevit_env_var(envvars.ROUTES_SERVER)

make_response(data, status=OK, headers=None)

Create Reponse object with.

Source code in pyrevitlib/pyrevit/routes/server/__init__.py
def make_response(data, status=OK, headers=None):
    """Create Reponse object with."""
    res = Response(status=status, data=data)
    for key, value in (headers or {}).items():
        res.add_header(key, value)
    return res

get_routes(api_name)

Get all registered routes for given API name.

Parameters:

Name Type Description Default
api_name str

unique name of the api

required
Source code in pyrevitlib/pyrevit/routes/server/__init__.py
def get_routes(api_name):
    """Get all registered routes for given API name.

    Args:
        api_name (str): unique name of the api
    """
    return router.get_routes(api_name)

add_route(api_name, pattern, method, handler_func)

Add new route for given API name.

Parameters:

Name Type Description Default
api_name str

unique name of the api

required
pattern str

route pattern

required
method str

method name

required
handler_func function

route handler function

required
Source code in pyrevitlib/pyrevit/routes/server/__init__.py
def add_route(api_name, pattern, method, handler_func):
    """Add new route for given API name.

    Args:
        api_name (str): unique name of the api
        pattern (str): route pattern
        method (str): method name
        handler_func (function): route handler function
    """
    return router.add_route(api_name, pattern, method, handler_func)

remove_route(api_name, pattern, method)

Remove previously registered route for given API name.

Parameters:

Name Type Description Default
api_name str

unique name of the api

required
pattern str

route pattern

required
method str

method name

required
Source code in pyrevitlib/pyrevit/routes/server/__init__.py
def remove_route(api_name, pattern, method):
    """Remove previously registered route for given API name.

    Args:
        api_name (str): unique name of the api
        pattern (str): route pattern
        method (str): method name
    """
    return router.remove_route(api_name, pattern, method)

active_routes_api()

Activates routes API.

Source code in pyrevitlib/pyrevit/routes/__init__.py
def active_routes_api():
    """Activates routes API."""
    from pyrevit.routes import api