Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/flo-bit/shapecraft. flo-bit.dev/shapecraft
Something went wrong. Try again.
130 kB · 3707 lines
JavaScript
at commit 22396844
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708// 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 clunkyfunction _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 newfunction _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 versafunction _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 optionslet 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, { option_strings: no_default, dest: no_default, default: false, required: false, help: undefined })
super({ option_strings, dest, const: true, default: default_value, required, help }) }})
const _StoreFalseAction = _callable(class _StoreFalseAction extends _StoreConstAction {
constructor() { let [ option_strings, dest, default_value, required, help ] = _parse_opts(arguments, { option_strings: no_default, dest: no_default, default: true, required: false, help: undefined })
super({ option_strings, dest, const: false, default: default_value, required, help }) }})
const _AppendAction = _callable(class _AppendAction 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 append actions must be != 0; if arg ' + 'strings are not supplying the value to append, ' + 'the append const action 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*/) { let items = getattr(namespace, this.dest, undefined) items = _copy_items(items) items.push(values) setattr(namespace, this.dest, items) }})
const _AppendConstAction = _callable(class _AppendConstAction 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, metavar }) }
call(parser, namespace/*, values, option_string = undefined*/) { let items = getattr(namespace, this.dest, undefined) items = _copy_items(items) items.push(this.const) setattr(namespace, this.dest, items) }})
const _CountAction = _callable(class _CountAction extends Action {
constructor() { let [ option_strings, dest, default_value, required, help ] = _parse_opts(arguments, { option_strings: no_default, dest: no_default, default: undefined, required: false, help: undefined })
super({ option_strings, dest, nargs: 0, default: default_value, required, help }) }
call(parser, namespace/*, values, option_string = undefined*/) { let count = getattr(namespace, this.dest, undefined) if (count === undefined) { count = 0 } setattr(namespace, this.dest, count + 1) }})
const _HelpAction = _callable(class _HelpAction extends Action {
constructor() { let [ option_strings, dest, default_value, help ] = _parse_opts(arguments, { option_strings: no_default, dest: SUPPRESS, default: SUPPRESS, help: undefined })
super({ option_strings, dest, default: default_value, nargs: 0, help }) }
call(parser/*, namespace, values, option_string = undefined*/) { parser.print_help() parser.exit() }})
const _VersionAction = _callable(class _VersionAction extends Action {
constructor() { let [ option_strings, version, dest, default_value, help ] = _parse_opts(arguments, { option_strings: no_default, version: undefined, dest: SUPPRESS, default: SUPPRESS, help: "show program's version number and exit" })
super({ option_strings, dest, default: default_value, nargs: 0, help }) this.version = version }
call(parser/*, namespace, values, option_string = undefined*/) { let version = this.version if (version === undefined) { version = parser.version } let formatter = parser._get_formatter() formatter.add_text(version) parser._print_message(formatter.format_help(), process.stdout) parser.exit() }})
const _SubParsersAction = _camelcase_alias(_callable(class _SubParsersAction extends Action {
constructor() { let [ option_strings, prog, parser_class, dest, required, help, metavar ] = _parse_opts(arguments, { option_strings: no_default, prog: no_default, parser_class: no_default, dest: SUPPRESS, required: false, help: undefined, metavar: undefined })
let name_parser_map = {}
super({ option_strings, dest, nargs: PARSER, choices: name_parser_map, required, help, metavar })
this._prog_prefix = prog this._parser_class = parser_class this._name_parser_map = name_parser_map this._choices_actions = [] }
add_parser() { let [ name, kwargs ] = _parse_opts(arguments, { name: no_default, '**kwargs': no_default })
// set prog from the existing prefix if (kwargs.prog === undefined) { kwargs.prog = sub('%s %s', this._prog_prefix, name) }
let aliases = getattr(kwargs, 'aliases', []) delete kwargs.aliases
// create a pseudo-action to hold the choice help if ('help' in kwargs) { let help = kwargs.help delete kwargs.help let choice_action = this._ChoicesPseudoAction(name, aliases, help) this._choices_actions.push(choice_action) }
// create the parser and add it to the map let parser = new this._parser_class(kwargs) this._name_parser_map[name] = parser
// make parser available under aliases also for (let alias of aliases) { this._name_parser_map[alias] = parser }
return parser }
_get_subactions() { return this._choices_actions }
call(parser, namespace, values/*, option_string = undefined*/) { let parser_name = values[0] let arg_strings = values.slice(1)
// set the parser name if requested if (this.dest !== SUPPRESS) { setattr(namespace, this.dest, parser_name) }
// select the parser if (hasattr(this._name_parser_map, parser_name)) { parser = this._name_parser_map[parser_name] } else { let args = {parser_name, choices: this._name_parser_map.join(', ')} let msg = sub('unknown parser %(parser_name)r (choices: %(choices)s)', args) throw new ArgumentError(this, msg) }
// parse all the remaining options into the namespace // store any unrecognized options on the object, so that the top // level parser can decide what to do with them
// In case this subparser defines new defaults, we parse them // in a new namespace object and then update the original // namespace for the relevant parts. let subnamespace [ subnamespace, arg_strings ] = parser.parse_known_args(arg_strings, undefined) for (let [ key, value ] of Object.entries(subnamespace)) { setattr(namespace, key, value) }
if (arg_strings.length) { setdefault(namespace, _UNRECOGNIZED_ARGS_ATTR, []) getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).push(...arg_strings) } }}))
_SubParsersAction.prototype._ChoicesPseudoAction = _callable(class _ChoicesPseudoAction extends Action { constructor(name, aliases, help) { let metavar = name, dest = name if (aliases.length) { metavar += sub(' (%s)', aliases.join(', ')) } super({ option_strings: [], dest, help, metavar }) }})
const _ExtendAction = _callable(class _ExtendAction extends _AppendAction { call(parser, namespace, values/*, option_string = undefined*/) { let items = getattr(namespace, this.dest, undefined) items = _copy_items(items) items = items.concat(values) setattr(namespace, this.dest, items) }})
// ==============// Type classes// ==============const FileType = _callable(class FileType extends Function { /* * Factory for creating file object types * * Instances of FileType are typically passed as type= arguments to the * ArgumentParser add_argument() method. * * Keyword Arguments: * - mode -- A string indicating how the file is to be opened. Accepts the * same values as the builtin open() function. * - bufsize -- The file's desired buffer size. Accepts the same values as * the builtin open() function. * - encoding -- The file's encoding. Accepts the same values as the * builtin open() function. * - errors -- A string indicating how encoding and decoding errors are to * be handled. Accepts the same value as the builtin open() function. */
constructor() { let [ flags, encoding, mode, autoClose, emitClose, start, end, highWaterMark, fs ] = _parse_opts(arguments, { flags: 'r', encoding: undefined, mode: undefined, // 0o666 autoClose: undefined, // true emitClose: undefined, // false start: undefined, // 0 end: undefined, // Infinity highWaterMark: undefined, // 64 * 1024 fs: 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)')
Object.defineProperty(this, 'name', { get() { return sub('FileType(%r)', flags) } }) this._flags = flags this._options = {} if (encoding !== undefined) this._options.encoding = encoding if (mode !== undefined) this._options.mode = mode if (autoClose !== undefined) this._options.autoClose = autoClose if (emitClose !== undefined) this._options.emitClose = emitClose if (start !== undefined) this._options.start = start if (end !== undefined) this._options.end = end if (highWaterMark !== undefined) this._options.highWaterMark = highWaterMark if (fs !== undefined) this._options.fs = fs }
call(string) { // the special argument "-" means sys.std{in,out} if (string === '-') { if (this._flags.includes('r')) { return process.stdin } else if (this._flags.includes('w')) { return process.stdout } else { let msg = sub('argument "-" with mode %r', this._flags) throw new TypeError(msg) } }
// all other arguments are used as file names let fd try { fd = fs.openSync(string, this._flags, this._options.mode) } catch (e) { let args = { filename: string, error: e.message } let message = "can't open '%(filename)s': %(error)s" throw new ArgumentTypeError(sub(message, args)) }
let options = Object.assign({ fd, flags: this._flags }, this._options) if (this._flags.includes('r')) { return fs.createReadStream(undefined, options) } else if (this._flags.includes('w')) { return fs.createWriteStream(undefined, options) } else { let msg = sub('argument "%s" with mode %r', string, this._flags) throw new TypeError(msg) } }
[util.inspect.custom]() { let args = [ this._flags ] let kwargs = Object.entries(this._options).map(([ k, v ]) => { if (k === 'mode') v = { value: v, [util.inspect.custom]() { return '0o' + this.value.toString(8) } } return [ k, v ] }) let args_str = [] .concat(args.filter(arg => arg !== -1).map(repr)) .concat(kwargs.filter(([/*kw*/, arg]) => arg !== undefined) .map(([kw, arg]) => sub('%s=%r', kw, arg))) .join(', ') return sub('%s(%s)', this.constructor.name, args_str) }
toString() { return this[util.inspect.custom]() }})
// ===========================// Optional and Positional Parsing// ===========================const Namespace = _callable(class Namespace extends _AttributeHolder() { /* * Simple object for storing attributes. * * Implements equality by attribute names and values, and provides a simple * string representation. */
constructor(options = {}) { super() Object.assign(this, options) }})
// unset string tag to mimic plain objectNamespace.prototype[Symbol.toStringTag] = undefined
const _ActionsContainer = _camelcase_alias(_callable(class _ActionsContainer {
constructor() { let [ description, prefix_chars, argument_default, conflict_handler ] = _parse_opts(arguments, { description: no_default, prefix_chars: no_default, argument_default: no_default, conflict_handler: no_default })
this.description = description this.argument_default = argument_default this.prefix_chars = prefix_chars this.conflict_handler = conflict_handler
// set up registries this._registries = {}
// register actions this.register('action', undefined, _StoreAction) this.register('action', 'store', _StoreAction) this.register('action', 'store_const', _StoreConstAction) this.register('action', 'store_true', _StoreTrueAction) this.register('action', 'store_false', _StoreFalseAction) this.register('action', 'append', _AppendAction) this.register('action', 'append_const', _AppendConstAction) this.register('action', 'count', _CountAction) this.register('action', 'help', _HelpAction) this.register('action', 'version', _VersionAction) this.register('action', 'parsers', _SubParsersAction) this.register('action', 'extend', _ExtendAction) // LEGACY (v1 compatibility): camelcase variants ;[ 'storeConst', 'storeTrue', 'storeFalse', 'appendConst' ].forEach(old_name => { let new_name = _to_new_name(old_name) this.register('action', old_name, util.deprecate(this._registry_get('action', new_name), sub('{action: "%s"} is renamed to {action: "%s"}', old_name, new_name))) }) // end
// raise an exception if the conflict handler is invalid this._get_handler()
// action storage this._actions = [] this._option_string_actions = {}
// groups this._action_groups = [] this._mutually_exclusive_groups = []
// defaults storage this._defaults = {}
// determines whether an "option" looks like a negative number this._negative_number_matcher = /^-\d+$|^-\d*\.\d+$/
// whether or not there are any optionals that look like negative // numbers -- uses a list so it can be shared and edited this._has_negative_number_optionals = [] }
// ==================== // Registration methods // ==================== register(registry_name, value, object) { let registry = setdefault(this._registries, registry_name, {}) registry[value] = object }
_registry_get(registry_name, value, default_value = undefined) { return getattr(this._registries[registry_name], value, default_value) }
// ================================== // Namespace default accessor methods // ================================== set_defaults(kwargs) { Object.assign(this._defaults, kwargs)
// if these defaults match any existing arguments, replace // the previous default on the object with the new one for (let action of this._actions) { if (action.dest in kwargs) { action.default = kwargs[action.dest] } } }
get_default(dest) { for (let action of this._actions) { if (action.dest === dest && action.default !== undefined) { return action.default } } return this._defaults[dest] }
// ======================= // Adding argument actions // ======================= add_argument() { /* * add_argument(dest, ..., name=value, ...) * add_argument(option_string, option_string, ..., name=value, ...) */ let [ args, kwargs ] = _parse_opts(arguments, { '*args': no_default, '**kwargs': no_default }) // LEGACY (v1 compatibility), old-style add_argument([ args ], { options }) if (args.length === 1 && Array.isArray(args[0])) { args = args[0] deprecate('argument-array', sub('use add_argument(%(args)s, {...}) instead of add_argument([ %(args)s ], { ... })', { args: args.map(repr).join(', ') })) } // end
// if no positional args are supplied or only one is supplied and // it doesn't look like an option string, parse a positional // argument let chars = this.prefix_chars if (!args.length || args.length === 1 && !chars.includes(args[0][0])) { if (args.length && 'dest' in kwargs) { throw new TypeError('dest supplied twice for positional argument') } kwargs = this._get_positional_kwargs(...args, kwargs)
// otherwise, we're adding an optional argument } else { kwargs = this._get_optional_kwargs(...args, kwargs) }
// if no default was supplied, use the parser-level default if (!('default' in kwargs)) { let dest = kwargs.dest if (dest in this._defaults) { kwargs.default = this._defaults[dest] } else if (this.argument_default !== undefined) { kwargs.default = this.argument_default } }
// create the action object, and add it to the parser let action_class = this._pop_action_class(kwargs) if (typeof action_class !== 'function') { throw new TypeError(sub('unknown action "%s"', action_class)) } // eslint-disable-next-line new-cap let action = new action_class(kwargs)
// raise an error if the action type is not callable let type_func = this._registry_get('type', action.type, action.type) if (typeof type_func !== 'function') { throw new TypeError(sub('%r is not callable', type_func)) }
if (type_func === FileType) { throw new TypeError(sub('%r is a FileType class object, instance of it' + ' must be passed', type_func)) }
// raise an error if the metavar does not match the type if ('_get_formatter' in this) { try { this._get_formatter()._format_args(action, undefined) } catch (err) { // check for 'invalid nargs value' is an artifact of TypeError and ValueError in js being the same if (err instanceof TypeError && err.message !== 'invalid nargs value') { throw new TypeError('length of metavar tuple does not match nargs') } else { throw err } } }
return this._add_action(action) }
add_argument_group() { let group = _ArgumentGroup(this, ...arguments) this._action_groups.push(group) return group }
add_mutually_exclusive_group() { // eslint-disable-next-line no-use-before-define let group = _MutuallyExclusiveGroup(this, ...arguments) this._mutually_exclusive_groups.push(group) return group }
_add_action(action) { // resolve any conflicts this._check_conflict(action)
// add to actions list this._actions.push(action) action.container = this
// index the action by any option strings it has for (let option_string of action.option_strings) { this._option_string_actions[option_string] = action }
// set the flag if any option strings look like negative numbers for (let option_string of action.option_strings) { if (this._negative_number_matcher.test(option_string)) { if (!this._has_negative_number_optionals.length) { this._has_negative_number_optionals.push(true) } } }
// return the created action return action }
_remove_action(action) { _array_remove(this._actions, action) }
_add_container_actions(container) { // collect groups by titles let title_group_map = {} for (let group of this._action_groups) { if (group.title in title_group_map) { let msg = 'cannot merge actions - two groups are named %r' throw new TypeError(sub(msg, group.title)) } title_group_map[group.title] = group }
// map each action to its group let group_map = new Map() for (let group of container._action_groups) {
// if a group with the title exists, use that, otherwise // create a new group matching the container's group if (!(group.title in title_group_map)) { title_group_map[group.title] = this.add_argument_group({ title: group.title, description: group.description, conflict_handler: group.conflict_handler }) }
// map the actions to their new group for (let action of group._group_actions) { group_map.set(action, title_group_map[group.title]) } }
// add container's mutually exclusive groups // NOTE: if add_mutually_exclusive_group ever gains title= and // description= then this code will need to be expanded as above for (let group of container._mutually_exclusive_groups) { let mutex_group = this.add_mutually_exclusive_group({ required: group.required })
// map the actions to their new mutex group for (let action of group._group_actions) { group_map.set(action, mutex_group) } }
// add all actions to this container or their group for (let action of container._actions) { group_map.get(action)._add_action(action) } }
_get_positional_kwargs() { let [ dest, kwargs ] = _parse_opts(arguments, { dest: no_default, '**kwargs': no_default })
// make sure required is not specified if ('required' in kwargs) { let msg = "'required' is an invalid argument for positionals" throw new TypeError(msg) }
// mark positional arguments as required if at least one is // always required if (![OPTIONAL, ZERO_OR_MORE].includes(kwargs.nargs)) { kwargs.required = true } if (kwargs.nargs === ZERO_OR_MORE && !('default' in kwargs)) { kwargs.required = true }
// return the keyword arguments with no option strings return Object.assign(kwargs, { dest, option_strings: [] }) }
_get_optional_kwargs() { let [ args, kwargs ] = _parse_opts(arguments, { '*args': no_default, '**kwargs': no_default })
// determine short and long option strings let option_strings = [] let long_option_strings = [] let option_string for (option_string of args) { // error on strings that don't start with an appropriate prefix if (!this.prefix_chars.includes(option_string[0])) { let args = {option: option_string, prefix_chars: this.prefix_chars} let msg = 'invalid option string %(option)r: ' + 'must start with a character %(prefix_chars)r' throw new TypeError(sub(msg, args)) }
// strings starting with two prefix characters are long options option_strings.push(option_string) if (option_string.length > 1 && this.prefix_chars.includes(option_string[1])) { long_option_strings.push(option_string) } }
// infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' let dest = kwargs.dest delete kwargs.dest if (dest === undefined) { let dest_option_string if (long_option_strings.length) { dest_option_string = long_option_strings[0] } else { dest_option_string = option_strings[0] } dest = _string_lstrip(dest_option_string, this.prefix_chars) if (!dest) { let msg = 'dest= is required for options like %r' throw new TypeError(sub(msg, option_string)) } dest = dest.replace(/-/g, '_') }
// return the updated keyword arguments return Object.assign(kwargs, { dest, option_strings }) }
_pop_action_class(kwargs, default_value = undefined) { let action = getattr(kwargs, 'action', default_value) delete kwargs.action return this._registry_get('action', action, action) }
_get_handler() { // determine function from conflict handler string let handler_func_name = sub('_handle_conflict_%s', this.conflict_handler) if (typeof this[handler_func_name] === 'function') { return this[handler_func_name] } else { let msg = 'invalid conflict_resolution value: %r' throw new TypeError(sub(msg, this.conflict_handler)) } }
_check_conflict(action) {
// find all options that conflict with this option let confl_optionals = [] for (let option_string of action.option_strings) { if (hasattr(this._option_string_actions, option_string)) { let confl_optional = this._option_string_actions[option_string] confl_optionals.push([ option_string, confl_optional ]) } }
// resolve any conflicts if (confl_optionals.length) { let conflict_handler = this._get_handler() conflict_handler.call(this, action, confl_optionals) } }
_handle_conflict_error(action, conflicting_actions) { let message = conflicting_actions.length === 1 ? 'conflicting option string: %s' : 'conflicting option strings: %s' let conflict_string = conflicting_actions.map(([ option_string/*, action*/ ]) => option_string).join(', ') throw new ArgumentError(action, sub(message, conflict_string)) }
_handle_conflict_resolve(action, conflicting_actions) {
// remove all conflicting options for (let [ option_string, action ] of conflicting_actions) {
// remove the conflicting option _array_remove(action.option_strings, option_string) delete this._option_string_actions[option_string]
// if the option now has no option string, remove it from the // container holding it if (!action.option_strings.length) { action.container._remove_action(action) } } }}))
const _ArgumentGroup = _callable(class _ArgumentGroup extends _ActionsContainer {
constructor() { let [ container, title, description, kwargs ] = _parse_opts(arguments, { container: no_default, title: undefined, description: undefined, '**kwargs': no_default })
// add any missing keyword arguments by checking the container setdefault(kwargs, 'conflict_handler', container.conflict_handler) setdefault(kwargs, 'prefix_chars', container.prefix_chars) setdefault(kwargs, 'argument_default', container.argument_default) super(Object.assign({ description }, kwargs))
// group attributes this.title = title this._group_actions = []
// share most attributes with the container this._registries = container._registries this._actions = container._actions this._option_string_actions = container._option_string_actions this._defaults = container._defaults this._has_negative_number_optionals = container._has_negative_number_optionals this._mutually_exclusive_groups = container._mutually_exclusive_groups }
_add_action(action) { action = super._add_action(action) this._group_actions.push(action) return action }
_remove_action(action) { super._remove_action(action) _array_remove(this._group_actions, action) }})
const _MutuallyExclusiveGroup = _callable(class _MutuallyExclusiveGroup extends _ArgumentGroup {
constructor() { let [ container, required ] = _parse_opts(arguments, { container: no_default, required: false })
super(container) this.required = required this._container = container }
_add_action(action) { if (action.required) { let msg = 'mutually exclusive arguments must be optional' throw new TypeError(msg) } action = this._container._add_action(action) this._group_actions.push(action) return action }
_remove_action(action) { this._container._remove_action(action) _array_remove(this._group_actions, action) }})
const ArgumentParser = _camelcase_alias(_callable(class ArgumentParser extends _AttributeHolder(_ActionsContainer) { /* * Object for parsing command line strings into Python objects. * * Keyword Arguments: * - prog -- The name of the program (default: sys.argv[0]) * - usage -- A usage message (default: auto-generated from arguments) * - description -- A description of what the program does * - epilog -- Text following the argument descriptions * - parents -- Parsers whose arguments should be copied into this one * - formatter_class -- HelpFormatter class for printing help messages * - prefix_chars -- Characters that prefix optional arguments * - fromfile_prefix_chars -- Characters that prefix files containing * additional arguments * - argument_default -- The default value for all arguments * - conflict_handler -- String indicating how to handle conflicts * - add_help -- Add a -h/-help option * - allow_abbrev -- Allow long options to be abbreviated unambiguously * - exit_on_error -- Determines whether or not ArgumentParser exits with * error info when an error occurs */
constructor() { let [ prog, usage, description, epilog, parents, formatter_class, prefix_chars, fromfile_prefix_chars, argument_default, conflict_handler, add_help, allow_abbrev, exit_on_error, debug, // LEGACY (v1 compatibility), debug mode version // LEGACY (v1 compatibility), version ] = _parse_opts(arguments, { prog: undefined, usage: undefined, description: undefined, epilog: undefined, parents: [], formatter_class: HelpFormatter, prefix_chars: '-', fromfile_prefix_chars: undefined, argument_default: undefined, conflict_handler: 'error', add_help: true, allow_abbrev: true, exit_on_error: true, debug: undefined, // LEGACY (v1 compatibility), debug mode version: undefined // LEGACY (v1 compatibility), version })
// LEGACY (v1 compatibility) if (debug !== undefined) { deprecate('debug', 'The "debug" argument to ArgumentParser is deprecated. Please ' + 'override ArgumentParser.exit function instead.' ) }
if (version !== undefined) { deprecate('version', 'The "version" argument to ArgumentParser is deprecated. Please use ' + "add_argument(..., { action: 'version', version: 'N', ... }) instead." ) } // end
super({ description, prefix_chars, argument_default, conflict_handler })
// default setting for prog if (prog === undefined) { prog = path.basename(get_argv()[0] || '') }
this.prog = prog this.usage = usage this.epilog = epilog this.formatter_class = formatter_class this.fromfile_prefix_chars = fromfile_prefix_chars this.add_help = add_help this.allow_abbrev = allow_abbrev this.exit_on_error = exit_on_error // LEGACY (v1 compatibility), debug mode this.debug = debug // end
this._positionals = this.add_argument_group('positional arguments') this._optionals = this.add_argument_group('optional arguments') this._subparsers = undefined
// register types function identity(string) { return string } this.register('type', undefined, identity) this.register('type', null, identity) this.register('type', 'auto', identity) this.register('type', 'int', function (x) { let result = Number(x) if (!Number.isInteger(result)) { throw new TypeError(sub('could not convert string to int: %r', x)) } return result }) this.register('type', 'float', function (x) { let result = Number(x) if (isNaN(result)) { throw new TypeError(sub('could not convert string to float: %r', x)) } return result }) this.register('type', 'str', String) // LEGACY (v1 compatibility): custom types this.register('type', 'string', util.deprecate(String, 'use {type:"str"} or {type:String} instead of {type:"string"}')) // end
// add help argument if necessary // (using explicit default to override global argument_default) let default_prefix = prefix_chars.includes('-') ? '-' : prefix_chars[0] if (this.add_help) { this.add_argument( default_prefix + 'h', default_prefix.repeat(2) + 'help', { action: 'help', default: SUPPRESS, help: 'show this help message and exit' } ) } // LEGACY (v1 compatibility), version if (version) { this.add_argument( default_prefix + 'v', default_prefix.repeat(2) + 'version', { action: 'version', default: SUPPRESS, version: this.version, help: "show program's version number and exit" } ) } // end
// add parent arguments and defaults for (let parent of parents) { this._add_container_actions(parent) Object.assign(this._defaults, parent._defaults) } }
// ======================= // Pretty __repr__ methods // ======================= _get_kwargs() { let names = [ 'prog', 'usage', 'description', 'formatter_class', 'conflict_handler', 'add_help' ] return names.map(name => [ name, getattr(this, name) ]) }
// ================================== // Optional/Positional adding methods // ================================== add_subparsers() { let [ kwargs ] = _parse_opts(arguments, { '**kwargs': no_default })
if (this._subparsers !== undefined) { this.error('cannot have multiple subparser arguments') }
// add the parser class to the arguments if it's not present setdefault(kwargs, 'parser_class', this.constructor)
if ('title' in kwargs || 'description' in kwargs) { let title = getattr(kwargs, 'title', 'subcommands') let description = getattr(kwargs, 'description', undefined) delete kwargs.title delete kwargs.description this._subparsers = this.add_argument_group(title, description) } else { this._subparsers = this._positionals }
// prog defaults to the usage message of this parser, skipping // optional arguments and with no "usage:" prefix if (kwargs.prog === undefined) { let formatter = this._get_formatter() let positionals = this._get_positional_actions() let groups = this._mutually_exclusive_groups formatter.add_usage(this.usage, positionals, groups, '') kwargs.prog = formatter.format_help().trim() }
// create the parsers action and add it to the positionals list let parsers_class = this._pop_action_class(kwargs, 'parsers') // eslint-disable-next-line new-cap let action = new parsers_class(Object.assign({ option_strings: [] }, kwargs)) this._subparsers._add_action(action)
// return the created parsers action return action }
_add_action(action) { if (action.option_strings.length) { this._optionals._add_action(action) } else { this._positionals._add_action(action) } return action }
_get_optional_actions() { return this._actions.filter(action => action.option_strings.length) }
_get_positional_actions() { return this._actions.filter(action => !action.option_strings.length) }
// ===================================== // Command line argument parsing methods // ===================================== parse_args(args = undefined, namespace = undefined) { let argv [ args, argv ] = this.parse_known_args(args, namespace) if (argv && argv.length > 0) { let msg = 'unrecognized arguments: %s' this.error(sub(msg, argv.join(' '))) } return args }
parse_known_args(args = undefined, namespace = undefined) { if (args === undefined) { args = get_argv().slice(1) }
// default Namespace built from parser defaults if (namespace === undefined) { namespace = new Namespace() }
// add any action defaults that aren't present for (let action of this._actions) { if (action.dest !== SUPPRESS) { if (!hasattr(namespace, action.dest)) { if (action.default !== SUPPRESS) { setattr(namespace, action.dest, action.default) } } } }
// add any parser defaults that aren't present for (let dest of Object.keys(this._defaults)) { if (!hasattr(namespace, dest)) { setattr(namespace, dest, this._defaults[dest]) } }
// parse the arguments and exit if there are any errors if (this.exit_on_error) { try { [ namespace, args ] = this._parse_known_args(args, namespace) } catch (err) { if (err instanceof ArgumentError) { this.error(err.message) } else { throw err } } } else { [ namespace, args ] = this._parse_known_args(args, namespace) }
if (hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) { args = args.concat(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) }
return [ namespace, args ] }
_parse_known_args(arg_strings, namespace) { // replace arg strings that are file references if (this.fromfile_prefix_chars !== undefined) { arg_strings = this._read_args_from_files(arg_strings) }
// map all mutually exclusive arguments to the other arguments // they can't occur with let action_conflicts = new Map() for (let mutex_group of this._mutually_exclusive_groups) { let group_actions = mutex_group._group_actions for (let [ i, mutex_action ] of Object.entries(mutex_group._group_actions)) { let conflicts = action_conflicts.get(mutex_action) || [] conflicts = conflicts.concat(group_actions.slice(0, +i)) conflicts = conflicts.concat(group_actions.slice(+i + 1)) action_conflicts.set(mutex_action, conflicts) } }
// find all option indices, and determine the arg_string_pattern // which has an 'O' if there is an option at an index, // an 'A' if there is an argument, or a '-' if there is a '--' let option_string_indices = {} let arg_string_pattern_parts = [] let arg_strings_iter = Object.entries(arg_strings)[Symbol.iterator]() for (let [ i, arg_string ] of arg_strings_iter) {
// all args after -- are non-options if (arg_string === '--') { arg_string_pattern_parts.push('-') for ([ i, arg_string ] of arg_strings_iter) { arg_string_pattern_parts.push('A') }
// otherwise, add the arg to the arg strings // and note the index if it was an option } else { let option_tuple = this._parse_optional(arg_string) let pattern if (option_tuple === undefined) { pattern = 'A' } else { option_string_indices[i] = option_tuple pattern = 'O' } arg_string_pattern_parts.push(pattern) } }
// join the pieces together to form the pattern let arg_strings_pattern = arg_string_pattern_parts.join('')
// converts arg strings to the appropriate and then takes the action let seen_actions = new Set() let seen_non_default_actions = new Set() let extras
let take_action = (action, argument_strings, option_string = undefined) => { seen_actions.add(action) let argument_values = this._get_values(action, argument_strings)
// error if this argument is not allowed with other previously // seen arguments, assuming that actions that use the default // value don't really count as "present" if (argument_values !== action.default) { seen_non_default_actions.add(action) for (let conflict_action of action_conflicts.get(action) || []) { if (seen_non_default_actions.has(conflict_action)) { let msg = 'not allowed with argument %s' let action_name = _get_action_name(conflict_action) throw new ArgumentError(action, sub(msg, action_name)) } } }
// take the action if we didn't receive a SUPPRESS value // (e.g. from a default) if (argument_values !== SUPPRESS) { action(this, namespace, argument_values, option_string) } }
// function to convert arg_strings into an optional action let consume_optional = start_index => {
// get the optional identified at this index let option_tuple = option_string_indices[start_index] let [ action, option_string, explicit_arg ] = option_tuple
// identify additional optionals in the same arg string // (e.g. -xyz is the same as -x -y -z if no args are required) let action_tuples = [] let stop for (;;) {
// if we found no optional action, skip it if (action === undefined) { extras.push(arg_strings[start_index]) return start_index + 1 }
// if there is an explicit argument, try to match the // optional's string arguments to only this if (explicit_arg !== undefined) { let arg_count = this._match_argument(action, 'A')
// if the action is a single-dash option and takes no // arguments, try to parse more single-dash options out // of the tail of the option string let chars = this.prefix_chars if (arg_count === 0 && !chars.includes(option_string[1])) { action_tuples.push([ action, [], option_string ]) let char = option_string[0] option_string = char + explicit_arg[0] let new_explicit_arg = explicit_arg.slice(1) || undefined let optionals_map = this._option_string_actions if (hasattr(optionals_map, option_string)) { action = optionals_map[option_string] explicit_arg = new_explicit_arg } else { let msg = 'ignored explicit argument %r' throw new ArgumentError(action, sub(msg, explicit_arg)) }
// if the action expect exactly one argument, we've // successfully matched the option; exit the loop } else if (arg_count === 1) { stop = start_index + 1 let args = [ explicit_arg ] action_tuples.push([ action, args, option_string ]) break
// error if a double-dash option did not use the // explicit argument } else { let msg = 'ignored explicit argument %r' throw new ArgumentError(action, sub(msg, explicit_arg)) }
// if there is no explicit argument, try to match the // optional's string arguments with the following strings // if successful, exit the loop } else { let start = start_index + 1 let selected_patterns = arg_strings_pattern.slice(start) let arg_count = this._match_argument(action, selected_patterns) stop = start + arg_count let args = arg_strings.slice(start, stop) action_tuples.push([ action, args, option_string ]) break } }
// add the Optional to the list and return the index at which // the Optional's string args stopped assert(action_tuples.length) for (let [ action, args, option_string ] of action_tuples) { take_action(action, args, option_string) } return stop }
// the list of Positionals left to be parsed; this is modified // by consume_positionals() let positionals = this._get_positional_actions()
// function to convert arg_strings into positional actions let consume_positionals = start_index => { // match as many Positionals as possible let selected_pattern = arg_strings_pattern.slice(start_index) let arg_counts = this._match_arguments_partial(positionals, selected_pattern)
// slice off the appropriate arg strings for each Positional // and add the Positional and its args to the list for (let i = 0; i < positionals.length && i < arg_counts.length; i++) { let action = positionals[i] let arg_count = arg_counts[i] let args = arg_strings.slice(start_index, start_index + arg_count) start_index += arg_count take_action(action, args) }
// slice off the Positionals that we just parsed and return the // index at which the Positionals' string args stopped positionals = positionals.slice(arg_counts.length) return start_index }
// consume Positionals and Optionals alternately, until we have // passed the last option string extras = [] let start_index = 0 let max_option_string_index = Math.max(-1, ...Object.keys(option_string_indices).map(Number)) while (start_index <= max_option_string_index) {
// consume any Positionals preceding the next option let next_option_string_index = Math.min( // eslint-disable-next-line no-loop-func ...Object.keys(option_string_indices).map(Number).filter(index => index >= start_index) ) if (start_index !== next_option_string_index) { let positionals_end_index = consume_positionals(start_index)
// only try to parse the next optional if we didn't consume // the option string during the positionals parsing if (positionals_end_index > start_index) { start_index = positionals_end_index continue } else { start_index = positionals_end_index } }
// if we consumed all the positionals we could and we're not // at the index of an option string, there were extra arguments if (!(start_index in option_string_indices)) { let strings = arg_strings.slice(start_index, next_option_string_index) extras = extras.concat(strings) start_index = next_option_string_index }
// consume the next optional and any arguments for it start_index = consume_optional(start_index) }
// consume any positionals following the last Optional let stop_index = consume_positionals(start_index)
// if we didn't consume all the argument strings, there were extras extras = extras.concat(arg_strings.slice(stop_index))
// make sure all required actions were present and also convert // action defaults which were not given as arguments let required_actions = [] for (let action of this._actions) { if (!seen_actions.has(action)) { if (action.required) { required_actions.push(_get_action_name(action)) } else { // Convert action default now instead of doing it before // parsing arguments to avoid calling convert functions // twice (which may fail) if the argument was given, but // only if it was defined already in the namespace if (action.default !== undefined && typeof action.default === 'string' && hasattr(namespace, action.dest) && action.default === getattr(namespace, action.dest)) { setattr(namespace, action.dest, this._get_value(action, action.default)) } } } }
if (required_actions.length) { this.error(sub('the following arguments are required: %s', required_actions.join(', '))) }
// make sure all required groups had one option present for (let group of this._mutually_exclusive_groups) { if (group.required) { let no_actions_used = true for (let action of group._group_actions) { if (seen_non_default_actions.has(action)) { no_actions_used = false break } }
// if no actions were used, report the error if (no_actions_used) { let names = group._group_actions .filter(action => action.help !== SUPPRESS) .map(action => _get_action_name(action)) let msg = 'one of the arguments %s is required' this.error(sub(msg, names.join(' '))) } } }
// return the updated namespace and the extra arguments return [ namespace, extras ] }
_read_args_from_files(arg_strings) { // expand arguments referencing files let new_arg_strings = [] for (let arg_string of arg_strings) {
// for regular arguments, just add them back into the list if (!arg_string || !this.fromfile_prefix_chars.includes(arg_string[0])) { new_arg_strings.push(arg_string)
// replace arguments referencing files with the file content } else { try { let args_file = fs.readFileSync(arg_string.slice(1), 'utf8') let arg_strings = [] for (let arg_line of splitlines(args_file)) { for (let arg of this.convert_arg_line_to_args(arg_line)) { arg_strings.push(arg) } } arg_strings = this._read_args_from_files(arg_strings) new_arg_strings = new_arg_strings.concat(arg_strings) } catch (err) { this.error(err.message) } } }
// return the modified argument list return new_arg_strings }
convert_arg_line_to_args(arg_line) { return [arg_line] }
_match_argument(action, arg_strings_pattern) { // match the pattern for this action to the arg strings let nargs_pattern = this._get_nargs_pattern(action) let match = arg_strings_pattern.match(new RegExp('^' + nargs_pattern))
// raise an exception if we weren't able to find a match if (match === null) { let nargs_errors = { undefined: 'expected one argument', [OPTIONAL]: 'expected at most one argument', [ONE_OR_MORE]: 'expected at least one argument' } let msg = nargs_errors[action.nargs] if (msg === undefined) { msg = sub(action.nargs === 1 ? 'expected %s argument' : 'expected %s arguments', action.nargs) } throw new ArgumentError(action, msg) }
// return the number of arguments matched return match[1].length }
_match_arguments_partial(actions, arg_strings_pattern) { // progressively shorten the actions list by slicing off the // final actions until we find a match let result = [] for (let i of range(actions.length, 0, -1)) { let actions_slice = actions.slice(0, i) let pattern = actions_slice.map(action => this._get_nargs_pattern(action)).join('') let match = arg_strings_pattern.match(new RegExp('^' + pattern)) if (match !== null) { result = result.concat(match.slice(1).map(string => string.length)) break } }
// return the list of arg string counts return result }
_parse_optional(arg_string) { // if it's an empty string, it was meant to be a positional if (!arg_string) { return undefined }
// if it doesn't start with a prefix, it was meant to be positional if (!this.prefix_chars.includes(arg_string[0])) { return undefined }
// if the option string is present in the parser, return the action if (arg_string in this._option_string_actions) { let action = this._option_string_actions[arg_string] return [ action, arg_string, undefined ] }
// if it's just a single character, it was meant to be positional if (arg_string.length === 1) { return undefined }
// if the option string before the "=" is present, return the action if (arg_string.includes('=')) { let [ option_string, explicit_arg ] = _string_split(arg_string, '=', 1) if (option_string in this._option_string_actions) { let action = this._option_string_actions[option_string] return [ action, option_string, explicit_arg ] } }
// search through all possible prefixes of the option string // and all actions in the parser for possible interpretations let option_tuples = this._get_option_tuples(arg_string)
// if multiple actions match, the option string was ambiguous if (option_tuples.length > 1) { let options = option_tuples.map(([ /*action*/, option_string/*, explicit_arg*/ ]) => option_string).join(', ') let args = {option: arg_string, matches: options} let msg = 'ambiguous option: %(option)s could match %(matches)s' this.error(sub(msg, args))
// if exactly one action matched, this segmentation is good, // so return the parsed action } else if (option_tuples.length === 1) { let [ option_tuple ] = option_tuples return option_tuple }
// if it was not found as an option, but it looks like a negative // number, it was meant to be positional // unless there are negative-number-like options if (this._negative_number_matcher.test(arg_string)) { if (!this._has_negative_number_optionals.length) { return undefined } }
// if it contains a space, it was meant to be a positional if (arg_string.includes(' ')) { return undefined }
// it was meant to be an optional but there is no such option // in this parser (though it might be a valid option in a subparser) return [ undefined, arg_string, undefined ] }
_get_option_tuples(option_string) { let result = []
// option strings starting with two prefix characters are only // split at the '=' let chars = this.prefix_chars if (chars.includes(option_string[0]) && chars.includes(option_string[1])) { if (this.allow_abbrev) { let option_prefix, explicit_arg if (option_string.includes('=')) { [ option_prefix, explicit_arg ] = _string_split(option_string, '=', 1) } else { option_prefix = option_string explicit_arg = undefined } for (let option_string of Object.keys(this._option_string_actions)) { if (option_string.startsWith(option_prefix)) { let action = this._option_string_actions[option_string] let tup = [ action, option_string, explicit_arg ] result.push(tup) } } }
// single character options can be concatenated with their arguments // but multiple character options always have to have their argument // separate } else if (chars.includes(option_string[0]) && !chars.includes(option_string[1])) { let option_prefix = option_string let explicit_arg = undefined let short_option_prefix = option_string.slice(0, 2) let short_explicit_arg = option_string.slice(2)
for (let option_string of Object.keys(this._option_string_actions)) { if (option_string === short_option_prefix) { let action = this._option_string_actions[option_string] let tup = [ action, option_string, short_explicit_arg ] result.push(tup) } else if (option_string.startsWith(option_prefix)) { let action = this._option_string_actions[option_string] let tup = [ action, option_string, explicit_arg ] result.push(tup) } }
// shouldn't ever get here } else { this.error(sub('unexpected option string: %s', option_string)) }
// return the collected option tuples return result }
_get_nargs_pattern(action) { // in all examples below, we have to allow for '--' args // which are represented as '-' in the pattern let nargs = action.nargs let nargs_pattern
// the default (None) is assumed to be a single argument if (nargs === undefined) { nargs_pattern = '(-*A-*)'
// allow zero or one arguments } else if (nargs === OPTIONAL) { nargs_pattern = '(-*A?-*)'
// allow zero or more arguments } else if (nargs === ZERO_OR_MORE) { nargs_pattern = '(-*[A-]*)'
// allow one or more arguments } else if (nargs === ONE_OR_MORE) { nargs_pattern = '(-*A[A-]*)'
// allow any number of options or arguments } else if (nargs === REMAINDER) { nargs_pattern = '([-AO]*)'
// allow one argument followed by any number of options or arguments } else if (nargs === PARSER) { nargs_pattern = '(-*A[-AO]*)'
// suppress action, like nargs=0 } else if (nargs === SUPPRESS) { nargs_pattern = '(-*-*)'
// all others should be integers } else { nargs_pattern = sub('(-*%s-*)', 'A'.repeat(nargs).split('').join('-*')) }
// if this is an optional action, -- is not allowed if (action.option_strings.length) { nargs_pattern = nargs_pattern.replace(/-\*/g, '') nargs_pattern = nargs_pattern.replace(/-/g, '') }
// return the pattern return nargs_pattern }
// ======================== // Alt command line argument parsing, allowing free intermix // ========================
parse_intermixed_args(args = undefined, namespace = undefined) { let argv [ args, argv ] = this.parse_known_intermixed_args(args, namespace) if (argv.length) { let msg = 'unrecognized arguments: %s' this.error(sub(msg, argv.join(' '))) } return args }
parse_known_intermixed_args(args = undefined, namespace = undefined) { // returns a namespace and list of extras // // positional can be freely intermixed with optionals. optionals are // first parsed with all positional arguments deactivated. The 'extras' // are then parsed. If the parser definition is incompatible with the // intermixed assumptions (e.g. use of REMAINDER, subparsers) a // TypeError is raised. // // positionals are 'deactivated' by setting nargs and default to // SUPPRESS. This blocks the addition of that positional to the // namespace
let extras let positionals = this._get_positional_actions() let a = positionals.filter(action => [ PARSER, REMAINDER ].includes(action.nargs)) if (a.length) { throw new TypeError(sub('parse_intermixed_args: positional arg' + ' with nargs=%s', a[0].nargs)) }
for (let group of this._mutually_exclusive_groups) { for (let action of group._group_actions) { if (positionals.includes(action)) { throw new TypeError('parse_intermixed_args: positional in' + ' mutuallyExclusiveGroup') } } }
let save_usage try { save_usage = this.usage let remaining_args try { if (this.usage === undefined) { // capture the full usage for use in error messages this.usage = this.format_usage().slice(7) } for (let action of positionals) { // deactivate positionals action.save_nargs = action.nargs // action.nargs = 0 action.nargs = SUPPRESS action.save_default = action.default action.default = SUPPRESS } [ namespace, remaining_args ] = this.parse_known_args(args, namespace) for (let action of positionals) { // remove the empty positional values from namespace let attr = getattr(namespace, action.dest) if (Array.isArray(attr) && attr.length === 0) { // eslint-disable-next-line no-console console.warn(sub('Do not expect %s in %s', action.dest, namespace)) delattr(namespace, action.dest) } } } finally { // restore nargs and usage before exiting for (let action of positionals) { action.nargs = action.save_nargs action.default = action.save_default } } let optionals = this._get_optional_actions() try { // parse positionals. optionals aren't normally required, but // they could be, so make sure they aren't. for (let action of optionals) { action.save_required = action.required action.required = false } for (let group of this._mutually_exclusive_groups) { group.save_required = group.required group.required = false } [ namespace, extras ] = this.parse_known_args(remaining_args, namespace) } finally { // restore parser values before exiting for (let action of optionals) { action.required = action.save_required } for (let group of this._mutually_exclusive_groups) { group.required = group.save_required } } } finally { this.usage = save_usage } return [ namespace, extras ] }
// ======================== // Value conversion methods // ======================== _get_values(action, arg_strings) { // for everything but PARSER, REMAINDER args, strip out first '--' if (![PARSER, REMAINDER].includes(action.nargs)) { try { _array_remove(arg_strings, '--') } catch (err) {} }
let value // optional argument produces a default when not present if (!arg_strings.length && action.nargs === OPTIONAL) { if (action.option_strings.length) { value = action.const } else { value = action.default } if (typeof value === 'string') { value = this._get_value(action, value) this._check_value(action, value) }
// when nargs='*' on a positional, if there were no command-line // args, use the default if it is anything other than None } else if (!arg_strings.length && action.nargs === ZERO_OR_MORE && !action.option_strings.length) { if (action.default !== undefined) { value = action.default } else { value = arg_strings } this._check_value(action, value)
// single argument or optional argument produces a single value } else if (arg_strings.length === 1 && [undefined, OPTIONAL].includes(action.nargs)) { let arg_string = arg_strings[0] value = this._get_value(action, arg_string) this._check_value(action, value)
// REMAINDER arguments convert all values, checking none } else if (action.nargs === REMAINDER) { value = arg_strings.map(v => this._get_value(action, v))
// PARSER arguments convert all values, but check only the first } else if (action.nargs === PARSER) { value = arg_strings.map(v => this._get_value(action, v)) this._check_value(action, value[0])
// SUPPRESS argument does not put anything in the namespace } else if (action.nargs === SUPPRESS) { value = SUPPRESS
// all other types of nargs produce a list } else { value = arg_strings.map(v => this._get_value(action, v)) for (let v of value) { this._check_value(action, v) } }
// return the converted value return value }
_get_value(action, arg_string) { let type_func = this._registry_get('type', action.type, action.type) if (typeof type_func !== 'function') { let msg = '%r is not callable' throw new ArgumentError(action, sub(msg, type_func)) }
// convert the value to the appropriate type let result try { try { result = type_func(arg_string) } catch (err) { // Dear TC39, why would you ever consider making es6 classes not callable? // We had one universal interface, [[Call]], which worked for anything // (with familiar this-instanceof guard for classes). Now we have two. if (err instanceof TypeError && /Class constructor .* cannot be invoked without 'new'/.test(err.message)) { // eslint-disable-next-line new-cap result = new type_func(arg_string) } else { throw err } }
} catch (err) { // ArgumentTypeErrors indicate errors if (err instanceof ArgumentTypeError) { //let name = getattr(action.type, 'name', repr(action.type)) let msg = err.message throw new ArgumentError(action, msg)
// TypeErrors or ValueErrors also indicate errors } else if (err instanceof TypeError) { let name = getattr(action.type, 'name', repr(action.type)) let args = {type: name, value: arg_string} let msg = 'invalid %(type)s value: %(value)r' throw new ArgumentError(action, sub(msg, args)) } else { throw err } }
// return the converted value return result }
_check_value(action, value) { // converted value must be one of the choices (if specified) if (action.choices !== undefined && !_choices_to_array(action.choices).includes(value)) { let args = {value, choices: _choices_to_array(action.choices).map(repr).join(', ')} let msg = 'invalid choice: %(value)r (choose from %(choices)s)' throw new ArgumentError(action, sub(msg, args)) } }
// ======================= // Help-formatting methods // ======================= format_usage() { let formatter = this._get_formatter() formatter.add_usage(this.usage, this._actions, this._mutually_exclusive_groups) return formatter.format_help() }
format_help() { let formatter = this._get_formatter()
// usage formatter.add_usage(this.usage, this._actions, this._mutually_exclusive_groups)
// description formatter.add_text(this.description)
// positionals, optionals and user-defined groups for (let action_group of this._action_groups) { formatter.start_section(action_group.title) formatter.add_text(action_group.description) formatter.add_arguments(action_group._group_actions) formatter.end_section() }
// epilog formatter.add_text(this.epilog)
// determine help from format above return formatter.format_help() }
_get_formatter() { // eslint-disable-next-line new-cap return new this.formatter_class({ prog: this.prog }) }
// ===================== // Help-printing methods // ===================== print_usage(file = undefined) { if (file === undefined) file = process.stdout this._print_message(this.format_usage(), file) }
print_help(file = undefined) { if (file === undefined) file = process.stdout this._print_message(this.format_help(), file) }
_print_message(message, file = undefined) { if (message) { if (file === undefined) file = process.stderr file.write(message) } }
// =============== // Exiting methods // =============== exit(status = 0, message = undefined) { if (message) { this._print_message(message, process.stderr) } process.exit(status) }
error(message) { /* * error(message: string) * * Prints a usage message incorporating the message to stderr and * exits. * * If you override this in a subclass, it should not return -- it * should either exit or raise an exception. */
// LEGACY (v1 compatibility), debug mode if (this.debug === true) throw new Error(message) // end this.print_usage(process.stderr) let args = {prog: this.prog, message: message} this.exit(2, sub('%(prog)s: error: %(message)s\n', args)) }}))
module.exports = { ArgumentParser, ArgumentError, ArgumentTypeError, BooleanOptionalAction, FileType, HelpFormatter, ArgumentDefaultsHelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, MetavarTypeHelpFormatter, Namespace, Action, ONE_OR_MORE, OPTIONAL, PARSER, REMAINDER, SUPPRESS, ZERO_OR_MORE}
// LEGACY (v1 compatibility), Const aliasObject.defineProperty(module.exports, 'Const', { get() { let result = {} Object.entries({ ONE_OR_MORE, OPTIONAL, PARSER, REMAINDER, SUPPRESS, ZERO_OR_MORE }).forEach(([ n, v ]) => { Object.defineProperty(result, n, { get() { deprecate(n, sub('use argparse.%s instead of argparse.Const.%s', n, n)) return v } }) }) Object.entries({ _UNRECOGNIZED_ARGS_ATTR }).forEach(([ n, v ]) => { Object.defineProperty(result, n, { get() { deprecate(n, sub('argparse.Const.%s is an internal symbol and will no longer be available', n)) return v } }) }) return result }, enumerable: false})// end