Belle II Software development
SubJob Class Reference
Inheritance diagram for SubJob:
Job

Public Member Functions

 __init__ (self, job, subjob_id, input_files=None)
 
 output_dir (self)
 
 working_dir (self)
 
 name (self)
 
 status (self)
 
 status (self, status)
 
 subjobs (self)
 
 job_dict (self)
 
 __getattr__ (self, attribute)
 
 __repr__ (self)
 
 ready (self)
 
 update_status (self)
 
 create_subjob (self, i, input_files=None, args=None)
 
 dump_to_json (self, file_path)
 
 from_json (cls, file_path)
 
 dump_input_data (self)
 
 copy_input_sandbox_files_to_working_dir (self)
 
 check_input_data_files (self)
 
 full_command (self)
 
 append_current_basf2_setup_cmds (self)
 

Public Attributes

 id = subjob_id
 Id of Subjob.
 
 parent = job
 Job() instance of parent to this SubJob.
 
 splitter = None
 The SubjobSplitter used to create subjobs if necessary.
 
list input_sandbox_files = []
 Files to be copied directly into the working directory (pathlib.Path).
 
list output_patterns = []
 Files that we produce during the job and want to be returned.
 
list cmd = []
 Command and arguments as a list that will be run by the job on the backend.
 
list args = []
 The arguments that will be applied to the cmd (These are ignored by SubJobs as they have their own arguments)
 
list input_files = []
 Input files to job (str), a list of these is copied to the working directory.
 
list setup_cmds = []
 Bash commands to run before the main self.cmd (mainly used for batch system setup)
 
dict backend_args = {}
 Config dictionary for the backend to use when submitting the job.
 
 result = None
 The result object of this Job.
 

Static Public Attributes

dict statuses = {"init": 0, "submitted": 1, "running": 2, "failed": 3, "completed": 4}
 Allowed Job status dictionary.
 
list exit_statuses = ["failed", "completed"]
 Job statuses that correspond to the Job being finished (successfully or not)
 

Protected Member Functions

 _get_overall_status_from_subjobs (self)
 

Protected Attributes

str _status = "init"
 The actual status of the overall Job.
 

Detailed Description

This mini-class simply holds basic information about which subjob you are
and a reference to the parent Job object to be able to access the main data there.
Rather than replicating all of the parent job's configuration again.

Definition at line 678 of file backends.py.

Constructor & Destructor Documentation

◆ __init__()

__init__ ( self,
job,
subjob_id,
input_files = None )
 

Definition at line 685 of file backends.py.

685 def __init__(self, job, subjob_id, input_files=None):
686 """
687 """
688
689 self.id = subjob_id
690
691 self.parent = job
692
693 if not input_files:
694 input_files = []
695 self.input_files = input_files
696
698 self.result = None
699
700 self._status = "init"
701
702 self.args = []
703

Member Function Documentation

◆ __getattr__()

__getattr__ ( self,
attribute )
Since a SubJob uses attributes from the parent Job, everything simply accesses the Job attributes
unless otherwise specified.

Definition at line 760 of file backends.py.

760 def __getattr__(self, attribute):
761 """
762 Since a SubJob uses attributes from the parent Job, everything simply accesses the Job attributes
763 unless otherwise specified.
764 """
765 return getattr(self.parent, attribute)
766

◆ __repr__()

__repr__ ( self)
 

Definition at line 767 of file backends.py.

767 def __repr__(self):
768 """
769 """
770 return f"SubJob({self.name})"
771
772

◆ _get_overall_status_from_subjobs()

_get_overall_status_from_subjobs ( self)
protectedinherited
 

Definition at line 463 of file backends.py.

463 def _get_overall_status_from_subjobs(self):
464 """
465 """
466 subjob_statuses = [subjob.status for subjob in self.subjobs.values()]
467 status_level = min([self.statuses[status] for status in subjob_statuses])
468 for status, level in self.statuses.items():
469 if level == status_level:
470 return status
471

◆ append_current_basf2_setup_cmds()

append_current_basf2_setup_cmds ( self)
inherited
This adds simple setup commands like ``source /path/to/tools/b2setup`` to your `Job`.
It should detect if you are using a local release or CVMFS and append the correct commands
so that the job will have the same basf2 release environment. It should also detect
if a local release is not compiled with the ``opt`` option.

Note that this *doesn't mean that every environment variable is inherited* from the submitting
process environment.

Definition at line 640 of file backends.py.

