Skip to content

start

Main entry point for AYON command.

Bootstrapping process of AYON.

This script is responsible for setting up the environment and bootstrapping AYON. It is also responsible for updating AYON from AYON server.

Arguments that are always handled by AYON launcher

--verbose - set log level --debug - enable debug mode --skip-headers - skip printing headers --skip-bootstrap - skip bootstrap process - use only for bootstrap logic --use-staging - use staging server --use-dev - use dev server --bundle - specify bundle name to use --headless - enable headless mode - bootstrap won't show any UI

AYON launcher can be running in multiple different states. The top layer of states is 'production', 'staging' and 'dev'.

To start in dev mode use one of following options
  • by passing '--use-dev' argument
  • by setting 'AYON_USE_DEV' environment variable to '1'
  • by passing '--bundle '
  • by setting 'AYON_BUNDLE_NAME' environment variable to dev bundle name
  • by passing '--studio-bundle '
  • by setting 'AYON_STUDIO_BUNDLE_NAME' environment variable to dev bundle name
By using bundle name you can start any dev bundle, even if is not

assigned to current user.

To start in staging mode make sure none of develop options are used and then use one of following options: - by passing '--use-staging' argument - by setting 'AYON_USE_STAGING' environment variable to '1'

Staging mode must be defined explicitly cannot be determined by bundle name. In all other cases AYON launcher will start in 'production' mode.

Headless mode is not guaranteed after bootstrap process. It is possible that some addon won't handle headless mode and will try to use UIs.

After bootstrap process AYON launcher will start 'ayon_core' addon. This addon is responsible for handling all other addons and their logic.

Environment variables set during bootstrap
  • AYON_VERSION - version of AYON launcher
  • AYON_BUNDLE_NAME - name of bundle to use
  • AYON_USE_STAGING - set to '1' if staging mode is enabled
  • AYON_USE_DEV - set to '1' if dev mode is enabled
  • AYON_DEBUG - set to '1' if debug mode is enabled
  • AYON_HEADLESS_MODE - set to '1' if headless mode is enabled
  • AYON_SERVER_URL - URL of AYON server
  • AYON_API_KEY - API key for AYON server
  • AYON_SERVER_TIMEOUT - timeout for AYON server
  • AYON_SERVER_RETRIES - number of retries for AYON server
  • AYON_EXECUTABLE - path to AYON executable
  • AYON_ROOT - path to AYON root directory
  • AYON_MENU_LABEL - label for AYON integrations menu
  • AYON_LAUNCHER_STORAGE_DIR - dir where addons, dependency packages, shim etc. are stored
  • AYON_LAUNCHER_LOCAL_DIR - dir where machine specific files are stored
  • AYON_ADDONS_DIR - path to AYON addons directory
  • AYON_DEPENDENCIES_DIR - path to AYON dependencies directory

Some of the environment variables are not in this script but in 'ayon_common' module. - Function 'create_global_connection' can change 'AYON_USE_DEV' and 'AYON_USE_STAGING'. - Bootstrap will set 'AYON_LAUNCHER_STORAGE_DIR' and 'AYON_LAUNCHER_LOCAL_DIR' if are not set yet. - Distribution logic can set 'AYON_ADDONS_DIR' and 'AYON_DEPENDENCIES_DIR' if are not set yet.

StartArgScript

Source code in start.py
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
class StartArgScript:
    def __init__(self, argument, script_path):
        self.argument = argument
        self.script_path = script_path

    @property
    def is_valid(self):
        return self.script_path is not None

    @property
    def is_dir(self):
        if self.argument:
            return os.path.isdir(self.argument)
        return False

    @classmethod
    def from_args(cls, args):
        """Get path argument from args and check if they can be started.

        Args:
            args (Iterable[str]): Arguments passed to AYON.

        Returns:
            StartArgScript: Object containing argument and script path.
        """

        if len(args) < 2:
            return cls(None, None)
        path = args[1]
        if os.path.exists(path):
            if os.path.isdir(path):
                new_path = os.path.join(path, "__main__.py")
                if os.path.exists(new_path):
                    return cls(path, new_path)
            else:
                path_ext = os.path.splitext(path)[1].lower()
                if path_ext in (".py", ".pyd", ".pyw", ".pyc"):
                    return cls(path, path)
        return cls(path, None)

from_args(args) classmethod

Get path argument from args and check if they can be started.

Parameters:

Name Type Description Default
args Iterable[str]

Arguments passed to AYON.

required

Returns:

Name Type Description
StartArgScript

Object containing argument and script path.

Source code in start.py
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
@classmethod
def from_args(cls, args):
    """Get path argument from args and check if they can be started.

    Args:
        args (Iterable[str]): Arguments passed to AYON.

    Returns:
        StartArgScript: Object containing argument and script path.
    """

    if len(args) < 2:
        return cls(None, None)
    path = args[1]
    if os.path.exists(path):
        if os.path.isdir(path):
            new_path = os.path.join(path, "__main__.py")
            if os.path.exists(new_path):
                return cls(path, new_path)
        else:
            path_ext = os.path.splitext(path)[1].lower()
            if path_ext in (".py", ".pyd", ".pyw", ".pyc"):
                return cls(path, path)
    return cls(path, None)

boot()

Bootstrap AYON launcher.

