ATLAS Offline Software
Loading...
Searching...
No Matches
AthOptionsParser.py
Go to the documentation of this file.
1# Copyright (C) 2002-2025 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
6import argparse
7import os
8import 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
19class 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
32class 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
49class 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
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
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
120def 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
140def 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("--mpi", action="store_true", default=None, help="activate MPI mode")
192
193 g.add_argument('--version', action='version', version=get_version(),
194 help='print version number')
195
196 g.add_argument('-h', '--help', metavar='FLAGS', nargs='?', action=AthHelpFlags,
197 help='show help message (for FLAGS, "flags" for all categories)' if __athenaCLI
198 else 'show help message (for FLAGS category)')
199
200
201 # --------------------------------------------------------------------------
202 g = parser.add_argument_group('Monitoring and debugging')
203
204 g.add_argument('--perfmon', metavar='MODE', nargs='?', const='fastmonmt',
205 help='enable performance monitoring toolkit in MODE')
206
207 g.add_argument('-i', '--interactive', nargs='?', choices=['init', 'run'], const='init',
208 help='interactive mode with optional stage (default: init)')
209
210 g.add_argument('--profile-python', metavar='FILE',
211 help='profile python code, dump in %(metavar)s (.pkl or .txt)')
212
213 g.add_argument('-d', '--debug', metavar='STAGE', nargs='?', const='init',
214 choices=['conf', 'init', 'exec', 'fini'],
215 help='attach debugger at stage: %(choices)s [%(const)s] (gdb or $ATLAS_DEBUGGER if set)')
216
217 g.add_argument('--debugWorker', action='store_true', dest='debug_worker',
218 help='pause AthenaMP workers at bootstrap until SIGUSR1 signal received')
219
220 g.add_argument('--leak-check', metavar='STAGE', dest='do_leak_chk', action=MemCheckAction,
221 choices=['initialize', 'start', 'beginrun', 'execute', 'finalize',
222 'endrun', 'stop', 'full', 'full-athena', 'all'],
223 help='perform basic memory leak checking, disables the use of tcmalloc.')
224
225 g.add_argument('--delete-check', metavar='STAGE', dest='do_leak_chk', action=MemCheckAction,
226 choices=['initialize', 'start', 'beginrun', 'execute', 'finalize',
227 'endrun', 'stop', 'full', 'full-athena', 'all'],
228 help='perform double delete checking, disables the use of tcmalloc.')
229
230 g.add_argument('--tracelevel', metavar='LEVEL', nargs='?', type=int, choices=range(1,4), const=3,
231 help='trace level for python configuration (%(choices)s)')
232
233 # --------------------------------------------------------------------------
234 if legacy_args:
235 g = parser.add_argument_group('Legacy options')
236
237 g.add_argument('-c', '--command', metavar='CMD',
238 help='one-liner, runs before any scripts')
239
240 g.add_argument('--drop-and-reload', action='store_true', dest='drop_reload',
241 help='offload configuration and start new process')
242
243 g.add_argument('--dump-configuration', metavar='FILE', dest='config_dump_file',
244 help='dump an ASCII version of the configuration to %(metavar)s')
245
246 g.add_argument('-s', '--showincludes', action='store_true',
247 help='show printout of included files')
248
249 # --------------------------------------------------------------------------
250 if __athenaCLI:
251 g = parser.add_argument_group('System options')
252
253 g.add_argument('--tcmalloc', action='store_true', dest='tcmalloc', default=True,
254 help='use tcmalloc.so for memory allocation [DEFAULT]')
255
256 g.add_argument('--stdcmalloc', action='store_false', dest='tcmalloc',
257 help='use libc malloc for memory allocation')
258
259 g.add_argument('--stdcmath', action='store_true', default=True,
260 help='use libc malloc for memory allocation [DEFAULT]')
261
262 g.add_argument('--imf', action='store_true',
263 help='use Intel Math Function library')
264
265 g.add_argument('--exctrace', action='store_true',
266 help='preload exception trace collector')
267
268 g.add_argument('--no-excabort', action='store_true',
269 help='disable converting some exceptions to abort')
270
271 g.add_argument('--preloadlib', metavar='LIB',
272 help='localized preload of library %(metavar)s')
273
274 g.add_argument('--enable-ers-hdlr', metavar='y/n', default='n', choices=['y','n'],
275 help='enable or not the ERS handler [%(default)s]')
276
277 return parser
278
279
280class AthOptionsError(SystemExit):
281 def __init__(self, reason=None, message=None):
282 import AthenaCommon.ExitCodes as ath_codes
283 if reason is None:
284 reason = ath_codes.OPTIONS_UNKNOWN
285 if message is None:
286 try:
287 message = ath_codes.codes[reason]
288 except KeyError:
289 message = ath_codes.codes[ath_codes.OPTIONS_UNKNOWN]
290
291 super().__init__(reason, message)
292
293
294def _help_and_exit(reason=None):
295 raise AthOptionsError(reason)
296
297
298def parse(legacy_args=False):
299 """parses command line arguments and returns an ``Options`` instance"""
300
301 # Everything after a single "-" is treated as "user options". This is for
302 # backwards compatibility with AthArgumentParser used in analysis.
303 # FIXME: we should revisit this and find an alternative
304 try:
305 dashpos = sys.argv.index("-")
306 except ValueError: # normal case, no isolated dash found
307 args = sys.argv[1:]
308 user_opts = []
309 else:
310 args = sys.argv[1:dashpos]
311 user_opts = sys.argv[dashpos+1:]
312
313 parser = getArgumentParser(legacy_args)
314
315 # perform a pre-parse without -h or --help
316 # if don't have a script, then will just re-parse with help active
317 # otherwise will effectively let script do the parsing
318 # i.e. this allows the following to work:
319 # athena MyScript.py --help
320 # to reveal the help messaging determined by MyScript.py
321 doHelp = any(a in ('-h', '--help') or a.startswith('--help=') for a in args)
322 if doHelp:
323 # need to unrequire any required arguments in order to do a "pre-parse"
324 unrequiredActions = []
325 for a in parser._actions:
326 if a.required:
327 unrequiredActions.append(a)
328 a.required = False
329
330 # remove the help actions for the "pre-parse"
331 helpActions = {'-h' : parser._option_string_actions.pop('-h'),
332 '--help' : parser._option_string_actions.pop('--help')}
333
334 # parse with the modified parser
335 opts, leftover = parser.parse_known_args(args)
336
337 # restore original settings
338 parser._option_string_actions.update(helpActions)
339 for a in unrequiredActions:
340 a.required = True
341
342 # no script, just run argparsing as normal
343 if not opts.scripts:
344 opts, leftover = parser.parse_known_args(args)
345 else:
346 opts, leftover = parser.parse_known_args(args)
347
348 opts.user_opts = user_opts
349
350 # If the argument parser has been extended, the script name(s) may end up
351 # in the leftovers. Try to find them there:
352 if not (opts.scripts or opts.fromdb) and leftover:
353 JobOptAction([], 'scripts')(parser, opts, leftover)
354
355 if not (opts.scripts or opts.fromdb) and not opts.interactive:
356 parser.error("the following arguments are required: scripts")
357
358 set_environment(opts)
359 check_tcmalloc(opts)
360
361 return opts
362
363
364if __name__ == '__main__':
365 from pprint import pprint
366 pprint(vars(parse()))
367 print('TDAQ_ERS_NO_SIGNAL_HANDLERS', os.getenv('TDAQ_ERS_NO_SIGNAL_HANDLERS'))
__call__(self, parser, namespace, values, option_string=None)
__call__(self, parser, args, values, option_string=None)
__call__(self, parser, args, values, option_string=None)
getArgumentParser(legacy_args=False, **kwargs)