640 def append_current_basf2_setup_cmds(self):
641 """
642 This adds simple setup commands like ``source /path/to/tools/b2setup`` to your `Job`.
643 It should detect if you are using a local release or CVMFS and append the correct commands
644 so that the job will have the same basf2 release environment. It should also detect
645 if a local release is not compiled with the ``opt`` option.
646
647 Note that this *doesn't mean that every environment variable is inherited* from the submitting
648 process environment.
649 """
650 def append_environment_variable(cmds, envvar):
651 """
652 Append a command for setting an environment variable.
653 """
654 if envvar in os.environ:
655 cmds.append(f"""if [ -z "${{{envvar}}}" ]; then""")
656 cmds.append(f" export {envvar}={os.environ[envvar]}")
657 cmds.append("fi")
658
659 if "BELLE2_TOOLS" not in os.environ:
660 raise BackendError("No BELLE2_TOOLS found in environment")
661 # Export all the environment variables defined via _backend_job_envvars
662 for envvar in _backend_job_envvars:
663 append_environment_variable(self.setup_cmds, envvar)
664 if "BELLE2_RELEASE" in os.environ:
665 self.setup_cmds.append(f"source {os.environ['BELLE2_TOOLS']}/b2setup {os.environ['BELLE2_RELEASE']}")
666 elif 'BELLE2_LOCAL_DIR' in os.environ:
667 self.setup_cmds.append("export BELLE2_NO_TOOLS_CHECK=\"TRUE\"")
668 self.setup_cmds.append(f"BACKEND_B2SETUP={os.environ['BELLE2_TOOLS']}/b2setup")
669 self.setup_cmds.append(f"BACKEND_BELLE2_RELEASE_LOC={os.environ['BELLE2_LOCAL_DIR']}")
670 self.setup_cmds.append(f"BACKEND_BELLE2_OPTION={os.environ['BELLE2_OPTION']}")
671 self.setup_cmds.append("pushd $BACKEND_BELLE2_RELEASE_LOC > /dev/null")
672 self.setup_cmds.append("source $BACKEND_B2SETUP")
673 # b2code-option has to be executed only after the source of the tools.
674 self.setup_cmds.append("b2code-option $BACKEND_BELLE2_OPTION")
675 self.setup_cmds.append("popd > /dev/null")
676
677

◆ check_input_data_files()

check_input_data_files ( self)
inherited
Check the input files and make sure that there aren't any duplicates.
Also check if the files actually exist if possible.

Definition at line 598 of file backends.py.

598 def check_input_data_files(self):
599 """
600 Check the input files and make sure that there aren't any duplicates.
601 Also check if the files actually exist if possible.
602 """
603 existing_input_files = [] # We use a list instead of set to avoid losing any ordering of files
604 for file_path in self.input_files:
605 file_uri = parse_file_uri(file_path)
606 if file_uri.scheme == "file":
607 p = Path(file_uri.path)
608 if p.is_file():
609 if file_uri.geturl() not in existing_input_files:
610 existing_input_files.append(file_uri.geturl())
611 else:
612 B2WARNING(f"Requested input file path {file_path} was already added, skipping it.")
613 else:
614 B2WARNING(f"Requested input file path {file_path} does not exist, skipping it.")
615 else:
616 B2DEBUG(29, f"{file_path} is not a local file URI. Skipping checking if file exists")
617 if file_path not in existing_input_files:
618 existing_input_files.append(file_path)
619 else:
620 B2WARNING(f"Requested input file path {file_path} was already added, skipping it.")
621 if self.input_files and not existing_input_files:
622 B2WARNING(f"No valid input file paths found for {self.name}, but some were requested.")
623
624 # Replace the Job's input files with the ones that exist + duplicates removed
625 self.input_files = existing_input_files
626

◆ copy_input_sandbox_files_to_working_dir()

copy_input_sandbox_files_to_working_dir ( self)
inherited
Get all of the requested files for the input sandbox and copy them to the working directory.
Files like the submit.sh or input_data.json are not part of this process.

Definition at line 587 of file backends.py.

587 def copy_input_sandbox_files_to_working_dir(self):
588 """
589 Get all of the requested files for the input sandbox and copy them to the working directory.
590 Files like the submit.sh or input_data.json are not part of this process.
591 """
592 for file_path in self.input_sandbox_files:
593 if file_path.is_dir():
594 shutil.copytree(file_path, Path(self.working_dir, file_path.name))
595 else:
596 shutil.copy(file_path, self.working_dir)
597

◆ create_subjob()

create_subjob ( self,
i,
input_files = None,
args = None )
inherited
Creates a subjob Job object that references that parent Job.
Returns the SubJob object at the end.

Definition at line 435 of file backends.py.

