Skip to content

sessionmgr

The loader module manages the workflow of loading a new pyRevit session.

Its main purpose is to orchestrate the process of finding pyRevit extensions, creating dll assemblies for them, and creating a user interface in the host application.

Everything starts from sessionmgr.load_session() function...

The only public function is load_session() that loads a new session. Everything else is private.

Attributes

mlogger = logger.get_logger(__name__) module-attribute

pyrevit_extcmdtype_cache = [] module-attribute

Classes

PyRevitExternalCommandType(extcmd_type, extcmd_availtype)

Bases: object

PyRevit external command type.

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def __init__(self, extcmd_type, extcmd_availtype):
    self._extcmd_type = extcmd_type
    self._extcmd = extcmd_type()
    if extcmd_availtype:
        self._extcmd_availtype = extcmd_availtype
        self._extcmd_avail = extcmd_availtype()
    else:
        self._extcmd_availtype = None
        self._extcmd_avail = None

Attributes

extcmd_type property
typename property
extcmd_availtype property
avail_typename property
script property
config_script property
search_paths property
arguments property
engine_cfgs property
helpsource property
tooltip property
name property
bundle property
extension property
unique_id property
control_id property

Methods:

is_available(category_set, zerodoc=False)
Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def is_available(self, category_set, zerodoc=False):
    if self._extcmd_availtype:
        return self._extcmd_avail.IsCommandAvailable(HOST_APP.uiapp, category_set)
    elif not zerodoc:
        return True

    return False

Functions:

perform_preload()

Run pre-load session setup before the C# loader builds the UI.

Invoked by the C# session orchestrator as the first step of a load. Sets up the session environment, output window, and pre-load services.

Note

Relies on the Preload entry script having cleared the attachment cache before importing this module, so every attachment lookup in the load reads the current attachment once and then reuses it.

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def perform_preload():
    """Run pre-load session setup before the C# loader builds the UI.

    Invoked by the C# session orchestrator as the first step of a load. Sets up
    the session environment, output window, and pre-load services.

    Note:
        Relies on the Preload entry script having cleared the attachment cache
        before importing this module, so every attachment lookup in the load
        reads the current attachment once and then reuses it.
    """
    with _perfblock("pyrevit.loader.sessionmgr:setup runtime vars"):
        sessioninfo.setup_runtime_vars()

    if EXEC_PARAMS.first_load:
        with _perfblock("pyrevit.loader.sessionmgr:output setup"):
            _setup_output()

    _perform_onsessionloadstart_ops()

perform_postload()

Finalize the session after the C# loader has built the UI.

Invoked by the C# session orchestrator as the last step of a load. Runs post-load services, registers the assemblies the C# loader produced, reports load time, and tears down the startup output stream.

Returns:

Type Description
str

session uuid

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def perform_postload():
    """Finalize the session after the C# loader has built the UI.

    Invoked by the C# session orchestrator as the last step of a load. Runs
    post-load services, registers the assemblies the C# loader produced, reports
    load time, and tears down the startup output stream.

    Returns:
        (str): session uuid
    """
    _perform_onsessionloadcomplete_ops()

    # so find_pyrevitcmd can locate commands the C# loader compiled
    _register_loaded_pyrevit_assemblies()

    _perfflush()

    load_time = elapsed_load_seconds()
    if load_time is not None:
        success_emoji = ":OK_hand:" if load_time < 3.00 else ":thumbs_up:"
        mlogger.info("Load time: %.2f seconds %s", load_time, success_emoji)

    # if everything went well, self destruct
    try:
        timeout = user_config.startuplog_timeout
        session_output = runtime_types.ScriptOutput.GetDefault()
        if (
            timeout > 0
            and not logger.loggers_have_errors()
            and session_output.IsWindowReady
        ):
            session_output.self_destruct(timeout)
    except Exception as imp_err:
        mlogger.error("Error setting up self_destruct on output window | %s", imp_err)

    _perform_postload_autoupdate()

    _cleanup_output()
    return sessioninfo.get_session_uuid()

