ATLAS Offline Software
AthOptionsParser.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 # @file AthenaCommon.AthOptionsParser
4 # @purpose the central module to parse command line options of athena.py
5 
6 import argparse
7 import os
8 import sys
9 
10 # Are we running within athena.py ?
11 __athenaCLI = False
12 
14  """Enable athena-specific command linea arguments"""
15  global __athenaCLI
16  __athenaCLI = True
17 
18 
19 class JobOptAction(argparse.Action):
20  """Check filename extension and fill relevant options"""
21  def __call__(self, parser, args, values, option_string=None):
22  scripts = [f for f in values if f[-3:] == '.py']
23  pkls = [f for f in values if f[-4:] == '.pkl']
24 
25  if (scripts and pkls) or len(pkls)>1:
26  raise ValueError('Only job options or one pickle file is allowed')
27 
28  setattr(args, self.dest, scripts)
29  args.fromdb = pkls[0] if pkls else None
30 
31 
32 class MemCheckAction(argparse.Action):
33  """Enable Hepheastus"""
34  def __call__(self, parser, args, values, option_string=None):
35 
36  setattr(args, self.dest,
37  [] if values=='all' else [values])
38 
39  # early import is needed for proper offloading later
40  import Hephaestus.MemoryTracker as memtrack # noqa: F401
41 
42  if option_string=='--delete-check':
43  args.memchk_mode = 'delete-check'
44  import Hephaestus.DeleteChecker # noqa: F401
45  else:
46  args.memchk_mode = 'leak-check'
47 
48 
49 class AthHelpFlags(argparse.Action):
50  """Custom help action to support flags"""
51  def __call__(self, parser, namespace, values, option_string=None):
52 
53  if not values:
54  parser.print_help()
55  else:
56  import runpy
57  sys.argv = ['athena.py', '--help']
58  if values != 'flags':
59  sys.argv.append(values)
60  runpy.run_module('AthenaConfiguration.AthNoop', run_name='__main__')
61 
62  sys.exit(0)
63 
64 
66  """Version string"""
67  from PyUtils.Helpers import release_metadata
68  return ('[%(project name)s-%(release)s] [%(platform)s] '
69  '[%(nightly name)s/%(nightly release)s] -- built on [%(date)s]' % release_metadata())
70 
71 
72 def set_environment(opts):
73  """Set required envirnoment variables based on command line"""
74 
75  # user decision about TDAQ ERS signal handlers
76  if opts.enable_ers_hdlr == 'y':
77  os.unsetenv('TDAQ_ERS_NO_SIGNAL_HANDLERS')
78  else:
79  os.environ['TDAQ_ERS_NO_SIGNAL_HANDLERS'] = '1'
80 
81  os.environ['LIBC_FATAL_STDERR_'] = '1' # ATEAM-241
82 
83 
84 def check_tcmalloc(opts):
85  libname = 'libtcmalloc' # also covers libtcmalloc_minimal.so
86  # Warn if...
87  if ( libname not in os.getenv('LD_PRELOAD','') and # tcmalloc not loaded
88  os.getenv('USETCMALLOC') in ('1', None) and # but requested (or default)
89  opts.do_leak_chk is None ): # and not disabled by leak checker
90 
91  print ('*******************************************************************************')
92  print ('WARNING: option --tcmalloc used or implied, but libtcmalloc.so not loaded.')
93  print (' This is probably because you\'re using athena.py in a non standard way')
94  print (' such as "python athena.py ..." or "nohup athena.py"')
95  print (' If you wish to use tcmalloc, you will have to manually LD_PRELOAD it')
96  print ('*******************************************************************************')
97  print ('')
98 
99 
101  from AthenaCommon.AthenaCommonFlags import athenaCommonFlags
102 
103  if opts.filesInput is not None:
104  import glob
105  files = []
106  for fe in opts.filesInput.split(","):
107  found = glob.glob(fe)
108  # if not found, add string directly
109  files += found if found else [fe]
110 
111  athenaCommonFlags.FilesInput.set_Value_and_Lock(files)
112 
113  if opts.evtMax is not None:
114  athenaCommonFlags.EvtMax.set_Value_and_Lock(opts.evtMax)
115 
116  if opts.skipEvents is not None:
117  athenaCommonFlags.SkipEvents.set_Value_and_Lock(opts.skipEvents)
118 
119 
120 def configureCAfromArgs(acc, opts):
121  """Configure CA from relevant command line arguments if running from pkl"""
122 
123  if opts.interactive:
124  acc.interactive = opts.interactive
125 
126  if opts.skipEvents:
127  try:
128  acc.getService('EventSelector').SkipEvents = opts.skipEvents
129  except Exception:
130  raise AthOptionsError("--skipEvents is not supported by this CA")
131 
132  if opts.debug:
133  acc.setDebugStage(opts.debug)
134 
135  if opts.loglevel:
136  from AthenaCommon import Constants
137  acc.getService('MessageSvc').OutputLevel = getattr(Constants, opts.loglevel)
138 
139 
140 def getArgumentParser(legacy_args=False, **kwargs):
141  """Create default argument parser"""
142 
143  parser = argparse.ArgumentParser(formatter_class=
144  lambda prog : argparse.HelpFormatter(
145  prog, max_help_position=40, width=100),
146  add_help=False, **kwargs)
147 
148  parser.expert_groups = [] # List of expert option groups
149 
150  # --------------------------------------------------------------------------
151  g = parser.add_argument_group('Main options')
152 
153  if __athenaCLI and legacy_args:
154  g.add_argument('scripts', nargs='*', action=JobOptAction,
155  help='scripts or pickle file to run')
156 
157  g.add_argument('-l', '--loglevel', metavar='LVL', type=str.upper, default='INFO',
158  choices=['ALL', 'VERBOSE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'FATAL'],
159  help='logging level: %(choices)s')
160 
161  g.add_argument('--filesInput', metavar='FILES',
162  help='set FilesInput property (comma-separated list with wildcards)')
163 
164  g.add_argument('--evtMax', metavar='N', type=int,
165  help='max number of events to process')
166 
167  g.add_argument('--skipEvents', metavar='N', type=int,
168  help='number of events to skip')
169 
170  g.add_argument('--nprocs', metavar='N', type=int,
171  help='enable AthenaMP if %(metavar)s>=1 or %(metavar)s==-1')
172 
173  g.add_argument('--threads', metavar='N', type=int,
174  help='number of threads for AthenaMT, threads per worker for athenaMP')
175 
176  g.add_argument('--concurrent-events', metavar='N', type=int,
177  help='number of concurrent events for AthenaMT')
178 
179  g.add_argument('--CA', action='store_true',
180  help='force ComponentAccumulator mode')
181 
182  g.add_argument('--config-only', metavar='FILE', nargs='?', default=False, const=True,
183  help='run only configuration and optionally store in %(metavar)s')
184 
185  g.add_argument('--mtes', action='store_true',
186  help='activate multi-threaded event service')
187 
188  g.add_argument('--mtes-channel', metavar='NAME', default='EventService_EventRanges',
189  help='yampl channel name between pilot and AthenaMT in event service mode')
190 
191  g.add_argument('--version', action='version', version=get_version(),
192  help='print version number')
193 
194  g.add_argument('-h', '--help', metavar='FLAGS', nargs='?', action=AthHelpFlags,
195  help='show help message (for FLAGS, "flags" for all categories)' if __athenaCLI
196  else 'show help message (for FLAGS category)')
197 
198  # --------------------------------------------------------------------------
199  g = parser.add_argument_group('Monitoring and debugging')
200 
201  g.add_argument('--perfmon', metavar='MODE', nargs='?', const='fastmonmt',
202  help='enable performance monitoring toolkit in MODE')
203 
204  g.add_argument('-i', '--interactive', nargs='?', choices=['init', 'run'], const='init',
205  help='interactive mode with optional stage (default: init)')
206 
207  g.add_argument('--profile-python', metavar='FILE',
208  help='profile python code, dump in %(metavar)s (.pkl or .txt)')
209 
210  g.add_argument('-d', '--debug', metavar='STAGE', nargs='?', const='init',
211  choices=['conf', 'init', 'exec', 'fini'],
212  help='attach debugger at stage: %(choices)s [%(const)s]')
213 
214  g.add_argument('--debugWorker', action='store_true', dest='debug_worker',
215  help='pause AthenaMP workers at bootstrap until SIGUSR1 signal received')
216 
217  g.add_argument('--leak-check', metavar='STAGE', dest='do_leak_chk', action=MemCheckAction,
218  choices=['initialize', 'start', 'beginrun', 'execute', 'finalize',
219  'endrun', 'stop', 'full', 'full-athena', 'all'],
220  help='perform basic memory leak checking, disables the use of tcmalloc.')
221 
222  g.add_argument('--delete-check', metavar='STAGE', dest='do_leak_chk', action=MemCheckAction,
223  choices=['initialize', 'start', 'beginrun', 'execute', 'finalize',
224  'endrun', 'stop', 'full', 'full-athena', 'all'],
225  help='perform double delete checking, disables the use of tcmalloc.')
226 
227  g.add_argument('--tracelevel', metavar='LEVEL', nargs='?', type=int, choices=range(1,4), const=3,
228  help='trace level for python configuration (%(choices)s)')
229 
230  # --------------------------------------------------------------------------
231  if legacy_args:
232  g = parser.add_argument_group('Legacy options')
233 
234  g.add_argument('-c', '--command', metavar='CMD',
235  help='one-liner, runs before any scripts')
236 
237  g.add_argument('--drop-and-reload', action='store_true', dest='drop_reload',
238  help='offload configuration and start new process')
239 
240  g.add_argument('--dump-configuration', metavar='FILE', dest='config_dump_file',
241  help='dump an ASCII version of the configuration to %(metavar)s')
242 
243  g.add_argument('-s', '--showincludes', action='store_true',
244  help='show printout of included files')
245 
246  # --------------------------------------------------------------------------
247  if __athenaCLI:
248  g = parser.add_argument_group('System options')
249 
250  g.add_argument('--tcmalloc', action='store_true', dest='tcmalloc', default=True,
251  help='use tcmalloc.so for memory allocation [DEFAULT]')
252 
253  g.add_argument('--stdcmalloc', action='store_false', dest='tcmalloc',
254  help='use libc malloc for memory allocation')
255 
256  g.add_argument('--stdcmath', action='store_true', default=True,
257  help='use libc malloc for memory allocation [DEFAULT]')
258 
259  g.add_argument('--imf', action='store_true',
260  help='use Intel Math Function library')
261 
262  g.add_argument('--exctrace', action='store_true',
263  help='preload exception trace collector')
264 
265  g.add_argument('--preloadlib', metavar='LIB',
266  help='localized preload of library %(metavar)s')
267 
268  g.add_argument('--enable-ers-hdlr', metavar='y/n', default='n', choices=['y','n'],
269  help='enable or not the ERS handler [%(default)s]')
270 
271  return parser
272 
273 
274 class AthOptionsError(SystemExit):
275  def __init__(self, reason=None):
276  import AthenaCommon.ExitCodes as ath_codes
277  if reason is None:
278  reason = ath_codes.OPTIONS_UNKNOWN
279  try:
280  message = ath_codes.codes[reason]
281  except KeyError:
282  message = ath_codes.codes[ath_codes.OPTIONS_UNKNOWN]
283 
284  SystemExit.__init__(self, reason, message)
285 
286 
287 def _help_and_exit(reason=None):
288  raise AthOptionsError(reason)
289 
290 
291 def parse(legacy_args=False):
292  """parses command line arguments and returns an ``Options`` instance"""
293 
294  # Everything after a single "-" is treated as "user options". This is for
295  # backwards compatibility with AthArgumentParser used in analysis.
296  # FIXME: we should revisit this and find an alternative
297  try:
298  dashpos = sys.argv.index("-")
299  except ValueError: # normal case, no isolated dash found
300  args = sys.argv[1:]
301  user_opts = []
302  else:
303  args = sys.argv[1:dashpos]
304  user_opts = sys.argv[dashpos+1:]
305 
306  parser = getArgumentParser(legacy_args)
307  opts, leftover = parser.parse_known_args(args)
308  opts.user_opts = user_opts
309 
310  # If the argument parser has been extended, the script name(s) may end up
311  # in the leftovers. Try to find them there:
312  if not (opts.scripts or opts.fromdb) and leftover:
313  JobOptAction([], 'scripts')(parser, opts, leftover)
314 
315  if not (opts.scripts or opts.fromdb) and not opts.interactive:
316  parser.error("the following arguments are required: scripts")
317 
318  set_environment(opts)
319  check_tcmalloc(opts)
320 
321  return opts
322 
323 
324 if __name__ == '__main__':
325  from pprint import pprint
326  pprint(vars(parse()))
327  print('TDAQ_ERS_NO_SIGNAL_HANDLERS', os.getenv('TDAQ_ERS_NO_SIGNAL_HANDLERS'))
python.AthOptionsParser.MemCheckAction
Definition: AthOptionsParser.py:32
python.AthOptionsParser.get_version
def get_version()
Definition: AthOptionsParser.py:65
python.AthOptionsParser.getArgumentParser
def getArgumentParser(legacy_args=False, **kwargs)
Definition: AthOptionsParser.py:140
python.AthOptionsParser.JobOptAction.__call__
def __call__(self, parser, args, values, option_string=None)
Definition: AthOptionsParser.py:21
python.AthOptionsParser.JobOptAction
Definition: AthOptionsParser.py:19
python.AthOptionsParser.MemCheckAction.__call__
def __call__(self, parser, args, values, option_string=None)
Definition: AthOptionsParser.py:34
python.Helpers.release_metadata
def release_metadata()
Definition: Tools/PyUtils/python/Helpers.py:143
python.AthOptionsParser.AthHelpFlags
Definition: AthOptionsParser.py:49
python.AthOptionsParser.AthOptionsError
Definition: AthOptionsParser.py:274
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
python.AthOptionsParser.set_environment
def set_environment(opts)
Definition: AthOptionsParser.py:72
python.AthOptionsParser._help_and_exit
def _help_and_exit(reason=None)
Definition: AthOptionsParser.py:287
python.AthOptionsParser.AthHelpFlags.__call__
def __call__(self, parser, namespace, values, option_string=None)
Definition: AthOptionsParser.py:51
python.AthOptionsParser.fill_athenaCommonFlags
def fill_athenaCommonFlags(opts)
Definition: AthOptionsParser.py:100
python.AthOptionsParser.AthOptionsError.__init__
def __init__(self, reason=None)
Definition: AthOptionsParser.py:275
python.AthOptionsParser.parse
def parse(legacy_args=False)
Definition: AthOptionsParser.py:291
python.AthOptionsParser.configureCAfromArgs
def configureCAfromArgs(acc, opts)
Definition: AthOptionsParser.py:120
Muon::print
std::string print(const MuPatSegment &)
Definition: MuonTrackSteering.cxx:28
python.AthOptionsParser.check_tcmalloc
def check_tcmalloc(opts)
Definition: AthOptionsParser.py:84
python.AthOptionsParser.enable_athenaCLI
def enable_athenaCLI()
Definition: AthOptionsParser.py:13