HPS-MC
component.py
Go to the documentation of this file.
1 """!
2 @package component
3 Defines the base interface that component classes should extend.
4 """
5 
6 import os
7 import sys
8 import subprocess
9 import logging
10 
11 from ._config import convert_config_value
12 from hpsmc import global_config
13 
14 
15 class Component(object):
16  """!
17  Base class for components in a job.
18 
19  Do not perform any logging in the init method of Component subclasses,
20  as this is not configured by the job manager until after the components
21  are created.
22 
23  Optional parameters are: **nevents**, **seed**
24 
25  @param name name of the component
26  @param command command to execute
27  @param nevents number of events to process
28  @param seed random seed
29  @param inputs list of input files
30  @param outputs list of output files
31  @param append_tok token to append to output file names
32  @param output_ext extension to append to output file names; format is .ext
33  @param ignore_job_params list of parameters to ignore when setting parameters
34  @param kwargs additional keyword arguments
35  """
36 
37  def __init__(self,
38  name,
39  command=None,
40  nevents=None,
41  seed=1,
42  inputs=[],
43  outputs=None,
44  append_tok=None,
45  output_ext=None,
46  ignore_job_params=[],
47  **kwargs):
48 
49  self.namename = name
50  self.commandcommand = command
51  self.neventsnevents = nevents
52  self.seedseed = seed
53  self.inputsinputs = inputs
54  self.outputsoutputs = outputs
55  self.append_tokappend_tok = append_tok
56  self.output_extoutput_ext = output_ext
57 
58 
59  self.ignore_job_paramsignore_job_params = ignore_job_params
60 
61  self.hpsmc_dirhpsmc_dir = os.getenv("HPSMC_DIR", None)
62  if self.hpsmc_dirhpsmc_dir is None:
63  raise Exception("The HPSMC_DIR is not set!")
64 
65  # Setup a logger specifically for this component. It will be configured later.
66  self.loggerlogger = logging.getLogger("{}.{}".format(__name__, self.__class__.__name__))
67 
68  def cmd_line_str(self):
69  cl = [self.commandcommand]
70  cl.extend(self.cmd_argscmd_args())
71  return ' '.join(cl)
72 
73  def execute(self, log_out=sys.stdout, log_err=sys.stderr):
74  """! Generic component execution method.
75 
76  Individual components may override this if specific behavior is required.
77 
78  @param log_out name of log file for output
79  @param log_err name of log file for error
80  @return error code
81  """
82  proc = subprocess.Popen(self.cmd_line_strcmd_line_str(), shell=True, stdout=log_out, stderr=log_err)
83  proc.communicate()
84  proc.wait()
85 
86  return proc.returncode
87 
88  def cmd_exists(self):
89  """! Check if the component's assigned command exists."""
90  return subprocess.call("type " + self.commandcommand, shell=True,
91  stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
92 
93  def cmd_args(self):
94  """! Return the command arguments of this component."""
95  return []
96 
97  def cmd_args_str(self):
98  """! Return list of arguments, making sure they are all converted to strings."""
99  return [str(c) for c in self.cmd_argscmd_args()]
100 
101  def setup(self):
102  """! Perform any necessary setup for this component to run such as making symlinks
103  to required directories.
104  """
105  pass
106 
107  def cleanup(self):
108  """! Perform post-job cleanup such as deleting temporary files."""
109  pass
110 
111  def config_logging(self, parser):
112  """!
113  Configure the logging for a component.
114 
115  @param parser the ConfigParser object passed from the job manager
116  """
117  classname = self.__class__.__name__
118  if classname in parser:
119  if 'loglevel' in parser[classname]:
120  loglevel = logging.getLevelName(parser[classname]['loglevel'])
121  self.logger.setLevel(loglevel)
122 
123  def config(self, parser):
124  """! Automatic configuration
125 
126  Automatically load attributes from config by reading in values from
127  the section with the same name as the class in the config file and
128  assigning them to class attributes with the same name.
129 
130  @param parser config parser
131  """
132  section_name = self.__class__.__name__
133  if parser.has_section(section_name):
134  for name, value in parser.items(section_name):
135  setattr(self, name, convert_config_value(value))
136  self.loggerlogger.debug("%s:%s:%s=%s" % (self.namename,
137  name,
138  getattr(self, name).__class__.__name__,
139  getattr(self, name)))
140 
141  def set_parameters(self, params):
142  """! Set class attributes for the component based on JSON parameters.
143 
144  Components should not need to override this method.
145 
146  @param params parameters to setup component
147  """
148 
149  # Set required parameters.
150  for p in self.required_parametersrequired_parameters():
151  if p not in params:
152  raise Exception("Required parameter '%s' is missing for component '%s'"
153  % (p, self.namename))
154  else:
155  if p not in self.ignore_job_paramsignore_job_params:
156  setattr(self, p, params[p])
157  self.loggerlogger.debug("%s:%s=%s [required]" % (self.namename, p, params[p]))
158  else:
159  self.loggerlogger.debug("Ignored job param '%s'" % p)
160 
161  # Set optional parameters.
162  for p in self.optional_parametersoptional_parameters():
163  if p in params:
164  if p not in self.ignore_job_paramsignore_job_params:
165  setattr(self, p, params[p])
166  self.loggerlogger.debug("%s:%s=%s [optional]" % (self.namename, p, params[p]))
167  else:
168  self.loggerlogger.debug("Ignored job param '%s'" % p)
169 
171  """!
172  Return a list of required parameters.
173 
174  The job will fail if these are not present in the JSON file.
175  """
176  return []
177 
179  """!
180  Return a list of optional parameters.
181 
182  Optional parameters are: **nevents**, **seed**
183  """
184  return ['nevents', 'seed']
185 
186  def required_config(self):
187  """!
188  Return a list of required configuration settings.
189 
190  There are none by default.
191  """
192  return []
193 
194  def check_config(self):
195  """! Raise an exception on the first missing config setting for this component."""
196  for c in self.required_configrequired_config():
197  if not hasattr(self, c):
198  raise Exception('Missing required config attribute: %s:%s' % (self.__class__.__name__, c))
199  if getattr(self, c) is None:
200  raise Exception('Config was not set: %s:%s' % (self.__class__.__name__, c))
201 
202  def input_files(self):
203  """! Get a list of input files for this component."""
204  return self.inputsinputs
205 
206  def output_files(self):
207  """! Return a list of output files created by this component.
208 
209  By default, a series of transformations will be performed on inputs to
210  transform them into outputs.
211  """
212  if self.outputsoutputs is not None and len(self.outputsoutputs):
213  return self.outputsoutputs
214  else:
215  return self._inputs_to_outputs_inputs_to_outputs()
216 
218  """! This is the default method for automatically transforming input file names
219  to outputs when output file names are not explicitly provided.
220  """
221  outputs = []
222  for infile in self.input_filesinput_files():
223  f, ext = os.path.splitext(infile)
224  if self.append_tokappend_tok is not None:
225  f += '_%s' % self.append_tokappend_tok
226  if self.output_extoutput_ext is not None:
227  ext = self.output_extoutput_ext
228  outputs.append('%s%s' % (f, ext))
229  return outputs
230 
232  """! Configure component from environment variables which are just upper case
233  versions of the required config names set in the shell environment."""
234  for c in self.required_configrequired_config():
235  self.loggerlogger.debug("Setting config '%s' from environ" % c)
236  if c.upper() in os.environ:
237  setattr(self, c, os.environ[c.upper()])
238  self.loggerlogger.debug("Set config '%s=%s' from env var '%s'" % (c, getattr(self, c), c.upper()))
239  else:
240  raise Exception("Missing config in environ for '%s'" % c)
241 
242 
244  """! A dummy component that just prints some information instead of executing a program."""
245 
246  def __init__(self, **kwargs):
247  Component.__init__(self, 'dummy', 'dummy', **kwargs)
248 
249  def execute(self, log_out=sys.stdout, log_err=sys.stderr):
250  self.loggerlogger.debug("dummy debug")
251  self.loggerlogger.info("dummy info")
252  self.loggerlogger.warning("dummy warn")
253  self.loggerlogger.critical("dummy critical")
254  self.loggerlogger.error("dummy error")
Base class for components in a job.
Definition: component.py:15
def check_config(self)
Raise an exception on the first missing config setting for this component.
Definition: component.py:194
def required_config(self)
Return a list of required configuration settings.
Definition: component.py:186
def cmd_args_str(self)
Return list of arguments, making sure they are all converted to strings.
Definition: component.py:97
def __init__(self, name, command=None, nevents=None, seed=1, inputs=[], outputs=None, append_tok=None, output_ext=None, ignore_job_params=[], **kwargs)
Definition: component.py:47
def execute(self, log_out=sys.stdout, log_err=sys.stderr)
Generic component execution method.
Definition: component.py:73
def config_from_environ(self)
Configure component from environment variables which are just upper case versions of the required con...
Definition: component.py:231
def config_logging(self, parser)
Configure the logging for a component.
Definition: component.py:111
def _inputs_to_outputs(self)
This is the default method for automatically transforming input file names to outputs when output fil...
Definition: component.py:217
def config(self, parser)
Automatic configuration.
Definition: component.py:123
def required_parameters(self)
Return a list of required parameters.
Definition: component.py:170
def output_files(self)
Return a list of output files created by this component.
Definition: component.py:206
def optional_parameters(self)
Return a list of optional parameters.
Definition: component.py:178
def input_files(self)
Get a list of input files for this component.
Definition: component.py:202
def cmd_exists(self)
Check if the component's assigned command exists.
Definition: component.py:88
def cleanup(self)
Perform post-job cleanup such as deleting temporary files.
Definition: component.py:107
def setup(self)
Perform any necessary setup for this component to run such as making symlinks to required directories...
Definition: component.py:101
def set_parameters(self, params)
Set class attributes for the component based on JSON parameters.
Definition: component.py:141
def cmd_args(self)
Return the command arguments of this component.
Definition: component.py:93
A dummy component that just prints some information instead of executing a program.
Definition: component.py:243
def execute(self, log_out=sys.stdout, log_err=sys.stderr)
Generic component execution method.
Definition: component.py:249
def __init__(self, **kwargs)
Definition: component.py:246
def convert_config_value(val)
Convert config value to Python readable value.
Definition: _config.py:24