load_session()

Handles loading/reloading of the pyRevit addin and extensions.

Delegates to the C# session orchestrator, which drives the full load: perform_preload(), the C# assembly/UI build, then perform_postload(). This is the entry used for reloads; the initial Revit startup invokes the C# orchestrator directly.

Examples:

from pyrevit.loader.sessionmgr import load_session
load_session()     # start loading a new pyRevit session

Returns:

Type Description
str

session uuid

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def load_session():
    """Handles loading/reloading of the pyRevit addin and extensions.

    Delegates to the C# session orchestrator, which drives the full load:
    perform_preload(), the C# assembly/UI build, then perform_postload(). This
    is the entry used for reloads; the initial Revit startup invokes the C#
    orchestrator directly.

    Examples:
        ```python
        from pyrevit.loader.sessionmgr import load_session
        load_session()     # start loading a new pyRevit session
        ```

    Returns:
        (str): session uuid
    """
    return _invoke_csharp_loadsession()

reload_pyrevit()

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def reload_pyrevit():
    mlogger.info("Reloading....")
    return load_session()

find_all_commands(category_set=None, cache=True)

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def find_all_commands(category_set=None, cache=True):
    global pyrevit_extcmdtype_cache  # pylint: disable=W0603
    if cache and pyrevit_extcmdtype_cache:  # pylint: disable=E0601
        pyrevit_extcmds = pyrevit_extcmdtype_cache
    else:
        pyrevit_extcmds = []
        for loaded_assm_name in sessioninfo.get_loaded_pyrevit_assemblies():
            loaded_assm = assmutils.find_loaded_asm(loaded_assm_name)
            if loaded_assm:
                all_exported_types = loaded_assm[0].GetTypes()

                for pyrvt_type in all_exported_types:
                    tname = pyrvt_type.FullName
                    availtname = pyrvt_type.Name + runtime.CMD_AVAIL_NAME_POSTFIX
                    pyrvt_availtype = None

                    if (
                        not tname.endswith(runtime.CMD_AVAIL_NAME_POSTFIX)
                        and runtime.RUNTIME_NAMESPACE not in tname
                        and pyrvt_type.IsSubclassOf(runtime.CMD_EXECUTOR_TYPE)
                    ):
                        for exported_type in all_exported_types:
                            if exported_type.Name == availtname:
                                pyrvt_availtype = exported_type

                        pyrevit_extcmds.append(
                            PyRevitExternalCommandType(pyrvt_type, pyrvt_availtype)
                        )
        if cache:
            pyrevit_extcmdtype_cache = pyrevit_extcmds

    # now check commands in current context if requested
    if category_set:
        return [
            x
            for x in pyrevit_extcmds
            if x.is_available(category_set=category_set, zerodoc=HOST_APP.uidoc is None)
        ]
    else:
        return pyrevit_extcmds

find_all_available_commands(use_current_context=True, cache=True)

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def find_all_available_commands(use_current_context=True, cache=True):
    if use_current_context:
        cset = revit.get_selection_category_set()
    else:
        cset = None

    return find_all_commands(category_set=cset, cache=cache)

find_pyrevitcmd(pyrevitcmd_unique_id)

Find a pyRevit command.

Searches the pyRevit-generated assemblies under current session for the command with the matching unique name (class name) and returns the command type. Notice that this returned value is a 'type' and should be instantiated before use.

Examples:

cmd = find_pyrevitcmd('pyrevitcore_pyrevit_pyrevit_tools_reload')
command_instance = cmd()
command_instance.Execute() # Provide commandData, message, elements

Parameters:

Name Type Description Default
pyrevitcmd_unique_id str

Unique name for the command

required

Returns:

Type Description
type

