| 1 | ## our_shell: ysh
 | 
| 2 | ## oils_failures_allowed: 1
 | 
| 3 | 
 | 
| 4 | #### args.ysh example usage
 | 
| 5 | source $LIB_YSH/args.ysh
 | 
| 6 | 
 | 
| 7 | parser (&spec) {
 | 
| 8 |   flag -v --verbose (help="Verbosely")  # default is Bool, false
 | 
| 9 | 
 | 
| 10 |   flag -P --max-procs ('int', default=-1, help='''
 | 
| 11 |     Run at most P processes at a time
 | 
| 12 |     ''')
 | 
| 13 | 
 | 
| 14 |   flag -i --invert ('bool', default=true, help='''
 | 
| 15 |     Long multiline
 | 
| 16 |     Description
 | 
| 17 |     ''')
 | 
| 18 | 
 | 
| 19 |   arg src (help='Source')
 | 
| 20 |   arg dest (help='Dest')
 | 
| 21 | 
 | 
| 22 |   rest files
 | 
| 23 | }
 | 
| 24 | 
 | 
| 25 | var args = parseArgs(spec, :| mysrc -P 12 mydest a b c |)
 | 
| 26 | 
 | 
| 27 | echo "Verbose $[args.verbose]"
 | 
| 28 | pp line (args)
 | 
| 29 | ## STDOUT:
 | 
| 30 | Verbose false
 | 
| 31 | (Dict)   {"src":"mysrc","max-procs":12,"dest":"mydest","files":["a","b","c"],"verbose":false,"invert":true}
 | 
| 32 | ## END
 | 
| 33 | 
 | 
| 34 | #### Bool flag, positional args, more positional
 | 
| 35 | 
 | 
| 36 | source $LIB_YSH/args.ysh
 | 
| 37 | 
 | 
| 38 | parser (&spec) {
 | 
| 39 |   flag -v --verbose ('bool')
 | 
| 40 |   arg src
 | 
| 41 |   arg dst
 | 
| 42 | 
 | 
| 43 |   rest more  # allow more args
 | 
| 44 | }
 | 
| 45 | #json write (spec)
 | 
| 46 | 
 | 
| 47 | var argv = ['-v', 'src/path', 'dst/path', 'x', 'y', 'z']
 | 
| 48 | 
 | 
| 49 | var args = parseArgs(spec, argv)
 | 
| 50 | 
 | 
| 51 | pp line (args)
 | 
| 52 | 
 | 
| 53 | if (args.verbose) {
 | 
| 54 |   echo "$[args.src] -> $[args.dst]"
 | 
| 55 |   write -- @[args.more]
 | 
| 56 | }
 | 
| 57 | 
 | 
| 58 | ## STDOUT:
 | 
| 59 | (Dict)   {"verbose":true,"src":"src/path","dst":"dst/path","more":["x","y","z"]}
 | 
| 60 | src/path -> dst/path
 | 
| 61 | x
 | 
| 62 | y
 | 
| 63 | z
 | 
| 64 | ## END
 | 
| 65 | 
 | 
| 66 | #### Test multiple ARGVs against a parser
 | 
| 67 | 
 | 
| 68 | source $LIB_YSH/args.ysh
 | 
| 69 | 
 | 
| 70 | parser (&spec) {
 | 
| 71 |   flag -v --verbose ('bool', default=false)
 | 
| 72 |   flag -c --count ('int', default=120)
 | 
| 73 |   arg file
 | 
| 74 | }
 | 
| 75 | 
 | 
| 76 | var argsCases = [
 | 
| 77 |   :| -v --count 120 example.sh |,
 | 
| 78 |   :| -v --count 120 example.sh -v |,  # duplicate flags are ignored
 | 
| 79 |   :| -v --count 120 example.sh -v --count 150 |,  # the last duplicate has precedence
 | 
| 80 | ]
 | 
| 81 | 
 | 
