def terminal_message(message, subject=None, color="green", footer=None, indent=True): """ Print a nicely formatted message as output to the user using a ``rich`` ``Panel``. :param: message: The message to print out :param: subject: An optional subject line to add in the header of the ``Panel`` :param: color: An optional color to style the ``subject`` header with :param: footer: An optional message to display in the footer of the ``Panel`` :param: indent: Adds padding to the left of the message """ panel_kwargs = dict(padding=1) if subject is not None: panel_kwargs["title"] = f"[{color}]{subject}" if footer is not None: panel_kwargs["subtitle"] = f"[dim italic]{footer}[/dim italic]" text = dedent(message) if indent: text = indent_text(text, prefix=" ") console = Console() console.print() console.print(Panel(text, **panel_kwargs)) console.print()
def test_dump_full_config(tmp_path): application_path = tmp_path / "dummy" application_path.mkdir() template_root_path = application_path / "templates" template_root_path.mkdir() file1 = template_root_path / "file1" file1.write_text("foo") file2 = template_root_path / "file2" file2.write_text("bar") config_path = application_path / JOBBERGATE_APPLICATION_CONFIG_FILE_NAME config_path.write_text( dedent(""" jobbergate_config: default_template: test-job-script.py.j2 output_directory: . application_config: partition: debug """)) assert yaml.safe_load(dump_full_config(application_path)) == dict( jobbergate_config=dict( default_template="test-job-script.py.j2", output_directory=".", template_files=[ "templates/file1", "templates/file2", ], ), application_config=dict(partition="debug", ), )
def test_validate_application_files__success(tmp_path): application_path = tmp_path / "dummy" application_path.mkdir() application_module = application_path / JOBBERGATE_APPLICATION_MODULE_FILE_NAME application_module.write_text( dedent(""" import sys print(f"Got some args, yo: {sys.argv}") """)) application_config = application_path / JOBBERGATE_APPLICATION_CONFIG_FILE_NAME application_config.write_text( dedent(""" foo: bar: baz """)) validate_application_files(application_path)
def dummy_template_source(): return dedent(""" #!/bin/python3 #SBATCH -J dummy_job #SBATCH -t 60 print("I am a very, very dumb job script") print(f"foo='{{foo}}'") print(f"bar='{{bar}}'") print(f"baz='{{baz}}'") """)
def dummy_module_source(): return dedent(""" from jobbergate_cli.subapps.applications.application_base import JobbergateApplicationBase from jobbergate_cli.subapps.applications.questions import Text class JobbergateApplication(JobbergateApplicationBase): def mainflow(self, data): data["nextworkflow"] = "subflow" return [Text("foo", message="gimme the foo!"), Text("bar", message="gimme the bar!")] def subflow(self, data): return [Text("baz", message="gimme the baz!", default="zab")] """)
def dummy_config_source(): return dedent(""" jobbergate_config: default_template: test-job-script.py.j2 template_files: - test-job-script.py.j2 output_directory: . supporting_files_output_name: supporting_files: job_script_name: application_config: foo: foo bar: bar baz: baz """)
def test_load_application_data__fails_if_application_config_is_not_valid_JobbergateApplicationConfig( dummy_module_source, ): app_data = ApplicationResponse( id=13, application_name="dummy", application_owner_email="*****@*****.**", application_uploaded=True, application_file=dummy_module_source, application_config=dedent(""" foo: bar """), ) with pytest.raises( Abort, match="The application config fetched from the API is not valid"): load_application_data(app_data)
def wrapper(*args, **kwargs): try: func(*args, **kwargs) except Abort as err: if not err.warn_only: if err.log_message is not None: logger.error(err.log_message) if err.original_error is not None: logger.error(f"Original exception: {err.original_error}") if settings.SENTRY_DSN: with sentry_sdk.push_scope() as scope: if err.sentry_context is not None: scope.set_context(**err.sentry_context) sentry_sdk.capture_exception(err.original_error if err.original_error is not None else err) sentry_sdk.flush() panel_kwargs = dict() if err.subject is not None: panel_kwargs["title"] = f"[red]{err.subject}" message = dedent(err.message) if err.support: support_message = unwrap( f""" [yellow]If the problem persists, please contact [bold]{OV_CONTACT}[/bold] for support and trouble-shooting """ ) message = f"{message}\n\n{support_message}" console = Console() console.print() console.print(Panel(message, **panel_kwargs)) console.print() raise typer.Exit(code=1)
Provide a stub module to maintain compatibility with previous versions. Issue a deprecation warning when this module is imported from if JOBBERGATE_COMPATIBILITY_MODE is enabled. If JOBBERGATE_COMPATIBILITY_MODE is not enabled, raise an import error when this module is imported. """ import warnings from jobbergate_cli.config import settings from jobbergate_cli.text_tools import dedent, unwrap if settings.JOBBERGATE_COMPATIBILITY_MODE: from jobbergate_cli.subapps.applications.application_base import JobbergateApplicationBase # noqa warnings.warn( dedent(""" Importing application_base from jobbergate_cli is deprecated. The module has been moved. Import 'application_base' from 'jobbergate_cli.subapps.applications' instead", """), DeprecationWarning, ) else: raise ImportError( unwrap(""" JobbergateApplicationBase has been moved to 'jobbergate_cli.subapps.applications.application_base.JobbergateApplicationBase' """))
def create( ctx: typer.Context, name: str = typer.Option( ..., help="The name of the job script to create.", ), application_id: Optional[int] = typer.Option( None, help="The id of the application from which to create the job script.", ), application_identifier: Optional[str] = typer.Option( None, help= "The identifier of the application from which to create the job script.", ), sbatch_params: Optional[List[str]] = typer.Option( None, help="Optional parameter to submit raw sbatch parameters.", ), param_file: Optional[pathlib.Path] = typer.Option( None, help=dedent(""" Supply a yaml file that contains the parameters for populating templates. If this is not supplied, the question asking in the application is triggered. """), ), fast: bool = typer.Option( False, help="Use default answers (when available) instead of asking the user.", ), submit: Optional[bool] = typer.Option( None, help="Do not ask the user if they want to submit a job.", ), ): """ Create a new job script. """ jg_ctx: JobbergateContext = ctx.obj # Make static type checkers happy assert jg_ctx.client is not None app_data = fetch_application_data(jg_ctx, id=application_id, identifier=application_identifier) (app_config, app_module) = load_application_data(app_data) request_data = JobScriptCreateRequestData( application_id=app_data.id, job_script_name=name, sbatch_params=sbatch_params, param_dict=app_config, ) supplied_params = validate_parameter_file( param_file) if param_file else dict() execute_application(app_module, app_config, supplied_params, fast_mode=fast) if app_config.jobbergate_config.job_script_name is not None: request_data.job_script_name = app_config.jobbergate_config.job_script_name job_script_result = cast( JobScriptResponse, make_request( jg_ctx.client, "/job-scripts", "POST", expected_status=201, abort_message="Couldn't create job script", support=True, request_model=request_data, response_model_cls=JobScriptResponse, ), ) render_single_result( jg_ctx, job_script_result, hidden_fields=HIDDEN_FIELDS, title="Created Job Script", ) # `submit` will be `None` --submit/--no-submit flag was not set if submit is None: # If not running in "fast" mode, ask the user what to do. if not fast: submit = typer.confirm( "Would you like to submit this job immediately?") # Otherwise, assume that the job script should be submitted immediately else: submit = True if not submit: return try: job_submission_result = create_job_submission( jg_ctx, job_script_result.id, job_script_result.job_script_name) except Exception as err: raise Abort( "Failed to immediately submit the job after job script creation.", subject="Automatic job submission failed", support=True, log_message= f"There was an issue submitting the job immediately job_script_id={job_script_result.id}.", original_error=err, ) render_single_result( jg_ctx, job_submission_result, hidden_fields=JOB_SUBMISSION_HIDDEN_FIELDS, title="Created Job Submission (Fast Mode)", )
Provide a stub module to maintain compatibility with previous versions. Issue a deprecation warning when this module is imported from if JOBBERGATE_COMPATIBILITY_MODE is enabled. If JOBBERGATE_COMPATIBILITY_MODE is not enabled, raise an import error when this module is imported. """ import warnings from jobbergate_cli.config import settings from jobbergate_cli.text_tools import dedent, unwrap if settings.JOBBERGATE_COMPATIBILITY_MODE: from jobbergate_cli.subapps.applications.application_helpers import * # noqa warnings.warn( dedent(""" Importing jobberappslib from jobbergate_cli is deprecated. The module has been moved. Import the helper functions from 'jobbergate_cli.subapps.applications.application_helpers' instead", """), DeprecationWarning, ) else: raise ImportError( unwrap(""" The 'jobberappslib' module has been renamed to 'application_helpers' and has been moved to 'jobbergate_cli.subapps.applications.application_helpers' """))