435 def create_subjob(self, i, input_files=None, args=None):
436 """
437 Creates a subjob Job object that references that parent Job.
438 Returns the SubJob object at the end.
439 """
440 if i not in self.subjobs:
441 B2INFO(f"Creating {self}.Subjob({i})")
442 subjob = SubJob(self, i, input_files)
443 if args:
444 subjob.args = args
445 self.subjobs[i] = subjob
446 return subjob
447 else:
448 B2WARNING(f"{self} already contains SubJob({i})! This will not be created.")
449

◆ dump_input_data()

dump_input_data ( self)
inherited
Dumps the `Job.input_files` attribute to a JSON file. input_files should be a list of
string URI objects.

Definition at line 579 of file backends.py.

579 def dump_input_data(self):
580 """
581 Dumps the `Job.input_files` attribute to a JSON file. input_files should be a list of
582 string URI objects.
583 """
584 with open(Path(self.working_dir, _input_data_file_path), mode="w") as input_data_file:
585 json.dump(self.input_files, input_data_file, indent=2)
586

◆ dump_to_json()

dump_to_json ( self,
file_path )
inherited
Dumps the Job object configuration to a JSON file so that it can be read in again later.

Parameters:
  file_path(`basf2.Path`): The filepath we'll dump to

Definition at line 538 of file backends.py.

538 def dump_to_json(self, file_path):
539 """
540 Dumps the Job object configuration to a JSON file so that it can be read in again later.
541
542 Parameters:
543 file_path(`basf2.Path`): The filepath we'll dump to
544 """
545 # \cond false positive doxygen warning about job_dict
546 with open(file_path, mode="w") as job_file:
547 json.dump(self.job_dict, job_file, indent=2)
548 # \endcond
549

◆ from_json()

from_json ( cls,
file_path )
inherited
 

Definition at line 551 of file backends.py.

551 def from_json(cls, file_path):
552 """
553 """
554 with open(file_path) as job_file:
555 job_dict = json.load(job_file)
556 return cls(job_dict["name"], job_dict=job_dict)
557

◆ full_command()

full_command ( self)
inherited
Returns:
    str: The full command that this job will run including any arguments.

Definition at line 628 of file backends.py.

628 def full_command(self):
629 """
630 Returns:
631 str: The full command that this job will run including any arguments.
632 """
633 all_components = self.cmd[:]
634 all_components.extend(self.args)
635 # We do a convert to string just in case arguments were generated as different types
636 full_command = " ".join(map(str, all_components))
637 B2DEBUG(29, f"Full command of {self} is '{full_command}'")
638 return full_command
639
STL class.

◆ job_dict()

job_dict ( self)
Returns:
    dict: A JSON serialisable representation of the `SubJob`. `Path <basf2.Path>` objects are converted to
    `string` via ``Path.as_posix()``. Since Subjobs inherit most of the parent job's config
    we only output the input files and arguments that are specific to this subjob and no other details.

Reimplemented from Job.

Definition at line 747 of file backends.py.

747 def job_dict(self):
748 """
749 Returns:
750 dict: A JSON serialisable representation of the `SubJob`. `Path <basf2.Path>` objects are converted to
751 `string` via ``Path.as_posix()``. Since Subjobs inherit most of the parent job's config
752 we only output the input files and arguments that are specific to this subjob and no other details.
753 """
754 job_dict = {}
755 job_dict["id"] = self.id
756 job_dict["input_files"] = self.input_files
757 job_dict["args"] = self.args
758 return job_dict
759

◆ name()

name ( self)
Getter for name of SubJob. Accesses the parent Job name to infer this.

Reimplemented from Job.

Definition at line 716 of file backends.py.

716 def name(self):
717 """Getter for name of SubJob. Accesses the parent Job name to infer this."""
718 return "_".join((self.parent.name, str(self.id)))
719

◆ output_dir()

output_dir ( self)
Getter for output_dir of SubJob. Accesses the parent Job output_dir to infer this.

Reimplemented from Job.

Definition at line 705 of file backends.py.

705 def output_dir(self):
706 """
707 Getter for output_dir of SubJob. Accesses the parent Job output_dir to infer this."""
708 return Path(self.parent.output_dir, str(self.id))
709

◆ ready()

ready ( self)
inherited
Returns whether or not the Job has finished. If the job has subjobs then it will return true when they are all finished.
It will return False as soon as it hits the first failure. Meaning that you cannot guarantee that all subjobs will have
their status updated when calling this method. Instead use :py:meth:`update_status` to update all statuses if necessary.

Definition at line 412 of file backends.py.

412 def ready(self):
413 """
414 Returns whether or not the Job has finished. If the job has subjobs then it will return true when they are all finished.
415 It will return False as soon as it hits the first failure. Meaning that you cannot guarantee that all subjobs will have
416 their status updated when calling this method. Instead use :py:meth:`update_status` to update all statuses if necessary.
417 """
418 if not self.result:
419 B2DEBUG(29, f"You requested the ready() status for {self} but there is no result object set, returning False.")
420 return False
421 else:
422 return self.result.ready()
423