Source code in start.py
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
def boot():
    """Bootstrap AYON launcher."""
    init_launcher_executable()

    # Setup site id in environment variable for all possible subprocesses
    if SITE_ID_ENV_KEY not in os.environ:
        os.environ[SITE_ID_ENV_KEY] = get_local_site_id()

    _connect_to_ayon_server()
    create_global_connection()
    _print(
        f">>> Global AYON connection created ({_Timing.total_time():.2f}s)."
    )
    _start_distribution()
    fill_pythonpath()

    # Call launcher storage dir getters to make sure their
    #   env variables are set
    get_launcher_local_dir()
    get_launcher_storage_dir()

fill_pythonpath()

Fill 'sys.path' with paths from PYTHONPATH environment variable.

Source code in start.py
850
851
852
853
854
855
856
def fill_pythonpath():
    """Fill 'sys.path' with paths from PYTHONPATH environment variable."""
    lookup_set = set(sys.path)
    for path in (os.getenv("PYTHONPATH") or "").split(os.pathsep):
        if path not in lookup_set:
            sys.path.append(path)
            lookup_set.add(path)

get_info(use_staging=None, use_dev=None)

Print additional information to console.

Source code in start.py
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
def get_info(use_staging=None, use_dev=None) -> list:
    """Print additional information to console."""

    inf = []
    studio_bundle_name = os.getenv("AYON_STUDIO_BUNDLE_NAME")
    project_bundle_name = os.getenv("AYON_BUNDLE_NAME")

    variant = "production"
    if use_dev:
        variant = f"dev ({project_bundle_name})"
    elif use_staging:
        variant = "staging"
    inf.append(("AYON variant", variant))
    inf.append(("AYON studio bundle", studio_bundle_name))
    if project_bundle_name == studio_bundle_name:
        project_bundle_name = "None"
    inf.append(("AYON project bundle", project_bundle_name))

    # NOTE add addons information

    maximum = max(len(i[0]) for i in inf)
    formatted = []
    for info in inf:
        padding = (maximum - len(info[0])) + 1
        formatted.append(f'... {info[0]}:{" " * padding}[ {info[1]} ]')
    return formatted

init_launcher_executable(ensure_protocol_is_registered=False)

Initialize AYON launcher executable.

Make sure current AYON launcher executable is stored to known executables and shim is deployed.

Source code in start.py
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
def init_launcher_executable(ensure_protocol_is_registered=False):
    """Initialize AYON launcher executable.

    Make sure current AYON launcher executable is stored to known executables
        and shim is deployed.

    """
    create_desktop_icons = "--create-desktop-icons" in sys.argv
    store_current_executable_info()
    try:
        deploy_ayon_launcher_shims(
            ensure_protocol_is_registered=ensure_protocol_is_registered,
            create_desktop_icons=create_desktop_icons,
        )
    except ShimDeploymentError as exc:
        if not HEADLESS_MODE_ENABLED:
            show_failed_shim_deployment(str(exc))
        sys.exit(1)
    except Exception:
        _print("Unexpected error during shim deployment.")
        traceback.print_exception(*sys.exc_info())
        if not HEADLESS_MODE_ENABLED:
            show_failed_shim_deployment()
        sys.exit(1)

main_cli()

Main startup logic.

This is the main entry point for the AYON launcher. At this moment is fully dependent on 'ayon_core' addon. Which means it contains more logic than it should.

Source code in start.py
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
def main_cli():
    """Main startup logic.

    This is the main entry point for the AYON launcher. At this
    moment is fully dependent on 'ayon_core' addon. Which means it
    contains more logic than it should.
    """
    try:
        import ayon_core  # noqa F401
    except ModuleNotFoundError:
        _on_main_addon_missing()

    try:
        from ayon_core import cli
    except ImportError as exc:
        traceback.print_exception(*sys.exc_info())
        _on_main_addon_import_error(exc)

    # print info when not running scripts defined in 'silent commands'
    if not SKIP_HEADERS:
        info = get_info(is_staging_enabled(), is_dev_mode_enabled())
        info.insert(0, f">>> Using AYON from [ {AYON_ROOT} ]")

        try:
            t_width = os.get_terminal_size().columns - 2
        except (ValueError, OSError):
            t_width = 20

        _header = f"*** AYON [{__version__}] "
        info.insert(0, _header + "-" * (t_width - len(_header)))

        for i in info:
            _print(i)

    _print(f">>> Initializing done ({_Timing.next():.2f}s).")
    try:
        cli.main()
    except Exception:  # noqa
        exc_info = sys.exc_info()
        _print("!!! AYON crashed:")
        traceback.print_exception(*exc_info)
        sys.exit(1)

script_cli(start_arg=None)

Run and execute script.

Source code in start.py
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
def script_cli(start_arg=None):
    """Run and execute script."""

    if start_arg is None:
        start_arg = StartArgScript.from_args(sys.argv)

    # Remove first argument from sys.argv
    # - start.py when running from code
    # - ayon executable when running from build
    sys.argv.pop(0)

    # Find '__main__.py' in directory
    if not start_arg.is_valid:
        if not start_arg.argument:
            raise RuntimeError("No script to run")

        if start_arg.is_dir:
            raise RuntimeError(
                f"Can't find '__main__' module in '{start_arg.argument}'")
        raise RuntimeError(f"Can't find script to run '{start_arg.argument}'")
    filepath = start_arg.script_path

    # Add parent dir to sys path
    sys.path.insert(0, os.path.dirname(filepath))

    # Read content and execute
    with open(filepath, "r") as stream:
        content = stream.read()

    script_globals = dict(globals())
    script_globals["__file__"] = filepath
    exec(compile(content, filepath, "exec"), script_globals)