parsing_opts.py revision 5257dbb8
1import argparse
2from collections import namedtuple, OrderedDict
3from .common import list_intersect, list_difference, is_valid_ipv4, is_valid_mac, list_remove_dup
4from .text_opts import format_text
5from ..trex_stl_types import *
6from .constants import ON_OFF_DICT, UP_DOWN_DICT, FLOW_CTRL_DICT
7
8import sys
9import re
10import os
11
12ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options'])
13ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options'])
14
15
16# list of available parsing options
17_constants = '''
18
19MULTIPLIER
20MULTIPLIER_STRICT
21PORT_LIST
22ALL_PORTS
23PORT_LIST_WITH_ALL
24FILE_PATH
25FILE_FROM_DB
26SERVER_IP
27STREAM_FROM_PATH_OR_FILE
28DURATION
29FORCE
30DRY_RUN
31XTERM
32TOTAL
33FULL_OUTPUT
34IPG
35MIN_IPG
36SPEEDUP
37COUNT
38PROMISCUOUS
39LINK_STATUS
40LED_STATUS
41TUNABLES
42REMOTE_FILE
43LOCKED
44PIN_CORES
45CORE_MASK
46DUAL
47FLOW_CTRL
48SUPPORTED
49FILE_PATH_NO_CHECK
50
51OUTPUT_FILENAME
52LIMIT
53PORT_RESTART
54
55RETRIES
56
57SINGLE_PORT
58DST_MAC
59
60PING_IPV4
61PING_COUNT
62PKT_SIZE
63
64SERVICE_OFF
65
66TX_PORT_LIST
67RX_PORT_LIST
68
69SRC_IPV4
70DST_IPV4
71
72GLOBAL_STATS
73PORT_STATS
74PORT_STATUS
75STREAMS_STATS
76STATS_MASK
77CPU_STATS
78MBUF_STATS
79EXTENDED_STATS
80EXTENDED_INC_ZERO_STATS
81
82STREAMS_MASK
83CORE_MASK_GROUP
84
85# ALL_STREAMS
86# STREAM_LIST_WITH_ALL
87
88# list of ArgumentGroup types
89MUTEX
90
91'''
92
93for index, line in enumerate(_constants.splitlines()):
94    var = line.strip().split()
95    if not var or '#' in var[0]:
96        continue
97    exec('%s = %s' % (var[0], index))
98
99
100def check_negative(value):
101    ivalue = int(value)
102    if ivalue < 0:
103        raise argparse.ArgumentTypeError("non positive value provided: '{0}'".format(value))
104    return ivalue
105
106def match_time_unit(val):
107    '''match some val against time shortcut inputs '''
108    match = re.match("^(\d+(\.\d+)?)([m|h]?)$", val)
109    if match:
110        digit = float(match.group(1))
111        unit = match.group(3)
112        if not unit:
113            return digit
114        elif unit == 'm':
115            return digit*60
116        else:
117            return digit*60*60
118    else:
119        raise argparse.ArgumentTypeError("Duration should be passed in the following format: \n"
120                                         "-d 100 : in sec \n"
121                                         "-d 10m : in min \n"
122                                         "-d 1h  : in hours")
123
124
125match_multiplier_help = """Multiplier should be passed in the following format:
126                          [number][<empty> | bps | kbps | mbps |  gbps | pps | kpps | mpps | %% ].
127
128                          no suffix will provide an absoulute factor and percentage
129                          will provide a percentage of the line rate. examples
130
131                          '-m 10',
132                          '-m 10kbps',
133                          '-m 10kbpsl1',
134                          '-m 10mpps',
135                          '-m 23%% '
136
137                          '-m 23%%' : is 23%% L1 bandwidth
138                          '-m 23mbps': is 23mbps in L2 bandwidth (including FCS+4)
139                          '-m 23mbpsl1': is 23mbps in L1 bandwidth
140
141                          """
142
143
144# decodes multiplier
145# if allow_update - no +/- is allowed
146# divide states between how many entities the
147# value should be divided
148def decode_multiplier(val, allow_update = False, divide_count = 1):
149
150    factor_table = {None: 1, 'k': 1e3, 'm': 1e6, 'g': 1e9}
151    pattern = "^(\d+(\.\d+)?)(((k|m|g)?(bpsl1|pps|bps))|%)?"
152
153    # do we allow updates ?  +/-
154    if not allow_update:
155        pattern += "$"
156        match = re.match(pattern, val)
157        op = None
158    else:
159        pattern += "([\+\-])?$"
160        match = re.match(pattern, val)
161        if match:
162            op  = match.group(7)
163        else:
164            op = None
165
166    result = {}
167
168    if not match:
169        return None
170
171    # value in group 1
172    value = float(match.group(1))
173
174    # decode unit as whole
175    unit = match.group(3)
176
177    # k,m,g
178    factor = match.group(5)
179
180    # type of multiplier
181    m_type = match.group(6)
182
183    # raw type (factor)
184    if not unit:
185        result['type'] = 'raw'
186        result['value'] = value
187
188    # percentage
189    elif unit == '%':
190        result['type'] = 'percentage'
191        result['value']  = value
192
193    elif m_type == 'bps':
194        result['type'] = 'bps'
195        result['value'] = value * factor_table[factor]
196
197    elif m_type == 'pps':
198        result['type'] = 'pps'
199        result['value'] = value * factor_table[factor]
200
201    elif m_type == 'bpsl1':
202        result['type'] = 'bpsl1'
203        result['value'] = value * factor_table[factor]
204
205
206    if op == "+":
207        result['op'] = "add"
208    elif op == "-":
209        result['op'] = "sub"
210    else:
211        result['op'] = "abs"
212
213    if result['op'] != 'percentage':
214        result['value'] = result['value'] / divide_count
215
216    return result
217
218
219
220def match_multiplier(val):
221    '''match some val against multiplier  shortcut inputs '''
222    result = decode_multiplier(val, allow_update = True)
223    if not result:
224        raise argparse.ArgumentTypeError(match_multiplier_help)
225
226    return val
227
228
229def match_multiplier_strict(val):
230    '''match some val against multiplier  shortcut inputs '''
231    result = decode_multiplier(val, allow_update = False)
232    if not result:
233        raise argparse.ArgumentTypeError(match_multiplier_help)
234
235    return val
236
237def hex_int (val):
238    pattern = r"0x[1-9a-fA-F][0-9a-fA-F]*"
239
240    if not re.match(pattern, val):
241        raise argparse.ArgumentTypeError("{0} is not a valid positive HEX formatted number".format(val))
242
243    return int(val, 16)
244
245
246def is_valid_file(filename):
247    if not os.path.isfile(filename):
248        raise argparse.ArgumentTypeError("The file '%s' does not exist" % filename)
249
250    return filename
251
252def check_ipv4_addr (ipv4_str):
253    if not is_valid_ipv4(ipv4_str):
254        raise argparse.ArgumentTypeError("invalid IPv4 address: '{0}'".format(ipv4_str))
255
256    return ipv4_str
257
258def check_pkt_size (pkt_size):
259    try:
260        pkt_size = int(pkt_size)
261    except ValueError:
262        raise argparse.ArgumentTypeError("invalid packet size type: '{0}'".format(pkt_size))
263
264    if (pkt_size < 64) or (pkt_size > 9216):
265        raise argparse.ArgumentTypeError("invalid packet size: '{0}' - valid range is 64 to 9216".format(pkt_size))
266
267    return pkt_size
268
269def check_mac_addr (addr):
270    if not is_valid_mac(addr):
271        raise argparse.ArgumentTypeError("not a valid MAC address: '{0}'".format(addr))
272
273    return addr
274
275
276def decode_tunables (tunable_str):
277    tunables = {}
278
279    # split by comma to tokens
280    tokens = tunable_str.split(',')
281
282    # each token is of form X=Y
283    for token in tokens:
284        m = re.search('(\S+)=(.+)', token)
285        if not m:
286            raise argparse.ArgumentTypeError("bad syntax for tunables: {0}".format(token))
287        val = m.group(2)           # string
288        if val.startswith(("'", '"')) and val.endswith(("'", '"')) and len(val) > 1: # need to remove the quotes from value
289            val = val[1:-1]
290        elif val.startswith('0x'): # hex
291            val = int(val, 16)
292        else:
293            try:
294                if '.' in val:     # float
295                    val = float(val)
296                else:              # int
297                    val = int(val)
298            except:
299                pass
300        tunables[m.group(1)] = val
301
302    return tunables
303
304
305
306OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
307                                 {'help': match_multiplier_help,
308                                  'dest': "mult",
309                                  'default': "1",
310                                  'type': match_multiplier}),
311
312              MULTIPLIER_STRICT: ArgumentPack(['-m', '--multiplier'],
313                               {'help': match_multiplier_help,
314                                  'dest': "mult",
315                                  'default': "1",
316                                  'type': match_multiplier_strict}),
317
318              TOTAL: ArgumentPack(['-t', '--total'],
319                                 {'help': "traffic will be divided between all ports specified",
320                                  'dest': "total",
321                                  'default': False,
322                                  'action': "store_true"}),
323
324              IPG: ArgumentPack(['-i', '--ipg'],
325                                {'help': "IPG value in usec between packets. default will be from the pcap",
326                                 'dest': "ipg_usec",
327                                 'default':  None,
328                                 'type': float}),
329
330              MIN_IPG: ArgumentPack(['--min-ipg'],
331                                {'help': "Minimal IPG value in usec between packets. Used to guard from too small IPGs.",
332                                 'dest': "min_ipg_usec",
333                                 'default':  None,
334                                 'type': float}),
335
336              SPEEDUP: ArgumentPack(['-s', '--speedup'],
337                                   {'help': "Factor to accelerate the injection. effectively means IPG = IPG / SPEEDUP",
338                                    'dest': "speedup",
339                                    'default':  1.0,
340                                    'type': float}),
341
342              COUNT: ArgumentPack(['-n', '--count'],
343                                  {'help': "How many times to perform action [default is 1, 0 means forever]",
344                                   'dest': "count",
345                                   'default':  1,
346                                   'type': int}),
347
348              PROMISCUOUS: ArgumentPack(['--prom'],
349                                        {'help': "Set port promiscuous on/off",
350                                         'choices': ON_OFF_DICT}),
351
352              LINK_STATUS: ArgumentPack(['--link'],
353                                     {'help': 'Set link status up/down',
354                                      'choices': UP_DOWN_DICT}),
355
356              LED_STATUS: ArgumentPack(['--led'],
357                                   {'help': 'Set LED status on/off',
358                                    'choices': ON_OFF_DICT}),
359
360              FLOW_CTRL: ArgumentPack(['--fc'],
361                                   {'help': 'Set Flow Control type',
362                                    'dest': 'flow_ctrl',
363                                    'choices': FLOW_CTRL_DICT}),
364
365              SRC_IPV4: ArgumentPack(['--src'],
366                                     {'help': 'Configure source IPv4 address',
367                                      'dest': 'src_ipv4',
368                                      'required': True,
369                                      'type': check_ipv4_addr}),
370
371              DST_IPV4: ArgumentPack(['--dst'],
372                                     {'help': 'Configure destination IPv4 address',
373                                      'dest': 'dst_ipv4',
374                                      'required': True,
375                                      'type': check_ipv4_addr}),
376
377
378              DST_MAC: ArgumentPack(['--dst'],
379                                    {'help': 'Configure destination MAC address',
380                                     'dest': 'dst_mac',
381                                     'required': True,
382                                     'type': check_mac_addr}),
383
384              RETRIES: ArgumentPack(['-r', '--retries'],
385                                    {'help': 'retries count [default is zero]',
386                                     'dest': 'retries',
387                                     'default':  0,
388                                     'type': int}),
389
390
391              OUTPUT_FILENAME: ArgumentPack(['-o', '--output'],
392                                            {'help': 'Output PCAP filename',
393                                             'dest': 'output_filename',
394                                             'default': None,
395                                             'required': True,
396                                             'type': str}),
397
398
399              PORT_RESTART: ArgumentPack(['-r', '--restart'],
400                                         {'help': 'hard restart port(s)',
401                                          'dest': 'restart',
402                                          'default': False,
403                                          'action': 'store_true'}),
404
405              LIMIT: ArgumentPack(['-l', '--limit'],
406                                  {'help': 'Limit the packet count to be written to the file',
407                                   'dest': 'limit',
408                                   'default':  1000,
409                                   'type': int}),
410
411
412              SUPPORTED: ArgumentPack(['--supp'],
413                                   {'help': 'Show which attributes are supported by current NICs',
414                                    'default': None,
415                                    'action': 'store_true'}),
416
417              TUNABLES: ArgumentPack(['-t'],
418                                     {'help': "Sets tunables for a profile. Example: '-t fsize=100,pg_id=7'",
419                                      'metavar': 'T1=VAL[,T2=VAL ...]',
420                                      'dest': "tunables",
421                                      'default': None,
422                                      'action': 'merge',
423                                      'type': decode_tunables}),
424
425              PORT_LIST: ArgumentPack(['--port', '-p'],
426                                        {"nargs": '+',
427                                         'dest':'ports',
428                                         'metavar': 'PORTS',
429                                         'action': 'merge',
430                                         'type': int,
431                                         'help': "A list of ports on which to apply the command",
432                                         'default': []}),
433
434
435              SINGLE_PORT: ArgumentPack(['--port', '-p'],
436                                        {'dest':'ports',
437                                         'type': int,
438                                         'metavar': 'PORT',
439                                         'help': 'source port for the action',
440                                         'required': True}),
441
442              PING_IPV4: ArgumentPack(['-d'],
443                                      {'help': 'which IPv4 to ping',
444                                      'dest': 'ping_ipv4',
445                                      'required': True,
446                                      'type': check_ipv4_addr}),
447
448              PING_COUNT: ArgumentPack(['-n', '--count'],
449                                       {'help': 'How many times to ping [default is 5]',
450                                        'dest': 'count',
451                                        'default':  5,
452                                        'type': int}),
453
454              PKT_SIZE: ArgumentPack(['-s'],
455                                     {'dest':'pkt_size',
456                                      'help': 'packet size to use',
457                                      'default': 64,
458                                      'type': check_pkt_size}),
459
460
461              ALL_PORTS: ArgumentPack(['-a'],
462                                        {"action": "store_true",
463                                         "dest": "all_ports",
464                                         'help': "Set this flag to apply the command on all available ports",
465                                         'default': False},),
466
467              DURATION: ArgumentPack(['-d'],
468                                        {'action': "store",
469                                         'metavar': 'TIME',
470                                         'dest': 'duration',
471                                         'type': match_time_unit,
472                                         'default': -1.0,
473                                         'help': "Set duration time for job."}),
474
475              FORCE: ArgumentPack(['--force'],
476                                        {"action": "store_true",
477                                         'default': False,
478                                         'help': "Set if you want to stop active ports before appyling command."}),
479
480              REMOTE_FILE: ArgumentPack(['-r', '--remote'],
481                                        {"action": "store_true",
482                                         'default': False,
483                                         'help': "file path should be interpeted by the server (remote file)"}),
484
485              DUAL: ArgumentPack(['--dual'],
486                                 {"action": "store_true",
487                                  'default': False,
488                                  'help': "Transmit in a dual mode - requires ownership on the adjacent port"}),
489
490              FILE_PATH: ArgumentPack(['-f'],
491                                      {'metavar': 'FILE',
492                                       'dest': 'file',
493                                       'nargs': 1,
494                                       'required': True,
495                                       'type': is_valid_file,
496                                       'help': "File path to load"}),
497
498              FILE_PATH_NO_CHECK: ArgumentPack(['-f'],
499                                      {'metavar': 'FILE',
500                                       'dest': 'file',
501                                       'nargs': 1,
502                                       'required': True,
503                                       'type': str,
504                                       'help': "File path to load"}),
505
506              FILE_FROM_DB: ArgumentPack(['--db'],
507                                         {'metavar': 'LOADED_STREAM_PACK',
508                                          'help': "A stream pack which already loaded into console cache."}),
509
510              SERVER_IP: ArgumentPack(['--server'],
511                                      {'metavar': 'SERVER',
512                                       'help': "server IP"}),
513
514              DRY_RUN: ArgumentPack(['-n', '--dry'],
515                                    {'action': 'store_true',
516                                     'dest': 'dry',
517                                     'default': False,
518                                     'help': "Dry run - no traffic will be injected"}),
519
520              XTERM: ArgumentPack(['-x', '--xterm'],
521                                  {'action': 'store_true',
522                                   'dest': 'xterm',
523                                   'default': False,
524                                   'help': "Starts TUI in xterm window"}),
525
526              LOCKED: ArgumentPack(['-l', '--locked'],
527                                   {'action': 'store_true',
528                                    'dest': 'locked',
529                                    'default': False,
530                                    'help': "Locks TUI on legend mode"}),
531
532              FULL_OUTPUT: ArgumentPack(['--full'],
533                                         {'action': 'store_true',
534                                          'help': "Prompt full info in a JSON format"}),
535
536              GLOBAL_STATS: ArgumentPack(['-g'],
537                                         {'action': 'store_true',
538                                          'help': "Fetch only global statistics"}),
539
540              PORT_STATS: ArgumentPack(['-p'],
541                                       {'action': 'store_true',
542                                        'help': "Fetch only port statistics"}),
543
544              PORT_STATUS: ArgumentPack(['--ps'],
545                                        {'action': 'store_true',
546                                         'help': "Fetch only port status data"}),
547
548              STREAMS_STATS: ArgumentPack(['-s'],
549                                          {'action': 'store_true',
550                                           'help': "Fetch only streams stats"}),
551
552              CPU_STATS: ArgumentPack(['-c'],
553                                      {'action': 'store_true',
554                                       'help': "Fetch only CPU utilization stats"}),
555
556              MBUF_STATS: ArgumentPack(['-m'],
557                                       {'action': 'store_true',
558                                        'help': "Fetch only MBUF utilization stats"}),
559
560              EXTENDED_STATS: ArgumentPack(['-x'],
561                                       {'action': 'store_true',
562                                        'help': "Fetch xstats of port, excluding lines with zero values"}),
563
564              EXTENDED_INC_ZERO_STATS: ArgumentPack(['--xz'],
565                                       {'action': 'store_true',
566                                        'help': "Fetch xstats of port, including lines with zero values"}),
567
568              STREAMS_MASK: ArgumentPack(['--streams'],
569                                         {"nargs": '+',
570                                          'dest':'streams',
571                                          'metavar': 'STREAMS',
572                                          'type': int,
573                                          'help': "A list of stream IDs to query about. Default: analyze all streams",
574                                          'default': []}),
575
576
577              PIN_CORES: ArgumentPack(['--pin'],
578                                      {'action': 'store_true',
579                                       'dest': 'pin_cores',
580                                       'default': False,
581                                       'help': "Pin cores to interfaces - cores will be divided between interfaces (performance boot for symetric profiles)"}),
582
583              CORE_MASK: ArgumentPack(['--core_mask'],
584                                      {'action': 'store',
585                                       'nargs': '+',
586                                       'type': hex_int,
587                                       'dest': 'core_mask',
588                                       'default': None,
589                                       'help': "Core mask - only cores responding to the bit mask will be active"}),
590
591              SERVICE_OFF: ArgumentPack(['--off'],
592                                        {'action': 'store_false',
593                                         'dest': 'enabled',
594                                         'default': True,
595                                         'help': 'Deactivates services on port(s)'}),
596
597              TX_PORT_LIST: ArgumentPack(['--tx'],
598                                         {'nargs': '+',
599                                          'dest':'tx_port_list',
600                                          'metavar': 'TX',
601                                          'action': 'merge',
602                                          'type': int,
603                                          'help': 'A list of ports to capture on the TX side',
604                                          'default': []}),
605
606              RX_PORT_LIST: ArgumentPack(['--rx'],
607                                         {'nargs': '+',
608                                          'dest':'rx_port_list',
609                                          'metavar': 'RX',
610                                          'action': 'merge',
611                                          'type': int,
612                                          'help': 'A list of ports to capture on the RX side',
613                                          'default': []}),
614
615              # advanced options
616              PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST,
617                                                        ALL_PORTS],
618                                                {'required': False}),
619
620
621              STREAM_FROM_PATH_OR_FILE: ArgumentGroup(MUTEX, [FILE_PATH,
622                                                              FILE_FROM_DB],
623                                                      {'required': True}),
624              STATS_MASK: ArgumentGroup(MUTEX, [GLOBAL_STATS,
625                                                PORT_STATS,
626                                                PORT_STATUS,
627                                                STREAMS_STATS,
628                                                CPU_STATS,
629                                                MBUF_STATS,
630                                                EXTENDED_STATS,
631                                                EXTENDED_INC_ZERO_STATS,],
632                                        {}),
633
634
635              CORE_MASK_GROUP:  ArgumentGroup(MUTEX, [PIN_CORES,
636                                                      CORE_MASK],
637                                              {'required': False}),
638
639              }
640
641class _MergeAction(argparse._AppendAction):
642    def __call__(self, parser, namespace, values, option_string=None):
643        items = getattr(namespace, self.dest)
644        if not items:
645            items = values
646        elif type(items) is list and type(values) is list:
647            items.extend(values)
648        elif type(items) is dict and type(values) is dict: # tunables are dict
649            items.update(values)
650        else:
651            raise Exception("Argparser 'merge' option should be used on dict or list.")
652
653        setattr(namespace, self.dest, items)
654
655class CCmdArgParser(argparse.ArgumentParser):
656
657    def __init__(self, stateless_client, *args, **kwargs):
658        super(CCmdArgParser, self).__init__(*args, **kwargs)
659        self.stateless_client = stateless_client
660        self.cmd_name = kwargs.get('prog')
661        self.register('action', 'merge', _MergeAction)
662
663    # hook this to the logger
664    def _print_message(self, message, file=None):
665        self.stateless_client.logger.log(message)
666
667    def error(self, message):
668        self.print_usage()
669        self._print_message(('%s: error: %s\n') % (self.prog, message))
670        raise ValueError(message)
671
672    def has_ports_cfg (self, opts):
673        return hasattr(opts, "all_ports") or hasattr(opts, "ports")
674
675    def parse_args(self, args=None, namespace=None, default_ports=None, verify_acquired=False):
676        try:
677            opts = super(CCmdArgParser, self).parse_args(args, namespace)
678            if opts is None:
679                return RC_ERR("'{0}' - invalid arguments".format(self.cmd_name))
680
681            if not self.has_ports_cfg(opts):
682                return opts
683
684            opts.ports = listify(opts.ports)
685
686            # if all ports are marked or
687            if (getattr(opts, "all_ports", None) == True) or (getattr(opts, "ports", None) == []):
688                if default_ports is None:
689                    opts.ports = self.stateless_client.get_acquired_ports()
690                else:
691                    opts.ports = default_ports
692
693            opts.ports = list_remove_dup(opts.ports)
694
695            # so maybe we have ports configured
696            invalid_ports = list_difference(opts.ports, self.stateless_client.get_all_ports())
697            if invalid_ports:
698
699                if len(invalid_ports) > 1:
700                    msg = "{0}: port(s) {1} are not valid port IDs".format(self.cmd_name, invalid_ports)
701                else:
702                    msg = "{0}: port {1} is not a valid port ID".format(self.cmd_name, invalid_ports[0])
703
704                self.stateless_client.logger.log(format_text(msg, 'bold'))
705                return RC_ERR(msg)
706
707            # verify acquired ports
708            if verify_acquired:
709                acquired_ports = self.stateless_client.get_acquired_ports()
710
711                diff = list_difference(opts.ports, acquired_ports)
712                if diff:
713                    msg = "{0} - port(s) {1} are not acquired".format(self.cmd_name, diff)
714                    self.stateless_client.logger.log(format_text(msg, 'bold'))
715                    return RC_ERR(msg)
716
717                # no acquire ports at all
718                if not acquired_ports:
719                    msg = "{0} - no acquired ports".format(self.cmd_name)
720                    self.stateless_client.logger.log(format_text(msg, 'bold'))
721                    return RC_ERR(msg)
722
723
724            return opts
725
726        except ValueError as e:
727            return RC_ERR("'{0}' - {1}".format(self.cmd_name, str(e)))
728
729        except SystemExit:
730            # recover from system exit scenarios, such as "help", or bad arguments.
731            return RC_ERR("'{0}' - {1}".format(self.cmd_name, "no action"))
732
733
734def get_flags (opt):
735    return OPTIONS_DB[opt].name_or_flags
736
737def gen_parser(stateless_client, op_name, description, *args):
738    parser = CCmdArgParser(stateless_client, prog=op_name, conflict_handler='resolve',
739                           description=description)
740    for param in args:
741        try:
742
743            if isinstance(param, int):
744                argument = OPTIONS_DB[param]
745            else:
746                argument = param
747
748            if isinstance(argument, ArgumentGroup):
749                if argument.type == MUTEX:
750                    # handle as mutually exclusive group
751                    group = parser.add_mutually_exclusive_group(**argument.options)
752                    for sub_argument in argument.args:
753                        group.add_argument(*OPTIONS_DB[sub_argument].name_or_flags,
754                                           **OPTIONS_DB[sub_argument].options)
755                else:
756                    # ignore invalid objects
757                    continue
758            elif isinstance(argument, ArgumentPack):
759                parser.add_argument(*argument.name_or_flags,
760                                    **argument.options)
761            else:
762                # ignore invalid objects
763                continue
764        except KeyError as e:
765            cause = e.args[0]
766            raise KeyError("The attribute '{0}' is missing as a field of the {1} option.\n".format(cause, param))
767    return parser
768
769
770if __name__ == "__main__":
771    pass
772