bmad-agent-init
Version:
Windsurf integration for BMAD-METHOD - automatic initialization of bmad-agent in projects
1,460 lines (1,278 loc) • 130 kB
JavaScript
// Port of python's argparse module, version 3.9.0:
// https://github.com/python/cpython/blob/v3.9.0rc1/Lib/argparse.py
'use strict'
// Copyright (C) 2010-2020 Python Software Foundation.
// Copyright (C) 2020 argparse.js authors
/*
* Command-line parsing library
*
* This module is an optparse-inspired command-line parsing library that:
*
* - handles both optional and positional arguments
* - produces highly informative usage messages
* - supports parsers that dispatch to sub-parsers
*
* The following is a simple usage example that sums integers from the
* command-line and writes the result to a file::
*
* parser = argparse.ArgumentParser(
* description='sum the integers at the command line')
* parser.add_argument(
* 'integers', metavar='int', nargs='+', type=int,
* help='an integer to be summed')
* parser.add_argument(
* '--log', default=sys.stdout, type=argparse.FileType('w'),
* help='the file where the sum should be written')
* args = parser.parse_args()
* args.log.write('%s' % sum(args.integers))
* args.log.close()
*
* The module contains the following public classes:
*
* - ArgumentParser -- The main entry point for command-line parsing. As the
* example above shows, the add_argument() method is used to populate
* the parser with actions for optional and positional arguments. Then
* the parse_args() method is invoked to convert the args at the
* command-line into an object with attributes.
*
* - ArgumentError -- The exception raised by ArgumentParser objects when
* there are errors with the parser's actions. Errors raised while
* parsing the command-line are caught by ArgumentParser and emitted
* as command-line messages.
*
* - FileType -- A factory for defining types of files to be created. As the
* example above shows, instances of FileType are typically passed as
* the type= argument of add_argument() calls.
*
* - Action -- The base class for parser actions. Typically actions are
* selected by passing strings like 'store_true' or 'append_const' to
* the action= argument of add_argument(). However, for greater
* customization of ArgumentParser actions, subclasses of Action may
* be defined and passed as the action= argument.
*
* - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter,
* ArgumentDefaultsHelpFormatter -- Formatter classes which
* may be passed as the formatter_class= argument to the
* ArgumentParser constructor. HelpFormatter is the default,
* RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser
* not to change the formatting for help text, and
* ArgumentDefaultsHelpFormatter adds information about argument defaults
* to the help.
*
* All other classes in this module are considered implementation details.
* (Also note that HelpFormatter and RawDescriptionHelpFormatter are only
* considered public as object names -- the API of the formatter objects is
* still considered an implementation detail.)
*/
const SUPPRESS = '==SUPPRESS=='
const OPTIONAL = '?'
const ZERO_OR_MORE = '*'
const ONE_OR_MORE = '+'
const PARSER = 'A...'
const REMAINDER = '...'
const _UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args'
// ==================================
// Utility functions used for porting
// ==================================
const assert = require('assert')
const util = require('util')
const fs = require('fs')
const sub = require('./lib/sub')
const path = require('path')
const repr = util.inspect
function get_argv() {
// omit first argument (which is assumed to be interpreter - `node`, `coffee`, `ts-node`, etc.)
return process.argv.slice(1)
}
function get_terminal_size() {
return {
columns: +process.env.COLUMNS || process.stdout.columns || 80
}
}
function hasattr(object, name) {
return Object.prototype.hasOwnProperty.call(object, name)
}
function getattr(object, name, value) {
return hasattr(object, name) ? object[name] : value
}
function setattr(object, name, value) {
object[name] = value
}
function setdefault(object, name, value) {
if (!hasattr(object, name)) object[name] = value
return object[name]
}
function delattr(object, name) {
delete object[name]
}
function range(from, to, step=1) {
// range(10) is equivalent to range(0, 10)
if (arguments.length === 1) [ to, from ] = [ from, 0 ]
if (typeof from !== 'number' || typeof to !== 'number' || typeof step !== 'number') {
throw new TypeError('argument cannot be interpreted as an integer')
}
if (step === 0) throw new TypeError('range() arg 3 must not be zero')
let result = []
if (step > 0) {
for (let i = from; i < to; i += step) result.push(i)
} else {
for (let i = from; i > to; i += step) result.push(i)
}
return result
}
function splitlines(str, keepends = false) {
let result
if (!keepends) {
result = str.split(/\r\n|[\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029]/)
} else {
result = []
let parts = str.split(/(\r\n|[\n\r\v\f\x1c\x1d\x1e\x85\u2028\u2029])/)
for (let i = 0; i < parts.length; i += 2) {
result.push(parts[i] + (i + 1 < parts.length ? parts[i + 1] : ''))
}
}
if (!result[result.length - 1]) result.pop()
return result
}
function _string_lstrip(string, prefix_chars) {
let idx = 0
while (idx < string.length && prefix_chars.includes(string[idx])) idx++
return idx ? string.slice(idx) : string
}
function _string_split(string, sep, maxsplit) {
let result = string.split(sep)
if (result.length > maxsplit) {
result = result.slice(0, maxsplit).concat([ result.slice(maxsplit).join(sep) ])
}
return result
}
function _array_equal(array1, array2) {
if (array1.length !== array2.length) return false
for (let i = 0; i < array1.length; i++) {
if (array1[i] !== array2[i]) return false
}
return true
}
function _array_remove(array, item) {
let idx = array.indexOf(item)
if (idx === -1) throw new TypeError(sub('%r not in list', item))
array.splice(idx, 1)
}
// normalize choices to array;
// this isn't required in python because `in` and `map` operators work with anything,
// but in js dealing with multiple types here is too clunky
function _choices_to_array(choices) {
if (choices === undefined) {
return []
} else if (Array.isArray(choices)) {
return choices
} else if (choices !== null && typeof choices[Symbol.iterator] === 'function') {
return Array.from(choices)
} else if (typeof choices === 'object' && choices !== null) {
return Object.keys(choices)
} else {
throw new Error(sub('invalid choices value: %r', choices))
}
}
// decorator that allows a class to be called without new
function _callable(cls) {
let result = { // object is needed for inferred class name
[cls.name]: function (...args) {
let this_class = new.target === result || !new.target
return Reflect.construct(cls, args, this_class ? cls : new.target)
}
}
result[cls.name].prototype = cls.prototype
// fix default tag for toString, e.g. [object Action] instead of [object Object]
cls.prototype[Symbol.toStringTag] = cls.name
return result[cls.name]
}
function _alias(object, from, to) {
try {
let name = object.constructor.name
Object.defineProperty(object, from, {
value: util.deprecate(object[to], sub('%s.%s() is renamed to %s.%s()',
name, from, name, to)),
enumerable: false
})
} catch {}
}
// decorator that allows snake_case class methods to be called with camelCase and vice versa
function _camelcase_alias(_class) {
for (let name of Object.getOwnPropertyNames(_class.prototype)) {
let camelcase = name.replace(/\w_[a-z]/g, s => s[0] + s[2].toUpperCase())
if (camelcase !== name) _alias(_class.prototype, camelcase, name)
}
return _class
}
function _to_legacy_name(key) {
key = key.replace(/\w_[a-z]/g, s => s[0] + s[2].toUpperCase())
if (key === 'default') key = 'defaultValue'
if (key === 'const') key = 'constant'
return key
}
function _to_new_name(key) {
if (key === 'defaultValue') key = 'default'
if (key === 'constant') key = 'const'
key = key.replace(/[A-Z]/g, c => '_' + c.toLowerCase())
return key
}
// parse options
let no_default = Symbol('no_default_value')
function _parse_opts(args, descriptor) {
function get_name() {
let stack = new Error().stack.split('\n')
.map(x => x.match(/^ at (.*) \(.*\)$/))
.filter(Boolean)
.map(m => m[1])
.map(fn => fn.match(/[^ .]*$/)[0])
if (stack.length && stack[0] === get_name.name) stack.shift()
if (stack.length && stack[0] === _parse_opts.name) stack.shift()
return stack.length ? stack[0] : ''
}
args = Array.from(args)
let kwargs = {}
let result = []
let last_opt = args.length && args[args.length - 1]
if (typeof last_opt === 'object' && last_opt !== null && !Array.isArray(last_opt) &&
(!last_opt.constructor || last_opt.constructor.name === 'Object')) {
kwargs = Object.assign({}, args.pop())
}
// LEGACY (v1 compatibility): camelcase
let renames = []
for (let key of Object.keys(descriptor)) {
let old_name = _to_legacy_name(key)
if (old_name !== key && (old_name in kwargs)) {
if (key in kwargs) {
// default and defaultValue specified at the same time, happens often in old tests
//throw new TypeError(sub('%s() got multiple values for argument %r', get_name(), key))
} else {
kwargs[key] = kwargs[old_name]
}
renames.push([ old_name, key ])
delete kwargs[old_name]
}
}
if (renames.length) {
let name = get_name()
deprecate('camelcase_' + name, sub('%s(): following options are renamed: %s',
name, renames.map(([ a, b ]) => sub('%r -> %r', a, b))))
}
// end
let missing_positionals = []
let positional_count = args.length
for (let [ key, def ] of Object.entries(descriptor)) {
if (key[0] === '*') {
if (key.length > 0 && key[1] === '*') {
// LEGACY (v1 compatibility): camelcase
let renames = []
for (let key of Object.keys(kwargs)) {
let new_name = _to_new_name(key)
if (new_name !== key && (key in kwargs)) {
if (new_name in kwargs) {
// default and defaultValue specified at the same time, happens often in old tests
//throw new TypeError(sub('%s() got multiple values for argument %r', get_name(), new_name))
} else {
kwargs[new_name] = kwargs[key]
}
renames.push([ key, new_name ])
delete kwargs[key]
}
}
if (renames.length) {
let name = get_name()
deprecate('camelcase_' + name, sub('%s(): following options are renamed: %s',
name, renames.map(([ a, b ]) => sub('%r -> %r', a, b))))
}
// end
result.push(kwargs)
kwargs = {}
} else {
result.push(args)
args = []
}
} else if (key in kwargs && args.length > 0) {
throw new TypeError(sub('%s() got multiple values for argument %r', get_name(), key))
} else if (key in kwargs) {
result.push(kwargs[key])
delete kwargs[key]
} else if (args.length > 0) {
result.push(args.shift())
} else if (def !== no_default) {
result.push(def)
} else {
missing_positionals.push(key)
}
}
if (Object.keys(kwargs).length) {
throw new TypeError(sub('%s() got an unexpected keyword argument %r',
get_name(), Object.keys(kwargs)[0]))
}
if (args.length) {
let from = Object.entries(descriptor).filter(([ k, v ]) => k[0] !== '*' && v !== no_default).length
let to = Object.entries(descriptor).filter(([ k ]) => k[0] !== '*').length
throw new TypeError(sub('%s() takes %s positional argument%s but %s %s given',
get_name(),
from === to ? sub('from %s to %s', from, to) : to,
from === to && to === 1 ? '' : 's',
positional_count,
positional_count === 1 ? 'was' : 'were'))
}
if (missing_positionals.length) {
let strs = missing_positionals.map(repr)
if (strs.length > 1) strs[strs.length - 1] = 'and ' + strs[strs.length - 1]
let str_joined = strs.join(strs.length === 2 ? '' : ', ')
throw new TypeError(sub('%s() missing %i required positional argument%s: %s',
get_name(), strs.length, strs.length === 1 ? '' : 's', str_joined))
}
return result
}
let _deprecations = {}
function deprecate(id, string) {
_deprecations[id] = _deprecations[id] || util.deprecate(() => {}, string)
_deprecations[id]()
}
// =============================
// Utility functions and classes
// =============================
function _AttributeHolder(cls = Object) {
/*
* Abstract base class that provides __repr__.
*
* The __repr__ method returns a string in the format::
* ClassName(attr=name, attr=name, ...)
* The attributes are determined either by a class-level attribute,
* '_kwarg_names', or by inspecting the instance __dict__.
*/
return class _AttributeHolder extends cls {
[util.inspect.custom]() {
let type_name = this.constructor.name
let arg_strings = []
let star_args = {}
for (let arg of this._get_args()) {
arg_strings.push(repr(arg))
}
for (let [ name, value ] of this._get_kwargs()) {
if (/^[a-z_][a-z0-9_$]*$/i.test(name)) {
arg_strings.push(sub('%s=%r', name, value))
} else {
star_args[name] = value
}
}
if (Object.keys(star_args).length) {
arg_strings.push(sub('**%s', repr(star_args)))
}
return sub('%s(%s)', type_name, arg_strings.join(', '))
}
toString() {
return this[util.inspect.custom]()
}
_get_kwargs() {
return Object.entries(this)
}
_get_args() {
return []
}
}
}
function _copy_items(items) {
if (items === undefined) {
return []
}
return items.slice(0)
}
// ===============
// Formatting Help
// ===============
const HelpFormatter = _camelcase_alias(_callable(class HelpFormatter {
/*
* Formatter for generating usage messages and argument help strings.
*
* Only the name of this class is considered a public API. All the methods
* provided by the class are considered an implementation detail.
*/
constructor() {
let [
prog,
indent_increment,
max_help_position,
width
] = _parse_opts(arguments, {
prog: no_default,
indent_increment: 2,
max_help_position: 24,
width: undefined
})
// default setting for width
if (width === undefined) {
width = get_terminal_size().columns
width -= 2
}
this._prog = prog
this._indent_increment = indent_increment
this._max_help_position = Math.min(max_help_position,
Math.max(width - 20, indent_increment * 2))
this._width = width
this._current_indent = 0
this._level = 0
this._action_max_length = 0
this._root_section = this._Section(this, undefined)
this._current_section = this._root_section
this._whitespace_matcher = /[ \t\n\r\f\v]+/g // equivalent to python /\s+/ with ASCII flag
this._long_break_matcher = /\n\n\n+/g
}
// ===============================
// Section and indentation methods
// ===============================
_indent() {
this._current_indent += this._indent_increment
this._level += 1
}
_dedent() {
this._current_indent -= this._indent_increment
assert(this._current_indent >= 0, 'Indent decreased below 0.')
this._level -= 1
}
_add_item(func, args) {
this._current_section.items.push([ func, args ])
}
// ========================
// Message building methods
// ========================
start_section(heading) {
this._indent()
let section = this._Section(this, this._current_section, heading)
this._add_item(section.format_help.bind(section), [])
this._current_section = section
}
end_section() {
this._current_section = this._current_section.parent
this._dedent()
}
add_text(text) {
if (text !== SUPPRESS && text !== undefined) {
this._add_item(this._format_text.bind(this), [text])
}
}
add_usage(usage, actions, groups, prefix = undefined) {
if (usage !== SUPPRESS) {
let args = [ usage, actions, groups, prefix ]
this._add_item(this._format_usage.bind(this), args)
}
}
add_argument(action) {
if (action.help !== SUPPRESS) {
// find all invocations
let invocations = [this._format_action_invocation(action)]
for (let subaction of this._iter_indented_subactions(action)) {
invocations.push(this._format_action_invocation(subaction))
}
// update the maximum item length
let invocation_length = Math.max(...invocations.map(invocation => invocation.length))
let action_length = invocation_length + this._current_indent
this._action_max_length = Math.max(this._action_max_length,
action_length)
// add the item to the list
this._add_item(this._format_action.bind(this), [action])
}
}
add_arguments(actions) {
for (let action of actions) {
this.add_argument(action)
}
}
// =======================
// Help-formatting methods
// =======================
format_help() {
let help = this._root_section.format_help()
if (help) {
help = help.replace(this._long_break_matcher, '\n\n')
help = help.replace(/^\n+|\n+$/g, '') + '\n'
}
return help
}
_join_parts(part_strings) {
return part_strings.filter(part => part && part !== SUPPRESS).join('')
}
_format_usage(usage, actions, groups, prefix) {
if (prefix === undefined) {
prefix = 'usage: '
}
// if usage is specified, use that
if (usage !== undefined) {
usage = sub(usage, { prog: this._prog })
// if no optionals or positionals are available, usage is just prog
} else if (usage === undefined && !actions.length) {
usage = sub('%(prog)s', { prog: this._prog })
// if optionals and positionals are available, calculate usage
} else if (usage === undefined) {
let prog = sub('%(prog)s', { prog: this._prog })
// split optionals from positionals
let optionals = []
let positionals = []
for (let action of actions) {
if (action.option_strings.length) {
optionals.push(action)
} else {
positionals.push(action)
}
}
// build full usage string
let action_usage = this._format_actions_usage([].concat(optionals).concat(positionals), groups)
usage = [ prog, action_usage ].map(String).join(' ')
// wrap the usage parts if it's too long
let text_width = this._width - this._current_indent
if (prefix.length + usage.length > text_width) {
// break usage into wrappable parts
let part_regexp = /\(.*?\)+(?=\s|$)|\[.*?\]+(?=\s|$)|\S+/g
let opt_usage = this._format_actions_usage(optionals, groups)
let pos_usage = this._format_actions_usage(positionals, groups)
let opt_parts = opt_usage.match(part_regexp) || []
let pos_parts = pos_usage.match(part_regexp) || []
assert(opt_parts.join(' ') === opt_usage)
assert(pos_parts.join(' ') === pos_usage)
// helper for wrapping lines
let get_lines = (parts, indent, prefix = undefined) => {
let lines = []
let line = []
let line_len
if (prefix !== undefined) {
line_len = prefix.length - 1
} else {
line_len = indent.length - 1
}
for (let part of parts) {
if (line_len + 1 + part.length > text_width && line) {
lines.push(indent + line.join(' '))
line = []
line_len = indent.length - 1
}
line.push(part)
line_len += part.length + 1
}
if (line.length) {
lines.push(indent + line.join(' '))
}
if (prefix !== undefined) {
lines[0] = lines[0].slice(indent.length)
}
return lines
}
let lines
// if prog is short, follow it with optionals or positionals
if (prefix.length + prog.length <= 0.75 * text_width) {
let indent = ' '.repeat(prefix.length + prog.length + 1)
if (opt_parts.length) {
lines = get_lines([prog].concat(opt_parts), indent, prefix)
lines = lines.concat(get_lines(pos_parts, indent))
} else if (pos_parts.length) {
lines = get_lines([prog].concat(pos_parts), indent, prefix)
} else {
lines = [prog]
}
// if prog is long, put it on its own line
} else {
let indent = ' '.repeat(prefix.length)
let parts = [].concat(opt_parts).concat(pos_parts)
lines = get_lines(parts, indent)
if (lines.length > 1) {
lines = []
lines = lines.concat(get_lines(opt_parts, indent))
lines = lines.concat(get_lines(pos_parts, indent))
}
lines = [prog].concat(lines)
}
// join lines into usage
usage = lines.join('\n')
}
}
// prefix with 'usage:'
return sub('%s%s\n\n', prefix, usage)
}
_format_actions_usage(actions, groups) {
// find group indices and identify actions in groups
let group_actions = new Set()
let inserts = {}
for (let group of groups) {
let start = actions.indexOf(group._group_actions[0])
if (start === -1) {
continue
} else {
let end = start + group._group_actions.length
if (_array_equal(actions.slice(start, end), group._group_actions)) {
for (let action of group._group_actions) {
group_actions.add(action)
}
if (!group.required) {
if (start in inserts) {
inserts[start] += ' ['
} else {
inserts[start] = '['
}
if (end in inserts) {
inserts[end] += ']'
} else {
inserts[end] = ']'
}
} else {
if (start in inserts) {
inserts[start] += ' ('
} else {
inserts[start] = '('
}
if (end in inserts) {
inserts[end] += ')'
} else {
inserts[end] = ')'
}
}
for (let i of range(start + 1, end)) {
inserts[i] = '|'
}
}
}
}
// collect all actions format strings
let parts = []
for (let [ i, action ] of Object.entries(actions)) {
// suppressed arguments are marked with None
// remove | separators for suppressed arguments
if (action.help === SUPPRESS) {
parts.push(undefined)
if (inserts[+i] === '|') {
delete inserts[+i]
} else if (inserts[+i + 1] === '|') {
delete inserts[+i + 1]
}
// produce all arg strings
} else if (!action.option_strings.length) {
let default_value = this._get_default_metavar_for_positional(action)
let part = this._format_args(action, default_value)
// if it's in a group, strip the outer []
if (group_actions.has(action)) {
if (part[0] === '[' && part[part.length - 1] === ']') {
part = part.slice(1, -1)
}
}
// add the action string to the list
parts.push(part)
// produce the first way to invoke the option in brackets
} else {
let option_string = action.option_strings[0]
let part
// if the Optional doesn't take a value, format is:
// -s or --long
if (action.nargs === 0) {
part = action.format_usage()
// if the Optional takes a value, format is:
// -s ARGS or --long ARGS
} else {
let default_value = this._get_default_metavar_for_optional(action)
let args_string = this._format_args(action, default_value)
part = sub('%s %s', option_string, args_string)
}
// make it look optional if it's not required or in a group
if (!action.required && !group_actions.has(action)) {
part = sub('[%s]', part)
}
// add the action string to the list
parts.push(part)
}
}
// insert things at the necessary indices
for (let i of Object.keys(inserts).map(Number).sort((a, b) => b - a)) {
parts.splice(+i, 0, inserts[+i])
}
// join all the action items with spaces
let text = parts.filter(Boolean).join(' ')
// clean up separators for mutually exclusive groups
text = text.replace(/([\[(]) /g, '$1')
text = text.replace(/ ([\])])/g, '$1')
text = text.replace(/[\[(] *[\])]/g, '')
text = text.replace(/\(([^|]*)\)/g, '$1', text)
text = text.trim()
// return the text
return text
}
_format_text(text) {
if (text.includes('%(prog)')) {
text = sub(text, { prog: this._prog })
}
let text_width = Math.max(this._width - this._current_indent, 11)
let indent = ' '.repeat(this._current_indent)
return this._fill_text(text, text_width, indent) + '\n\n'
}
_format_action(action) {
// determine the required width and the entry label
let help_position = Math.min(this._action_max_length + 2,
this._max_help_position)
let help_width = Math.max(this._width - help_position, 11)
let action_width = help_position - this._current_indent - 2
let action_header = this._format_action_invocation(action)
let indent_first
// no help; start on same line and add a final newline
if (!action.help) {
let tup = [ this._current_indent, '', action_header ]
action_header = sub('%*s%s\n', ...tup)
// short action name; start on the same line and pad two spaces
} else if (action_header.length <= action_width) {
let tup = [ this._current_indent, '', action_width, action_header ]
action_header = sub('%*s%-*s ', ...tup)
indent_first = 0
// long action name; start on the next line
} else {
let tup = [ this._current_indent, '', action_header ]
action_header = sub('%*s%s\n', ...tup)
indent_first = help_position
}
// collect the pieces of the action help
let parts = [action_header]
// if there was help for the action, add lines of help text
if (action.help) {
let help_text = this._expand_help(action)
let help_lines = this._split_lines(help_text, help_width)
parts.push(sub('%*s%s\n', indent_first, '', help_lines[0]))
for (let line of help_lines.slice(1)) {
parts.push(sub('%*s%s\n', help_position, '', line))
}
// or add a newline if the description doesn't end with one
} else if (!action_header.endsWith('\n')) {
parts.push('\n')
}
// if there are any sub-actions, add their help as well
for (let subaction of this._iter_indented_subactions(action)) {
parts.push(this._format_action(subaction))
}
// return a single string
return this._join_parts(parts)
}
_format_action_invocation(action) {
if (!action.option_strings.length) {
let default_value = this._get_default_metavar_for_positional(action)
let metavar = this._metavar_formatter(action, default_value)(1)[0]
return metavar
} else {
let parts = []
// if the Optional doesn't take a value, format is:
// -s, --long
if (action.nargs === 0) {
parts = parts.concat(action.option_strings)
// if the Optional takes a value, format is:
// -s ARGS, --long ARGS
} else {
let default_value = this._get_default_metavar_for_optional(action)
let args_string = this._format_args(action, default_value)
for (let option_string of action.option_strings) {
parts.push(sub('%s %s', option_string, args_string))
}
}
return parts.join(', ')
}
}
_metavar_formatter(action, default_metavar) {
let result
if (action.metavar !== undefined) {
result = action.metavar
} else if (action.choices !== undefined) {
let choice_strs = _choices_to_array(action.choices).map(String)
result = sub('{%s}', choice_strs.join(','))
} else {
result = default_metavar
}
function format(tuple_size) {
if (Array.isArray(result)) {
return result
} else {
return Array(tuple_size).fill(result)
}
}
return format
}
_format_args(action, default_metavar) {
let get_metavar = this._metavar_formatter(action, default_metavar)
let result
if (action.nargs === undefined) {
result = sub('%s', ...get_metavar(1))
} else if (action.nargs === OPTIONAL) {
result = sub('[%s]', ...get_metavar(1))
} else if (action.nargs === ZERO_OR_MORE) {
let metavar = get_metavar(1)
if (metavar.length === 2) {
result = sub('[%s [%s ...]]', ...metavar)
} else {
result = sub('[%s ...]', ...metavar)
}
} else if (action.nargs === ONE_OR_MORE) {
result = sub('%s [%s ...]', ...get_metavar(2))
} else if (action.nargs === REMAINDER) {
result = '...'
} else if (action.nargs === PARSER) {
result = sub('%s ...', ...get_metavar(1))
} else if (action.nargs === SUPPRESS) {
result = ''
} else {
let formats
try {
formats = range(action.nargs).map(() => '%s')
} catch (err) {
throw new TypeError('invalid nargs value')
}
result = sub(formats.join(' '), ...get_metavar(action.nargs))
}
return result
}
_expand_help(action) {
let params = Object.assign({ prog: this._prog }, action)
for (let name of Object.keys(params)) {
if (params[name] === SUPPRESS) {
delete params[name]
}
}
for (let name of Object.keys(params)) {
if (params[name] && params[name].name) {
params[name] = params[name].name
}
}
if (params.choices !== undefined) {
let choices_str = _choices_to_array(params.choices).map(String).join(', ')
params.choices = choices_str
}
// LEGACY (v1 compatibility): camelcase
for (let key of Object.keys(params)) {
let old_name = _to_legacy_name(key)
if (old_name !== key) {
params[old_name] = params[key]
}
}
// end
return sub(this._get_help_string(action), params)
}
* _iter_indented_subactions(action) {
if (typeof action._get_subactions === 'function') {
this._indent()
yield* action._get_subactions()
this._dedent()
}
}
_split_lines(text, width) {
text = text.replace(this._whitespace_matcher, ' ').trim()
// The textwrap module is used only for formatting help.
// Delay its import for speeding up the common usage of argparse.
let textwrap = require('./lib/textwrap')
return textwrap.wrap(text, { width })
}
_fill_text(text, width, indent) {
text = text.replace(this._whitespace_matcher, ' ').trim()
let textwrap = require('./lib/textwrap')
return textwrap.fill(text, { width,
initial_indent: indent,
subsequent_indent: indent })
}
_get_help_string(action) {
return action.help
}
_get_default_metavar_for_optional(action) {
return action.dest.toUpperCase()
}
_get_default_metavar_for_positional(action) {
return action.dest
}
}))
HelpFormatter.prototype._Section = _callable(class _Section {
constructor(formatter, parent, heading = undefined) {
this.formatter = formatter
this.parent = parent
this.heading = heading
this.items = []
}
format_help() {
// format the indented section
if (this.parent !== undefined) {
this.formatter._indent()
}
let item_help = this.formatter._join_parts(this.items.map(([ func, args ]) => func.apply(null, args)))
if (this.parent !== undefined) {
this.formatter._dedent()
}
// return nothing if the section was empty
if (!item_help) {
return ''
}
// add the heading if the section was non-empty
let heading
if (this.heading !== SUPPRESS && this.heading !== undefined) {
let current_indent = this.formatter._current_indent
heading = sub('%*s%s:\n', current_indent, '', this.heading)
} else {
heading = ''
}
// join the section-initial newline, the heading and the help
return this.formatter._join_parts(['\n', heading, item_help, '\n'])
}
})
const RawDescriptionHelpFormatter = _camelcase_alias(_callable(class RawDescriptionHelpFormatter extends HelpFormatter {
/*
* Help message formatter which retains any formatting in descriptions.
*
* Only the name of this class is considered a public API. All the methods
* provided by the class are considered an implementation detail.
*/
_fill_text(text, width, indent) {
return splitlines(text, true).map(line => indent + line).join('')
}
}))
const RawTextHelpFormatter = _camelcase_alias(_callable(class RawTextHelpFormatter extends RawDescriptionHelpFormatter {
/*
* Help message formatter which retains formatting of all help text.
*
* Only the name of this class is considered a public API. All the methods
* provided by the class are considered an implementation detail.
*/
_split_lines(text/*, width*/) {
return splitlines(text)
}
}))
const ArgumentDefaultsHelpFormatter = _camelcase_alias(_callable(class ArgumentDefaultsHelpFormatter extends HelpFormatter {
/*
* Help message formatter which adds default values to argument help.
*
* Only the name of this class is considered a public API. All the methods
* provided by the class are considered an implementation detail.
*/
_get_help_string(action) {
let help = action.help
// LEGACY (v1 compatibility): additional check for defaultValue needed
if (!action.help.includes('%(default)') && !action.help.includes('%(defaultValue)')) {
if (action.default !== SUPPRESS) {
let defaulting_nargs = [OPTIONAL, ZERO_OR_MORE]
if (action.option_strings.length || defaulting_nargs.includes(action.nargs)) {
help += ' (default: %(default)s)'
}
}
}
return help
}
}))
const MetavarTypeHelpFormatter = _camelcase_alias(_callable(class MetavarTypeHelpFormatter extends HelpFormatter {
/*
* Help message formatter which uses the argument 'type' as the default
* metavar value (instead of the argument 'dest')
*
* Only the name of this class is considered a public API. All the methods
* provided by the class are considered an implementation detail.
*/
_get_default_metavar_for_optional(action) {
return typeof action.type === 'function' ? action.type.name : action.type
}
_get_default_metavar_for_positional(action) {
return typeof action.type === 'function' ? action.type.name : action.type
}
}))
// =====================
// Options and Arguments
// =====================
function _get_action_name(argument) {
if (argument === undefined) {
return undefined
} else if (argument.option_strings.length) {
return argument.option_strings.join('/')
} else if (![ undefined, SUPPRESS ].includes(argument.metavar)) {
return argument.metavar
} else if (![ undefined, SUPPRESS ].includes(argument.dest)) {
return argument.dest
} else {
return undefined
}
}
const ArgumentError = _callable(class ArgumentError extends Error {
/*
* An error from creating or using an argument (optional or positional).
*
* The string value of this exception is the message, augmented with
* information about the argument that caused it.
*/
constructor(argument, message) {
super()
this.name = 'ArgumentError'
this._argument_name = _get_action_name(argument)
this._message = message
this.message = this.str()
}
str() {
let format
if (this._argument_name === undefined) {
format = '%(message)s'
} else {
format = 'argument %(argument_name)s: %(message)s'
}
return sub(format, { message: this._message,
argument_name: this._argument_name })
}
})
const ArgumentTypeError = _callable(class ArgumentTypeError extends Error {
/*
* An error from trying to convert a command line string to a type.
*/
constructor(message) {
super(message)
this.name = 'ArgumentTypeError'
}
})
// ==============
// Action classes
// ==============
const Action = _camelcase_alias(_callable(class Action extends _AttributeHolder(Function) {
/*
* Information about how to convert command line strings to Python objects.
*
* Action objects are used by an ArgumentParser to represent the information
* needed to parse a single argument from one or more strings from the
* command line. The keyword arguments to the Action constructor are also
* all attributes of Action instances.
*
* Keyword Arguments:
*
* - option_strings -- A list of command-line option strings which
* should be associated with this action.
*
* - dest -- The name of the attribute to hold the created object(s)
*
* - nargs -- The number of command-line arguments that should be
* consumed. By default, one argument will be consumed and a single
* value will be produced. Other values include:
* - N (an integer) consumes N arguments (and produces a list)
* - '?' consumes zero or one arguments
* - '*' consumes zero or more arguments (and produces a list)
* - '+' consumes one or more arguments (and produces a list)
* Note that the difference between the default and nargs=1 is that
* with the default, a single value will be produced, while with
* nargs=1, a list containing a single value will be produced.
*
* - const -- The value to be produced if the option is specified and the
* option uses an action that takes no values.
*
* - default -- The value to be produced if the option is not specified.
*
* - type -- A callable that accepts a single string argument, and
* returns the converted value. The standard Python types str, int,
* float, and complex are useful examples of such callables. If None,
* str is used.
*
* - choices -- A container of values that should be allowed. If not None,
* after a command-line argument has been converted to the appropriate
* type, an exception will be raised if it is not a member of this
* collection.
*
* - required -- True if the action must always be specified at the
* command line. This is only meaningful for optional command-line
* arguments.
*
* - help -- The help string describing the argument.
*
* - metavar -- The name to be used for the option's argument with the
* help string. If None, the 'dest' value will be used as the name.
*/
constructor() {
let [
option_strings,
dest,
nargs,
const_value,
default_value,
type,
choices,
required,
help,
metavar
] = _parse_opts(arguments, {
option_strings: no_default,
dest: no_default,
nargs: undefined,
const: undefined,
default: undefined,
type: undefined,
choices: undefined,
required: false,
help: undefined,
metavar: undefined
})
// when this class is called as a function, redirect it to .call() method of itself
super('return arguments.callee.call.apply(arguments.callee, arguments)')
this.option_strings = option_strings
this.dest = dest
this.nargs = nargs
this.const = const_value
this.default = default_value
this.type = type
this.choices = choices
this.required = required
this.help = help
this.metavar = metavar
}
_get_kwargs() {
let names = [
'option_strings',
'dest',
'nargs',
'const',
'default',
'type',
'choices',
'help',
'metavar'
]
return names.map(name => [ name, getattr(this, name) ])
}
format_usage() {
return this.option_strings[0]
}
call(/*parser, namespace, values, option_string = undefined*/) {
throw new Error('.call() not defined')
}
}))
const BooleanOptionalAction = _camelcase_alias(_callable(class BooleanOptionalAction extends Action {
constructor() {
let [
option_strings,
dest,
default_value,
type,
choices,
required,
help,
metavar
] = _parse_opts(arguments, {
option_strings: no_default,
dest: no_default,
default: undefined,
type: undefined,
choices: undefined,
required: false,
help: undefined,
metavar: undefined
})
let _option_strings = []
for (let option_string of option_strings) {
_option_strings.push(option_string)
if (option_string.startsWith('--')) {
option_string = '--no-' + option_string.slice(2)
_option_strings.push(option_string)
}
}
if (help !== undefined && default_value !== undefined) {
help += ` (default: ${default_value})`
}
super({
option_strings: _option_strings,
dest,
nargs: 0,
default: default_value,
type,
choices,
required,
help,
metavar
})
}
call(parser, namespace, values, option_string = undefined) {
if (this.option_strings.includes(option_string)) {
setattr(namespace, this.dest, !option_string.startsWith('--no-'))
}
}
format_usage() {
return this.option_strings.join(' | ')
}
}))
const _StoreAction = _callable(class _StoreAction extends Action {
constructor() {
let [
option_strings,
dest,
nargs,
const_value,
default_value,
type,
choices,
required,
help,
metavar
] = _parse_opts(arguments, {
option_strings: no_default,
dest: no_default,
nargs: undefined,
const: undefined,
default: undefined,
type: undefined,
choices: undefined,
required: false,
help: undefined,
metavar: undefined
})
if (nargs === 0) {
throw new TypeError('nargs for store actions must be != 0; if you ' +
'have nothing to store, actions such as store ' +
'true or store const may be more appropriate')
}
if (const_value !== undefined && nargs !== OPTIONAL) {
throw new TypeError(sub('nargs must be %r to supply const', OPTIONAL))
}
super({
option_strings,
dest,
nargs,
const: const_value,
default: default_value,
type,
choices,
required,
help,
metavar
})
}
call(parser, namespace, values/*, option_string = undefined*/) {
setattr(namespace, this.dest, values)
}
})
const _StoreConstAction = _callable(class _StoreConstAction extends Action {
constructor() {
let [
option_strings,
dest,
const_value,
default_value,
required,
help
//, metavar
] = _parse_opts(arguments, {
option_strings: no_default,
dest: no_default,
const: no_default,
default: undefined,
required: false,
help: undefined,
metavar: undefined
})
super({
option_strings,
dest,
nargs: 0,
const: const_value,
default: default_value,
required,
help
})
}
call(parser, namespace/*, values, option_string = undefined*/) {
setattr(namespace, this.dest, this.const)
}
})
const _StoreTrueAction = _callable(class _StoreTrueAction extends _StoreConstAction {
constructor() {
let [
option_strings,
dest,
default_value,
required,
help
] = _parse_opts(arguments, {