def _create_scheduler_run( instance, logger, schedule_time_utc, repo_location, external_repo, external_schedule, external_pipeline, tick_holder, ): schedule_execution_data = repo_location.get_external_schedule_execution_data( instance=instance, repository_handle=external_repo.handle, schedule_name=external_schedule.name, schedule_execution_data_mode=ScheduleExecutionDataMode. LAUNCH_SCHEDULED_EXECUTION, scheduled_execution_datetime_utc=schedule_time_utc, ) if isinstance(schedule_execution_data, ExternalScheduleExecutionErrorData): error = schedule_execution_data.error logger.error( "Failed to fetch schedule data for {schedule_name}: {error}". format(schedule_name=external_schedule.name, error=error.to_string()), ) tick_holder.update_with_status(ScheduleTickStatus.FAILURE, error=error) return None elif not schedule_execution_data.should_execute: logger.info( "should_execute returned False for {schedule_name}, skipping". format(schedule_name=external_schedule.name)) # Update tick to skipped state and return tick_holder.update_with_status(ScheduleTickStatus.SKIPPED) return None run_config = schedule_execution_data.run_config schedule_tags = schedule_execution_data.tags execution_plan_errors = [] execution_plan_snapshot = None try: external_execution_plan = repo_location.get_external_execution_plan( external_pipeline, run_config, external_schedule.mode, step_keys_to_execute=None, ) execution_plan_snapshot = external_execution_plan.execution_plan_snapshot except DagsterSubprocessError as e: execution_plan_errors.extend(e.subprocess_error_infos) except Exception as e: # pylint: disable=broad-except execution_plan_errors.append( serializable_error_info_from_exc_info(sys.exc_info())) pipeline_tags = external_pipeline.tags or {} check_tags(pipeline_tags, "pipeline_tags") tags = merge_dicts(pipeline_tags, schedule_tags) tags[SCHEDULED_EXECUTION_TIME_TAG] = schedule_time_utc.isoformat() # If the run was scheduled correctly but there was an error creating its # run config, enter it into the run DB with a FAILURE status possibly_invalid_pipeline_run = instance.create_run( pipeline_name=external_schedule.pipeline_name, run_id=None, run_config=run_config, mode=external_schedule.mode, solids_to_execute=external_pipeline.solids_to_execute, step_keys_to_execute=None, solid_selection=external_pipeline.solid_selection, status=(PipelineRunStatus.FAILURE if len(execution_plan_errors) > 0 else PipelineRunStatus.NOT_STARTED), root_run_id=None, parent_run_id=None, tags=tags, pipeline_snapshot=external_pipeline.pipeline_snapshot, execution_plan_snapshot=execution_plan_snapshot, parent_pipeline_snapshot=external_pipeline.parent_pipeline_snapshot, ) if len(execution_plan_errors) > 0: for error in execution_plan_errors: instance.report_engine_event( error.message, possibly_invalid_pipeline_run, EngineEventData.engine_error(error), ) instance.report_run_failed(possibly_invalid_pipeline_run) logger.error( "Failed to fetch execution plan for {schedule_name}: {error_string}" .format( schedule_name=external_schedule.name, error_string="\n".join( [error.to_string() for error in execution_plan_errors]), ), ) return possibly_invalid_pipeline_run
def _launch_scheduled_execution(instance, schedule_def, pipeline, tick, stream): pipeline_def = pipeline.get_definition() # Run should_execute and halt if it returns False schedule_context = ScheduleExecutionContext(instance) with user_code_error_boundary( ScheduleExecutionError, lambda: 'Error occurred during the execution of should_execute for schedule ' '{schedule_name}'.format(schedule_name=schedule_def.name), ): should_execute = schedule_def.should_execute(schedule_context) if not should_execute: # Update tick to skipped state and return tick.update_with_status(ScheduleTickStatus.SKIPPED) stream.send(ScheduledExecutionSkipped()) return errors = [] run_config = {} schedule_tags = {} try: with user_code_error_boundary( ScheduleExecutionError, lambda: 'Error occurred during the execution of run_config_fn for schedule ' '{schedule_name}'.format(schedule_name=schedule_def.name), ): run_config = schedule_def.get_run_config(schedule_context) except DagsterUserCodeExecutionError: error_data = serializable_error_info_from_exc_info(sys.exc_info()) errors.append(error_data) try: with user_code_error_boundary( ScheduleExecutionError, lambda: 'Error occurred during the execution of tags_fn for schedule ' '{schedule_name}'.format(schedule_name=schedule_def.name), ): schedule_tags = schedule_def.get_tags(schedule_context) except DagsterUserCodeExecutionError: error_data = serializable_error_info_from_exc_info(sys.exc_info()) errors.append(error_data) pipeline_tags = pipeline_def.tags or {} check_tags(pipeline_tags, 'pipeline_tags') tags = merge_dicts(pipeline_tags, schedule_tags) mode = schedule_def.mode execution_plan_snapshot = None try: execution_plan = create_execution_plan( pipeline_def, run_config=run_config, mode=mode, ) execution_plan_snapshot = snapshot_from_execution_plan( execution_plan, pipeline_def.get_pipeline_snapshot_id()) except DagsterInvalidConfigError: error_data = serializable_error_info_from_exc_info(sys.exc_info()) errors.append(error_data) # Enter the run in the DB with the information we have possibly_invalid_pipeline_run = instance.create_run( pipeline_name=schedule_def.pipeline_name, run_id=None, run_config=run_config, mode=mode, solids_to_execute=pipeline.solids_to_execute, step_keys_to_execute=None, solid_selection=pipeline.solid_selection, status=None, root_run_id=None, parent_run_id=None, tags=tags, pipeline_snapshot=pipeline_def.get_pipeline_snapshot(), execution_plan_snapshot=execution_plan_snapshot, parent_pipeline_snapshot=pipeline_def.get_parent_pipeline_snapshot(), ) tick.update_with_status(ScheduleTickStatus.SUCCESS, run_id=possibly_invalid_pipeline_run.run_id) # If there were errors, inject them into the event log and fail the run if len(errors) > 0: for error in errors: instance.report_engine_event( error.message, possibly_invalid_pipeline_run, EngineEventData.engine_error(error), ) instance.report_run_failed(possibly_invalid_pipeline_run) stream.send( ScheduledExecutionFailed( run_id=possibly_invalid_pipeline_run.run_id, errors=errors)) return # Otherwise the run should be valid so lets launch it # Need an ExternalPipeline to launch so make one here recon_repo = pipeline.get_reconstructable_repository() repo_location = InProcessRepositoryLocation(recon_repo) external_pipeline = repo_location.get_repository( recon_repo.get_definition().name).get_full_external_pipeline( pipeline_def.name) try: launched_run = instance.launch_run( possibly_invalid_pipeline_run.run_id, external_pipeline) except DagsterLaunchFailedError: error = serializable_error_info_from_exc_info(sys.exc_info()) instance.report_engine_event( error.message, possibly_invalid_pipeline_run, EngineEventData.engine_error(error), ) instance.report_run_failed(possibly_invalid_pipeline_run) stream.send( ScheduledExecutionFailed( run_id=possibly_invalid_pipeline_run.run_id, errors=[error])) return stream.send(ScheduledExecutionSuccess(run_id=launched_run.run_id)) return
def _create_sensor_run(context, instance, repo_location, external_sensor, external_pipeline, run_request): execution_plan_errors = [] execution_plan_snapshot = None try: external_execution_plan = repo_location.get_external_execution_plan( external_pipeline, run_request.run_config, external_sensor.mode, step_keys_to_execute=None, ) execution_plan_snapshot = external_execution_plan.execution_plan_snapshot except DagsterSubprocessError as e: execution_plan_errors.extend(e.subprocess_error_infos) except Exception as e: # pylint: disable=broad-except execution_plan_errors.append( serializable_error_info_from_exc_info(sys.exc_info())) pipeline_tags = external_pipeline.tags or {} check_tags(pipeline_tags, "pipeline_tags") tags = merge_dicts( merge_dicts(pipeline_tags, run_request.tags), PipelineRun.tags_for_sensor(external_sensor), ) if run_request.run_key: tags[RUN_KEY_TAG] = run_request.run_key run = instance.create_run( pipeline_name=external_sensor.pipeline_name, run_id=None, run_config=run_request.run_config, mode=external_sensor.mode, solids_to_execute=external_pipeline.solids_to_execute, step_keys_to_execute=None, solid_selection=external_sensor.solid_selection, status=(PipelineRunStatus.FAILURE if len(execution_plan_errors) > 0 else PipelineRunStatus.NOT_STARTED), root_run_id=None, parent_run_id=None, tags=tags, pipeline_snapshot=external_pipeline.pipeline_snapshot, execution_plan_snapshot=execution_plan_snapshot, parent_pipeline_snapshot=external_pipeline.parent_pipeline_snapshot, external_pipeline_origin=external_pipeline.get_external_origin(), ) if len(execution_plan_errors) > 0: for error in execution_plan_errors: instance.report_engine_event( error.message, run, EngineEventData.engine_error(error), ) instance.report_run_failed(run) context.logger.error( "Failed to fetch execution plan for {sensor_name}: {error_string}". format( sensor_name=external_sensor.name, error_string="\n".join( [error.to_string() for error in execution_plan_errors]), ), ) return run
def start_scheduled_execution(graphene_info, schedule_name): ''' When a scheduler ticks and needs to run for a given schedule, it issues a START_SCHEDULED_EXECUTION mutation with just the schedule name. The mutation is resolved entirely by this method. ''' check.inst_param(graphene_info, 'graphene_info', ResolveInfo) check.str_param(schedule_name, 'schedule_name') tick = None try: # We first load the repository and schedule definition to create # and store a ScheduleTick. # If this fails, this error should be sent to the file based scheduler logs. repository = graphene_info.context.get_repository() schedule_def = get_dagster_schedule_def(graphene_info, schedule_name) cron_schedule = "Unknown" if not schedule_def else schedule_def.cron_schedule tick = graphene_info.context.instance.create_schedule_tick( repository, ScheduleTickData( schedule_name=schedule_name, cron_schedule=cron_schedule, timestamp=time.time(), status=ScheduleTickStatus.STARTED, ), ) # Run should_execute and halt if it returns False schedule_context = ScheduleExecutionContext(graphene_info.context.instance, repository) with user_code_error_boundary( ScheduleExecutionError, lambda: 'Error occurred during the execution should_execute for schedule ' '{schedule_name}'.format(schedule_name=schedule_def.name), ): should_execute = schedule_def.should_execute(schedule_context) if not should_execute: # Update tick to skipped state and return tick = tick.with_status(ScheduleTickStatus.SKIPPED) graphene_info.context.instance.update_schedule_tick(repository, tick) # Return skipped specific gql response return graphene_info.schema.type_named('ScheduledExecutionBlocked')( message='Schedule {schedule_name} did not run because the should_execute did not return' ' True'.format(schedule_name=schedule_name) ) errors = [] environment_dict = {} schedule_tags = {} try: with user_code_error_boundary( ScheduleExecutionError, lambda: 'Error occurred during the execution of environment_dict_fn for schedule ' '{schedule_name}'.format(schedule_name=schedule_def.name), ): environment_dict = schedule_def.get_environment_dict(schedule_context) except DagsterUserCodeExecutionError as exc: error_data = serializable_error_info_from_exc_info(sys.exc_info()) errors.append(error_data) try: with user_code_error_boundary( ScheduleExecutionError, lambda: 'Error occurred during the execution of tags_fn for schedule ' '{schedule_name}'.format(schedule_name=schedule_def.name), ): schedule_tags = schedule_def.get_tags(schedule_context) except DagsterUserCodeExecutionError: error_data = serializable_error_info_from_exc_info(sys.exc_info()) errors.append(error_data) pipeline_def = get_pipeline_def_from_selector(graphene_info, schedule_def.selector) pipeline_tags = pipeline_def.tags or {} check_tags(pipeline_tags, 'pipeline_tags') tags = merge_dicts(pipeline_tags, schedule_tags) selector = schedule_def.selector mode = schedule_def.mode execution_params = ExecutionParams( selector=selector, environment_dict=environment_dict, mode=mode, execution_metadata=ExecutionMetadata(tags=tags, run_id=None), step_keys=None, ) run, result = _execute_schedule(graphene_info, pipeline_def, execution_params, errors) graphene_info.context.instance.update_schedule_tick( repository, tick.with_status(ScheduleTickStatus.SUCCESS, run_id=run.run_id), ) return result except Exception as exc: # pylint: disable=broad-except error_data = serializable_error_info_from_exc_info(sys.exc_info()) if tick: graphene_info.context.instance.update_schedule_tick( repository, tick.with_status(ScheduleTickStatus.FAILURE, error=error_data), ) raise exc
def _launch_run(instance, repo_location, external_schedule, external_pipeline, tick_context, run_request): run_config = run_request.run_config schedule_tags = run_request.tags execution_plan_snapshot = None errors = [] try: external_execution_plan = repo_location.get_external_execution_plan( external_pipeline, run_config, external_schedule.mode, step_keys_to_execute=None, ) execution_plan_snapshot = external_execution_plan.execution_plan_snapshot except DagsterSubprocessError as e: errors.extend(e.subprocess_error_infos) except Exception as e: # pylint: disable=broad-except errors.append(serializable_error_info_from_exc_info(sys.exc_info())) pipeline_tags = external_pipeline.tags or {} check_tags(pipeline_tags, "pipeline_tags") tags = merge_dicts(pipeline_tags, schedule_tags) # Enter the run in the DB with the information we have possibly_invalid_pipeline_run = instance.create_run( pipeline_name=external_schedule.pipeline_name, run_id=None, run_config=run_config, mode=external_schedule.mode, solids_to_execute=external_pipeline.solids_to_execute, step_keys_to_execute=None, solid_selection=external_pipeline.solid_selection, status=None, root_run_id=None, parent_run_id=None, tags=tags, pipeline_snapshot=external_pipeline.pipeline_snapshot, execution_plan_snapshot=execution_plan_snapshot, parent_pipeline_snapshot=external_pipeline.parent_pipeline_snapshot, external_pipeline_origin=external_pipeline.get_external_origin(), ) tick_context.add_run(run_id=possibly_invalid_pipeline_run.run_id, run_key=run_request.run_key) # If there were errors, inject them into the event log and fail the run if len(errors) > 0: for error in errors: instance.report_engine_event( error.message, possibly_invalid_pipeline_run, EngineEventData.engine_error(error), ) instance.report_run_failed(possibly_invalid_pipeline_run) tick_context.stream.send( ScheduledExecutionFailed( run_id=possibly_invalid_pipeline_run.run_id, errors=errors)) return try: launched_run = instance.submit_run( possibly_invalid_pipeline_run.run_id, external_pipeline) except Exception: # pylint: disable=broad-except tick_context.stream.send( ScheduledExecutionFailed( run_id=possibly_invalid_pipeline_run.run_id, errors=[serializable_error_info_from_exc_info(sys.exc_info())], )) return tick_context.stream.send( ScheduledExecutionSuccess(run_id=launched_run.run_id))
def _launch_scheduled_execution(instance, repo_location, external_repo, external_schedule, tick, stream): pipeline_selector = PipelineSelector( location_name=repo_location.name, repository_name=external_repo.name, pipeline_name=external_schedule.pipeline_name, solid_selection=external_schedule.solid_selection, ) subset_pipeline_result = repo_location.get_subset_external_pipeline_result( pipeline_selector) external_pipeline = ExternalPipeline( subset_pipeline_result.external_pipeline_data, external_repo.handle, ) schedule_execution_data = repo_location.get_external_schedule_execution_data( instance=instance, repository_handle=external_repo.handle, schedule_name=external_schedule.name, schedule_execution_data_mode=ScheduleExecutionDataMode. LAUNCH_SCHEDULED_EXECUTION, scheduled_execution_time= None, # No way to know this in general for this scheduler ) run_config = {} schedule_tags = {} execution_plan_snapshot = None errors = [] if isinstance(schedule_execution_data, ExternalScheduleExecutionErrorData): error = schedule_execution_data.error tick.update_with_status(ScheduleTickStatus.FAILURE, error=error) stream.send(ScheduledExecutionFailed(run_id=None, errors=[error])) return elif not schedule_execution_data.should_execute: # Update tick to skipped state and return tick.update_with_status(ScheduleTickStatus.SKIPPED) stream.send(ScheduledExecutionSkipped()) return else: run_config = schedule_execution_data.run_config schedule_tags = schedule_execution_data.tags try: external_execution_plan = repo_location.get_external_execution_plan( external_pipeline, run_config, external_schedule.mode, step_keys_to_execute=None, ) execution_plan_snapshot = external_execution_plan.execution_plan_snapshot except DagsterSubprocessError as e: errors.extend(e.subprocess_error_infos) except Exception as e: # pylint: disable=broad-except errors.append(serializable_error_info_from_exc_info( sys.exc_info())) pipeline_tags = external_pipeline.tags or {} check_tags(pipeline_tags, "pipeline_tags") tags = merge_dicts(pipeline_tags, schedule_tags) # Enter the run in the DB with the information we have possibly_invalid_pipeline_run = instance.create_run( pipeline_name=external_schedule.pipeline_name, run_id=None, run_config=run_config, mode=external_schedule.mode, solids_to_execute=external_pipeline.solids_to_execute, step_keys_to_execute=None, solid_selection=external_pipeline.solid_selection, status=None, root_run_id=None, parent_run_id=None, tags=tags, pipeline_snapshot=external_pipeline.pipeline_snapshot, execution_plan_snapshot=execution_plan_snapshot, parent_pipeline_snapshot=external_pipeline.parent_pipeline_snapshot, ) tick.update_with_status(ScheduleTickStatus.SUCCESS, run_id=possibly_invalid_pipeline_run.run_id) # If there were errors, inject them into the event log and fail the run if len(errors) > 0: for error in errors: instance.report_engine_event( error.message, possibly_invalid_pipeline_run, EngineEventData.engine_error(error), ) instance.report_run_failed(possibly_invalid_pipeline_run) stream.send( ScheduledExecutionFailed( run_id=possibly_invalid_pipeline_run.run_id, errors=errors)) return try: launched_run = instance.launch_run( possibly_invalid_pipeline_run.run_id, external_pipeline) except Exception: # pylint: disable=broad-except stream.send( ScheduledExecutionFailed( run_id=possibly_invalid_pipeline_run.run_id, errors=[error])) return stream.send(ScheduledExecutionSuccess(run_id=launched_run.run_id)) return
def _create_scheduler_run( instance, logger, schedule_time, repo_location, external_schedule, external_pipeline, run_request, ): run_config = run_request.run_config schedule_tags = run_request.tags execution_plan_errors = [] execution_plan_snapshot = None try: external_execution_plan = repo_location.get_external_execution_plan( external_pipeline, run_config, external_schedule.mode, step_keys_to_execute=None, ) execution_plan_snapshot = external_execution_plan.execution_plan_snapshot except DagsterSubprocessError as e: execution_plan_errors.extend(e.subprocess_error_infos) except Exception as e: # pylint: disable=broad-except execution_plan_errors.append( serializable_error_info_from_exc_info(sys.exc_info())) pipeline_tags = external_pipeline.tags or {} check_tags(pipeline_tags, "pipeline_tags") tags = merge_dicts(pipeline_tags, schedule_tags) tags[SCHEDULED_EXECUTION_TIME_TAG] = schedule_time.in_tz("UTC").isoformat() if run_request.run_key: tags[RUN_KEY_TAG] = run_request.run_key # If the run was scheduled correctly but there was an error creating its # run config, enter it into the run DB with a FAILURE status possibly_invalid_pipeline_run = instance.create_run( pipeline_name=external_schedule.pipeline_name, run_id=None, run_config=run_config, mode=external_schedule.mode, solids_to_execute=external_pipeline.solids_to_execute, step_keys_to_execute=None, solid_selection=external_pipeline.solid_selection, status=(PipelineRunStatus.FAILURE if len(execution_plan_errors) > 0 else PipelineRunStatus.NOT_STARTED), root_run_id=None, parent_run_id=None, tags=tags, pipeline_snapshot=external_pipeline.pipeline_snapshot, execution_plan_snapshot=execution_plan_snapshot, parent_pipeline_snapshot=external_pipeline.parent_pipeline_snapshot, external_pipeline_origin=external_pipeline.get_external_origin(), ) if len(execution_plan_errors) > 0: for error in execution_plan_errors: instance.report_engine_event( error.message, possibly_invalid_pipeline_run, EngineEventData.engine_error(error), ) instance.report_run_failed(possibly_invalid_pipeline_run) error_string = "\n".join( [error.to_string() for error in execution_plan_errors]) logger.error( f"Failed to fetch execution plan for {external_schedule.name}: {error_string}" ) return possibly_invalid_pipeline_run
def __init__( self, name, cron_schedule, pipeline_name, run_config=None, run_config_fn=None, tags=None, tags_fn=None, solid_selection=None, mode="default", should_execute=None, environment_vars=None, execution_timezone=None, execution_fn=None, description=None, ): if not croniter.is_valid(cron_schedule): raise DagsterInvalidDefinitionError( f"Found invalid cron schedule '{cron_schedule}' for schedule '{name}''." ) self._name = check_valid_name(name) self._pipeline_name = check.str_param(pipeline_name, "pipeline_name") self._mode = check.opt_str_param(mode, "mode", DEFAULT_MODE_NAME) self._solid_selection = check.opt_nullable_list_param( solid_selection, "solid_selection", of_type=str ) self._description = check.opt_str_param(description, "description") self._cron_schedule = check.str_param(cron_schedule, "cron_schedule") self._environment_vars = check.opt_dict_param( environment_vars, "environment_vars", key_type=str, value_type=str ) self._execution_timezone = check.opt_str_param(execution_timezone, "execution_timezone") if execution_fn and (run_config_fn or tags_fn or should_execute or tags or run_config): raise DagsterInvalidDefinitionError( "Attempted to provide both execution_fn and individual run_config/tags arguments " "to ScheduleDefinition. Must provide only one of the two." ) elif execution_fn: self._execution_fn = check.opt_callable_param(execution_fn, "execution_fn") else: if run_config_fn and run_config: raise DagsterInvalidDefinitionError( "Attempted to provide both run_config_fn and run_config as arguments" " to ScheduleDefinition. Must provide only one of the two." ) run_config_fn = check.opt_callable_param( run_config_fn, "run_config_fn", default=lambda _context: check.opt_dict_param(run_config, "run_config"), ) if tags_fn and tags: raise DagsterInvalidDefinitionError( "Attempted to provide both tags_fn and tags as arguments" " to ScheduleDefinition. Must provide only one of the two." ) elif tags: check_tags(tags, "tags") tags_fn = lambda _context: tags else: tags_fn = check.opt_callable_param(tags_fn, "tags_fn", default=lambda _context: {}) should_execute = check.opt_callable_param( should_execute, "should_execute", default=lambda _context: True ) def _execution_fn(context): with user_code_error_boundary( ScheduleExecutionError, lambda: f"Error occurred during the execution of should_execute for schedule {name}", ): if not should_execute(context): yield SkipReason( "should_execute function for {schedule_name} returned false.".format( schedule_name=name ) ) return with user_code_error_boundary( ScheduleExecutionError, lambda: f"Error occurred during the execution of run_config_fn for schedule {name}", ): evaluated_run_config = run_config_fn(context) with user_code_error_boundary( ScheduleExecutionError, lambda: f"Error occurred during the execution of tags_fn for schedule {name}", ): evaluated_tags = tags_fn(context) yield RunRequest( run_key=None, run_config=evaluated_run_config, tags=evaluated_tags, ) self._execution_fn = _execution_fn if self._execution_timezone: try: # Verify that the timezone can be loaded pendulum.timezone(self._execution_timezone) except Exception: raise DagsterInvalidDefinitionError( "Invalid execution timezone {timezone} for {schedule_name}".format( schedule_name=name, timezone=self._execution_timezone ) )
def __init__( self, name, cron_schedule, pipeline_name, run_config=None, run_config_fn=None, tags=None, tags_fn=None, solid_selection=None, mode="default", should_execute=None, environment_vars=None, execution_timezone=None, ): super(ScheduleDefinition, self).__init__( check_valid_name(name), job_type=JobType.SCHEDULE, pipeline_name=check.str_param(pipeline_name, "pipeline_name"), mode=check.opt_str_param(mode, "mode", DEFAULT_MODE_NAME), solid_selection=check.opt_nullable_list_param(solid_selection, "solid_selection", of_type=str), ) self._cron_schedule = check.str_param(cron_schedule, "cron_schedule") if run_config_fn and run_config: raise DagsterInvalidDefinitionError( "Attempted to provide both run_config_fn and run_config as arguments" " to ScheduleDefinition. Must provide only one of the two.") self._run_config_fn = check.opt_callable_param( run_config_fn, "run_config_fn", default=lambda _context: check.opt_dict_param( run_config, "run_config"), ) if tags_fn and tags: raise DagsterInvalidDefinitionError( "Attempted to provide both tags_fn and tags as arguments" " to ScheduleDefinition. Must provide only one of the two.") elif tags: check_tags(tags, "tags") self._tags_fn = lambda _context: tags else: self._tags_fn = check.opt_callable_param( tags_fn, "tags_fn", default=lambda _context: {}) self._should_execute = check.opt_callable_param( should_execute, "should_execute", lambda _context: True) self._environment_vars = check.opt_dict_param(environment_vars, "environment_vars", key_type=str, value_type=str) self._execution_timezone = check.opt_str_param(execution_timezone, "execution_timezone") if self._execution_timezone: try: # Verify that the timezone can be loaded pendulum.timezone(self._execution_timezone) except ValueError: raise DagsterInvalidDefinitionError( "Invalid execution timezone {timezone} for {schedule_name}" .format(schedule_name=name, timezone=self._execution_timezone))