◆ status() [1/2]

status ( self)
Returns the status of this SubJob.

Reimplemented from Job.

Definition at line 721 of file backends.py.

721 def status(self):
722 """
723 Returns the status of this SubJob.
724 """
725 return self._status
726

◆ status() [2/2]

status ( self,
status )
Sets the status of this Job.

Reimplemented from Job.

Definition at line 728 of file backends.py.

728 def status(self, status):
729 """
730 Sets the status of this Job.
731 """
732 # Print an error only if the job failed.
733 if status == "failed":
734 B2ERROR(f"Setting {self.name} status to failed")
735 else:
736 B2INFO(f"Setting {self.name} status to {status}")
737 self._status = status
738

◆ subjobs()

subjobs ( self)
A subjob cannot have subjobs. Always return empty list.

Reimplemented from Job.

Definition at line 740 of file backends.py.

740 def subjobs(self):
741 """
742 A subjob cannot have subjobs. Always return empty list.
743 """
744 return []
745

◆ update_status()

update_status ( self)
inherited
Calls :py:meth:`update_status` on the job's result. The result object should update all of the subjobs (if there are any)
in the best way for the type of result object/backend.

Definition at line 424 of file backends.py.

424 def update_status(self):
425 """
426 Calls :py:meth:`update_status` on the job's result. The result object should update all of the subjobs (if there are any)
427 in the best way for the type of result object/backend.
428 """
429 if not self.result:
430 B2DEBUG(29, f"You requested update_status() for {self} but there is no result object set yet. Probably not submitted.")
431 else:
432 self.result.update_status()
433 return self.status
434

◆ working_dir()

working_dir ( self)
Getter for working_dir of SubJob. Accesses the parent Job working_dir to infer this.

Reimplemented from Job.

Definition at line 711 of file backends.py.

711 def working_dir(self):
712 """Getter for working_dir of SubJob. Accesses the parent Job working_dir to infer this."""
713 return Path(self.parent.working_dir, str(self.id))
714

Member Data Documentation

◆ _status

str _status = "init"
protectedinherited

The actual status of the overall Job.

The property handles querying for the subjob status to set this

Definition at line 404 of file backends.py.

◆ args

args = []
inherited

The arguments that will be applied to the cmd (These are ignored by SubJobs as they have their own arguments)

Definition at line 376 of file backends.py.

◆ backend_args

dict backend_args = {}
inherited

Config dictionary for the backend to use when submitting the job.

Saves us from having multiple attributes that may or may not be used.

Definition at line 383 of file backends.py.

◆ cmd

list cmd = []
inherited

Command and arguments as a list that will be run by the job on the backend.

Definition at line 374 of file backends.py.

◆ exit_statuses

list exit_statuses = ["failed", "completed"]
staticinherited

Job statuses that correspond to the Job being finished (successfully or not)

Definition at line 355 of file backends.py.

◆ id

id = subjob_id

Id of Subjob.

Definition at line 689 of file backends.py.

◆ input_files

input_files = []
inherited

Input files to job (str), a list of these is copied to the working directory.

Definition at line 378 of file backends.py.

◆ input_sandbox_files

list input_sandbox_files = []
inherited

Files to be copied directly into the working directory (pathlib.Path).

Not the input root files, those should be in Job.input_files.

Definition at line 366 of file backends.py.

◆ output_patterns

list output_patterns = []
inherited

Files that we produce during the job and want to be returned.

Can use wildcard (*)

Definition at line 372 of file backends.py.

◆ parent

parent = job

Job() instance of parent to this SubJob.

Definition at line 691 of file backends.py.

◆ result

result = None
inherited

The result object of this Job.

Only filled once the job is submitted to a backend since the backend creates a special result class depending on its type.

Definition at line 402 of file backends.py.

◆ setup_cmds

setup_cmds = []
inherited

Bash commands to run before the main self.cmd (mainly used for batch system setup)

Definition at line 380 of file backends.py.

◆ splitter

splitter = None
inherited

The SubjobSplitter used to create subjobs if necessary.

Definition at line 363 of file backends.py.

◆ statuses

dict statuses = {"init": 0, "submitted": 1, "running": 2, "failed": 3, "completed": 4}
staticinherited

Allowed Job status dictionary.

The key is the status name and the value is its level. The lowest level out of all subjobs is the one that is the overall status of the overall job.

Definition at line 352 of file backends.py.


The documentation for this class was generated from the following file: