def handle_iteration( client: RunClient, iteration: int = None, suggestions: List[Dict] = None, summary: Dict = None, name: str = None, ): summary = summary or {} summary.update({ "iteration": iteration, "suggestions": [sanitize_dict(s) for s in suggestions], }) def handler(): if suggestions: artifact_run = V1RunArtifact( name=name or "out-iteration-{}".format(iteration), kind=V1ArtifactKind.ITERATION, summary=summary, is_input=False, ) client.log_artifact_lineage(artifact_run) Printer.print_success("Tuner generated new suggestions.") else: client.log_succeeded(message="Iterative operation has succeeded") Printer.print_success("Iterative optimization succeeded") try: handler() except Exception as e: exp = "Polyaxon tuner failed fetching iteration definition: {}\n{}".format( repr(e), traceback.format_exc()) client.log_failed(message="PolyaxonTunerFailed", traceback=exp) logger.warning(e)
def test_get_statuses_watch(self, sdk_get_run_statuses): settings.CLIENT_CONFIG.watch_interval = 1 client = RunClient(owner="owner", project="project", run_uuid=uuid.uuid4().hex) for _ in client.watch_statuses(): resp = MagicMock(status=V1Statuses.FAILED, status_conditions=[]) sdk_get_run_statuses.return_value = resp assert sdk_get_run_statuses.call_count == 2
def restart(ctx, copy, polyaxonfile, u): """Restart run. Uses /docs/core/cli/#caching Examples: \b $ polyaxon run --uid=8aac02e3a62a4f0aaa257c59da5eab80 restart """ content = None if polyaxonfile: content = "{}".format(ConfigSpec.read_from(polyaxonfile)) # Check if we need to upload if u: ctx.invoke(upload, sync=False) owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) try: polyaxon_client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) response = polyaxon_client.restart(override_config=content, copy=copy) Printer.print_success("Run was {} with uid {}".format( "copied" if copy else "restarted", response.uuid)) except (ApiException, HTTPError) as e: handle_cli_error( e, message="Could not restart run `{}`.".format(run_uuid)) sys.exit(1)
def artifacts(ctx, path, path_to, no_untar): """Download outputs/artifacts for run. Uses /docs/core/cli/#caching Examples: \b $ polyaxon ops -uid=8aac02e3a62a4f0aaa257c59da5eab80 artifacts \b $ polyaxon ops -uid=8aac02e3a62a4f0aaa257c59da5eab80 artifacts path="events/only" \b $ polyaxon ops -uid=8aac02e3a62a4f0aaa257c59da5eab80 artifacts path_to="this/path" """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) try: client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) download_path = client.download_artifacts( path=path or "", path_to=path_to, untar=not no_untar ) except (ApiException, HTTPError) as e: handle_cli_error( e, message="Could not download outputs for run `{}`.".format(run_uuid) ) sys.exit(1) Printer.print_success("Files downloaded: path: {}".format(download_path))
def create_run(): click.echo("Creating a run.") try: polyaxon_client = RunClient(owner=owner, project=project_name) response = polyaxon_client.create( name=name, description=description, tags=tags, content=op_spec ) config = polyaxon_client.client.sanitize_for_serialization(response) cache.cache( config_manager=RunManager, config=config, owner=owner, project=project_name, ) Printer.print_success("A new run `{}` was created".format(response.uuid)) click.echo( "You can view this run on Polyaxon UI: {}".format( get_dashboard_url( subpath="{}/{}/runs/{}".format( owner, project_name, response.uuid ) ) ) ) except (ApiException, HTTPError) as e: handle_cli_error(e, message="Could not create a run.") sys.exit(1)
def resume(ctx, polyaxonfile, u): """Resume run. Uses [Caching](/references/polyaxon-cli/#caching) Examples: \b ```bash $ polyaxon runs --uid=8aac02e3a62a4f0aaa257c59da5eab80 resume ``` """ content = None if polyaxonfile: content = "{}".format(reader.read(polyaxonfile)) # Check if we need to upload if u: ctx.invoke(upload, sync=False) owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True) try: polyaxon_client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) response = polyaxon_client.resume(override_config=content) Printer.print_success("Run was resumed with uid {}".format( response.uuid)) except (ApiException, HTTPError) as e: handle_cli_error(e, message="Could not resume run `{}`.".format(run_uuid)) sys.exit(1)
def delete(ctx): """Delete a run. Uses /docs/core/cli/#caching Example: \b $ polyaxon ops delete \b $ polyaxon ops --uid=8aac02e3a62a4f0aaa257c59da5eab80 delete # project is cached \b $ polyaxon ops --project=cats-vs-dogs -uid 8aac02e3a62a4f0aaa257c59da5eab80 delete """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) if not click.confirm("Are sure you want to delete run `{}`".format(run_uuid)): click.echo("Existing without deleting the run.") sys.exit(1) try: polyaxon_client = RunClient( owner=owner, project=project_name, run_uuid=run_uuid ) polyaxon_client.delete() # Purge caching RunManager.purge() except (ApiException, HTTPError) as e: handle_cli_error(e, message="Could not delete run `{}`.".format(run_uuid)) sys.exit(1) Printer.print_success("Run `{}` was delete successfully".format(run_uuid))
def test_get_statuses(self, sdk_get_run_statuses): client = RunClient(owner="owner", project="project", run_uuid=uuid.uuid4().hex) for _ in client.get_statuses(): pass assert sdk_get_run_statuses.call_count == 1
def create_auth_context(): try: run_client = RunClient() except PolyaxonClientException as e: raise PolyaxonContainerException(e) retry = 1 done = False exp = None while not done and retry <= 3: try: impersonate( owner=run_client.owner, project=run_client.project, run_uuid=run_client.run_uuid, client=run_client.client, ) print("Auth context initialized.") return except PolyaxonClientException as e: retry += 1 print("Could not establish connection, retrying ...") exp = "Polyaxon auth initialized failed authenticating the operation: {}\n{}".format( repr(e), traceback.format_exc()) time.sleep(retry) run_client.log_failed("Could not create an auth context.", traceback=exp) raise PolyaxonContainerException( "Init job did not succeed authenticating job.")
def invalidate(ctx, project, uid): """Invalidate the run's cache state. Uses /docs/core/cli/#caching Examples: \b $ polyaxon ops invalidate """ owner, project_name, run_uuid = get_project_run_or_local( project or ctx.obj.get("project"), uid or ctx.obj.get("run_uuid"), is_cli=True, ) try: polyaxon_client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) response = polyaxon_client.invalidate() Printer.print_success("Run was invalidated with uid {}".format( response.uuid)) except (ApiException, HTTPError) as e: handle_cli_error( e, message="Could not invalidate run `{}`.".format(run_uuid)) sys.exit(1)
def resume(ctx, polyaxonfile): """Resume run. Uses /docs/core/cli/#caching Examples: \b $ polyaxon ops --uid=8aac02e3a62a4f0aaa257c59da5eab80 resume """ content = None if polyaxonfile: content = "{}".format(ConfigSpec.read_from(polyaxonfile)) owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) try: polyaxon_client = RunClient( owner=owner, project=project_name, run_uuid=run_uuid ) response = polyaxon_client.resume(override_config=content) Printer.print_success("Run was resumed with uid {}".format(response.uuid)) except (ApiException, HTTPError) as e: handle_cli_error(e, message="Could not resume run `{}`.".format(run_uuid)) sys.exit(1)
def approve(ctx, project, uid): """Approve a run waiting for human validation. Uses /docs/core/cli/#caching Examples: \b $ polyaxon ops approve \b $ polyaxon ops approve --uid=8aac02e3a62a4f0aaa257c59da5eab80 """ owner, project_name, run_uuid = get_project_run_or_local( project or ctx.obj.get("project"), uid or ctx.obj.get("run_uuid"), is_cli=True, ) try: polyaxon_client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) polyaxon_client.approve() except (ApiException, HTTPError) as e: handle_cli_error( e, message="Could not approve run `{}`.".format(run_uuid)) sys.exit(1) Printer.print_success("Run is approved")
def restart(ctx, project, uid, copy, polyaxonfile): """Restart run. Uses /docs/core/cli/#caching Examples: \b $ polyaxon run --uid=8aac02e3a62a4f0aaa257c59da5eab80 restart """ content = None if polyaxonfile: content = OperationSpecification.read( polyaxonfile, is_preset=True).to_dict(dump=True) owner, project_name, run_uuid = get_project_run_or_local( project or ctx.obj.get("project"), uid or ctx.obj.get("run_uuid"), is_cli=True, ) try: polyaxon_client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) response = polyaxon_client.restart(override_config=content, copy=copy) Printer.print_success("Run was {} with uid {}".format( "copied" if copy else "restarted", response.uuid)) except (ApiException, HTTPError) as e: handle_cli_error( e, message="Could not restart run `{}`.".format(run_uuid)) sys.exit(1)
def artifacts(ctx): """Download outputs/artifacts for run. Uses /docs/core/cli/#caching Examples: \b $ polyaxon ops -uid=8aac02e3a62a4f0aaa257c59da5eab80 artifacts """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) try: client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) client.download_artifacts() except (ApiException, HTTPError) as e: handle_cli_error( e, message="Could not download outputs for run `{}`.".format( run_uuid)) sys.exit(1) Printer.print_success("Files downloaded.")
def invalidate_run(ctx): """Invalidate runs' cache inside this project. Uses [Caching](/references/polyaxon-cli/#caching) Examples: \b ```bash $ polyaxon invalidate_builds ``` """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True) try: polyaxon_client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) response = polyaxon_client.invalidate() Printer.print_success("Run was invalidated with uid {}".format( response.uuid)) except (ApiException, HTTPError) as e: handle_cli_error( e, message="Could not invalidate run `{}`.".format(run_uuid)) sys.exit(1)
def sync_summaries(last_check: Optional[datetime], run_uuid: str, client: RunClient): events_path = CONTEXT_MOUNT_RUN_EVENTS_FORMAT.format(run_uuid) # check if there's a path to sync if not os.path.exists(events_path): return # crawl dirs summaries = [] last_values = {} connection = get_artifacts_connection() connection_name = connection.name if connection else None for events_kind in get_dirs_under_path(events_path): _summaries, _last_values = sync_events_summaries( events_path=events_path, events_kind=events_kind, last_check=last_check, connection_name=connection_name, ) summaries += _summaries last_values.update(_last_values) if summaries: client.log_artifact_lineage(summaries) if last_values: client.log_outputs(**last_values)
def stop(ctx, yes): """Stop run. Uses /docs/core/cli/#caching Examples: \b $ polyaxon ops stop \b $ polyaxon ops --uid=8aac02e3a62a4f0aaa257c59da5eab80 stop """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) if not yes and not click.confirm( "Are sure you want to stop " "run `{}`".format(run_uuid) ): click.echo("Existing without stopping run.") sys.exit(0) try: polyaxon_client = RunClient( owner=owner, project=project_name, run_uuid=run_uuid ) polyaxon_client.stop() except (ApiException, HTTPError) as e: handle_cli_error(e, message="Could not stop run `{}`.".format(run_uuid)) sys.exit(1) Printer.print_success("Run is being stopped.")
def test_get_statuses(self, sdk_patch_run): client = RunClient(owner="owner", project="project", run_uuid=uuid.uuid4().hex) assert client.run_data.tags is None client.log_tags(["foo", "bar"]) assert client.run_data.tags == ["foo", "bar"] assert sdk_patch_run.call_count == 1
def statuses(ctx, watch): """Get run or run job statuses. Uses /docs/core/cli/#caching Examples getting run statuses: \b $ polyaxon ops statuses \b $ polyaxon ops -uid=8aac02e3a62a4f0aaa257c59da5eab80 statuses """ def _handle_run_statuses(): if not conditions: return Printer.print_header("Latest status:") latest_status = Printer.add_status_color( {"status": status}, status_key="status" ) click.echo("{}\n".format(latest_status["status"])) objects = list_dicts_to_tabulate( [ Printer.add_status_color(o.to_dict(), status_key="type") for o in conditions ] ) if objects: Printer.print_header("Conditions:") dict_tabulate(objects, is_list_dict=True) owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) if watch: try: for status, conditions in client.watch_statuses(): _handle_run_statuses() except (ApiException, HTTPError, PolyaxonClientException) as e: handle_cli_error( e, message="Could get status for run `{}`.".format(run_uuid) ) sys.exit(1) else: try: status, conditions = client.get_statuses() _handle_run_statuses() except (ApiException, HTTPError, PolyaxonClientException) as e: handle_cli_error( e, message="Could get status for run `{}`.".format(run_uuid) ) sys.exit(1)
def get_iteration_definition( client: RunClient, iteration: int, search: V1ParamSearch, optimization_metric: str, name: str = None, ): def handler(): runs = ( client.list( query=search.query, sort=search.sort, limit=search.limit, offset=search.offset, ).results or [] ) configs = [] metrics = [] run_uuids = [] for run in runs: if optimization_metric in run.outputs: run_uuids.append(run.uuid) configs.append(run.inputs) metrics.append(run.outputs[optimization_metric]) if configs or metrics or run_uuids: artifact_run = V1RunArtifact( name=name or "in-iteration-{}".format(iteration), kind=V1ArtifactKind.ITERATION, summary={ "iteration": iteration, "configs": [sanitize_dict(s) for s in configs], "metrics": [sanitize_np_types(s) for s in metrics], "uuid": run_uuids, }, is_input=True, ) client.log_artifact_lineage(artifact_run) return run_uuids, configs, metrics try: return handler() except Exception as e: exp = "Polyaxon tuner failed fetching iteration definition: {}\n{}".format( repr(e), traceback.format_exc() ) client.log_failed(message="PolyaxonTunerFailed", traceback=exp) logger.warning(e)
def update(ctx, name, description, tags): """Update run. Uses [Caching](/references/polyaxon-cli/#caching) Examples: \b ```bash $ polyaxon runs --uid=8aac02e3a62a4f0aaa257c59da5eab80 update --description="new description for my runs" ``` \b ```bash $ polyaxon runs --project=cats-vs-dogs -id 8aac02e3a62a4f0aaa257c59da5eab80 update --tags="foo, bar" --name="unique-name" ``` """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True) update_dict = {} if name: update_dict["name"] = name if description: update_dict["description"] = description tags = validate_tags(tags) if tags: update_dict["tags"] = tags if not update_dict: Printer.print_warning("No argument was provided to update the run.") sys.exit(0) try: polyaxon_client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) response = polyaxon_client.update(update_dict) except (ApiException, HTTPError) as e: handle_cli_error(e, message="Could not update run `{}`.".format(run_uuid)) sys.exit(1) Printer.print_success("Run updated.") get_run_details(response)
def get(ctx, project, uid): """Get run. Uses /docs/core/cli/#caching Examples for getting a run: \b $ polyaxon ops get # if run is cached \b $ polyaxon ops get --uid=8aac02e3a62a4f0aaa257c59da5eab80 # project is cached \b $ polyaxon ops get --project=cats-vs-dogs -uid 8aac02e3a62a4f0aaa257c59da5eab80 \b $ polyaxon ops get -p alain/cats-vs-dogs --uid=8aac02e3a62a4f0aaa257c59da5eab80 """ owner, project_name, run_uuid = get_project_run_or_local( project or ctx.obj.get("project"), uid or ctx.obj.get("run_uuid"), is_cli=True, ) try: polyaxon_client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) polyaxon_client.refresh_data() config = polyaxon_client.client.sanitize_for_serialization( polyaxon_client.run_data) cache.cache( config_manager=RunConfigManager, config=config, owner=owner, project=project_name, ) except (ApiException, HTTPError) as e: handle_cli_error( e, message="Could not load run `{}/{}/{}` info.".format( owner, project_name, run_uuid), ) sys.exit(1) get_run_details(polyaxon_client.run_data)
def create_code_repo(repo_path: str, url: str, revision: str, connection: str = None): try: clone_url = get_clone_url(url) except Exception as e: raise PolyaxonContainerException( "Error parsing url: {}.".format(url)) from e clone_git_repo(repo_path=repo_path, url=clone_url) set_remote(repo_path=repo_path, url=url) if revision: checkout_revision(repo_path=repo_path, revision=revision) if not settings.CLIENT_CONFIG.no_api: try: owner, project, run_uuid = get_run_info() except PolyaxonClientException as e: raise PolyaxonContainerException(e) code_ref = get_code_reference(path=repo_path, url=url) artifact_run = V1RunArtifact( name=code_ref.get("commit"), kind=V1ArtifactKind.CODEREF, connection=connection, summary=code_ref, is_input=True, ) RunClient(owner=owner, project=project, run_uuid=run_uuid).log_artifact_lineage(artifact_run)
def logs(ctx, follow, hide_time, all_info): """Get run or run job logs. Uses [Caching](/references/polyaxon-cli/#caching) Examples for getting run logs: \b ```bash $ polyaxon run logs ``` \b ```bash $ polyaxon runs -uid=8aac02e3a62a4f0aaa257c59da5eab80 -p mnist logs ``` """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True) client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) get_run_logs( client=client, hide_time=hide_time, all_info=all_info, follow=follow, )
def logs(ctx, follow, hide_time, all_info): """Get run or run job logs. Uses /docs/core/cli/#caching Examples for getting run logs: \b $ polyaxon run logs \b $ polyaxon ops -uid=8aac02e3a62a4f0aaa257c59da5eab80 -p mnist logs """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) get_run_logs( client=client, hide_time=hide_time, all_info=all_info, follow=follow, )
def handle_iteration( client: RunClient, suggestions: List[Dict] = None, ): if not suggestions: logger.warning("No new suggestions were created") return try: logger.info("Generated new {} suggestions".format(len(suggestions))) client.log_outputs(suggestions=[sanitize_dict(s) for s in suggestions], async_req=False) except Exception as e: exp = "Polyaxon tuner failed logging iteration definition: {}\n{}".format( repr(e), traceback.format_exc()) client.log_failed(reason="PolyaxonTunerIteration", message=exp) logger.warning(e)
def logs(ctx, follow, hide_time, all_info): """Get run or run job logs. Uses /docs/core/cli/#caching Examples for getting run logs: \b $ polyaxon run logs \b $ polyaxon ops -uid=8aac02e3a62a4f0aaa257c59da5eab80 -p mnist logs """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) try: get_run_logs( client=client, hide_time=hide_time, all_info=all_info, follow=follow, ) except (ApiException, HTTPError, PolyaxonClientException) as e: handle_cli_error( e, message="Could not get logs for run `{}`.".format(client.run_uuid), ) sys.exit(1)
def get(ctx): """Get run. Uses [Caching](/references/polyaxon-cli/#caching) Examples for getting a run: \b ```bash $ polyaxon runs get # if run is cached ``` \b ```bash $ polyaxon runs --uid=8aac02e3a62a4f0aaa257c59da5eab80 get # project is cached ``` \b ```bash $ polyaxon runs --project=cats-vs-dogs -id 8aac02e3a62a4f0aaa257c59da5eab80 get ``` \b ```bash $ polyaxon runs -p alain/cats-vs-dogs --uid=8aac02e3a62a4f0aaa257c59da5eab80 get ``` """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True) try: polyaxon_client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) polyaxon_client.refresh_data() config = polyaxon_client.client.sanitize_for_serialization( polyaxon_client.run_data) cache.cache(config_manager=RunManager, config=config) except (ApiException, HTTPError) as e: handle_cli_error( e, message="Could not load run `{}` info.".format(run_uuid)) sys.exit(1) get_run_details(polyaxon_client.run_data)
def create_code_repo( repo_path: str, url: str, revision: str, connection: str = None, flags: List[str] = None, ): try: clone_url = get_clone_url(url) except Exception as e: raise PolyaxonContainerException( "Error parsing url: {}.".format(url)) from e if flags and "--experimental-fetch" in flags: flags.remove("--experimental-fetch") fetch_git_repo(repo_path=repo_path, clone_url=clone_url, revision=revision, flags=flags) else: clone_and_checkout_git_repo(repo_path=repo_path, clone_url=clone_url, revision=revision, flags=flags) # Update remote set_remote(repo_path=repo_path, url=url) if settings.CLIENT_CONFIG.no_api: return try: run_client = RunClient() except PolyaxonClientException as e: raise PolyaxonContainerException(e) code_ref = get_code_reference(path=repo_path, url=url) artifact_run = V1RunArtifact( name=code_ref.get("commit"), kind=V1ArtifactKind.CODEREF, connection=connection, summary=code_ref, is_input=True, ) run_client.log_artifact_lineage(artifact_run)
def service(ctx, yes, external, url): """Open the operation service in browser. N.B. The operation must have a run kind service, otherwise it will raise an error. You can open the service embedded in Polyaxon UI or using the real service URL, please use the `--external` flag. """ owner, project_name, run_uuid = get_project_run_or_local( ctx.obj.get("project"), ctx.obj.get("run_uuid"), is_cli=True, ) client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid) client.refresh_data() if client.run_data.kind != V1RunKind.SERVICE: Printer.print_warning("Command expected a operations of " "kind `service` received kind: {}!".format( client.run_data.kind)) sys.exit(1) dashboard_url = settings.CLIENT_CONFIG.host Printer.print_header("Waiting for running condition ...") client.wait_for_condition(statuses=[V1Statuses.RUNNING], print_status=True) client.refresh_data() namespace = client.run_data.settings.namespace run_url = "{}/{}/{}/runs/{}/service".format(dashboard_url, owner, project_name, run_uuid) service_endpoint = SERVICES_V1 if client.run_data.meta_info.get("rewrite_path", False): service_endpoint = REWRITE_SERVICES_V1 external_run_url = "{}/{}/{}/{}/{}/runs/{}/".format( dashboard_url, service_endpoint, namespace, owner, project_name, run_uuid) if url: Printer.print_header( "The service will be available at: {}".format(run_url)) Printer.print_header( "You can also view it in an external link at: {}".format( external_run_url)) sys.exit(0) if not yes: click.confirm( "Dashboard page will now open in your browser. Continue?", abort=True, default=True, ) if external: click.launch(external_run_url) sys.exit(0) click.launch(run_url)