| 82 | for args in (argsCases) {
 | 
| 83 |   var args_str = join(args, ' ')
 | 
| 84 |   echo "----------  $args_str  ----------"
 | 
| 85 |   echo "\$ bin/ysh example.sh $args_str"
 | 
| 86 |   pp line (parseArgs(spec, args))
 | 
| 87 | 
 | 
| 88 |   echo
 | 
| 89 | }
 | 
| 90 | ## STDOUT:
 | 
| 91 | ----------  -v --count 120 example.sh  ----------
 | 
| 92 | $ bin/ysh example.sh -v --count 120 example.sh
 | 
| 93 | (Dict)   {"verbose":true,"count":120,"file":"example.sh"}
 | 
| 94 | 
 | 
| 95 | ----------  -v --count 120 example.sh -v  ----------
 | 
| 96 | $ bin/ysh example.sh -v --count 120 example.sh -v
 | 
| 97 | (Dict)   {"verbose":true,"count":120,"file":"example.sh"}
 | 
| 98 | 
 | 
| 99 | ----------  -v --count 120 example.sh -v --count 150  ----------
 | 
| 100 | $ bin/ysh example.sh -v --count 120 example.sh -v --count 150
 | 
| 101 | (Dict)   {"verbose":true,"count":150,"file":"example.sh"}
 | 
| 102 | 
 | 
| 103 | ## END
 | 
| 104 | 
 | 
| 105 | #### Basic help message
 | 
| 106 | 
 | 
| 107 | source $LIB_YSH/args.ysh
 | 
| 108 | 
 | 
| 109 | parser (&spec) {
 | 
| 110 |   # TODO: implement description, prog and help message
 | 
| 111 |   description '''
 | 
| 112 |      Reference Implementation
 | 
| 113 |   '''
 | 
| 114 |   prog "program-name"
 | 
| 115 | 
 | 
| 116 |   arg -v --verbose (Bool, help = "Verbose")
 | 
| 117 |   arg src
 | 
| 118 |   arg dst
 | 
| 119 | }
 | 
| 120 | var argv = ['-h', 'src', 'dst']
 | 
| 121 | 
 | 
| 122 | # Help
 | 
| 123 | var args = parseArgs(spec, argv)
 | 
| 124 | 
 | 
| 125 | ## STDOUT:
 | 
| 126 | usage: program-name [-h] [-v] src dst
 | 
| 127 | 
 | 
| 128 | Reference Implementation
 | 
| 129 | 
 | 
| 130 | positional arguments:
 | 
| 131 |  src
 | 
| 132 |  dst
 | 
| 133 | 
 | 
| 134 | options:
 | 
| 135 |  -h, --help           show this help message and exit
 | 
| 136 |  -v, --verbose        Verbose
 | 
| 137 | ## END
 | 
| 138 | 
 | 
| 139 | #### Compare parseArgs() vs Python argparse
 | 
| 140 | 
 | 
| 141 | source $LIB_YSH/args.ysh
 | 
| 142 | 
 | 
| 143 | var spec = {
 | 
| 144 |   flags: [
 | 
| 145 |     {short: '-v', long: '--verbose', name: 'verbose', type: null, default: '', help: 'Enable verbose logging'},
 | 
| 146 |     {short: '-c', long: '--count', name: 'count', type: 'int', default: 80, help: 'Maximum line length'},
 | 
| 147 |   ],
 | 
| 148 |   args: [
 | 
| 149 |     {name: 'file', type: 'str', help: 'File to check line lengths of'}
 | 
| 150 |   ],
 | 
| 151 |   rest: null,
 | 
| 152 | }
 | 
| 153 | 
 | 
| 154 | var argsCases = [
 | 
| 155 |   :| -v --count 120 example.sh |,
 | 
| 156 |   :| -v --count 120 example.sh -v |,  # duplicate flags are ignored
 | 
| 157 |   :| -v --count 120 example.sh -v --count 150 |,  # the last duplicate has precedence
 | 
| 158 | ]
 | 
| 159 | 
 | 