Type for the command with matching unique name

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def find_pyrevitcmd(pyrevitcmd_unique_id):
    """Find a pyRevit command.

    Searches the pyRevit-generated assemblies under current session for
    the command with the matching unique name (class name) and returns the
    command type. Notice that this returned value is a 'type' and should be
    instantiated before use.

    Examples:
        ```python
        cmd = find_pyrevitcmd('pyrevitcore_pyrevit_pyrevit_tools_reload')
        command_instance = cmd()
        command_instance.Execute() # Provide commandData, message, elements
        ```

    Args:
        pyrevitcmd_unique_id (str): Unique name for the command

    Returns:
        (type):Type for the command with matching unique name
    """

    def _normalize_lookup_id(value):
        if not value:
            return ""

        normalized = []
        for char in str(value).lower():
            normalized.append(char if char.isalnum() else "_")

        if normalized and normalized[0].isdigit():
            normalized.insert(0, "_")

        return "".join(normalized)

    lookup_id = _normalize_lookup_id(pyrevitcmd_unique_id)

    # go through assmebles loaded under current pyRevit session
    # and try to find the command
    mlogger.debug("Searching for pyrevit command: %s", pyrevitcmd_unique_id)
    for loaded_assm_name in sessioninfo.get_loaded_pyrevit_assemblies():
        mlogger.debug("Expecting assm: %s", loaded_assm_name)
        loaded_assm = assmutils.find_loaded_asm(loaded_assm_name)
        if loaded_assm:
            mlogger.debug("Found assm: %s", loaded_assm_name)
            for pyrvt_type in loaded_assm[0].GetTypes():
                mlogger.debug("Found Type: %s", pyrvt_type)
                if (
                    pyrvt_type.FullName == pyrevitcmd_unique_id
                    or pyrvt_type.Name == pyrevitcmd_unique_id
                    or _normalize_lookup_id(pyrvt_type.FullName) == lookup_id
                    or _normalize_lookup_id(pyrvt_type.Name) == lookup_id
                ):
                    mlogger.debug("Found pyRevit command in %s", loaded_assm_name)
                    return pyrvt_type
            mlogger.debug("Could not find pyRevit command.")
        else:
            mlogger.debug("Can not find assm: %s", loaded_assm_name)

    return None

create_tmp_commanddata()

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def create_tmp_commanddata():
    tmp_cmd_data = framework.FormatterServices.GetUninitializedObject(
        UI.ExternalCommandData
    )
    tmp_cmd_data.Application = HOST_APP.uiapp
    # tmp_cmd_data.IsReadOnly = False
    # tmp_cmd_data.View = None
    # tmp_cmd_data.JournalData = None
    return tmp_cmd_data

execute_command_cls(extcmd_type, arguments=None, config_mode=False, exec_from_ui=True)

Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def execute_command_cls(
    extcmd_type, arguments=None, config_mode=False, exec_from_ui=True
):

    command_instance = extcmd_type()
    # pass the arguments to the instance
    if arguments:
        command_instance.ScriptRuntimeConfigs.Arguments = framework.to_clr_list(
            str, arguments
        )
    # this is a manual execution from python code and not by user
    command_instance.ExecConfigs.MimicExecFromUI = exec_from_ui
    # force using the config script
    command_instance.ExecConfigs.UseConfigScript = config_mode

    # Execute(
    # ExternalCommandData commandData,
    # string message,
    # ElementSet elements
    # )
    re = command_instance.Execute(create_tmp_commanddata(), "", DB.ElementSet())
    command_instance = None
    return re

execute_command(pyrevitcmd_unique_id)

Executes a pyRevit command.

Parameters:

Name Type Description Default
pyrevitcmd_unique_id str

Unique/Class Name of the pyRevit command

required
Source code in pyrevitlib/pyrevit/loader/sessionmgr.py
def execute_command(pyrevitcmd_unique_id):
    """Executes a pyRevit command.

    Args:
        pyrevitcmd_unique_id (str): Unique/Class Name of the pyRevit command
    """
    cmd_class = find_pyrevitcmd(pyrevitcmd_unique_id)

    if not cmd_class:
        mlogger.error("Can not find command with unique name: %s", pyrevitcmd_unique_id)
        return None
    else:
        execute_command_cls(cmd_class)