UNPKG

130 kBJavaScriptView Raw
1// Port of python's argparse module, version 3.9.0:
2// https://github.com/python/cpython/blob/v3.9.0rc1/Lib/argparse.py
3
4'use strict'
5
6// Copyright (C) 2010-2020 Python Software Foundation.
7// Copyright (C) 2020 argparse.js authors
8
9/*
10 * Command-line parsing library
11 *
12 * This module is an optparse-inspired command-line parsing library that:
13 *
14 * - handles both optional and positional arguments
15 * - produces highly informative usage messages
16 * - supports parsers that dispatch to sub-parsers
17 *
18 * The following is a simple usage example that sums integers from the
19 * command-line and writes the result to a file::
20 *
21 * parser = argparse.ArgumentParser(
22 * description='sum the integers at the command line')
23 * parser.add_argument(
24 * 'integers', metavar='int', nargs='+', type=int,
25 * help='an integer to be summed')
26 * parser.add_argument(
27 * '--log', default=sys.stdout, type=argparse.FileType('w'),
28 * help='the file where the sum should be written')
29 * args = parser.parse_args()
30 * args.log.write('%s' % sum(args.integers))
31 * args.log.close()
32 *
33 * The module contains the following public classes:
34 *
35 * - ArgumentParser -- The main entry point for command-line parsing. As the
36 * example above shows, the add_argument() method is used to populate
37 * the parser with actions for optional and positional arguments. Then
38 * the parse_args() method is invoked to convert the args at the
39 * command-line into an object with attributes.
40 *
41 * - ArgumentError -- The exception raised by ArgumentParser objects when
42 * there are errors with the parser's actions. Errors raised while
43 * parsing the command-line are caught by ArgumentParser and emitted
44 * as command-line messages.
45 *
46 * - FileType -- A factory for defining types of files to be created. As the
47 * example above shows, instances of FileType are typically passed as
48 * the type= argument of add_argument() calls.
49 *
50 * - Action -- The base class for parser actions. Typically actions are
51 * selected by passing strings like 'store_true' or 'append_const' to
52 * the action= argument of add_argument(). However, for greater
53 * customization of ArgumentParser actions, subclasses of Action may
54 * be defined and passed as the action= argument.
55 *
56 * - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter,
57 * ArgumentDefaultsHelpFormatter -- Formatter classes which
58 * may be passed as the formatter_class= argument to the
59 * ArgumentParser constructor. HelpFormatter is the default,
60 * RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser
61 * not to change the formatting for help text, and
62 * ArgumentDefaultsHelpFormatter adds information about argument defaults
63 * to the help.
64 *
65 * All other classes in this module are considered implementation details.
66 * (Also note that HelpFormatter and RawDescriptionHelpFormatter are only
67 * considered public as object names -- the API of the formatter objects is
68 * still considered an implementation detail.)
69 */
70
71const SUPPRESS = '==SUPPRESS=='
72
73const OPTIONAL = '?'
74const ZERO_OR_MORE = '*'
75const ONE_OR_MORE = '+'
76const PARSER = 'A...'
77const REMAINDER = '...'
78const _UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args'
79
80
81// ==================================
82// Utility functions used for porting
83// ==================================
84const assert = require('assert')
85const util = require('util')
86const fs = require('fs')
87const sub = require('./lib/sub')
88const path = require('path')
89const repr = util.inspect
90
91function get_argv() {
92 // omit first argument (which is assumed to be interpreter - `node`, `coffee`, `ts-node`, etc.)
93 return process.argv.slice(1)
94}
95
96function get_terminal_size() {
97 return {
98 columns: +process.env.COLUMNS || process.stdout.columns || 80
99 }
100}
101
102function hasattr(object, name) {
103 return Object.prototype.hasOwnProperty.call(object, name)
104}
105
106function getattr(object, name, value) {
107 return hasattr(object, name) ? object[name] : value
108}
109
110function setattr(object, name, value) {
111 object[name] = value
112}
113
114function setdefault(object, name, value) {
115 if (!hasattr(object, name)) object[name] = value
116 return object[name]
117}
118
119function delattr(object, name) {
120 delete object[name]
121}
122
123function range(from, to, step=1) {
124 // range(10) is equivalent to range(0, 10)
125 if (arguments.length === 1) [ to, from ] = [ from, 0 ]
126 if (typeof from !== 'number' || typeof to !== 'number' || typeof step !== 'number') {
127 throw new TypeError('argument cannot be interpreted as an integer')
128 }
129 if (step === 0) throw new TypeError('range() arg 3 must not be zero')
130
131 let result = []
132 if (step > 0) {
133 for (let i = from; i < to; i += step) result.push(i)
134 } else {
135 for (let i = from; i > to; i += step) result.push(i)
136 }
137 return result
138}
139
140function splitlines(str, keepends = false) {
141 let result
142 if (!keepends) {
143 result = str.split(/\r\n|[\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029]/)
144 } else {
145 result = []
146 let parts = str.split(/(\r\n|[\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029])/)
147 for (let i = 0; i < parts.length; i += 2) {
148 result.push(parts[i] + (i + 1 < parts.length ? parts[i + 1] : ''))
149 }
150 }
151 if (!result[result.length - 1]) result.pop()
152 return result
153}
154
155function _string_lstrip(string, prefix_chars) {
156 let idx = 0
157 while (idx < string.length && prefix_chars.includes(string[idx])) idx++
158 return idx ? string.slice(idx) : string
159}
160
161function _string_split(string, sep, maxsplit) {
162 let result = string.split(sep)
163 if (result.length > maxsplit) {
164 result = result.slice(0, maxsplit).concat([ result.slice(maxsplit).join(sep) ])
165 }
166 return result
167}
168
169function _array_equal(array1, array2) {
170 if (array1.length !== array2.length) return false
171 for (let i = 0; i < array1.length; i++) {
172 if (array1[i] !== array2[i]) return false
173 }
174 return true
175}
176
177function _array_remove(array, item) {
178 let idx = array.indexOf(item)
179 if (idx === -1) throw new TypeError(sub('%r not in list', item))
180 array.splice(idx, 1)
181}
182
183// normalize choices to array;
184// this isn't required in python because `in` and `map` operators work with anything,
185// but in js dealing with multiple types here is too clunky
186function _choices_to_array(choices) {
187 if (choices === undefined) {
188 return []
189 } else if (Array.isArray(choices)) {
190 return choices
191 } else if (choices !== null && typeof choices[Symbol.iterator] === 'function') {
192 return Array.from(choices)
193 } else if (typeof choices === 'object' && choices !== null) {
194 return Object.keys(choices)
195 } else {
196 throw new Error(sub('invalid choices value: %r', choices))
197 }
198}
199
200// decorator that allows a class to be called without new
201function _callable(cls) {
202 let result = { // object is needed for inferred class name
203 [cls.name]: function (...args) {
204 let this_class = new.target === result || !new.target
205 return Reflect.construct(cls, args, this_class ? cls : new.target)
206 }
207 }
208 result[cls.name].prototype = cls.prototype
209 // fix default tag for toString, e.g. [object Action] instead of [object Object]
210 cls.prototype[Symbol.toStringTag] = cls.name
211 return result[cls.name]
212}
213
214function _alias(object, from, to) {
215 try {
216 let name = object.constructor.name
217 Object.defineProperty(object, from, {
218 value: util.deprecate(object[to], sub('%s.%s() is renamed to %s.%s()',
219 name, from, name, to)),
220 enumerable: false
221 })
222 } catch {}
223}
224
225// decorator that allows snake_case class methods to be called with camelCase and vice versa
226function _camelcase_alias(_class) {
227 for (let name of Object.getOwnPropertyNames(_class.prototype)) {
228 let camelcase = name.replace(/\w_[a-z]/g, s => s[0] + s[2].toUpperCase())
229 if (camelcase !== name) _alias(_class.prototype, camelcase, name)
230 }
231 return _class
232}
233
234function _to_legacy_name(key) {
235 key = key.replace(/\w_[a-z]/g, s => s[0] + s[2].toUpperCase())
236 if (key === 'default') key = 'defaultValue'
237 if (key === 'const') key = 'constant'
238 return key
239}
240
241function _to_new_name(key) {
242 if (key === 'defaultValue') key = 'default'
243 if (key === 'constant') key = 'const'
244 key = key.replace(/[A-Z]/g, c => '_' + c.toLowerCase())
245 return key
246}
247
248// parse options
249let no_default = Symbol('no_default_value')
250function _parse_opts(args, descriptor) {
251 function get_name() {
252 let stack = new Error().stack.split('\n')
253 .map(x => x.match(/^ at (.*) \(.*\)$/))
254 .filter(Boolean)
255 .map(m => m[1])
256 .map(fn => fn.match(/[^ .]*$/)[0])
257
258 if (stack.length && stack[0] === get_name.name) stack.shift()
259 if (stack.length && stack[0] === _parse_opts.name) stack.shift()
260 return stack.length ? stack[0] : ''
261 }
262
263 args = Array.from(args)
264 let kwargs = {}
265 let result = []
266 let last_opt = args.length && args[args.length - 1]
267
268 if (typeof last_opt === 'object' && last_opt !== null && !Array.isArray(last_opt) &&
269 (!last_opt.constructor || last_opt.constructor.name === 'Object')) {
270 kwargs = Object.assign({}, args.pop())
271 }
272
273 // LEGACY (v1 compatibility): camelcase
274 let renames = []
275 for (let key of Object.keys(descriptor)) {
276 let old_name = _to_legacy_name(key)
277 if (old_name !== key && (old_name in kwargs)) {
278 if (key in kwargs) {
279 // default and defaultValue specified at the same time, happens often in old tests
280 //throw new TypeError(sub('%s() got multiple values for argument %r', get_name(), key))
281 } else {
282 kwargs[key] = kwargs[old_name]
283 }
284 renames.push([ old_name, key ])
285 delete kwargs[old_name]
286 }
287 }
288 if (renames.length) {
289 let name = get_name()
290 deprecate('camelcase_' + name, sub('%s(): following options are renamed: %s',
291 name, renames.map(([ a, b ]) => sub('%r -> %r', a, b))))
292 }
293 // end
294
295 let missing_positionals = []
296 let positional_count = args.length
297
298 for (let [ key, def ] of Object.entries(descriptor)) {
299 if (key[0] === '*') {
300 if (key.length > 0 && key[1] === '*') {
301 // LEGACY (v1 compatibility): camelcase
302 let renames = []
303 for (let key of Object.keys(kwargs)) {
304 let new_name = _to_new_name(key)
305 if (new_name !== key && (key in kwargs)) {
306 if (new_name in kwargs) {
307 // default and defaultValue specified at the same time, happens often in old tests
308 //throw new TypeError(sub('%s() got multiple values for argument %r', get_name(), new_name))
309 } else {
310 kwargs[new_name] = kwargs[key]
311 }
312 renames.push([ key, new_name ])
313 delete kwargs[key]
314 }
315 }
316 if (renames.length) {
317 let name = get_name()
318 deprecate('camelcase_' + name, sub('%s(): following options are renamed: %s',
319 name, renames.map(([ a, b ]) => sub('%r -> %r', a, b))))
320 }
321 // end
322 result.push(kwargs)
323 kwargs = {}
324 } else {
325 result.push(args)
326 args = []
327 }
328 } else if (key in kwargs && args.length > 0) {
329 throw new TypeError(sub('%s() got multiple values for argument %r', get_name(), key))
330 } else if (key in kwargs) {
331 result.push(kwargs[key])
332 delete kwargs[key]
333 } else if (args.length > 0) {
334 result.push(args.shift())
335 } else if (def !== no_default) {
336 result.push(def)
337 } else {
338 missing_positionals.push(key)
339 }
340 }
341
342 if (Object.keys(kwargs).length) {
343 throw new TypeError(sub('%s() got an unexpected keyword argument %r',
344 get_name(), Object.keys(kwargs)[0]))
345 }
346
347 if (args.length) {
348 let from = Object.entries(descriptor).filter(([ k, v ]) => k[0] !== '*' && v !== no_default).length
349 let to = Object.entries(descriptor).filter(([ k ]) => k[0] !== '*').length
350 throw new TypeError(sub('%s() takes %s positional argument%s but %s %s given',
351 get_name(),
352 from === to ? sub('from %s to %s', from, to) : to,
353 from === to && to === 1 ? '' : 's',
354 positional_count,
355 positional_count === 1 ? 'was' : 'were'))
356 }
357
358 if (missing_positionals.length) {
359 let strs = missing_positionals.map(repr)
360 if (strs.length > 1) strs[strs.length - 1] = 'and ' + strs[strs.length - 1]
361 let str_joined = strs.join(strs.length === 2 ? '' : ', ')
362 throw new TypeError(sub('%s() missing %i required positional argument%s: %s',
363 get_name(), strs.length, strs.length === 1 ? '' : 's', str_joined))
364 }
365
366 return result
367}
368
369let _deprecations = {}
370function deprecate(id, string) {
371 _deprecations[id] = _deprecations[id] || util.deprecate(() => {}, string)
372 _deprecations[id]()
373}
374
375
376// =============================
377// Utility functions and classes
378// =============================
379function _AttributeHolder(cls = Object) {
380 /*
381 * Abstract base class that provides __repr__.
382 *
383 * The __repr__ method returns a string in the format::
384 * ClassName(attr=name, attr=name, ...)
385 * The attributes are determined either by a class-level attribute,
386 * '_kwarg_names', or by inspecting the instance __dict__.
387 */
388
389 return class _AttributeHolder extends cls {
390 [util.inspect.custom]() {
391 let type_name = this.constructor.name
392 let arg_strings = []
393 let star_args = {}
394 for (let arg of this._get_args()) {
395 arg_strings.push(repr(arg))
396 }
397 for (let [ name, value ] of this._get_kwargs()) {
398 if (/^[a-z_][a-z0-9_$]*$/i.test(name)) {
399 arg_strings.push(sub('%s=%r', name, value))
400 } else {
401 star_args[name] = value
402 }
403 }
404 if (Object.keys(star_args).length) {
405 arg_strings.push(sub('**%s', repr(star_args)))
406 }
407 return sub('%s(%s)', type_name, arg_strings.join(', '))
408 }
409
410 toString() {
411 return this[util.inspect.custom]()
412 }
413
414 _get_kwargs() {
415 return Object.entries(this)
416 }
417
418 _get_args() {
419 return []
420 }
421 }
422}
423
424
425function _copy_items(items) {
426 if (items === undefined) {
427 return []
428 }
429 return items.slice(0)
430}
431
432
433// ===============
434// Formatting Help
435// ===============
436const HelpFormatter = _camelcase_alias(_callable(class HelpFormatter {
437 /*
438 * Formatter for generating usage messages and argument help strings.
439 *
440 * Only the name of this class is considered a public API. All the methods
441 * provided by the class are considered an implementation detail.
442 */
443
444 constructor() {
445 let [
446 prog,
447 indent_increment,
448 max_help_position,
449 width
450 ] = _parse_opts(arguments, {
451 prog: no_default,
452 indent_increment: 2,
453 max_help_position: 24,
454 width: undefined
455 })
456
457 // default setting for width
458 if (width === undefined) {
459 width = get_terminal_size().columns
460 width -= 2
461 }
462
463 this._prog = prog
464 this._indent_increment = indent_increment
465 this._max_help_position = Math.min(max_help_position,
466 Math.max(width - 20, indent_increment * 2))
467 this._width = width
468
469 this._current_indent = 0
470 this._level = 0
471 this._action_max_length = 0
472
473 this._root_section = this._Section(this, undefined)
474 this._current_section = this._root_section
475
476 this._whitespace_matcher = /[ \t\n\r\f\v]+/g // equivalent to python /\s+/ with ASCII flag
477 this._long_break_matcher = /\n\n\n+/g
478 }
479
480 // ===============================
481 // Section and indentation methods
482 // ===============================
483 _indent() {
484 this._current_indent += this._indent_increment
485 this._level += 1
486 }
487
488 _dedent() {
489 this._current_indent -= this._indent_increment
490 assert(this._current_indent >= 0, 'Indent decreased below 0.')
491 this._level -= 1
492 }
493
494 _add_item(func, args) {
495 this._current_section.items.push([ func, args ])
496 }
497
498 // ========================
499 // Message building methods
500 // ========================
501 start_section(heading) {
502 this._indent()
503 let section = this._Section(this, this._current_section, heading)
504 this._add_item(section.format_help.bind(section), [])
505 this._current_section = section
506 }
507
508 end_section() {
509 this._current_section = this._current_section.parent
510 this._dedent()
511 }
512
513 add_text(text) {
514 if (text !== SUPPRESS && text !== undefined) {
515 this._add_item(this._format_text.bind(this), [text])
516 }
517 }
518
519 add_usage(usage, actions, groups, prefix = undefined) {
520 if (usage !== SUPPRESS) {
521 let args = [ usage, actions, groups, prefix ]
522 this._add_item(this._format_usage.bind(this), args)
523 }
524 }
525
526 add_argument(action) {
527 if (action.help !== SUPPRESS) {
528
529 // find all invocations
530 let invocations = [this._format_action_invocation(action)]
531 for (let subaction of this._iter_indented_subactions(action)) {
532 invocations.push(this._format_action_invocation(subaction))
533 }
534
535 // update the maximum item length
536 let invocation_length = Math.max(...invocations.map(invocation => invocation.length))
537 let action_length = invocation_length + this._current_indent
538 this._action_max_length = Math.max(this._action_max_length,
539 action_length)
540
541 // add the item to the list
542 this._add_item(this._format_action.bind(this), [action])
543 }
544 }
545
546 add_arguments(actions) {
547 for (let action of actions) {
548 this.add_argument(action)
549 }
550 }
551
552 // =======================
553 // Help-formatting methods
554 // =======================
555 format_help() {
556 let help = this._root_section.format_help()
557 if (help) {
558 help = help.replace(this._long_break_matcher, '\n\n')
559 help = help.replace(/^\n+|\n+$/g, '') + '\n'
560 }
561 return help
562 }
563
564 _join_parts(part_strings) {
565 return part_strings.filter(part => part && part !== SUPPRESS).join('')
566 }
567
568 _format_usage(usage, actions, groups, prefix) {
569 if (prefix === undefined) {
570 prefix = 'usage: '
571 }
572
573 // if usage is specified, use that
574 if (usage !== undefined) {
575 usage = sub(usage, { prog: this._prog })
576
577 // if no optionals or positionals are available, usage is just prog
578 } else if (usage === undefined && !actions.length) {
579 usage = sub('%(prog)s', { prog: this._prog })
580
581 // if optionals and positionals are available, calculate usage
582 } else if (usage === undefined) {
583 let prog = sub('%(prog)s', { prog: this._prog })
584
585 // split optionals from positionals
586 let optionals = []
587 let positionals = []
588 for (let action of actions) {
589 if (action.option_strings.length) {
590 optionals.push(action)
591 } else {
592 positionals.push(action)
593 }
594 }
595
596 // build full usage string
597 let action_usage = this._format_actions_usage([].concat(optionals).concat(positionals), groups)
598 usage = [ prog, action_usage ].map(String).join(' ')
599
600 // wrap the usage parts if it's too long
601 let text_width = this._width - this._current_indent
602 if (prefix.length + usage.length > text_width) {
603
604 // break usage into wrappable parts
605 let part_regexp = /\(.*?\)+(?=\s|$)|\[.*?\]+(?=\s|$)|\S+/g
606 let opt_usage = this._format_actions_usage(optionals, groups)
607 let pos_usage = this._format_actions_usage(positionals, groups)
608 let opt_parts = opt_usage.match(part_regexp) || []
609 let pos_parts = pos_usage.match(part_regexp) || []
610 assert(opt_parts.join(' ') === opt_usage)
611 assert(pos_parts.join(' ') === pos_usage)
612
613 // helper for wrapping lines
614 let get_lines = (parts, indent, prefix = undefined) => {
615 let lines = []
616 let line = []
617 let line_len
618 if (prefix !== undefined) {
619 line_len = prefix.length - 1
620 } else {
621 line_len = indent.length - 1
622 }
623 for (let part of parts) {
624 if (line_len + 1 + part.length > text_width && line) {
625 lines.push(indent + line.join(' '))
626 line = []
627 line_len = indent.length - 1
628 }
629 line.push(part)
630 line_len += part.length + 1
631 }
632 if (line.length) {
633 lines.push(indent + line.join(' '))
634 }
635 if (prefix !== undefined) {
636 lines[0] = lines[0].slice(indent.length)
637 }
638 return lines
639 }
640
641 let lines
642
643 // if prog is short, follow it with optionals or positionals
644 if (prefix.length + prog.length <= 0.75 * text_width) {
645 let indent = ' '.repeat(prefix.length + prog.length + 1)
646 if (opt_parts.length) {
647 lines = get_lines([prog].concat(opt_parts), indent, prefix)
648 lines = lines.concat(get_lines(pos_parts, indent))
649 } else if (pos_parts.length) {
650 lines = get_lines([prog].concat(pos_parts), indent, prefix)
651 } else {
652 lines = [prog]
653 }
654
655 // if prog is long, put it on its own line
656 } else {
657 let indent = ' '.repeat(prefix.length)
658 let parts = [].concat(opt_parts).concat(pos_parts)
659 lines = get_lines(parts, indent)
660 if (lines.length > 1) {
661 lines = []
662 lines = lines.concat(get_lines(opt_parts, indent))
663 lines = lines.concat(get_lines(pos_parts, indent))
664 }
665 lines = [prog].concat(lines)
666 }
667
668 // join lines into usage
669 usage = lines.join('\n')
670 }
671 }
672
673 // prefix with 'usage:'
674 return sub('%s%s\n\n', prefix, usage)
675 }
676
677 _format_actions_usage(actions, groups) {
678 // find group indices and identify actions in groups
679 let group_actions = new Set()
680 let inserts = {}
681 for (let group of groups) {
682 let start = actions.indexOf(group._group_actions[0])
683 if (start === -1) {
684 continue
685 } else {
686 let end = start + group._group_actions.length
687 if (_array_equal(actions.slice(start, end), group._group_actions)) {
688 for (let action of group._group_actions) {
689 group_actions.add(action)
690 }
691 if (!group.required) {
692 if (start in inserts) {
693 inserts[start] += ' ['
694 } else {
695 inserts[start] = '['
696 }
697 if (end in inserts) {
698 inserts[end] += ']'
699 } else {
700 inserts[end] = ']'
701 }
702 } else {
703 if (start in inserts) {
704 inserts[start] += ' ('
705 } else {
706 inserts[start] = '('
707 }
708 if (end in inserts) {
709 inserts[end] += ')'
710 } else {
711 inserts[end] = ')'
712 }
713 }
714 for (let i of range(start + 1, end)) {
715 inserts[i] = '|'
716 }
717 }
718 }
719 }
720
721 // collect all actions format strings
722 let parts = []
723 for (let [ i, action ] of Object.entries(actions)) {
724
725 // suppressed arguments are marked with None
726 // remove | separators for suppressed arguments
727 if (action.help === SUPPRESS) {
728 parts.push(undefined)
729 if (inserts[+i] === '|') {
730 delete inserts[+i]
731 } else if (inserts[+i + 1] === '|') {
732 delete inserts[+i + 1]
733 }
734
735 // produce all arg strings
736 } else if (!action.option_strings.length) {
737 let default_value = this._get_default_metavar_for_positional(action)
738 let part = this._format_args(action, default_value)
739
740 // if it's in a group, strip the outer []
741 if (group_actions.has(action)) {
742 if (part[0] === '[' && part[part.length - 1] === ']') {
743 part = part.slice(1, -1)
744 }
745 }
746
747 // add the action string to the list
748 parts.push(part)
749
750 // produce the first way to invoke the option in brackets
751 } else {
752 let option_string = action.option_strings[0]
753 let part
754
755 // if the Optional doesn't take a value, format is:
756 // -s or --long
757 if (action.nargs === 0) {
758 part = action.format_usage()
759
760 // if the Optional takes a value, format is:
761 // -s ARGS or --long ARGS
762 } else {
763 let default_value = this._get_default_metavar_for_optional(action)
764 let args_string = this._format_args(action, default_value)
765 part = sub('%s %s', option_string, args_string)
766 }
767
768 // make it look optional if it's not required or in a group
769 if (!action.required && !group_actions.has(action)) {
770 part = sub('[%s]', part)
771 }
772
773 // add the action string to the list
774 parts.push(part)
775 }
776 }
777
778 // insert things at the necessary indices
779 for (let i of Object.keys(inserts).map(Number).sort((a, b) => b - a)) {
780 parts.splice(+i, 0, inserts[+i])
781 }
782
783 // join all the action items with spaces
784 let text = parts.filter(Boolean).join(' ')
785
786 // clean up separators for mutually exclusive groups
787 text = text.replace(/([\[(]) /g, '$1')
788 text = text.replace(/ ([\])])/g, '$1')
789 text = text.replace(/[\[(] *[\])]/g, '')
790 text = text.replace(/\(([^|]*)\)/g, '$1', text)
791 text = text.trim()
792
793 // return the text
794 return text
795 }
796
797 _format_text(text) {
798 if (text.includes('%(prog)')) {
799 text = sub(text, { prog: this._prog })
800 }
801 let text_width = Math.max(this._width - this._current_indent, 11)
802 let indent = ' '.repeat(this._current_indent)
803 return this._fill_text(text, text_width, indent) + '\n\n'
804 }
805
806 _format_action(action) {
807 // determine the required width and the entry label
808 let help_position = Math.min(this._action_max_length + 2,
809 this._max_help_position)
810 let help_width = Math.max(this._width - help_position, 11)
811 let action_width = help_position - this._current_indent - 2
812 let action_header = this._format_action_invocation(action)
813 let indent_first
814
815 // no help; start on same line and add a final newline
816 if (!action.help) {
817 let tup = [ this._current_indent, '', action_header ]
818 action_header = sub('%*s%s\n', ...tup)
819
820 // short action name; start on the same line and pad two spaces
821 } else if (action_header.length <= action_width) {
822 let tup = [ this._current_indent, '', action_width, action_header ]
823 action_header = sub('%*s%-*s ', ...tup)
824 indent_first = 0
825
826 // long action name; start on the next line
827 } else {
828 let tup = [ this._current_indent, '', action_header ]
829 action_header = sub('%*s%s\n', ...tup)
830 indent_first = help_position
831 }
832
833 // collect the pieces of the action help
834 let parts = [action_header]
835
836 // if there was help for the action, add lines of help text
837 if (action.help) {
838 let help_text = this._expand_help(action)
839 let help_lines = this._split_lines(help_text, help_width)
840 parts.push(sub('%*s%s\n', indent_first, '', help_lines[0]))
841 for (let line of help_lines.slice(1)) {
842 parts.push(sub('%*s%s\n', help_position, '', line))
843 }
844
845 // or add a newline if the description doesn't end with one
846 } else if (!action_header.endsWith('\n')) {
847 parts.push('\n')
848 }
849
850 // if there are any sub-actions, add their help as well
851 for (let subaction of this._iter_indented_subactions(action)) {
852 parts.push(this._format_action(subaction))
853 }
854
855 // return a single string
856 return this._join_parts(parts)
857 }
858
859 _format_action_invocation(action) {
860 if (!action.option_strings.length) {
861 let default_value = this._get_default_metavar_for_positional(action)
862 let metavar = this._metavar_formatter(action, default_value)(1)[0]
863 return metavar
864
865 } else {
866 let parts = []
867
868 // if the Optional doesn't take a value, format is:
869 // -s, --long
870 if (action.nargs === 0) {
871 parts = parts.concat(action.option_strings)
872
873 // if the Optional takes a value, format is:
874 // -s ARGS, --long ARGS
875 } else {
876 let default_value = this._get_default_metavar_for_optional(action)
877 let args_string = this._format_args(action, default_value)
878 for (let option_string of action.option_strings) {
879 parts.push(sub('%s %s', option_string, args_string))
880 }
881 }
882
883 return parts.join(', ')
884 }
885 }
886
887 _metavar_formatter(action, default_metavar) {
888 let result
889 if (action.metavar !== undefined) {
890 result = action.metavar
891 } else if (action.choices !== undefined) {
892 let choice_strs = _choices_to_array(action.choices).map(String)
893 result = sub('{%s}', choice_strs.join(','))
894 } else {
895 result = default_metavar
896 }
897
898 function format(tuple_size) {
899 if (Array.isArray(result)) {
900 return result
901 } else {
902 return Array(tuple_size).fill(result)
903 }
904 }
905 return format
906 }
907
908 _format_args(action, default_metavar) {
909 let get_metavar = this._metavar_formatter(action, default_metavar)
910 let result
911 if (action.nargs === undefined) {
912 result = sub('%s', ...get_metavar(1))
913 } else if (action.nargs === OPTIONAL) {
914 result = sub('[%s]', ...get_metavar(1))
915 } else if (action.nargs === ZERO_OR_MORE) {
916 let metavar = get_metavar(1)
917 if (metavar.length === 2) {
918 result = sub('[%s [%s ...]]', ...metavar)
919 } else {
920 result = sub('[%s ...]', ...metavar)
921 }
922 } else if (action.nargs === ONE_OR_MORE) {
923 result = sub('%s [%s ...]', ...get_metavar(2))
924 } else if (action.nargs === REMAINDER) {
925 result = '...'
926 } else if (action.nargs === PARSER) {
927 result = sub('%s ...', ...get_metavar(1))
928 } else if (action.nargs === SUPPRESS) {
929 result = ''
930 } else {
931 let formats
932 try {
933 formats = range(action.nargs).map(() => '%s')
934 } catch (err) {
935 throw new TypeError('invalid nargs value')
936 }
937 result = sub(formats.join(' '), ...get_metavar(action.nargs))
938 }
939 return result
940 }
941
942 _expand_help(action) {
943 let params = Object.assign({ prog: this._prog }, action)
944 for (let name of Object.keys(params)) {
945 if (params[name] === SUPPRESS) {
946 delete params[name]
947 }
948 }
949 for (let name of Object.keys(params)) {
950 if (params[name] && params[name].name) {
951 params[name] = params[name].name
952 }
953 }
954 if (params.choices !== undefined) {
955 let choices_str = _choices_to_array(params.choices).map(String).join(', ')
956 params.choices = choices_str
957 }
958 // LEGACY (v1 compatibility): camelcase
959 for (let key of Object.keys(params)) {
960 let old_name = _to_legacy_name(key)
961 if (old_name !== key) {
962 params[old_name] = params[key]
963 }
964 }
965 // end
966 return sub(this._get_help_string(action), params)
967 }
968
969 * _iter_indented_subactions(action) {
970 if (typeof action._get_subactions === 'function') {
971 this._indent()
972 yield* action._get_subactions()
973 this._dedent()
974 }
975 }
976
977 _split_lines(text, width) {
978 text = text.replace(this._whitespace_matcher, ' ').trim()
979 // The textwrap module is used only for formatting help.
980 // Delay its import for speeding up the common usage of argparse.
981 let textwrap = require('./lib/textwrap')
982 return textwrap.wrap(text, { width })
983 }
984
985 _fill_text(text, width, indent) {
986 text = text.replace(this._whitespace_matcher, ' ').trim()
987 let textwrap = require('./lib/textwrap')
988 return textwrap.fill(text, { width,
989 initial_indent: indent,
990 subsequent_indent: indent })
991 }
992
993 _get_help_string(action) {
994 return action.help
995 }
996
997 _get_default_metavar_for_optional(action) {
998 return action.dest.toUpperCase()
999 }
1000
1001 _get_default_metavar_for_positional(action) {
1002 return action.dest
1003 }
1004}))
1005
1006HelpFormatter.prototype._Section = _callable(class _Section {
1007
1008 constructor(formatter, parent, heading = undefined) {
1009 this.formatter = formatter
1010 this.parent = parent
1011 this.heading = heading
1012 this.items = []
1013 }
1014
1015 format_help() {
1016 // format the indented section
1017 if (this.parent !== undefined) {
1018 this.formatter._indent()
1019 }
1020 let item_help = this.formatter._join_parts(this.items.map(([ func, args ]) => func.apply(null, args)))
1021 if (this.parent !== undefined) {
1022 this.formatter._dedent()
1023 }
1024
1025 // return nothing if the section was empty
1026 if (!item_help) {
1027 return ''
1028 }
1029
1030 // add the heading if the section was non-empty
1031 let heading
1032 if (this.heading !== SUPPRESS && this.heading !== undefined) {
1033 let current_indent = this.formatter._current_indent
1034 heading = sub('%*s%s:\n', current_indent, '', this.heading)
1035 } else {
1036 heading = ''
1037 }
1038
1039 // join the section-initial newline, the heading and the help
1040 return this.formatter._join_parts(['\n', heading, item_help, '\n'])
1041 }
1042})
1043
1044
1045const RawDescriptionHelpFormatter = _camelcase_alias(_callable(class RawDescriptionHelpFormatter extends HelpFormatter {
1046 /*
1047 * Help message formatter which retains any formatting in descriptions.
1048 *
1049 * Only the name of this class is considered a public API. All the methods
1050 * provided by the class are considered an implementation detail.
1051 */
1052
1053 _fill_text(text, width, indent) {
1054 return splitlines(text, true).map(line => indent + line).join('')
1055 }
1056}))
1057
1058
1059const RawTextHelpFormatter = _camelcase_alias(_callable(class RawTextHelpFormatter extends RawDescriptionHelpFormatter {
1060 /*
1061 * Help message formatter which retains formatting of all help text.
1062 *
1063 * Only the name of this class is considered a public API. All the methods
1064 * provided by the class are considered an implementation detail.
1065 */
1066
1067 _split_lines(text/*, width*/) {
1068 return splitlines(text)
1069 }
1070}))
1071
1072
1073const ArgumentDefaultsHelpFormatter = _camelcase_alias(_callable(class ArgumentDefaultsHelpFormatter extends HelpFormatter {
1074 /*
1075 * Help message formatter which adds default values to argument help.
1076 *
1077 * Only the name of this class is considered a public API. All the methods
1078 * provided by the class are considered an implementation detail.
1079 */
1080
1081 _get_help_string(action) {
1082 let help = action.help
1083 // LEGACY (v1 compatibility): additional check for defaultValue needed
1084 if (!action.help.includes('%(default)') && !action.help.includes('%(defaultValue)')) {
1085 if (action.default !== SUPPRESS) {
1086 let defaulting_nargs = [OPTIONAL, ZERO_OR_MORE]
1087 if (action.option_strings.length || defaulting_nargs.includes(action.nargs)) {
1088 help += ' (default: %(default)s)'
1089 }
1090 }
1091 }
1092 return help
1093 }
1094}))
1095
1096
1097const MetavarTypeHelpFormatter = _camelcase_alias(_callable(class MetavarTypeHelpFormatter extends HelpFormatter {
1098 /*
1099 * Help message formatter which uses the argument 'type' as the default
1100 * metavar value (instead of the argument 'dest')
1101 *
1102 * Only the name of this class is considered a public API. All the methods
1103 * provided by the class are considered an implementation detail.
1104 */
1105
1106 _get_default_metavar_for_optional(action) {
1107 return typeof action.type === 'function' ? action.type.name : action.type
1108 }
1109
1110 _get_default_metavar_for_positional(action) {
1111 return typeof action.type === 'function' ? action.type.name : action.type
1112 }
1113}))
1114
1115
1116// =====================
1117// Options and Arguments
1118// =====================
1119function _get_action_name(argument) {
1120 if (argument === undefined) {
1121 return undefined
1122 } else if (argument.option_strings.length) {
1123 return argument.option_strings.join('/')
1124 } else if (![ undefined, SUPPRESS ].includes(argument.metavar)) {
1125 return argument.metavar
1126 } else if (![ undefined, SUPPRESS ].includes(argument.dest)) {
1127 return argument.dest
1128 } else {
1129 return undefined
1130 }
1131}
1132
1133
1134const ArgumentError = _callable(class ArgumentError extends Error {
1135 /*
1136 * An error from creating or using an argument (optional or positional).
1137 *
1138 * The string value of this exception is the message, augmented with
1139 * information about the argument that caused it.
1140 */
1141
1142 constructor(argument, message) {
1143 super()
1144 this.name = 'ArgumentError'
1145 this._argument_name = _get_action_name(argument)
1146 this._message = message
1147 this.message = this.str()
1148 }
1149
1150 str() {
1151 let format
1152 if (this._argument_name === undefined) {
1153 format = '%(message)s'
1154 } else {
1155 format = 'argument %(argument_name)s: %(message)s'
1156 }
1157 return sub(format, { message: this._message,
1158 argument_name: this._argument_name })
1159 }
1160})
1161
1162
1163const ArgumentTypeError = _callable(class ArgumentTypeError extends Error {
1164 /*
1165 * An error from trying to convert a command line string to a type.
1166 */
1167
1168 constructor(message) {
1169 super(message)
1170 this.name = 'ArgumentTypeError'
1171 }
1172})
1173
1174
1175// ==============
1176// Action classes
1177// ==============
1178const Action = _camelcase_alias(_callable(class Action extends _AttributeHolder(Function) {
1179 /*
1180 * Information about how to convert command line strings to Python objects.
1181 *
1182 * Action objects are used by an ArgumentParser to represent the information
1183 * needed to parse a single argument from one or more strings from the
1184 * command line. The keyword arguments to the Action constructor are also
1185 * all attributes of Action instances.
1186 *
1187 * Keyword Arguments:
1188 *
1189 * - option_strings -- A list of command-line option strings which
1190 * should be associated with this action.
1191 *
1192 * - dest -- The name of the attribute to hold the created object(s)
1193 *
1194 * - nargs -- The number of command-line arguments that should be
1195 * consumed. By default, one argument will be consumed and a single
1196 * value will be produced. Other values include:
1197 * - N (an integer) consumes N arguments (and produces a list)
1198 * - '?' consumes zero or one arguments
1199 * - '*' consumes zero or more arguments (and produces a list)
1200 * - '+' consumes one or more arguments (and produces a list)
1201 * Note that the difference between the default and nargs=1 is that
1202 * with the default, a single value will be produced, while with
1203 * nargs=1, a list containing a single value will be produced.
1204 *
1205 * - const -- The value to be produced if the option is specified and the
1206 * option uses an action that takes no values.
1207 *
1208 * - default -- The value to be produced if the option is not specified.
1209 *
1210 * - type -- A callable that accepts a single string argument, and
1211 * returns the converted value. The standard Python types str, int,
1212 * float, and complex are useful examples of such callables. If None,
1213 * str is used.
1214 *
1215 * - choices -- A container of values that should be allowed. If not None,
1216 * after a command-line argument has been converted to the appropriate
1217 * type, an exception will be raised if it is not a member of this
1218 * collection.
1219 *
1220 * - required -- True if the action must always be specified at the
1221 * command line. This is only meaningful for optional command-line
1222 * arguments.
1223 *
1224 * - help -- The help string describing the argument.
1225 *
1226 * - metavar -- The name to be used for the option's argument with the
1227 * help string. If None, the 'dest' value will be used as the name.
1228 */
1229
1230 constructor() {
1231 let [
1232 option_strings,
1233 dest,
1234 nargs,
1235 const_value,
1236 default_value,
1237 type,
1238 choices,
1239 required,
1240 help,
1241 metavar
1242 ] = _parse_opts(arguments, {
1243 option_strings: no_default,
1244 dest: no_default,
1245 nargs: undefined,
1246 const: undefined,
1247 default: undefined,
1248 type: undefined,
1249 choices: undefined,
1250 required: false,
1251 help: undefined,
1252 metavar: undefined
1253 })
1254
1255 // when this class is called as a function, redirect it to .call() method of itself
1256 super('return arguments.callee.call.apply(arguments.callee, arguments)')
1257
1258 this.option_strings = option_strings
1259 this.dest = dest
1260 this.nargs = nargs
1261 this.const = const_value
1262 this.default = default_value
1263 this.type = type
1264 this.choices = choices
1265 this.required = required
1266 this.help = help
1267 this.metavar = metavar
1268 }
1269
1270 _get_kwargs() {
1271 let names = [
1272 'option_strings',
1273 'dest',
1274 'nargs',
1275 'const',
1276 'default',
1277 'type',
1278 'choices',
1279 'help',
1280 'metavar'
1281 ]
1282 return names.map(name => [ name, getattr(this, name) ])
1283 }
1284
1285 format_usage() {
1286 return this.option_strings[0]
1287 }
1288
1289 call(/*parser, namespace, values, option_string = undefined*/) {
1290 throw new Error('.call() not defined')
1291 }
1292}))
1293
1294
1295const BooleanOptionalAction = _camelcase_alias(_callable(class BooleanOptionalAction extends Action {
1296
1297 constructor() {
1298 let [
1299 option_strings,
1300 dest,
1301 default_value,
1302 type,
1303 choices,
1304 required,
1305 help,
1306 metavar
1307 ] = _parse_opts(arguments, {
1308 option_strings: no_default,
1309 dest: no_default,
1310 default: undefined,
1311 type: undefined,
1312 choices: undefined,
1313 required: false,
1314 help: undefined,
1315 metavar: undefined
1316 })
1317
1318 let _option_strings = []
1319 for (let option_string of option_strings) {
1320 _option_strings.push(option_string)
1321
1322 if (option_string.startsWith('--')) {
1323 option_string = '--no-' + option_string.slice(2)
1324 _option_strings.push(option_string)
1325 }
1326 }
1327
1328 if (help !== undefined && default_value !== undefined) {
1329 help += ` (default: ${default_value})`
1330 }
1331
1332 super({
1333 option_strings: _option_strings,
1334 dest,
1335 nargs: 0,
1336 default: default_value,
1337 type,
1338 choices,
1339 required,
1340 help,
1341 metavar
1342 })
1343 }
1344
1345 call(parser, namespace, values, option_string = undefined) {
1346 if (this.option_strings.includes(option_string)) {
1347 setattr(namespace, this.dest, !option_string.startsWith('--no-'))
1348 }
1349 }
1350
1351 format_usage() {
1352 return this.option_strings.join(' | ')
1353 }
1354}))
1355
1356
1357const _StoreAction = _callable(class _StoreAction extends Action {
1358
1359 constructor() {
1360 let [
1361 option_strings,
1362 dest,
1363 nargs,
1364 const_value,
1365 default_value,
1366 type,
1367 choices,
1368 required,
1369 help,
1370 metavar
1371 ] = _parse_opts(arguments, {
1372 option_strings: no_default,
1373 dest: no_default,
1374 nargs: undefined,
1375 const: undefined,
1376 default: undefined,
1377 type: undefined,
1378 choices: undefined,
1379 required: false,
1380 help: undefined,
1381 metavar: undefined
1382 })
1383
1384 if (nargs === 0) {
1385 throw new TypeError('nargs for store actions must be != 0; if you ' +
1386 'have nothing to store, actions such as store ' +
1387 'true or store const may be more appropriate')
1388 }
1389 if (const_value !== undefined && nargs !== OPTIONAL) {
1390 throw new TypeError(sub('nargs must be %r to supply const', OPTIONAL))
1391 }
1392 super({
1393 option_strings,
1394 dest,
1395 nargs,
1396 const: const_value,
1397 default: default_value,
1398 type,
1399 choices,
1400 required,
1401 help,
1402 metavar
1403 })
1404 }
1405
1406 call(parser, namespace, values/*, option_string = undefined*/) {
1407 setattr(namespace, this.dest, values)
1408 }
1409})
1410
1411
1412const _StoreConstAction = _callable(class _StoreConstAction extends Action {
1413
1414 constructor() {
1415 let [
1416 option_strings,
1417 dest,
1418 const_value,
1419 default_value,
1420 required,
1421 help
1422 //, metavar
1423 ] = _parse_opts(arguments, {
1424 option_strings: no_default,
1425 dest: no_default,
1426 const: no_default,
1427 default: undefined,
1428 required: false,
1429 help: undefined,
1430 metavar: undefined
1431 })
1432
1433 super({
1434 option_strings,
1435 dest,
1436 nargs: 0,
1437 const: const_value,
1438 default: default_value,
1439 required,
1440 help
1441 })
1442 }
1443
1444 call(parser, namespace/*, values, option_string = undefined*/) {
1445 setattr(namespace, this.dest, this.const)
1446 }
1447})
1448
1449
1450const _StoreTrueAction = _callable(class _StoreTrueAction extends _StoreConstAction {
1451
1452 constructor() {
1453 let [
1454 option_strings,
1455 dest,
1456 default_value,
1457 required,
1458 help
1459 ] = _parse_opts(arguments, {
1460 option_strings: no_default,
1461 dest: no_default,
1462 default: false,
1463 required: false,
1464 help: undefined
1465 })
1466
1467 super({
1468 option_strings,
1469 dest,
1470 const: true,
1471 default: default_value,
1472 required,
1473 help
1474 })
1475 }
1476})
1477
1478
1479const _StoreFalseAction = _callable(class _StoreFalseAction extends _StoreConstAction {
1480
1481 constructor() {
1482 let [
1483 option_strings,
1484 dest,
1485 default_value,
1486 required,
1487 help
1488 ] = _parse_opts(arguments, {
1489 option_strings: no_default,
1490 dest: no_default,
1491 default: true,
1492 required: false,
1493 help: undefined
1494 })
1495
1496 super({
1497 option_strings,
1498 dest,
1499 const: false,
1500 default: default_value,
1501 required,
1502 help
1503 })
1504 }
1505})
1506
1507
1508const _AppendAction = _callable(class _AppendAction extends Action {
1509
1510 constructor() {
1511 let [
1512 option_strings,
1513 dest,
1514 nargs,
1515 const_value,
1516 default_value,
1517 type,
1518 choices,
1519 required,
1520 help,
1521 metavar
1522 ] = _parse_opts(arguments, {
1523 option_strings: no_default,
1524 dest: no_default,
1525 nargs: undefined,
1526 const: undefined,
1527 default: undefined,
1528 type: undefined,
1529 choices: undefined,
1530 required: false,
1531 help: undefined,
1532 metavar: undefined
1533 })
1534
1535 if (nargs === 0) {
1536 throw new TypeError('nargs for append actions must be != 0; if arg ' +
1537 'strings are not supplying the value to append, ' +
1538 'the append const action may be more appropriate')
1539 }
1540 if (const_value !== undefined && nargs !== OPTIONAL) {
1541 throw new TypeError(sub('nargs must be %r to supply const', OPTIONAL))
1542 }
1543 super({
1544 option_strings,
1545 dest,
1546 nargs,
1547 const: const_value,
1548 default: default_value,
1549 type,
1550 choices,
1551 required,
1552 help,
1553 metavar
1554 })
1555 }
1556
1557 call(parser, namespace, values/*, option_string = undefined*/) {
1558 let items = getattr(namespace, this.dest, undefined)
1559 items = _copy_items(items)
1560 items.push(values)
1561 setattr(namespace, this.dest, items)
1562 }
1563})
1564
1565
1566const _AppendConstAction = _callable(class _AppendConstAction extends Action {
1567
1568 constructor() {
1569 let [
1570 option_strings,
1571 dest,
1572 const_value,
1573 default_value,
1574 required,
1575 help,
1576 metavar
1577 ] = _parse_opts(arguments, {
1578 option_strings: no_default,
1579 dest: no_default,
1580 const: no_default,
1581 default: undefined,
1582 required: false,
1583 help: undefined,
1584 metavar: undefined
1585 })
1586
1587 super({
1588 option_strings,
1589 dest,
1590 nargs: 0,
1591 const: const_value,
1592 default: default_value,
1593 required,
1594 help,
1595 metavar
1596 })
1597 }
1598
1599 call(parser, namespace/*, values, option_string = undefined*/) {
1600 let items = getattr(namespace, this.dest, undefined)
1601 items = _copy_items(items)
1602 items.push(this.const)
1603 setattr(namespace, this.dest, items)
1604 }
1605})
1606
1607
1608const _CountAction = _callable(class _CountAction extends Action {
1609
1610 constructor() {
1611 let [
1612 option_strings,
1613 dest,
1614 default_value,
1615 required,
1616 help
1617 ] = _parse_opts(arguments, {
1618 option_strings: no_default,
1619 dest: no_default,
1620 default: undefined,
1621 required: false,
1622 help: undefined
1623 })
1624
1625 super({
1626 option_strings,
1627 dest,
1628 nargs: 0,
1629 default: default_value,
1630 required,
1631 help
1632 })
1633 }
1634
1635 call(parser, namespace/*, values, option_string = undefined*/) {
1636 let count = getattr(namespace, this.dest, undefined)
1637 if (count === undefined) {
1638 count = 0
1639 }
1640 setattr(namespace, this.dest, count + 1)
1641 }
1642})
1643
1644
1645const _HelpAction = _callable(class _HelpAction extends Action {
1646
1647 constructor() {
1648 let [
1649 option_strings,
1650 dest,
1651 default_value,
1652 help
1653 ] = _parse_opts(arguments, {
1654 option_strings: no_default,
1655 dest: SUPPRESS,
1656 default: SUPPRESS,
1657 help: undefined
1658 })
1659
1660 super({
1661 option_strings,
1662 dest,
1663 default: default_value,
1664 nargs: 0,
1665 help
1666 })
1667 }
1668
1669 call(parser/*, namespace, values, option_string = undefined*/) {
1670 parser.print_help()
1671 parser.exit()
1672 }
1673})
1674
1675
1676const _VersionAction = _callable(class _VersionAction extends Action {
1677
1678 constructor() {
1679 let [
1680 option_strings,
1681 version,
1682 dest,
1683 default_value,
1684 help
1685 ] = _parse_opts(arguments, {
1686 option_strings: no_default,
1687 version: undefined,
1688 dest: SUPPRESS,
1689 default: SUPPRESS,
1690 help: "show program's version number and exit"
1691 })
1692
1693 super({
1694 option_strings,
1695 dest,
1696 default: default_value,
1697 nargs: 0,
1698 help
1699 })
1700 this.version = version
1701 }
1702
1703 call(parser/*, namespace, values, option_string = undefined*/) {
1704 let version = this.version
1705 if (version === undefined) {
1706 version = parser.version
1707 }
1708 let formatter = parser._get_formatter()
1709 formatter.add_text(version)
1710 parser._print_message(formatter.format_help(), process.stdout)
1711 parser.exit()
1712 }
1713})
1714
1715
1716const _SubParsersAction = _camelcase_alias(_callable(class _SubParsersAction extends Action {
1717
1718 constructor() {
1719 let [
1720 option_strings,
1721 prog,
1722 parser_class,
1723 dest,
1724 required,
1725 help,
1726 metavar
1727 ] = _parse_opts(arguments, {
1728 option_strings: no_default,
1729 prog: no_default,
1730 parser_class: no_default,
1731 dest: SUPPRESS,
1732 required: false,
1733 help: undefined,
1734 metavar: undefined
1735 })
1736
1737 let name_parser_map = {}
1738
1739 super({
1740 option_strings,
1741 dest,
1742 nargs: PARSER,
1743 choices: name_parser_map,
1744 required,
1745 help,
1746 metavar
1747 })
1748
1749 this._prog_prefix = prog
1750 this._parser_class = parser_class
1751 this._name_parser_map = name_parser_map
1752 this._choices_actions = []
1753 }
1754
1755 add_parser() {
1756 let [
1757 name,
1758 kwargs
1759 ] = _parse_opts(arguments, {
1760 name: no_default,
1761 '**kwargs': no_default
1762 })
1763
1764 // set prog from the existing prefix
1765 if (kwargs.prog === undefined) {
1766 kwargs.prog = sub('%s %s', this._prog_prefix, name)
1767 }
1768
1769 let aliases = getattr(kwargs, 'aliases', [])
1770 delete kwargs.aliases
1771
1772 // create a pseudo-action to hold the choice help
1773 if ('help' in kwargs) {
1774 let help = kwargs.help
1775 delete kwargs.help
1776 let choice_action = this._ChoicesPseudoAction(name, aliases, help)
1777 this._choices_actions.push(choice_action)
1778 }
1779
1780 // create the parser and add it to the map
1781 let parser = new this._parser_class(kwargs)
1782 this._name_parser_map[name] = parser
1783
1784 // make parser available under aliases also
1785 for (let alias of aliases) {
1786 this._name_parser_map[alias] = parser
1787 }
1788
1789 return parser
1790 }
1791
1792 _get_subactions() {
1793 return this._choices_actions
1794 }
1795
1796 call(parser, namespace, values/*, option_string = undefined*/) {
1797 let parser_name = values[0]
1798 let arg_strings = values.slice(1)
1799
1800 // set the parser name if requested
1801 if (this.dest !== SUPPRESS) {
1802 setattr(namespace, this.dest, parser_name)
1803 }
1804
1805 // select the parser
1806 if (hasattr(this._name_parser_map, parser_name)) {
1807 parser = this._name_parser_map[parser_name]
1808 } else {
1809 let args = {parser_name,
1810 choices: this._name_parser_map.join(', ')}
1811 let msg = sub('unknown parser %(parser_name)r (choices: %(choices)s)', args)
1812 throw new ArgumentError(this, msg)
1813 }
1814
1815 // parse all the remaining options into the namespace
1816 // store any unrecognized options on the object, so that the top
1817 // level parser can decide what to do with them
1818
1819 // In case this subparser defines new defaults, we parse them
1820 // in a new namespace object and then update the original
1821 // namespace for the relevant parts.
1822 let subnamespace
1823 [ subnamespace, arg_strings ] = parser.parse_known_args(arg_strings, undefined)
1824 for (let [ key, value ] of Object.entries(subnamespace)) {
1825 setattr(namespace, key, value)
1826 }
1827
1828 if (arg_strings.length) {
1829 setdefault(namespace, _UNRECOGNIZED_ARGS_ATTR, [])
1830 getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).push(...arg_strings)
1831 }
1832 }
1833}))
1834
1835
1836_SubParsersAction.prototype._ChoicesPseudoAction = _callable(class _ChoicesPseudoAction extends Action {
1837 constructor(name, aliases, help) {
1838 let metavar = name, dest = name
1839 if (aliases.length) {
1840 metavar += sub(' (%s)', aliases.join(', '))
1841 }
1842 super({ option_strings: [], dest, help, metavar })
1843 }
1844})
1845
1846
1847const _ExtendAction = _callable(class _ExtendAction extends _AppendAction {
1848 call(parser, namespace, values/*, option_string = undefined*/) {
1849 let items = getattr(namespace, this.dest, undefined)
1850 items = _copy_items(items)
1851 items = items.concat(values)
1852 setattr(namespace, this.dest, items)
1853 }
1854})
1855
1856
1857// ==============
1858// Type classes
1859// ==============
1860const FileType = _callable(class FileType extends Function {
1861 /*
1862 * Factory for creating file object types
1863 *
1864 * Instances of FileType are typically passed as type= arguments to the
1865 * ArgumentParser add_argument() method.
1866 *
1867 * Keyword Arguments:
1868 * - mode -- A string indicating how the file is to be opened. Accepts the
1869 * same values as the builtin open() function.
1870 * - bufsize -- The file's desired buffer size. Accepts the same values as
1871 * the builtin open() function.
1872 * - encoding -- The file's encoding. Accepts the same values as the
1873 * builtin open() function.
1874 * - errors -- A string indicating how encoding and decoding errors are to
1875 * be handled. Accepts the same value as the builtin open() function.
1876 */
1877
1878 constructor() {
1879 let [
1880 flags,
1881 encoding,
1882 mode,
1883 autoClose,
1884 emitClose,
1885 start,
1886 end,
1887 highWaterMark,
1888 fs
1889 ] = _parse_opts(arguments, {
1890 flags: 'r',
1891 encoding: undefined,
1892 mode: undefined, // 0o666
1893 autoClose: undefined, // true
1894 emitClose: undefined, // false
1895 start: undefined, // 0
1896 end: undefined, // Infinity
1897 highWaterMark: undefined, // 64 * 1024
1898 fs: undefined
1899 })
1900
1901 // when this class is called as a function, redirect it to .call() method of itself
1902 super('return arguments.callee.call.apply(arguments.callee, arguments)')
1903
1904 Object.defineProperty(this, 'name', {
1905 get() {
1906 return sub('FileType(%r)', flags)
1907 }
1908 })
1909 this._flags = flags
1910 this._options = {}
1911 if (encoding !== undefined) this._options.encoding = encoding
1912 if (mode !== undefined) this._options.mode = mode
1913 if (autoClose !== undefined) this._options.autoClose = autoClose
1914 if (emitClose !== undefined) this._options.emitClose = emitClose
1915 if (start !== undefined) this._options.start = start
1916 if (end !== undefined) this._options.end = end
1917 if (highWaterMark !== undefined) this._options.highWaterMark = highWaterMark
1918 if (fs !== undefined) this._options.fs = fs
1919 }
1920
1921 call(string) {
1922 // the special argument "-" means sys.std{in,out}
1923 if (string === '-') {
1924 if (this._flags.includes('r')) {
1925 return process.stdin
1926 } else if (this._flags.includes('w')) {
1927 return process.stdout
1928 } else {
1929 let msg = sub('argument "-" with mode %r', this._flags)
1930 throw new TypeError(msg)
1931 }
1932 }
1933
1934 // all other arguments are used as file names
1935 let fd
1936 try {
1937 fd = fs.openSync(string, this._flags, this._options.mode)
1938 } catch (e) {
1939 let args = { filename: string, error: e.message }
1940 let message = "can't open '%(filename)s': %(error)s"
1941 throw new ArgumentTypeError(sub(message, args))
1942 }
1943
1944 let options = Object.assign({ fd, flags: this._flags }, this._options)
1945 if (this._flags.includes('r')) {
1946 return fs.createReadStream(undefined, options)
1947 } else if (this._flags.includes('w')) {
1948 return fs.createWriteStream(undefined, options)
1949 } else {
1950 let msg = sub('argument "%s" with mode %r', string, this._flags)
1951 throw new TypeError(msg)
1952 }
1953 }
1954
1955 [util.inspect.custom]() {
1956 let args = [ this._flags ]
1957 let kwargs = Object.entries(this._options).map(([ k, v ]) => {
1958 if (k === 'mode') v = { value: v, [util.inspect.custom]() { return '0o' + this.value.toString(8) } }
1959 return [ k, v ]
1960 })
1961 let args_str = []
1962 .concat(args.filter(arg => arg !== -1).map(repr))
1963 .concat(kwargs.filter(([/*kw*/, arg]) => arg !== undefined)
1964 .map(([kw, arg]) => sub('%s=%r', kw, arg)))
1965 .join(', ')
1966 return sub('%s(%s)', this.constructor.name, args_str)
1967 }
1968
1969 toString() {
1970 return this[util.inspect.custom]()
1971 }
1972})
1973
1974// ===========================
1975// Optional and Positional Parsing
1976// ===========================
1977const Namespace = _callable(class Namespace extends _AttributeHolder() {
1978 /*
1979 * Simple object for storing attributes.
1980 *
1981 * Implements equality by attribute names and values, and provides a simple
1982 * string representation.
1983 */
1984
1985 constructor(options = {}) {
1986 super()
1987 Object.assign(this, options)
1988 }
1989})
1990
1991// unset string tag to mimic plain object
1992Namespace.prototype[Symbol.toStringTag] = undefined
1993
1994
1995const _ActionsContainer = _camelcase_alias(_callable(class _ActionsContainer {
1996
1997 constructor() {
1998 let [
1999 description,
2000 prefix_chars,
2001 argument_default,
2002 conflict_handler
2003 ] = _parse_opts(arguments, {
2004 description: no_default,
2005 prefix_chars: no_default,
2006 argument_default: no_default,
2007 conflict_handler: no_default
2008 })
2009
2010 this.description = description
2011 this.argument_default = argument_default
2012 this.prefix_chars = prefix_chars
2013 this.conflict_handler = conflict_handler
2014
2015 // set up registries
2016 this._registries = {}
2017
2018 // register actions
2019 this.register('action', undefined, _StoreAction)
2020 this.register('action', 'store', _StoreAction)
2021 this.register('action', 'store_const', _StoreConstAction)
2022 this.register('action', 'store_true', _StoreTrueAction)
2023 this.register('action', 'store_false', _StoreFalseAction)
2024 this.register('action', 'append', _AppendAction)
2025 this.register('action', 'append_const', _AppendConstAction)
2026 this.register('action', 'count', _CountAction)
2027 this.register('action', 'help', _HelpAction)
2028 this.register('action', 'version', _VersionAction)
2029 this.register('action', 'parsers', _SubParsersAction)
2030 this.register('action', 'extend', _ExtendAction)
2031 // LEGACY (v1 compatibility): camelcase variants
2032 ;[ 'storeConst', 'storeTrue', 'storeFalse', 'appendConst' ].forEach(old_name => {
2033 let new_name = _to_new_name(old_name)
2034 this.register('action', old_name, util.deprecate(this._registry_get('action', new_name),
2035 sub('{action: "%s"} is renamed to {action: "%s"}', old_name, new_name)))
2036 })
2037 // end
2038
2039 // raise an exception if the conflict handler is invalid
2040 this._get_handler()
2041
2042 // action storage
2043 this._actions = []
2044 this._option_string_actions = {}
2045
2046 // groups
2047 this._action_groups = []
2048 this._mutually_exclusive_groups = []
2049
2050 // defaults storage
2051 this._defaults = {}
2052
2053 // determines whether an "option" looks like a negative number
2054 this._negative_number_matcher = /^-\d+$|^-\d*\.\d+$/
2055
2056 // whether or not there are any optionals that look like negative
2057 // numbers -- uses a list so it can be shared and edited
2058 this._has_negative_number_optionals = []
2059 }
2060
2061 // ====================
2062 // Registration methods
2063 // ====================
2064 register(registry_name, value, object) {
2065 let registry = setdefault(this._registries, registry_name, {})
2066 registry[value] = object
2067 }
2068
2069 _registry_get(registry_name, value, default_value = undefined) {
2070 return getattr(this._registries[registry_name], value, default_value)
2071 }
2072
2073 // ==================================
2074 // Namespace default accessor methods
2075 // ==================================
2076 set_defaults(kwargs) {
2077 Object.assign(this._defaults, kwargs)
2078
2079 // if these defaults match any existing arguments, replace
2080 // the previous default on the object with the new one
2081 for (let action of this._actions) {
2082 if (action.dest in kwargs) {
2083 action.default = kwargs[action.dest]
2084 }
2085 }
2086 }
2087
2088 get_default(dest) {
2089 for (let action of this._actions) {
2090 if (action.dest === dest && action.default !== undefined) {
2091 return action.default
2092 }
2093 }
2094 return this._defaults[dest]
2095 }
2096
2097
2098 // =======================
2099 // Adding argument actions
2100 // =======================
2101 add_argument() {
2102 /*
2103 * add_argument(dest, ..., name=value, ...)
2104 * add_argument(option_string, option_string, ..., name=value, ...)
2105 */
2106 let [
2107 args,
2108 kwargs
2109 ] = _parse_opts(arguments, {
2110 '*args': no_default,
2111 '**kwargs': no_default
2112 })
2113 // LEGACY (v1 compatibility), old-style add_argument([ args ], { options })
2114 if (args.length === 1 && Array.isArray(args[0])) {
2115 args = args[0]
2116 deprecate('argument-array',
2117 sub('use add_argument(%(args)s, {...}) instead of add_argument([ %(args)s ], { ... })', {
2118 args: args.map(repr).join(', ')
2119 }))
2120 }
2121 // end
2122
2123 // if no positional args are supplied or only one is supplied and
2124 // it doesn't look like an option string, parse a positional
2125 // argument
2126 let chars = this.prefix_chars
2127 if (!args.length || args.length === 1 && !chars.includes(args[0][0])) {
2128 if (args.length && 'dest' in kwargs) {
2129 throw new TypeError('dest supplied twice for positional argument')
2130 }
2131 kwargs = this._get_positional_kwargs(...args, kwargs)
2132
2133 // otherwise, we're adding an optional argument
2134 } else {
2135 kwargs = this._get_optional_kwargs(...args, kwargs)
2136 }
2137
2138 // if no default was supplied, use the parser-level default
2139 if (!('default' in kwargs)) {
2140 let dest = kwargs.dest
2141 if (dest in this._defaults) {
2142 kwargs.default = this._defaults[dest]
2143 } else if (this.argument_default !== undefined) {
2144 kwargs.default = this.argument_default
2145 }
2146 }
2147
2148 // create the action object, and add it to the parser
2149 let action_class = this._pop_action_class(kwargs)
2150 if (typeof action_class !== 'function') {
2151 throw new TypeError(sub('unknown action "%s"', action_class))
2152 }
2153 // eslint-disable-next-line new-cap
2154 let action = new action_class(kwargs)
2155
2156 // raise an error if the action type is not callable
2157 let type_func = this._registry_get('type', action.type, action.type)
2158 if (typeof type_func !== 'function') {
2159 throw new TypeError(sub('%r is not callable', type_func))
2160 }
2161
2162 if (type_func === FileType) {
2163 throw new TypeError(sub('%r is a FileType class object, instance of it' +
2164 ' must be passed', type_func))
2165 }
2166
2167 // raise an error if the metavar does not match the type
2168 if ('_get_formatter' in this) {
2169 try {
2170 this._get_formatter()._format_args(action, undefined)
2171 } catch (err) {
2172 // check for 'invalid nargs value' is an artifact of TypeError and ValueError in js being the same
2173 if (err instanceof TypeError && err.message !== 'invalid nargs value') {
2174 throw new TypeError('length of metavar tuple does not match nargs')
2175 } else {
2176 throw err
2177 }
2178 }
2179 }
2180
2181 return this._add_action(action)
2182 }
2183
2184 add_argument_group() {
2185 let group = _ArgumentGroup(this, ...arguments)
2186 this._action_groups.push(group)
2187 return group
2188 }
2189
2190 add_mutually_exclusive_group() {
2191 // eslint-disable-next-line no-use-before-define
2192 let group = _MutuallyExclusiveGroup(this, ...arguments)
2193 this._mutually_exclusive_groups.push(group)
2194 return group
2195 }
2196
2197 _add_action(action) {
2198 // resolve any conflicts
2199 this._check_conflict(action)
2200
2201 // add to actions list
2202 this._actions.push(action)
2203 action.container = this
2204
2205 // index the action by any option strings it has
2206 for (let option_string of action.option_strings) {
2207 this._option_string_actions[option_string] = action
2208 }
2209
2210 // set the flag if any option strings look like negative numbers
2211 for (let option_string of action.option_strings) {
2212 if (this._negative_number_matcher.test(option_string)) {
2213 if (!this._has_negative_number_optionals.length) {
2214 this._has_negative_number_optionals.push(true)
2215 }
2216 }
2217 }
2218
2219 // return the created action
2220 return action
2221 }
2222
2223 _remove_action(action) {
2224 _array_remove(this._actions, action)
2225 }
2226
2227 _add_container_actions(container) {
2228 // collect groups by titles
2229 let title_group_map = {}
2230 for (let group of this._action_groups) {
2231 if (group.title in title_group_map) {
2232 let msg = 'cannot merge actions - two groups are named %r'
2233 throw new TypeError(sub(msg, group.title))
2234 }
2235 title_group_map[group.title] = group
2236 }
2237
2238 // map each action to its group
2239 let group_map = new Map()
2240 for (let group of container._action_groups) {
2241
2242 // if a group with the title exists, use that, otherwise
2243 // create a new group matching the container's group
2244 if (!(group.title in title_group_map)) {
2245 title_group_map[group.title] = this.add_argument_group({
2246 title: group.title,
2247 description: group.description,
2248 conflict_handler: group.conflict_handler
2249 })
2250 }
2251
2252 // map the actions to their new group
2253 for (let action of group._group_actions) {
2254 group_map.set(action, title_group_map[group.title])
2255 }
2256 }
2257
2258 // add container's mutually exclusive groups
2259 // NOTE: if add_mutually_exclusive_group ever gains title= and
2260 // description= then this code will need to be expanded as above
2261 for (let group of container._mutually_exclusive_groups) {
2262 let mutex_group = this.add_mutually_exclusive_group({
2263 required: group.required
2264 })
2265
2266 // map the actions to their new mutex group
2267 for (let action of group._group_actions) {
2268 group_map.set(action, mutex_group)
2269 }
2270 }
2271
2272 // add all actions to this container or their group
2273 for (let action of container._actions) {
2274 group_map.get(action)._add_action(action)
2275 }
2276 }
2277
2278 _get_positional_kwargs() {
2279 let [
2280 dest,
2281 kwargs
2282 ] = _parse_opts(arguments, {
2283 dest: no_default,
2284 '**kwargs': no_default
2285 })
2286
2287 // make sure required is not specified
2288 if ('required' in kwargs) {
2289 let msg = "'required' is an invalid argument for positionals"
2290 throw new TypeError(msg)
2291 }
2292
2293 // mark positional arguments as required if at least one is
2294 // always required
2295 if (![OPTIONAL, ZERO_OR_MORE].includes(kwargs.nargs)) {
2296 kwargs.required = true
2297 }
2298 if (kwargs.nargs === ZERO_OR_MORE && !('default' in kwargs)) {
2299 kwargs.required = true
2300 }
2301
2302 // return the keyword arguments with no option strings
2303 return Object.assign(kwargs, { dest, option_strings: [] })
2304 }
2305
2306 _get_optional_kwargs() {
2307 let [
2308 args,
2309 kwargs
2310 ] = _parse_opts(arguments, {
2311 '*args': no_default,
2312 '**kwargs': no_default
2313 })
2314
2315 // determine short and long option strings
2316 let option_strings = []
2317 let long_option_strings = []
2318 let option_string
2319 for (option_string of args) {
2320 // error on strings that don't start with an appropriate prefix
2321 if (!this.prefix_chars.includes(option_string[0])) {
2322 let args = {option: option_string,
2323 prefix_chars: this.prefix_chars}
2324 let msg = 'invalid option string %(option)r: ' +
2325 'must start with a character %(prefix_chars)r'
2326 throw new TypeError(sub(msg, args))
2327 }
2328
2329 // strings starting with two prefix characters are long options
2330 option_strings.push(option_string)
2331 if (option_string.length > 1 && this.prefix_chars.includes(option_string[1])) {
2332 long_option_strings.push(option_string)
2333 }
2334 }
2335
2336 // infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x'
2337 let dest = kwargs.dest
2338 delete kwargs.dest
2339 if (dest === undefined) {
2340 let dest_option_string
2341 if (long_option_strings.length) {
2342 dest_option_string = long_option_strings[0]
2343 } else {
2344 dest_option_string = option_strings[0]
2345 }
2346 dest = _string_lstrip(dest_option_string, this.prefix_chars)
2347 if (!dest) {
2348 let msg = 'dest= is required for options like %r'
2349 throw new TypeError(sub(msg, option_string))
2350 }
2351 dest = dest.replace(/-/g, '_')
2352 }
2353
2354 // return the updated keyword arguments
2355 return Object.assign(kwargs, { dest, option_strings })
2356 }
2357
2358 _pop_action_class(kwargs, default_value = undefined) {
2359 let action = getattr(kwargs, 'action', default_value)
2360 delete kwargs.action
2361 return this._registry_get('action', action, action)
2362 }
2363
2364 _get_handler() {
2365 // determine function from conflict handler string
2366 let handler_func_name = sub('_handle_conflict_%s', this.conflict_handler)
2367 if (typeof this[handler_func_name] === 'function') {
2368 return this[handler_func_name]
2369 } else {
2370 let msg = 'invalid conflict_resolution value: %r'
2371 throw new TypeError(sub(msg, this.conflict_handler))
2372 }
2373 }
2374
2375 _check_conflict(action) {
2376
2377 // find all options that conflict with this option
2378 let confl_optionals = []
2379 for (let option_string of action.option_strings) {
2380 if (hasattr(this._option_string_actions, option_string)) {
2381 let confl_optional = this._option_string_actions[option_string]
2382 confl_optionals.push([ option_string, confl_optional ])
2383 }
2384 }
2385
2386 // resolve any conflicts
2387 if (confl_optionals.length) {
2388 let conflict_handler = this._get_handler()
2389 conflict_handler.call(this, action, confl_optionals)
2390 }
2391 }
2392
2393 _handle_conflict_error(action, conflicting_actions) {
2394 let message = conflicting_actions.length === 1 ?
2395 'conflicting option string: %s' :
2396 'conflicting option strings: %s'
2397 let conflict_string = conflicting_actions.map(([ option_string/*, action*/ ]) => option_string).join(', ')
2398 throw new ArgumentError(action, sub(message, conflict_string))
2399 }
2400
2401 _handle_conflict_resolve(action, conflicting_actions) {
2402
2403 // remove all conflicting options
2404 for (let [ option_string, action ] of conflicting_actions) {
2405
2406 // remove the conflicting option
2407 _array_remove(action.option_strings, option_string)
2408 delete this._option_string_actions[option_string]
2409
2410 // if the option now has no option string, remove it from the
2411 // container holding it
2412 if (!action.option_strings.length) {
2413 action.container._remove_action(action)
2414 }
2415 }
2416 }
2417}))
2418
2419
2420const _ArgumentGroup = _callable(class _ArgumentGroup extends _ActionsContainer {
2421
2422 constructor() {
2423 let [
2424 container,
2425 title,
2426 description,
2427 kwargs
2428 ] = _parse_opts(arguments, {
2429 container: no_default,
2430 title: undefined,
2431 description: undefined,
2432 '**kwargs': no_default
2433 })
2434
2435 // add any missing keyword arguments by checking the container
2436 setdefault(kwargs, 'conflict_handler', container.conflict_handler)
2437 setdefault(kwargs, 'prefix_chars', container.prefix_chars)
2438 setdefault(kwargs, 'argument_default', container.argument_default)
2439 super(Object.assign({ description }, kwargs))
2440
2441 // group attributes
2442 this.title = title
2443 this._group_actions = []
2444
2445 // share most attributes with the container
2446 this._registries = container._registries
2447 this._actions = container._actions
2448 this._option_string_actions = container._option_string_actions
2449 this._defaults = container._defaults
2450 this._has_negative_number_optionals =
2451 container._has_negative_number_optionals
2452 this._mutually_exclusive_groups = container._mutually_exclusive_groups
2453 }
2454
2455 _add_action(action) {
2456 action = super._add_action(action)
2457 this._group_actions.push(action)
2458 return action
2459 }
2460
2461 _remove_action(action) {
2462 super._remove_action(action)
2463 _array_remove(this._group_actions, action)
2464 }
2465})
2466
2467
2468const _MutuallyExclusiveGroup = _callable(class _MutuallyExclusiveGroup extends _ArgumentGroup {
2469
2470 constructor() {
2471 let [
2472 container,
2473 required
2474 ] = _parse_opts(arguments, {
2475 container: no_default,
2476 required: false
2477 })
2478
2479 super(container)
2480 this.required = required
2481 this._container = container
2482 }
2483
2484 _add_action(action) {
2485 if (action.required) {
2486 let msg = 'mutually exclusive arguments must be optional'
2487 throw new TypeError(msg)
2488 }
2489 action = this._container._add_action(action)
2490 this._group_actions.push(action)
2491 return action
2492 }
2493
2494 _remove_action(action) {
2495 this._container._remove_action(action)
2496 _array_remove(this._group_actions, action)
2497 }
2498})
2499
2500
2501const ArgumentParser = _camelcase_alias(_callable(class ArgumentParser extends _AttributeHolder(_ActionsContainer) {
2502 /*
2503 * Object for parsing command line strings into Python objects.
2504 *
2505 * Keyword Arguments:
2506 * - prog -- The name of the program (default: sys.argv[0])
2507 * - usage -- A usage message (default: auto-generated from arguments)
2508 * - description -- A description of what the program does
2509 * - epilog -- Text following the argument descriptions
2510 * - parents -- Parsers whose arguments should be copied into this one
2511 * - formatter_class -- HelpFormatter class for printing help messages
2512 * - prefix_chars -- Characters that prefix optional arguments
2513 * - fromfile_prefix_chars -- Characters that prefix files containing
2514 * additional arguments
2515 * - argument_default -- The default value for all arguments
2516 * - conflict_handler -- String indicating how to handle conflicts
2517 * - add_help -- Add a -h/-help option
2518 * - allow_abbrev -- Allow long options to be abbreviated unambiguously
2519 * - exit_on_error -- Determines whether or not ArgumentParser exits with
2520 * error info when an error occurs
2521 */
2522
2523 constructor() {
2524 let [
2525 prog,
2526 usage,
2527 description,
2528 epilog,
2529 parents,
2530 formatter_class,
2531 prefix_chars,
2532 fromfile_prefix_chars,
2533 argument_default,
2534 conflict_handler,
2535 add_help,
2536 allow_abbrev,
2537 exit_on_error,
2538 debug, // LEGACY (v1 compatibility), debug mode
2539 version // LEGACY (v1 compatibility), version
2540 ] = _parse_opts(arguments, {
2541 prog: undefined,
2542 usage: undefined,
2543 description: undefined,
2544 epilog: undefined,
2545 parents: [],
2546 formatter_class: HelpFormatter,
2547 prefix_chars: '-',
2548 fromfile_prefix_chars: undefined,
2549 argument_default: undefined,
2550 conflict_handler: 'error',
2551 add_help: true,
2552 allow_abbrev: true,
2553 exit_on_error: true,
2554 debug: undefined, // LEGACY (v1 compatibility), debug mode
2555 version: undefined // LEGACY (v1 compatibility), version
2556 })
2557
2558 // LEGACY (v1 compatibility)
2559 if (debug !== undefined) {
2560 deprecate('debug',
2561 'The "debug" argument to ArgumentParser is deprecated. Please ' +
2562 'override ArgumentParser.exit function instead.'
2563 )
2564 }
2565
2566 if (version !== undefined) {
2567 deprecate('version',
2568 'The "version" argument to ArgumentParser is deprecated. Please use ' +
2569 "add_argument(..., { action: 'version', version: 'N', ... }) instead."
2570 )
2571 }
2572 // end
2573
2574 super({
2575 description,
2576 prefix_chars,
2577 argument_default,
2578 conflict_handler
2579 })
2580
2581 // default setting for prog
2582 if (prog === undefined) {
2583 prog = path.basename(get_argv()[0] || '')
2584 }
2585
2586 this.prog = prog
2587 this.usage = usage
2588 this.epilog = epilog
2589 this.formatter_class = formatter_class
2590 this.fromfile_prefix_chars = fromfile_prefix_chars
2591 this.add_help = add_help
2592 this.allow_abbrev = allow_abbrev
2593 this.exit_on_error = exit_on_error
2594 // LEGACY (v1 compatibility), debug mode
2595 this.debug = debug
2596 // end
2597
2598 this._positionals = this.add_argument_group('positional arguments')
2599 this._optionals = this.add_argument_group('optional arguments')
2600 this._subparsers = undefined
2601
2602 // register types
2603 function identity(string) {
2604 return string
2605 }
2606 this.register('type', undefined, identity)
2607 this.register('type', null, identity)
2608 this.register('type', 'auto', identity)
2609 this.register('type', 'int', function (x) {
2610 let result = Number(x)
2611 if (!Number.isInteger(result)) {
2612 throw new TypeError(sub('could not convert string to int: %r', x))
2613 }
2614 return result
2615 })
2616 this.register('type', 'float', function (x) {
2617 let result = Number(x)
2618 if (isNaN(result)) {
2619 throw new TypeError(sub('could not convert string to float: %r', x))
2620 }
2621 return result
2622 })
2623 this.register('type', 'str', String)
2624 // LEGACY (v1 compatibility): custom types
2625 this.register('type', 'string',
2626 util.deprecate(String, 'use {type:"str"} or {type:String} instead of {type:"string"}'))
2627 // end
2628
2629 // add help argument if necessary
2630 // (using explicit default to override global argument_default)
2631 let default_prefix = prefix_chars.includes('-') ? '-' : prefix_chars[0]
2632 if (this.add_help) {
2633 this.add_argument(
2634 default_prefix + 'h',
2635 default_prefix.repeat(2) + 'help',
2636 {
2637 action: 'help',
2638 default: SUPPRESS,
2639 help: 'show this help message and exit'
2640 }
2641 )
2642 }
2643 // LEGACY (v1 compatibility), version
2644 if (version) {
2645 this.add_argument(
2646 default_prefix + 'v',
2647 default_prefix.repeat(2) + 'version',
2648 {
2649 action: 'version',
2650 default: SUPPRESS,
2651 version: this.version,
2652 help: "show program's version number and exit"
2653 }
2654 )
2655 }
2656 // end
2657
2658 // add parent arguments and defaults
2659 for (let parent of parents) {
2660 this._add_container_actions(parent)
2661 Object.assign(this._defaults, parent._defaults)
2662 }
2663 }
2664
2665 // =======================
2666 // Pretty __repr__ methods
2667 // =======================
2668 _get_kwargs() {
2669 let names = [
2670 'prog',
2671 'usage',
2672 'description',
2673 'formatter_class',
2674 'conflict_handler',
2675 'add_help'
2676 ]
2677 return names.map(name => [ name, getattr(this, name) ])
2678 }
2679
2680 // ==================================
2681 // Optional/Positional adding methods
2682 // ==================================
2683 add_subparsers() {
2684 let [
2685 kwargs
2686 ] = _parse_opts(arguments, {
2687 '**kwargs': no_default
2688 })
2689
2690 if (this._subparsers !== undefined) {
2691 this.error('cannot have multiple subparser arguments')
2692 }
2693
2694 // add the parser class to the arguments if it's not present
2695 setdefault(kwargs, 'parser_class', this.constructor)
2696
2697 if ('title' in kwargs || 'description' in kwargs) {
2698 let title = getattr(kwargs, 'title', 'subcommands')
2699 let description = getattr(kwargs, 'description', undefined)
2700 delete kwargs.title
2701 delete kwargs.description
2702 this._subparsers = this.add_argument_group(title, description)
2703 } else {
2704 this._subparsers = this._positionals
2705 }
2706
2707 // prog defaults to the usage message of this parser, skipping
2708 // optional arguments and with no "usage:" prefix
2709 if (kwargs.prog === undefined) {
2710 let formatter = this._get_formatter()
2711 let positionals = this._get_positional_actions()
2712 let groups = this._mutually_exclusive_groups
2713 formatter.add_usage(this.usage, positionals, groups, '')
2714 kwargs.prog = formatter.format_help().trim()
2715 }
2716
2717 // create the parsers action and add it to the positionals list
2718 let parsers_class = this._pop_action_class(kwargs, 'parsers')
2719 // eslint-disable-next-line new-cap
2720 let action = new parsers_class(Object.assign({ option_strings: [] }, kwargs))
2721 this._subparsers._add_action(action)
2722
2723 // return the created parsers action
2724 return action
2725 }
2726
2727 _add_action(action) {
2728 if (action.option_strings.length) {
2729 this._optionals._add_action(action)
2730 } else {
2731 this._positionals._add_action(action)
2732 }
2733 return action
2734 }
2735
2736 _get_optional_actions() {
2737 return this._actions.filter(action => action.option_strings.length)
2738 }
2739
2740 _get_positional_actions() {
2741 return this._actions.filter(action => !action.option_strings.length)
2742 }
2743
2744 // =====================================
2745 // Command line argument parsing methods
2746 // =====================================
2747 parse_args(args = undefined, namespace = undefined) {
2748 let argv
2749 [ args, argv ] = this.parse_known_args(args, namespace)
2750 if (argv && argv.length > 0) {
2751 let msg = 'unrecognized arguments: %s'
2752 this.error(sub(msg, argv.join(' ')))
2753 }
2754 return args
2755 }
2756
2757 parse_known_args(args = undefined, namespace = undefined) {
2758 if (args === undefined) {
2759 args = get_argv().slice(1)
2760 }
2761
2762 // default Namespace built from parser defaults
2763 if (namespace === undefined) {
2764 namespace = new Namespace()
2765 }
2766
2767 // add any action defaults that aren't present
2768 for (let action of this._actions) {
2769 if (action.dest !== SUPPRESS) {
2770 if (!hasattr(namespace, action.dest)) {
2771 if (action.default !== SUPPRESS) {
2772 setattr(namespace, action.dest, action.default)
2773 }
2774 }
2775 }
2776 }
2777
2778 // add any parser defaults that aren't present
2779 for (let dest of Object.keys(this._defaults)) {
2780 if (!hasattr(namespace, dest)) {
2781 setattr(namespace, dest, this._defaults[dest])
2782 }
2783 }
2784
2785 // parse the arguments and exit if there are any errors
2786 if (this.exit_on_error) {
2787 try {
2788 [ namespace, args ] = this._parse_known_args(args, namespace)
2789 } catch (err) {
2790 if (err instanceof ArgumentError) {
2791 this.error(err.message)
2792 } else {
2793 throw err
2794 }
2795 }
2796 } else {
2797 [ namespace, args ] = this._parse_known_args(args, namespace)
2798 }
2799
2800 if (hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) {
2801 args = args.concat(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR))
2802 delattr(namespace, _UNRECOGNIZED_ARGS_ATTR)
2803 }
2804
2805 return [ namespace, args ]
2806 }
2807
2808 _parse_known_args(arg_strings, namespace) {
2809 // replace arg strings that are file references
2810 if (this.fromfile_prefix_chars !== undefined) {
2811 arg_strings = this._read_args_from_files(arg_strings)
2812 }
2813
2814 // map all mutually exclusive arguments to the other arguments
2815 // they can't occur with
2816 let action_conflicts = new Map()
2817 for (let mutex_group of this._mutually_exclusive_groups) {
2818 let group_actions = mutex_group._group_actions
2819 for (let [ i, mutex_action ] of Object.entries(mutex_group._group_actions)) {
2820 let conflicts = action_conflicts.get(mutex_action) || []
2821 conflicts = conflicts.concat(group_actions.slice(0, +i))
2822 conflicts = conflicts.concat(group_actions.slice(+i + 1))
2823 action_conflicts.set(mutex_action, conflicts)
2824 }
2825 }
2826
2827 // find all option indices, and determine the arg_string_pattern
2828 // which has an 'O' if there is an option at an index,
2829 // an 'A' if there is an argument, or a '-' if there is a '--'
2830 let option_string_indices = {}
2831 let arg_string_pattern_parts = []
2832 let arg_strings_iter = Object.entries(arg_strings)[Symbol.iterator]()
2833 for (let [ i, arg_string ] of arg_strings_iter) {
2834
2835 // all args after -- are non-options
2836 if (arg_string === '--') {
2837 arg_string_pattern_parts.push('-')
2838 for ([ i, arg_string ] of arg_strings_iter) {
2839 arg_string_pattern_parts.push('A')
2840 }
2841
2842 // otherwise, add the arg to the arg strings
2843 // and note the index if it was an option
2844 } else {
2845 let option_tuple = this._parse_optional(arg_string)
2846 let pattern
2847 if (option_tuple === undefined) {
2848 pattern = 'A'
2849 } else {
2850 option_string_indices[i] = option_tuple
2851 pattern = 'O'
2852 }
2853 arg_string_pattern_parts.push(pattern)
2854 }
2855 }
2856
2857 // join the pieces together to form the pattern
2858 let arg_strings_pattern = arg_string_pattern_parts.join('')
2859
2860 // converts arg strings to the appropriate and then takes the action
2861 let seen_actions = new Set()
2862 let seen_non_default_actions = new Set()
2863 let extras
2864
2865 let take_action = (action, argument_strings, option_string = undefined) => {
2866 seen_actions.add(action)
2867 let argument_values = this._get_values(action, argument_strings)
2868
2869 // error if this argument is not allowed with other previously
2870 // seen arguments, assuming that actions that use the default
2871 // value don't really count as "present"
2872 if (argument_values !== action.default) {
2873 seen_non_default_actions.add(action)
2874 for (let conflict_action of action_conflicts.get(action) || []) {
2875 if (seen_non_default_actions.has(conflict_action)) {
2876 let msg = 'not allowed with argument %s'
2877 let action_name = _get_action_name(conflict_action)
2878 throw new ArgumentError(action, sub(msg, action_name))
2879 }
2880 }
2881 }
2882
2883 // take the action if we didn't receive a SUPPRESS value
2884 // (e.g. from a default)
2885 if (argument_values !== SUPPRESS) {
2886 action(this, namespace, argument_values, option_string)
2887 }
2888 }
2889
2890 // function to convert arg_strings into an optional action
2891 let consume_optional = start_index => {
2892
2893 // get the optional identified at this index
2894 let option_tuple = option_string_indices[start_index]
2895 let [ action, option_string, explicit_arg ] = option_tuple
2896
2897 // identify additional optionals in the same arg string
2898 // (e.g. -xyz is the same as -x -y -z if no args are required)
2899 let action_tuples = []
2900 let stop
2901 for (;;) {
2902
2903 // if we found no optional action, skip it
2904 if (action === undefined) {
2905 extras.push(arg_strings[start_index])
2906 return start_index + 1
2907 }
2908
2909 // if there is an explicit argument, try to match the
2910 // optional's string arguments to only this
2911 if (explicit_arg !== undefined) {
2912 let arg_count = this._match_argument(action, 'A')
2913
2914 // if the action is a single-dash option and takes no
2915 // arguments, try to parse more single-dash options out
2916 // of the tail of the option string
2917 let chars = this.prefix_chars
2918 if (arg_count === 0 && !chars.includes(option_string[1])) {
2919 action_tuples.push([ action, [], option_string ])
2920 let char = option_string[0]
2921 option_string = char + explicit_arg[0]
2922 let new_explicit_arg = explicit_arg.slice(1) || undefined
2923 let optionals_map = this._option_string_actions
2924 if (hasattr(optionals_map, option_string)) {
2925 action = optionals_map[option_string]
2926 explicit_arg = new_explicit_arg
2927 } else {
2928 let msg = 'ignored explicit argument %r'
2929 throw new ArgumentError(action, sub(msg, explicit_arg))
2930 }
2931
2932 // if the action expect exactly one argument, we've
2933 // successfully matched the option; exit the loop
2934 } else if (arg_count === 1) {
2935 stop = start_index + 1
2936 let args = [ explicit_arg ]
2937 action_tuples.push([ action, args, option_string ])
2938 break
2939
2940 // error if a double-dash option did not use the
2941 // explicit argument
2942 } else {
2943 let msg = 'ignored explicit argument %r'
2944 throw new ArgumentError(action, sub(msg, explicit_arg))
2945 }
2946
2947 // if there is no explicit argument, try to match the
2948 // optional's string arguments with the following strings
2949 // if successful, exit the loop
2950 } else {
2951 let start = start_index + 1
2952 let selected_patterns = arg_strings_pattern.slice(start)
2953 let arg_count = this._match_argument(action, selected_patterns)
2954 stop = start + arg_count
2955 let args = arg_strings.slice(start, stop)
2956 action_tuples.push([ action, args, option_string ])
2957 break
2958 }
2959 }
2960
2961 // add the Optional to the list and return the index at which
2962 // the Optional's string args stopped
2963 assert(action_tuples.length)
2964 for (let [ action, args, option_string ] of action_tuples) {
2965 take_action(action, args, option_string)
2966 }
2967 return stop
2968 }
2969
2970 // the list of Positionals left to be parsed; this is modified
2971 // by consume_positionals()
2972 let positionals = this._get_positional_actions()
2973
2974 // function to convert arg_strings into positional actions
2975 let consume_positionals = start_index => {
2976 // match as many Positionals as possible
2977 let selected_pattern = arg_strings_pattern.slice(start_index)
2978 let arg_counts = this._match_arguments_partial(positionals, selected_pattern)
2979
2980 // slice off the appropriate arg strings for each Positional
2981 // and add the Positional and its args to the list
2982 for (let i = 0; i < positionals.length && i < arg_counts.length; i++) {
2983 let action = positionals[i]
2984 let arg_count = arg_counts[i]
2985 let args = arg_strings.slice(start_index, start_index + arg_count)
2986 start_index += arg_count
2987 take_action(action, args)
2988 }
2989
2990 // slice off the Positionals that we just parsed and return the
2991 // index at which the Positionals' string args stopped
2992 positionals = positionals.slice(arg_counts.length)
2993 return start_index
2994 }
2995
2996 // consume Positionals and Optionals alternately, until we have
2997 // passed the last option string
2998 extras = []
2999 let start_index = 0
3000 let max_option_string_index = Math.max(-1, ...Object.keys(option_string_indices).map(Number))
3001 while (start_index <= max_option_string_index) {
3002
3003 // consume any Positionals preceding the next option
3004 let next_option_string_index = Math.min(
3005 // eslint-disable-next-line no-loop-func
3006 ...Object.keys(option_string_indices).map(Number).filter(index => index >= start_index)
3007 )
3008 if (start_index !== next_option_string_index) {
3009 let positionals_end_index = consume_positionals(start_index)
3010
3011 // only try to parse the next optional if we didn't consume
3012 // the option string during the positionals parsing
3013 if (positionals_end_index > start_index) {
3014 start_index = positionals_end_index
3015 continue
3016 } else {
3017 start_index = positionals_end_index
3018 }
3019 }
3020
3021 // if we consumed all the positionals we could and we're not
3022 // at the index of an option string, there were extra arguments
3023 if (!(start_index in option_string_indices)) {
3024 let strings = arg_strings.slice(start_index, next_option_string_index)
3025 extras = extras.concat(strings)
3026 start_index = next_option_string_index
3027 }
3028
3029 // consume the next optional and any arguments for it
3030 start_index = consume_optional(start_index)
3031 }
3032
3033 // consume any positionals following the last Optional
3034 let stop_index = consume_positionals(start_index)
3035
3036 // if we didn't consume all the argument strings, there were extras
3037 extras = extras.concat(arg_strings.slice(stop_index))
3038
3039 // make sure all required actions were present and also convert
3040 // action defaults which were not given as arguments
3041 let required_actions = []
3042 for (let action of this._actions) {
3043 if (!seen_actions.has(action)) {
3044 if (action.required) {
3045 required_actions.push(_get_action_name(action))
3046 } else {
3047 // Convert action default now instead of doing it before
3048 // parsing arguments to avoid calling convert functions
3049 // twice (which may fail) if the argument was given, but
3050 // only if it was defined already in the namespace
3051 if (action.default !== undefined &&
3052 typeof action.default === 'string' &&
3053 hasattr(namespace, action.dest) &&
3054 action.default === getattr(namespace, action.dest)) {
3055 setattr(namespace, action.dest,
3056 this._get_value(action, action.default))
3057 }
3058 }
3059 }
3060 }
3061
3062 if (required_actions.length) {
3063 this.error(sub('the following arguments are required: %s',
3064 required_actions.join(', ')))
3065 }
3066
3067 // make sure all required groups had one option present
3068 for (let group of this._mutually_exclusive_groups) {
3069 if (group.required) {
3070 let no_actions_used = true
3071 for (let action of group._group_actions) {
3072 if (seen_non_default_actions.has(action)) {
3073 no_actions_used = false
3074 break
3075 }
3076 }
3077
3078 // if no actions were used, report the error
3079 if (no_actions_used) {
3080 let names = group._group_actions
3081 .filter(action => action.help !== SUPPRESS)
3082 .map(action => _get_action_name(action))
3083 let msg = 'one of the arguments %s is required'
3084 this.error(sub(msg, names.join(' ')))
3085 }
3086 }
3087 }
3088
3089 // return the updated namespace and the extra arguments
3090 return [ namespace, extras ]
3091 }
3092
3093 _read_args_from_files(arg_strings) {
3094 // expand arguments referencing files
3095 let new_arg_strings = []
3096 for (let arg_string of arg_strings) {
3097
3098 // for regular arguments, just add them back into the list
3099 if (!arg_string || !this.fromfile_prefix_chars.includes(arg_string[0])) {
3100 new_arg_strings.push(arg_string)
3101
3102 // replace arguments referencing files with the file content
3103 } else {
3104 try {
3105 let args_file = fs.readFileSync(arg_string.slice(1), 'utf8')
3106 let arg_strings = []
3107 for (let arg_line of splitlines(args_file)) {
3108 for (let arg of this.convert_arg_line_to_args(arg_line)) {
3109 arg_strings.push(arg)
3110 }
3111 }
3112 arg_strings = this._read_args_from_files(arg_strings)
3113 new_arg_strings = new_arg_strings.concat(arg_strings)
3114 } catch (err) {
3115 this.error(err.message)
3116 }
3117 }
3118 }
3119
3120 // return the modified argument list
3121 return new_arg_strings
3122 }
3123
3124 convert_arg_line_to_args(arg_line) {
3125 return [arg_line]
3126 }
3127
3128 _match_argument(action, arg_strings_pattern) {
3129 // match the pattern for this action to the arg strings
3130 let nargs_pattern = this._get_nargs_pattern(action)
3131 let match = arg_strings_pattern.match(new RegExp('^' + nargs_pattern))
3132
3133 // raise an exception if we weren't able to find a match
3134 if (match === null) {
3135 let nargs_errors = {
3136 undefined: 'expected one argument',
3137 [OPTIONAL]: 'expected at most one argument',
3138 [ONE_OR_MORE]: 'expected at least one argument'
3139 }
3140 let msg = nargs_errors[action.nargs]
3141 if (msg === undefined) {
3142 msg = sub(action.nargs === 1 ? 'expected %s argument' : 'expected %s arguments', action.nargs)
3143 }
3144 throw new ArgumentError(action, msg)
3145 }
3146
3147 // return the number of arguments matched
3148 return match[1].length
3149 }
3150
3151 _match_arguments_partial(actions, arg_strings_pattern) {
3152 // progressively shorten the actions list by slicing off the
3153 // final actions until we find a match
3154 let result = []
3155 for (let i of range(actions.length, 0, -1)) {
3156 let actions_slice = actions.slice(0, i)
3157 let pattern = actions_slice.map(action => this._get_nargs_pattern(action)).join('')
3158 let match = arg_strings_pattern.match(new RegExp('^' + pattern))
3159 if (match !== null) {
3160 result = result.concat(match.slice(1).map(string => string.length))
3161 break
3162 }
3163 }
3164
3165 // return the list of arg string counts
3166 return result
3167 }
3168
3169 _parse_optional(arg_string) {
3170 // if it's an empty string, it was meant to be a positional
3171 if (!arg_string) {
3172 return undefined
3173 }
3174
3175 // if it doesn't start with a prefix, it was meant to be positional
3176 if (!this.prefix_chars.includes(arg_string[0])) {
3177 return undefined
3178 }
3179
3180 // if the option string is present in the parser, return the action
3181 if (arg_string in this._option_string_actions) {
3182 let action = this._option_string_actions[arg_string]
3183 return [ action, arg_string, undefined ]
3184 }
3185
3186 // if it's just a single character, it was meant to be positional
3187 if (arg_string.length === 1) {
3188 return undefined
3189 }
3190
3191 // if the option string before the "=" is present, return the action
3192 if (arg_string.includes('=')) {
3193 let [ option_string, explicit_arg ] = _string_split(arg_string, '=', 1)
3194 if (option_string in this._option_string_actions) {
3195 let action = this._option_string_actions[option_string]
3196 return [ action, option_string, explicit_arg ]
3197 }
3198 }
3199
3200 // search through all possible prefixes of the option string
3201 // and all actions in the parser for possible interpretations
3202 let option_tuples = this._get_option_tuples(arg_string)
3203
3204 // if multiple actions match, the option string was ambiguous
3205 if (option_tuples.length > 1) {
3206 let options = option_tuples.map(([ /*action*/, option_string/*, explicit_arg*/ ]) => option_string).join(', ')
3207 let args = {option: arg_string, matches: options}
3208 let msg = 'ambiguous option: %(option)s could match %(matches)s'
3209 this.error(sub(msg, args))
3210
3211 // if exactly one action matched, this segmentation is good,
3212 // so return the parsed action
3213 } else if (option_tuples.length === 1) {
3214 let [ option_tuple ] = option_tuples
3215 return option_tuple
3216 }
3217
3218 // if it was not found as an option, but it looks like a negative
3219 // number, it was meant to be positional
3220 // unless there are negative-number-like options
3221 if (this._negative_number_matcher.test(arg_string)) {
3222 if (!this._has_negative_number_optionals.length) {
3223 return undefined
3224 }
3225 }
3226
3227 // if it contains a space, it was meant to be a positional
3228 if (arg_string.includes(' ')) {
3229 return undefined
3230 }
3231
3232 // it was meant to be an optional but there is no such option
3233 // in this parser (though it might be a valid option in a subparser)
3234 return [ undefined, arg_string, undefined ]
3235 }
3236
3237 _get_option_tuples(option_string) {
3238 let result = []
3239
3240 // option strings starting with two prefix characters are only
3241 // split at the '='
3242 let chars = this.prefix_chars
3243 if (chars.includes(option_string[0]) && chars.includes(option_string[1])) {
3244 if (this.allow_abbrev) {
3245 let option_prefix, explicit_arg
3246 if (option_string.includes('=')) {
3247 [ option_prefix, explicit_arg ] = _string_split(option_string, '=', 1)
3248 } else {
3249 option_prefix = option_string
3250 explicit_arg = undefined
3251 }
3252 for (let option_string of Object.keys(this._option_string_actions)) {
3253 if (option_string.startsWith(option_prefix)) {
3254 let action = this._option_string_actions[option_string]
3255 let tup = [ action, option_string, explicit_arg ]
3256 result.push(tup)
3257 }
3258 }
3259 }
3260
3261 // single character options can be concatenated with their arguments
3262 // but multiple character options always have to have their argument
3263 // separate
3264 } else if (chars.includes(option_string[0]) && !chars.includes(option_string[1])) {
3265 let option_prefix = option_string
3266 let explicit_arg = undefined
3267 let short_option_prefix = option_string.slice(0, 2)
3268 let short_explicit_arg = option_string.slice(2)
3269
3270 for (let option_string of Object.keys(this._option_string_actions)) {
3271 if (option_string === short_option_prefix) {
3272 let action = this._option_string_actions[option_string]
3273 let tup = [ action, option_string, short_explicit_arg ]
3274 result.push(tup)
3275 } else if (option_string.startsWith(option_prefix)) {
3276 let action = this._option_string_actions[option_string]
3277 let tup = [ action, option_string, explicit_arg ]
3278 result.push(tup)
3279 }
3280 }
3281
3282 // shouldn't ever get here
3283 } else {
3284 this.error(sub('unexpected option string: %s', option_string))
3285 }
3286
3287 // return the collected option tuples
3288 return result
3289 }
3290
3291 _get_nargs_pattern(action) {
3292 // in all examples below, we have to allow for '--' args
3293 // which are represented as '-' in the pattern
3294 let nargs = action.nargs
3295 let nargs_pattern
3296
3297 // the default (None) is assumed to be a single argument
3298 if (nargs === undefined) {
3299 nargs_pattern = '(-*A-*)'
3300
3301 // allow zero or one arguments
3302 } else if (nargs === OPTIONAL) {
3303 nargs_pattern = '(-*A?-*)'
3304
3305 // allow zero or more arguments
3306 } else if (nargs === ZERO_OR_MORE) {
3307 nargs_pattern = '(-*[A-]*)'
3308
3309 // allow one or more arguments
3310 } else if (nargs === ONE_OR_MORE) {
3311 nargs_pattern = '(-*A[A-]*)'
3312
3313 // allow any number of options or arguments
3314 } else if (nargs === REMAINDER) {
3315 nargs_pattern = '([-AO]*)'
3316
3317 // allow one argument followed by any number of options or arguments
3318 } else if (nargs === PARSER) {
3319 nargs_pattern = '(-*A[-AO]*)'
3320
3321 // suppress action, like nargs=0
3322 } else if (nargs === SUPPRESS) {
3323 nargs_pattern = '(-*-*)'
3324
3325 // all others should be integers
3326 } else {
3327 nargs_pattern = sub('(-*%s-*)', 'A'.repeat(nargs).split('').join('-*'))
3328 }
3329
3330 // if this is an optional action, -- is not allowed
3331 if (action.option_strings.length) {
3332 nargs_pattern = nargs_pattern.replace(/-\*/g, '')
3333 nargs_pattern = nargs_pattern.replace(/-/g, '')
3334 }
3335
3336 // return the pattern
3337 return nargs_pattern
3338 }
3339
3340 // ========================
3341 // Alt command line argument parsing, allowing free intermix
3342 // ========================
3343
3344 parse_intermixed_args(args = undefined, namespace = undefined) {
3345 let argv
3346 [ args, argv ] = this.parse_known_intermixed_args(args, namespace)
3347 if (argv.length) {
3348 let msg = 'unrecognized arguments: %s'
3349 this.error(sub(msg, argv.join(' ')))
3350 }
3351 return args
3352 }
3353
3354 parse_known_intermixed_args(args = undefined, namespace = undefined) {
3355 // returns a namespace and list of extras
3356 //
3357 // positional can be freely intermixed with optionals. optionals are
3358 // first parsed with all positional arguments deactivated. The 'extras'
3359 // are then parsed. If the parser definition is incompatible with the
3360 // intermixed assumptions (e.g. use of REMAINDER, subparsers) a
3361 // TypeError is raised.
3362 //
3363 // positionals are 'deactivated' by setting nargs and default to
3364 // SUPPRESS. This blocks the addition of that positional to the
3365 // namespace
3366
3367 let extras
3368 let positionals = this._get_positional_actions()
3369 let a = positionals.filter(action => [ PARSER, REMAINDER ].includes(action.nargs))
3370 if (a.length) {
3371 throw new TypeError(sub('parse_intermixed_args: positional arg' +
3372 ' with nargs=%s', a[0].nargs))
3373 }
3374
3375 for (let group of this._mutually_exclusive_groups) {
3376 for (let action of group._group_actions) {
3377 if (positionals.includes(action)) {
3378 throw new TypeError('parse_intermixed_args: positional in' +
3379 ' mutuallyExclusiveGroup')
3380 }
3381 }
3382 }
3383
3384 let save_usage
3385 try {
3386 save_usage = this.usage
3387 let remaining_args
3388 try {
3389 if (this.usage === undefined) {
3390 // capture the full usage for use in error messages
3391 this.usage = this.format_usage().slice(7)
3392 }
3393 for (let action of positionals) {
3394 // deactivate positionals
3395 action.save_nargs = action.nargs
3396 // action.nargs = 0
3397 action.nargs = SUPPRESS
3398 action.save_default = action.default
3399 action.default = SUPPRESS
3400 }
3401 [ namespace, remaining_args ] = this.parse_known_args(args,
3402 namespace)
3403 for (let action of positionals) {
3404 // remove the empty positional values from namespace
3405 let attr = getattr(namespace, action.dest)
3406 if (Array.isArray(attr) && attr.length === 0) {
3407 // eslint-disable-next-line no-console
3408 console.warn(sub('Do not expect %s in %s', action.dest, namespace))
3409 delattr(namespace, action.dest)
3410 }
3411 }
3412 } finally {
3413 // restore nargs and usage before exiting
3414 for (let action of positionals) {
3415 action.nargs = action.save_nargs
3416 action.default = action.save_default
3417 }
3418 }
3419 let optionals = this._get_optional_actions()
3420 try {
3421 // parse positionals. optionals aren't normally required, but
3422 // they could be, so make sure they aren't.
3423 for (let action of optionals) {
3424 action.save_required = action.required
3425 action.required = false
3426 }
3427 for (let group of this._mutually_exclusive_groups) {
3428 group.save_required = group.required
3429 group.required = false
3430 }
3431 [ namespace, extras ] = this.parse_known_args(remaining_args,
3432 namespace)
3433 } finally {
3434 // restore parser values before exiting
3435 for (let action of optionals) {
3436 action.required = action.save_required
3437 }
3438 for (let group of this._mutually_exclusive_groups) {
3439 group.required = group.save_required
3440 }
3441 }
3442 } finally {
3443 this.usage = save_usage
3444 }
3445 return [ namespace, extras ]
3446 }
3447
3448 // ========================
3449 // Value conversion methods
3450 // ========================
3451 _get_values(action, arg_strings) {
3452 // for everything but PARSER, REMAINDER args, strip out first '--'
3453 if (![PARSER, REMAINDER].includes(action.nargs)) {
3454 try {
3455 _array_remove(arg_strings, '--')
3456 } catch (err) {}
3457 }
3458
3459 let value
3460 // optional argument produces a default when not present
3461 if (!arg_strings.length && action.nargs === OPTIONAL) {
3462 if (action.option_strings.length) {
3463 value = action.const
3464 } else {
3465 value = action.default
3466 }
3467 if (typeof value === 'string') {
3468 value = this._get_value(action, value)
3469 this._check_value(action, value)
3470 }
3471
3472 // when nargs='*' on a positional, if there were no command-line
3473 // args, use the default if it is anything other than None
3474 } else if (!arg_strings.length && action.nargs === ZERO_OR_MORE &&
3475 !action.option_strings.length) {
3476 if (action.default !== undefined) {
3477 value = action.default
3478 } else {
3479 value = arg_strings
3480 }
3481 this._check_value(action, value)
3482
3483 // single argument or optional argument produces a single value
3484 } else if (arg_strings.length === 1 && [undefined, OPTIONAL].includes(action.nargs)) {
3485 let arg_string = arg_strings[0]
3486 value = this._get_value(action, arg_string)
3487 this._check_value(action, value)
3488
3489 // REMAINDER arguments convert all values, checking none
3490 } else if (action.nargs === REMAINDER) {
3491 value = arg_strings.map(v => this._get_value(action, v))
3492
3493 // PARSER arguments convert all values, but check only the first
3494 } else if (action.nargs === PARSER) {
3495 value = arg_strings.map(v => this._get_value(action, v))
3496 this._check_value(action, value[0])
3497
3498 // SUPPRESS argument does not put anything in the namespace
3499 } else if (action.nargs === SUPPRESS) {
3500 value = SUPPRESS
3501
3502 // all other types of nargs produce a list
3503 } else {
3504 value = arg_strings.map(v => this._get_value(action, v))
3505 for (let v of value) {
3506 this._check_value(action, v)
3507 }
3508 }
3509
3510 // return the converted value
3511 return value
3512 }
3513
3514 _get_value(action, arg_string) {
3515 let type_func = this._registry_get('type', action.type, action.type)
3516 if (typeof type_func !== 'function') {
3517 let msg = '%r is not callable'
3518 throw new ArgumentError(action, sub(msg, type_func))
3519 }
3520
3521 // convert the value to the appropriate type
3522 let result
3523 try {
3524 try {
3525 result = type_func(arg_string)
3526 } catch (err) {
3527 // Dear TC39, why would you ever consider making es6 classes not callable?
3528 // We had one universal interface, [[Call]], which worked for anything
3529 // (with familiar this-instanceof guard for classes). Now we have two.
3530 if (err instanceof TypeError &&
3531 /Class constructor .* cannot be invoked without 'new'/.test(err.message)) {
3532 // eslint-disable-next-line new-cap
3533 result = new type_func(arg_string)
3534 } else {
3535 throw err
3536 }
3537 }
3538
3539 } catch (err) {
3540 // ArgumentTypeErrors indicate errors
3541 if (err instanceof ArgumentTypeError) {
3542 //let name = getattr(action.type, 'name', repr(action.type))
3543 let msg = err.message
3544 throw new ArgumentError(action, msg)
3545
3546 // TypeErrors or ValueErrors also indicate errors
3547 } else if (err instanceof TypeError) {
3548 let name = getattr(action.type, 'name', repr(action.type))
3549 let args = {type: name, value: arg_string}
3550 let msg = 'invalid %(type)s value: %(value)r'
3551 throw new ArgumentError(action, sub(msg, args))
3552 } else {
3553 throw err
3554 }
3555 }
3556
3557 // return the converted value
3558 return result
3559 }
3560
3561 _check_value(action, value) {
3562 // converted value must be one of the choices (if specified)
3563 if (action.choices !== undefined && !_choices_to_array(action.choices).includes(value)) {
3564 let args = {value,
3565 choices: _choices_to_array(action.choices).map(repr).join(', ')}
3566 let msg = 'invalid choice: %(value)r (choose from %(choices)s)'
3567 throw new ArgumentError(action, sub(msg, args))
3568 }
3569 }
3570
3571 // =======================
3572 // Help-formatting methods
3573 // =======================
3574 format_usage() {
3575 let formatter = this._get_formatter()
3576 formatter.add_usage(this.usage, this._actions,
3577 this._mutually_exclusive_groups)
3578 return formatter.format_help()
3579 }
3580
3581 format_help() {
3582 let formatter = this._get_formatter()
3583
3584 // usage
3585 formatter.add_usage(this.usage, this._actions,
3586 this._mutually_exclusive_groups)
3587
3588 // description
3589 formatter.add_text(this.description)
3590
3591 // positionals, optionals and user-defined groups
3592 for (let action_group of this._action_groups) {
3593 formatter.start_section(action_group.title)
3594 formatter.add_text(action_group.description)
3595 formatter.add_arguments(action_group._group_actions)
3596 formatter.end_section()
3597 }
3598
3599 // epilog
3600 formatter.add_text(this.epilog)
3601
3602 // determine help from format above
3603 return formatter.format_help()
3604 }
3605
3606 _get_formatter() {
3607 // eslint-disable-next-line new-cap
3608 return new this.formatter_class({ prog: this.prog })
3609 }
3610
3611 // =====================
3612 // Help-printing methods
3613 // =====================
3614 print_usage(file = undefined) {
3615 if (file === undefined) file = process.stdout
3616 this._print_message(this.format_usage(), file)
3617 }
3618
3619 print_help(file = undefined) {
3620 if (file === undefined) file = process.stdout
3621 this._print_message(this.format_help(), file)
3622 }
3623
3624 _print_message(message, file = undefined) {
3625 if (message) {
3626 if (file === undefined) file = process.stderr
3627 file.write(message)
3628 }
3629 }
3630
3631 // ===============
3632 // Exiting methods
3633 // ===============
3634 exit(status = 0, message = undefined) {
3635 if (message) {
3636 this._print_message(message, process.stderr)
3637 }
3638 process.exit(status)
3639 }
3640
3641 error(message) {
3642 /*
3643 * error(message: string)
3644 *
3645 * Prints a usage message incorporating the message to stderr and
3646 * exits.
3647 *
3648 * If you override this in a subclass, it should not return -- it
3649 * should either exit or raise an exception.
3650 */
3651
3652 // LEGACY (v1 compatibility), debug mode
3653 if (this.debug === true) throw new Error(message)
3654 // end
3655 this.print_usage(process.stderr)
3656 let args = {prog: this.prog, message: message}
3657 this.exit(2, sub('%(prog)s: error: %(message)s\n', args))
3658 }
3659}))
3660
3661
3662module.exports = {
3663 ArgumentParser,
3664 ArgumentError,
3665 ArgumentTypeError,
3666 BooleanOptionalAction,
3667 FileType,
3668 HelpFormatter,
3669 ArgumentDefaultsHelpFormatter,
3670 RawDescriptionHelpFormatter,
3671 RawTextHelpFormatter,
3672 MetavarTypeHelpFormatter,
3673 Namespace,
3674 Action,
3675 ONE_OR_MORE,
3676 OPTIONAL,
3677 PARSER,
3678 REMAINDER,
3679 SUPPRESS,
3680 ZERO_OR_MORE
3681}
3682
3683// LEGACY (v1 compatibility), Const alias
3684Object.defineProperty(module.exports, 'Const', {
3685 get() {
3686 let result = {}
3687 Object.entries({ ONE_OR_MORE, OPTIONAL, PARSER, REMAINDER, SUPPRESS, ZERO_OR_MORE }).forEach(([ n, v ]) => {
3688 Object.defineProperty(result, n, {
3689 get() {
3690 deprecate(n, sub('use argparse.%s instead of argparse.Const.%s', n, n))
3691 return v
3692 }
3693 })
3694 })
3695 Object.entries({ _UNRECOGNIZED_ARGS_ATTR }).forEach(([ n, v ]) => {
3696 Object.defineProperty(result, n, {
3697 get() {
3698 deprecate(n, sub('argparse.Const.%s is an internal symbol and will no longer be available', n))
3699 return v
3700 }
3701 })
3702 })
3703 return result
3704 },
3705 enumerable: false
3706})
3707// end