| 160 | var argparse_py = '''
 | 
| 161 | import argparse
 | 
| 162 | import sys
 | 
| 163 | 
 | 
| 164 | spec = argparse.ArgumentParser()
 | 
| 165 | spec.add_argument("filename")
 | 
| 166 | spec.add_argument("-c", "--count")
 | 
| 167 | spec.add_argument("-v", "--verbose",
 | 
| 168 |                   action="store_true")
 | 
| 169 | 
 | 
| 170 | result = spec.parse_args(sys.argv[1:])
 | 
| 171 | print(result)
 | 
| 172 | '''
 | 
| 173 | 
 | 
| 174 | for args in (argsCases) {
 | 
| 175 |   var args_str = args->join(" ")
 | 
| 176 |   echo "----------  $args_str  ----------"
 | 
| 177 |   echo "\$ bin/ysh example.sh $args_str"
 | 
| 178 |   pp line (parseArgs(spec, args))
 | 
| 179 | 
 | 
| 180 |   echo
 | 
| 181 |   echo "\$ python3 example.py $args_str"
 | 
| 182 |   python3 -c $argparse_py @args
 | 
| 183 | 
 | 
| 184 |   echo
 | 
| 185 | }
 | 
| 186 | ## STDOUT:
 | 
| 187 | ----------  -v --count 120 example.sh  ----------
 | 
| 188 | $ bin/ysh example.sh -v --count 120 example.sh
 | 
| 189 | (Dict)   {"verbose":true,"count":120,"file":"example.sh"}
 | 
| 190 | 
 | 
| 191 | $ python3 example.py -v --count 120 example.sh
 | 
| 192 | Namespace(filename='example.sh', count='120', verbose=True)
 | 
| 193 | 
 | 
| 194 | ----------  -v --count 120 example.sh -v  ----------
 | 
| 195 | $ bin/ysh example.sh -v --count 120 example.sh -v
 | 
| 196 | (Dict)   {"verbose":true,"count":120,"file":"example.sh"}
 | 
| 197 | 
 | 
| 198 | $ python3 example.py -v --count 120 example.sh -v
 | 
| 199 | Namespace(filename='example.sh', count='120', verbose=True)
 | 
| 200 | 
 | 
| 201 | ----------  -v --count 120 example.sh -v --count 150  ----------
 | 
| 202 | $ bin/ysh example.sh -v --count 120 example.sh -v --count 150
 | 
| 203 | (Dict)   {"verbose":true,"count":150,"file":"example.sh"}
 | 
| 204 | 
 | 
| 205 | $ python3 example.py -v --count 120 example.sh -v --count 150
 | 
| 206 | Namespace(filename='example.sh', count='150', verbose=True)
 | 
| 207 | 
 | 
| 208 | ## END
 | 
| 209 | 
 | 
| 210 | #### Define spec and print it
 | 
| 211 | 
 | 
| 212 | source $LIB_YSH/args.ysh
 | 
| 213 | 
 | 
| 214 | parser (&spec) {
 | 
| 215 |   flag -v --verbose ('bool')
 | 
| 216 |   arg src
 | 
| 217 |   arg dst
 | 
| 218 | 
 | 
| 219 |   rest more  # allow more args
 | 
| 220 | }
 | 
| 221 | 
 | 
| 222 | json write (spec)
 | 
| 223 | ## STDOUT:
 | 
| 224 | {
 | 
| 225 |   "flags": [
 | 
| 226 |     {
 | 
| 227 |       "short": "-v",
 | 
| 228 |       "long": "--verbose",
 | 
| 229 |       "name": "verbose",
 | 
| 230 |       "type": "bool",
 | 
| 231 |       "default": false,
 | 
| 232 |       "help": null
 | 
| 233 |     }
 | 
| 234 |   ],
 | 
| 235 |   "args": [
 | 
| 236 |     {
 | 
| 237 |       "name": "src",
 | 
| 238 |       "help": null
 | 
| 239 |     },
 | 
| 240 |     {
 | 
| 241 |       "name": "dst",
 | 
| 242 |       "help": null
 | 
| 243 |     }
 | 
| 244 |   ],
 | 
| 245 |   "rest": "more"
 | 
| 246 | }
 | 
| 247 | ## END
 | 
| 248 | 
 | 
| 249 | #### Default values
 | 
| 250 | source $LIB_YSH/args.ysh
 | 
| 251 | 
 | 
| 252 | parser (&spec) {
 | 
| 253 |   flag -S --sanitize ('bool', default=false)
 | 
| 254 |   flag -v --verbose ('bool', default=false)
 | 
| 255 |   flag -P --max-procs ('int')  # Will set to null (the default default)
 | 
| 256 | }
 | 
| 257 | 
 | 
| 258 | var args = parseArgs(spec, [])
 | 
| 259 | 
 | 
| 260 | pp line (args)
 | 
| 261 | ## STDOUT:
 | 
| 262 | (Dict)   {"sanitize":false,"verbose":false,"max-procs":null}
 | 
| 263 | ## END
 | 
| 264 | 
 | 
| 265 | #### Duplicate argument/flag names
 | 
| 266 | source $LIB_YSH/args.ysh
 | 
| 267 | 
 | 
| 268 | try {
 | 
| 269 |   parser (&spec) {
 | 
| 270 |     flag -n --name
 | 
| 271 |     flag -N --name
 | 
| 272 |   }
 | 
| 273 | }
 | 
| 274 | echo status=$_status
 | 
| 275 | 
 | 
| 276 | try {
 | 
| 277 |   parser (&spec) {
 | 
| 278 |     flag -n --name
 | 
| 279 |     arg name
 | 
| 280 |   }
 | 
| 281 | }
 | 
| 282 | echo status=$_status
 | 
| 283 | 
 | 
| 284 | try {
 | 
| 285 |   parser (&spec) {
 | 
| 286 |     arg name
 | 
| 287 |     flag -o --other
 | 
| 288 |     arg name
 | 
| 289 |   }
 | 
| 290 | }
 | 
| 291 | echo status=$_status
 | 
| 292 | ## STDOUT:
 | 
| 293 | status=3
 | 
| 294 | status=3
 | 
| 295 | status=3
 | 
| 296 | ## END
 | 
| 297 | 
 | 
| 298 | #### Error cases
 | 
| 299 | source $LIB_YSH/args.ysh
 | 
| 300 | 
 | 
| 301 | parser (&spec) {
 | 
| 302 |   flag -v --verbose
 | 
| 303 |   flag -n --num ('int', required=true)
 | 
| 304 | 
 | 
| 305 |   arg action
 | 
| 306 |   arg other (required=false)
 | 
| 307 | }
 | 
| 308 | 
 | 
| 309 | try { call parseArgs(spec, :| -n 10 action other extra |) }
 | 
| 310 | echo status=$_status
 | 
| 311 | 
 | 
| 312 | try { call parseArgs(spec, :| -n |) }
 | 
| 313 | echo status=$_status
 | 
| 314 | 
 | 
| 315 | try { call parseArgs(spec, :| -n -v |) }
 | 
| 316 | echo status=$_status
 | 
| 317 | 
 | 
| 318 | try { = parseArgs(spec, :| -n 10 |) }
 | 
| 319 | echo status=$_status
 | 
| 320 | 
 | 
| 321 | try { call parseArgs(spec, :| -v action |) }
 | 
| 322 | echo status=$_status
 | 
| 323 | 
 | 
| 324 | try { call parseArgs(spec, :| --unknown |) }
 | 
| 325 | echo status=$_status
 | 
| 326 | ## STDOUT:
 | 
| 327 | status=2
 | 
| 328 | status=2
 | 
| 329 | status=2
 | 
| 330 | status=2
 | 
| 331 | status=2
 | 
| 332 | status=2
 | 
| 333 | ## END
 |