argparse-0.5.0/0000755000175000017500000000000012632007121012344 5ustar vsevavsevaargparse-0.5.0/.luacov0000644000175000017500000000006312632007121013635 0ustar vsevavsevareturn {modules = {argparse = "src/argparse.lua"}} argparse-0.5.0/LICENSE0000644000175000017500000000210312632007121013345 0ustar vsevavsevaThe MIT License (MIT) Copyright (c) 2013 - 2015 Peter Melnichenko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. argparse-0.5.0/README.md0000644000175000017500000000562512632007121013633 0ustar vsevavseva# argparse [![Build Status](https://travis-ci.org/mpeterv/argparse.png?branch=master)](https://travis-ci.org/mpeterv/argparse) [![Coverage Status](https://coveralls.io/repos/mpeterv/argparse/badge.svg?branch=master&service=github)](https://coveralls.io/github/mpeterv/argparse?branch=master) Argparse is a feature-rich command line parser for Lua inspired by argparse for Python. Argparse supports positional arguments, options, flags, optional arguments, subcommands and more. Argparse automatically generates usage, help and error messages. Simple example: ```lua -- script.lua local argparse = require "argparse" local parser = argparse("script", "An example.") parser:argument("input", "Input file.") parser:option("-o --output", "Output file.", "a.out") parser:option("-I --include", "Include locations."):count("*") local args = parser:parse() print(args) -- Assuming print is patched to handle tables nicely. ``` ```bash $ lua script.lua foo ``` ```lua { input = "foo", output = "a.out", include = {} } ``` ```bash $ lua script.lua foo -I/usr/local/include -Isrc -o bar ``` ```lua { input = "foo", output = "bar", include = {"/usr/local/include", "src"} } ``` ```bash $ lua script.lua foo bar ``` ``` Usage: script [-o ] [-I ] [-h] Error: too many arguments ``` ```bash $ lua script.lua --help ``` ``` Usage: script [-o ] [-I ] [-h] An example. Arguments: input Input file. Options: -o , --output Output file. (default: a.out) -I , --include Include locations. -h, --help Show this help message and exit. ``` ```bash $ lua script.lua foo --outptu=bar ``` ``` Usage: script [-o ] [-I ] [-h] Error: unknown option '--outptu' Did you mean '--output'? ``` ## Contents * [Installation](#installation) * [Tutorial](#tutorial) * [Testing](#testing) * [License](#license) ## Installation ### Using LuaRocks Installing argparse using [LuaRocks](http://luarocks.org) is simple: ```bash $ luarocks install argparse ``` ### Without LuaRocks Download `src/argparse.lua` file and put it into the directory for Lua libraries or your working directory. ## Tutorial The tutorial is available [online](http://argparse.readthedocs.org). If argparse has been installed using LuaRocks 2.1.2 or later, it can be viewed using `luarocks doc argparse` command. Tutorial HTML files can be built using [Sphinx](http://sphinx-doc.org/): `sphinx-build docsrc doc`, the files will be found inside `doc/`. ## Testing argparse comes with a testing suite located in `spec` directory. [busted](http://olivinelabs.com/busted/) is required for testing, it can be installed using LuaRocks. Run the tests using `busted` command from the argparse folder. ## License argparse is licensed under the same terms as Lua itself (MIT license). argparse-0.5.0/argparse-scm-1.rockspec0000644000175000017500000000112712632007121016622 0ustar vsevavsevapackage = "argparse" version = "scm-1" source = { url = "git://github.com/mpeterv/argparse.git" } description = { summary = "A feature-rich command-line argument parser", detailed = "argparse supports positional arguments, options, flags, optional arguments, subcommands and more. argparse automatically generates usage, help and error messages. ", homepage = "https://github.com/mpeterv/argparse", license = "MIT/X11" } dependencies = { "lua >= 5.1, < 5.4" } build = { type = "builtin", modules = { argparse = "src/argparse.lua" }, copy_directories = {"spec"} } argparse-0.5.0/spec/0000755000175000017500000000000012632007121013276 5ustar vsevavsevaargparse-0.5.0/spec/arguments_spec.lua0000644000175000017500000001261312632007121017023 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to positional arguments", function() describe("passing correct arguments", function() it("handles empty parser correctly", function() local parser = Parser() local args = parser:parse({}) assert.same({}, args) end) it("handles one argument correctly", function() local parser = Parser() parser:argument "foo" local args = parser:parse({"bar"}) assert.same({foo = "bar"}, args) end) it("handles optional argument correctly", function() local parser = Parser() parser:argument "foo" :args "?" local args = parser:parse({"bar"}) assert.same({foo = "bar"}, args) end) it("handles several arguments correctly", function() local parser = Parser() parser:argument "foo1" parser:argument "foo2" local args = parser:parse({"bar", "baz"}) assert.same({foo1 = "bar", foo2 = "baz"}, args) end) it("handles multi-argument correctly", function() local parser = Parser() parser:argument "foo" { args = "*" } local args = parser:parse({"bar", "baz", "qu"}) assert.same({foo = {"bar", "baz", "qu"}}, args) end) it("handles restrained multi-argument correctly", function() local parser = Parser() parser:argument "foo" { args = "2-4" } local args = parser:parse({"bar", "baz"}) assert.same({foo = {"bar", "baz"}}, args) end) it("handles several multi-arguments correctly", function() local parser = Parser() parser:argument "foo1" { args = "1-2" } parser:argument "foo2" { args = "*" } local args = parser:parse({"bar"}) assert.same({foo1 = {"bar"}, foo2 = {}}, args) args = parser:parse({"bar", "baz", "qu"}) assert.same({foo1 = {"bar", "baz"}, foo2 = {"qu"}}, args) end) it("handles hyphen correctly", function() local parser = Parser() parser:argument "foo" local args = parser:parse({"-"}) assert.same({foo = "-"}, args) end) it("handles double hyphen correctly", function() local parser = Parser() parser:argument "foo" local args = parser:parse({"--", "-q"}) assert.same({foo = "-q"}, args) end) end) describe("passing incorrect arguments", function() it("handles extra arguments with empty parser correctly", function() local parser = Parser() assert.has_error(function() parser:parse{"foo"} end, "too many arguments") end) it("handles extra arguments with one argument correctly", function() local parser = Parser() parser:argument "foo" assert.has_error(function() parser:parse{"bar", "baz"} end, "too many arguments") end) it("handles too few arguments with one argument correctly", function() local parser = Parser() parser:argument "foo" assert.has_error(function() parser:parse{} end, "missing argument 'foo'") end) it("handles extra arguments with several arguments correctly", function() local parser = Parser() parser:argument "foo1" parser:argument "foo2" assert.has_error(function() parser:parse{"bar", "baz", "qu"} end, "too many arguments") end) it("handles too few arguments with several arguments correctly", function() local parser = Parser() parser:argument "foo1" parser:argument "foo2" assert.has_error(function() parser:parse{"bar"} end, "missing argument 'foo2'") end) it("handles too few arguments with multi-argument correctly", function() local parser = Parser() parser:argument "foo" { args = "+" } assert.has_error(function() parser:parse{} end, "missing argument 'foo'") end) it("handles too many arguments with multi-argument correctly", function() local parser = Parser() parser:argument "foo" { args = "2-4" } assert.has_error(function() parser:parse{"foo", "bar", "baz", "qu", "quu"} end, "too many arguments") end) it("handles too few arguments with multi-argument correctly", function() local parser = Parser() parser:argument "foo" { args = "2-4" } assert.has_error(function() parser:parse{"foo"} end, "argument 'foo' requires at least 2 arguments") end) it("handles too many arguments with several multi-arguments correctly", function() local parser = Parser() parser:argument "foo1" { args = "1-2" } parser:argument "foo2" { args = "0-1" } assert.has_error(function() parser:parse{"foo", "bar", "baz", "qu"} end, "too many arguments") end) it("handles too few arguments with several multi-arguments correctly", function() local parser = Parser() parser:argument "foo1" { args = "1-2" } parser:argument "foo2" { args = "*" } assert.has_error(function() parser:parse{} end, "missing argument 'foo1'") end) end) end) argparse-0.5.0/spec/usage_spec.lua0000644000175000017500000001237112632007121016123 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to usage message generation", function() it("creates correct usage message for empty parser", function() local parser = Parser "foo" :add_help(false) assert.equal(parser:get_usage(), "Usage: foo") end) it("creates correct usage message for arguments", function() local parser = Parser "foo" :add_help(false) parser:argument "first" parser:argument "second-and-third" :args "2" parser:argument "maybe-fourth" :args "?" parser:argument "others" :args "*" assert.equal([[ Usage: foo [] [] ...]], parser:get_usage() ) end) it("creates correct usage message for options", function() local parser = Parser "foo" :add_help(false) parser:flag "-q" "--quiet" parser:option "--from" :count "1" :target "server" parser:option "--config" assert.equal( [=[Usage: foo [-q] --from [--config ]]=], parser:get_usage() ) end) it("creates correct usage message for options with variable argument count", function() local parser = Parser "foo" :add_help(false) parser:argument "files" :args "+" parser:flag "-q" "--quiet" parser:option "--globals" :args "*" assert.equal( [=[Usage: foo [-q] [] ... [--globals [] ...]]=], parser:get_usage() ) end) it("creates correct usage message for arguments with default value", function() local parser = Parser "foo" :add_help(false) parser:argument "input" :default "a.in" parser:argument "pair" :args(2) :default "foo" parser:argument "pair2" :args(2) :default "bar" :defmode "arg" assert.equal( [=[Usage: foo [] [ ] [] []]=], parser:get_usage() ) end) it("creates correct usage message for options with default value", function() local parser = Parser "foo" :add_help(false) parser:option "-f" "--from" :default "there" parser:option "-o" "--output" :default "a.out" :defmode "arg" assert.equal( [=[Usage: foo [-f ] [-o []]]=], parser:get_usage() ) end) it("creates correct usage message for commands", function() local parser = Parser "foo" :add_help(false) parser:flag "-q" "--quiet" local run = parser:command "run" run:option "--where" assert.equal( [=[Usage: foo [-q] ...]=], parser:get_usage() ) end) it("creates correct usage message for subcommands", function() local parser = Parser "foo" :add_help(false) parser:flag "-q" "--quiet" local run = parser:command "run" :add_help(false) run:option "--where" assert.equal( [=[Usage: foo run [--where ]]=], run:get_usage() ) end) it("usage messages for commands are correct after several invocations", function() local parser = Parser "foo" :add_help(false) parser:flag "-q" "--quiet" local run = parser:command "run" :add_help(false) run:option "--where" parser:parse{"run"} parser:parse{"run"} assert.equal( [=[Usage: foo run [--where ]]=], run:get_usage() ) end) describe("usage generation can be customized", function() it("uses message provided by user", function() local parser = Parser "foo" :usage "Usage: obvious" :add_help(false) parser:flag "-q" "--quiet" assert.equal( [=[Usage: obvious]=], parser:get_usage() ) end) it("uses argnames provided by user", function() local parser = Parser "foo" :add_help(false) parser:argument "inputs" :args "1-2" :argname "" assert.equal( [=[Usage: foo []]=], parser:get_usage() ) end) it("uses array of argnames provided by user", function() local parser = Parser "foo" :add_help(false) parser:option "--pair" :args(2) :count "*" :argname{"", ""} assert.equal( [=[Usage: foo [--pair ]]=], parser:get_usage() ) end) end) it("creates correct usage message for mutexes", function() local parser = Parser "foo" :add_help(false) parser:mutex( parser:flag "-q" "--quiet", parser:flag "-v" "--verbose", parser:flag "-i" "--interactive" ) parser:mutex( parser:flag "-l" "--local", parser:option "-f" "--from" ) parser:option "--yet-another-option" assert.equal([=[ Usage: foo ([-q] | [-v] | [-i]) ([-l] | [-f ]) [--yet-another-option ]]=], parser:get_usage() ) end) end) argparse-0.5.0/spec/options_spec.lua0000644000175000017500000002570412632007121016516 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to options", function() describe("passing correct options", function() it("handles no options passed correctly", function() local parser = Parser() parser:option "-s" "--server" local args = parser:parse({}) assert.same({}, args) end) it("handles one option correctly", function() local parser = Parser() parser:option "-s" "--server" local args = parser:parse({"--server", "foo"}) assert.same({server = "foo"}, args) end) it("normalizes default target", function() local parser = Parser() parser:option "--from-server" local args = parser:parse({"--from-server", "foo"}) assert.same({from_server = "foo"}, args) end) it("handles non-standard charset", function() local parser = Parser() parser:option "/s" parser:flag "/?" local args = parser:parse{"/s", "foo", "/?"} assert.same({s = "foo", ["?"] = true}, args) end) it("handles GNU-style long options", function() local parser = Parser() parser:option "-s" "--server" local args = parser:parse({"--server=foo"}) assert.same({server = "foo"}, args) end) it("handles GNU-style long options even when it could take more arguments", function() local parser = Parser() parser:option "-s" "--server" { args = "*" } local args = parser:parse({"--server=foo"}) assert.same({server = {"foo"}}, args) end) it("handles GNU-style long options for multi-argument options", function() local parser = Parser() parser:option "-s" "--server" { args = "1-2" } local args = parser:parse({"--server=foo", "bar"}) assert.same({server = {"foo", "bar"}}, args) end) it("handles short option correclty", function() local parser = Parser() parser:option "-s" "--server" local args = parser:parse({"-s", "foo"}) assert.same({server = "foo"}, args) end) it("handles flag correclty", function() local parser = Parser() parser:flag "-q" "--quiet" local args = parser:parse({"--quiet"}) assert.same({quiet = true}, args) args = parser:parse({}) assert.same({}, args) end) it("handles combined flags correclty", function() local parser = Parser() parser:flag "-q" "--quiet" parser:flag "-f" "--fast" local args = parser:parse({"-qf"}) assert.same({quiet = true, fast = true}, args) end) it("handles short options without space between option and argument", function() local parser = Parser() parser:option "-s" "--server" local args = parser:parse({"-sfoo"}) assert.same({server = "foo"}, args) end) it("handles flags combined with short option correclty", function() local parser = Parser() parser:flag "-q" "--quiet" parser:option "-s" "--server" local args = parser:parse({"-qsfoo"}) assert.same({quiet = true, server = "foo"}, args) end) it("interprets extra option arguments as positional arguments", function() local parser = Parser() parser:argument "input" :args "2+" parser:option "-s" "--server" local args = parser:parse{"foo", "-sFROM", "bar"} assert.same({input = {"foo", "bar"}, server = "FROM"}, args) end) it("does not interpret extra option arguments as other option's arguments", function() local parser = Parser() parser:argument "output" parser:option "--input" :args "+" parser:option "-s" "--server" local args = parser:parse{"--input", "foo", "-sFROM", "bar"} assert.same({input = {"foo"}, server = "FROM", output = "bar"}, args) end) it("does not pass arguments to options after double hyphen", function() local parser = Parser() parser:argument "input" :args "?" parser:option "--exclude" :args "*" local args = parser:parse{"--exclude", "--", "foo"} assert.same({input = "foo", exclude = {}}, args) end) it("does not interpret options if disabled", function() local parser = Parser() parser:handle_options(false) parser:argument "input" :args "*" parser:option "-f" "--foo" :args "*" local args = parser:parse{"bar", "-f", "--foo" , "bar"} assert.same({input = {"bar", "-f", "--foo" , "bar"}}, args) end) describe("Special chars set", function() it("handles windows-style options", function() local parser = Parser() :add_help(false) parser:option "\\I" :count "*" :target "include" local args = parser:parse{"\\I", "src", "\\I", "misc"} assert.same({include = {"src", "misc"}}, args) end) it("corrects charset in commands", function() local parser = Parser "name" :add_help(false) parser:flag "-v" "--verbose" :count "*" parser:command "deep" :add_help(false) :option "/s" local args = parser:parse{"-v", "deep", "/s", "foo", "-vv"} assert.same({verbose = 3, deep = true, s = "foo"}, args) end) end) describe("Options with optional argument", function() it("handles emptiness correctly", function() local parser = Parser() parser:option("-p --password", "Secure password for special security", nil, nil, "?") local args = parser:parse({}) assert.same({}, args) end) it("handles option without argument correctly", function() local parser = Parser() parser:option "-p" "--password" { args = "?" } local args = parser:parse({"-p"}) assert.same({password = {}}, args) end) it("handles option with argument correctly", function() local parser = Parser() parser:option "-p" "--password" { args = "?" } local args = parser:parse({"-p", "password"}) assert.same({password = {"password"}}, args) end) end) it("handles multi-argument options correctly", function() local parser = Parser() parser:option "--pair" { args = 2 } local args = parser:parse({"--pair", "Alice", "Bob"}) assert.same({pair = {"Alice", "Bob"}}, args) end) describe("Multi-count options", function() it("handles multi-count option correctly", function() local parser = Parser() parser:option "-e" "--exclude" { count = "*" } local args = parser:parse({"-efoo", "--exclude=bar", "-e", "baz"}) assert.same({exclude = {"foo", "bar", "baz"}}, args) end) it("handles not used multi-count option correctly", function() local parser = Parser() parser:option "-e" "--exclude" { count = "*" } local args = parser:parse({}) assert.same({exclude = {}}, args) end) it("handles multi-count multi-argument option correctly", function() local parser = Parser() parser:option "-e" "--exclude" { count = "*", args = 2 } local args = parser:parse({"-e", "Alice", "Bob", "-e", "Emma", "Jacob"}) assert.same({exclude = {{"Alice", "Bob"}, {"Emma", "Jacob"}}}, args) end) it("handles multi-count flag correctly", function() local parser = Parser() parser:flag "-q" "--quiet" { count = "*" } local args = parser:parse({"-qq", "--quiet"}) assert.same({quiet = 3}, args) end) it("overwrites old invocations", function() local parser = Parser() parser:option "-u" "--user" { count = "0-2" } local args = parser:parse({"-uAlice", "--user=Bob", "--user", "John"}) assert.same({user = {"Bob", "John"}}, args) end) it("handles not used multi-count flag correctly", function() local parser = Parser() parser:flag "-q" "--quiet" { count = "*" } local args = parser:parse({}) assert.same({quiet = 0}, args) end) end) end) describe("passing incorrect options", function() it("handles lack of required argument correctly", function() local parser = Parser() parser:option "-s" "--server" assert.has_error(function() parser:parse{"--server"} end, "option '--server' requires an argument") assert.has_error(function() parser:parse{"-s"} end, "option '-s' requires an argument") end) it("handles unknown options correctly", function() local parser = Parser() :add_help(false) parser:option "--option" assert.has_error(function() parser:parse{"--server"} end, "unknown option '--server'") assert.has_error(function() parser:parse{"--server=localhost"} end, "unknown option '--server'") assert.has_error(function() parser:parse{"-s"} end, "unknown option '-s'") assert.has_error(function() parser:parse{"-slocalhost"} end, "unknown option '-s'") end) it("handles too many arguments correctly", function() local parser = Parser() parser:option "-s" "--server" assert.has_error(function() parser:parse{"-sfoo", "bar"} end, "too many arguments") end) it("doesn't accept GNU-like long options when it doesn't need arguments", function() local parser = Parser() parser:flag "-q" "--quiet" assert.has_error(function() parser:parse{"--quiet=very_quiet"} end, "option '--quiet' does not take arguments") end) it("handles too many invocations correctly", function() local parser = Parser() parser:flag "-q" "--quiet" { count = 1, overwrite = false } assert.has_error(function() parser:parse{"-qq"} end, "option '-q' must be used 1 time") end) it("handles too few invocations correctly", function() local parser = Parser() parser:option "-f" "--foo" { count = "3-4" } assert.has_error(function() parser:parse{"-fFOO", "--foo=BAR"} end, "option '--foo' must be used at least 3 times") assert.has_error(function() parser:parse{} end, "missing option '-f'") end) end) end) argparse-0.5.0/spec/mutex_spec.lua0000644000175000017500000000504712632007121016163 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to mutexes", function() it("handles mutex correctly", function() local parser = Parser() parser:mutex( parser:flag "-q" "--quiet" :description "Supress logging. ", parser:flag "-v" "--verbose" :description "Print additional debug information. " ) local args = parser:parse{"-q"} assert.same({quiet = true}, args) args = parser:parse{"-v"} assert.same({verbose = true}, args) args = parser:parse{} assert.same({}, args) end) it("handles mutex with default value", function() local parser = Parser() parser:mutex( parser:flag "-q" "--quiet", parser:option "-o" "--output" :default "a.out" ) local args = parser:parse{"-q"} assert.same({quiet = true, output = "a.out"}, args) end) it("raises an error if mutex is broken", function() local parser = Parser() parser:mutex( parser:flag "-q" "--quiet" :description "Supress logging. ", parser:flag "-v" "--verbose" :description "Print additional debug information. " ) assert.has_error(function() parser:parse{"-qv"} end, "option '-v' can not be used together with option '-q'") assert.has_error(function() parser:parse{"-v", "--quiet"} end, "option '--quiet' can not be used together with option '-v'") end) it("handles multiple mutexes", function() local parser = Parser() parser:mutex( parser:flag "-q" "--quiet", parser:flag "-v" "--verbose" ) parser:mutex( parser:flag "-l" "--local", parser:option "-f" "--from" ) local args = parser:parse{"-qq", "-fTHERE"} assert.same({quiet = true, from = "THERE"}, args) args = parser:parse{"-vl"} assert.same({verbose = true, ["local"] = true}, args) end) it("handles mutexes in commands", function() local parser = Parser() parser:mutex( parser:flag "-q" "--quiet", parser:flag "-v" "--verbose" ) local install = parser:command "install" install:mutex( install:flag "-l" "--local", install:option "-f" "--from" ) local args = parser:parse{"install", "-l"} assert.same({install = true, ["local"] = true}, args) assert.has_error(function() parser:parse{"install", "-qlv"} end, "option '-v' can not be used together with option '-q'") end) end) argparse-0.5.0/spec/actions_spec.lua0000644000175000017500000001506012632007121016455 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("actions", function() it("for arguments are called", function() local parser = Parser() local foo parser:argument("foo"):action(function(_, _, passed_foo) foo = passed_foo end) local baz parser:argument("baz"):args("*"):action(function(_, _, passed_baz) baz = passed_baz end) parser:parse{"a"} assert.equals("a", foo) assert.same({}, baz) parser:parse{"b", "c"} assert.equals("b", foo) assert.same({"c"}, baz) parser:parse{"d", "e", "f"} assert.equals("d", foo) assert.same({"e", "f"}, baz) end) it("for options are called", function() local action1 = spy.new(function(_, _, arg) assert.equal("nowhere", arg) end) local expected_args = {"Alice", "Bob"} local action2 = spy.new(function(_, _, args) assert.same(expected_args, args) expected_args = {"Emma", "John"} end) local parser = Parser() parser:option "-f" "--from" { action = function(...) return action1(...) end } parser:option "-p" "--pair" { action = function(...) return action2(...) end, count = "*", args = 2 } parser:parse{"-fnowhere", "--pair", "Alice", "Bob", "-p", "Emma", "John"} assert.spy(action1).called(1) assert.spy(action2).called(2) end) it("for flags are called", function() local action1 = spy.new(function() end) local action2 = spy.new(function() end) local action3 = spy.new(function() end) local parser = Parser() parser:flag "-v" "--verbose" { action = function(...) return action1(...) end, count = "0-3" } parser:flag "-q" "--quiet" { action = function(...) return action2(...) end } parser:flag "-a" "--another-flag" { action = function(...) return action3(...) end } parser:parse{"-vv", "--quiet"} assert.spy(action1).called(2) assert.spy(action2).called(1) assert.spy(action3).called(0) end) it("for options allow custom storing of arguments", function() local parser = Parser() parser:option("-p --path"):action(function(result, target, argument) result[target] = (result[target] or ".") .. "/" .. argument end) local args = parser:parse{"-pfirst", "--path", "second", "--path=third"} assert.same({path = "./first/second/third"}, args) end) it("for options with several arguments allow custom storing of arguments", function() local parser = Parser() parser:option("-p --path"):args("*"):action(function(result, target, arguments) for _, argument in ipairs(arguments) do result[target] = (result[target] or ".") .. "/" .. argument end end) local args = parser:parse{"-p", "first", "second", "third"} assert.same({path = "./first/second/third"}, args) end) it("for options allow using strings as actions", function() local parser = Parser() parser:flag("--no-foo"):target("foo"):action("store_false") parser:flag("--no-bar"):target("bar"):action("store_false") parser:option("--things"):args("+"):count("*"):action("concat") local args = parser:parse{"--things", "a", "b", "--no-foo", "--things", "c", "d"} assert.same({foo = false, things = {"a", "b", "c", "d"}}, args) end) it("for options allow setting initial stored value", function() local parser = Parser() parser:flag("--no-foo"):target("foo"):action("store_false"):init(true) parser:flag("--no-bar"):target("bar"):action("store_false"):init(true) local args = parser:parse{"--no-foo"} assert.same({foo = false, bar = true}, args) end) it("'append' and 'concat' respect initial value", function() local parser = Parser() parser:option("-f"):count("*"):init(nil) parser:option("-g"):args("*"):count("*"):action("concat"):init(nil) local args = parser:parse{} assert.same({}, args) args = parser:parse{"-fabc", "-fdef", "-g"} assert.same({f = {"abc", "def"}, g = {}}, args) args = parser:parse{"-g", "abc", "def", "-g123", "-f123"} assert.same({f = {"123"}, g = {"abc", "def", "123"}}, args) end) it("for options allow setting initial stored value as non-string argument to default", function() local parser = Parser() parser:flag("--no-foo", "Foo the bar.", true):target("foo"):action("store_false") parser:flag("--no-bar", "Bar the foo.", true):target("bar"):action("store_false") local args = parser:parse{"--no-foo"} assert.same({foo = false, bar = true}, args) end) it("pass overwrite flag as the fourth argument", function() local parser = Parser() local overwrites = {} parser:flag("-f"):count("0-2"):action(function(_, _, _, overwrite) table.insert(overwrites, overwrite) end) parser:parse{"-ffff"} assert.same({false, false, true, true}, overwrites) end) it("pass user-defined target", function() local parser = Parser() local target parser:flag("-f"):target("force"):action(function(_, passed_target) target = passed_target end) parser:parse{"-f"} assert.equals("force", target) end) it("apply convert before passing arguments", function() local parser = Parser() local numbers = {} parser:option("-n"):convert(tonumber):default("0"):defmode("a"):action(function(_, _, n) table.insert(numbers, n) end) parser:parse{"-n", "-n1", "-n", "-n", "2"} assert.same({0, 1, 0, 2}, numbers) end) it("for parser are called", function() local parser = Parser() parser:flag("-f"):count("0-3") local args parser:action(function(passed_args) args = passed_args end) parser:parse{"-ff"} assert.same({f = 2}, args) end) it("for commands are called in reverse order", function() local args = {} local parser = Parser():action(function(passed_args) args[1] = passed_args args.last = 1 end) parser:flag("-f"):count("0-3") local foo = parser:command("foo"):action(function(passed_args, name) assert.equals("foo", name) args[2] = passed_args args.last = 2 end) foo:flag("-g") parser:parse{"foo", "-f", "-g", "-f"} assert.same({ last = 1, {foo = true, f = 2, g = true}, {foo = true, f = 2, g = true} }, args) end) end) argparse-0.5.0/spec/tip_spec.lua0000644000175000017500000000607212632007121015614 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to tips", function() describe("provides tips when data is too long", function() it("for options", function() local parser = Parser() parser:option "-q" "--quiet" assert.has_error(function() parser:parse{"--quiett=true"} end, "unknown option '--quiett'\nDid you mean '--quiet'?") end) it("for commands", function() local parser = Parser "name" parser:command "install" assert.has_error(function() parser:parse{"installq"} end, "unknown command 'installq'\nDid you mean 'install'?") end) end) describe("provides tips when data is too short", function() it("for options", function() local parser = Parser() parser:option "-q" "--quiet" assert.has_error(function() parser:parse{"--quet=true"} end, "unknown option '--quet'\nDid you mean '--quiet'?") end) it("for commands", function() local parser = Parser "name" parser:command "install" assert.has_error(function() parser:parse{"nstall"} end, "unknown command 'nstall'\nDid you mean 'install'?") end) end) describe("provides tips on substitution", function() it("for options", function() local parser = Parser() parser:option "-q" "--quiet" assert.has_error(function() parser:parse{"--qriet=true"} end, "unknown option '--qriet'\nDid you mean '--quiet'?") end) it("for commands", function() local parser = Parser "name" parser:command "install" assert.has_error(function() parser:parse{"inntall"} end, "unknown command 'inntall'\nDid you mean 'install'?") end) end) describe("provides tips on transpositions", function() it("for options", function() local parser = Parser() parser:option "-q" "--quiet" assert.has_error(function() parser:parse{"--queit=true"} end, "unknown option '--queit'\nDid you mean '--quiet'?") end) it("for commands", function() local parser = Parser "name" parser:command "install" assert.has_error(function() parser:parse{"isntall"} end, "unknown command 'isntall'\nDid you mean 'install'?") end) end) describe("provides multiple tips", function() it("for options", function() local parser = Parser() parser:option "-q" "--quiet" parser:option "--quick" assert.has_error(function() parser:parse{"--quiec=true"} end, "unknown option '--quiec'\nDid you mean one of these: '--quick' '--quiet'?") end) it("for commands", function() local parser = Parser "name" parser:command "install" parser:command "instant" assert.has_error(function() parser:parse{"instanl"} end, "unknown command 'instanl'\nDid you mean one of these: 'install' 'instant'?") end) end) end) argparse-0.5.0/spec/convert_spec.lua0000644000175000017500000000340212632007121016472 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to converters", function() it("converts arguments", function() local parser = Parser() parser:argument "numbers" { convert = tonumber, args = "+" } local args = parser:parse{"1", "2", "500"} assert.same({numbers = {1, 2, 500}}, args) end) it("converts arguments using mapping", function() local choice = { foo = 1, bar = 2 } local parser = Parser() parser:argument "choice" { convert = choice, args = "+" } local args = parser:parse{"foo", "bar"} assert.same({choice = {1, 2}}, args) end) it("accepts false", function() local function toboolean(x) if x == "true" then return true elseif x == "false" then return false end end local parser = Parser() parser:argument "booleans" { convert = toboolean, args = "+" } local args = parser:parse{"true", "false"} assert.same({booleans = {true, false}}, args) end) it("raises an error when it can't convert", function() local parser = Parser() parser:argument "numbers" { convert = tonumber, args = "+" } assert.has_error(function() parser:parse{"foo", "bar", "baz"} end, "malformed argument 'foo'") end) it("second return value is used as error message", function() local parser = Parser() parser:argument "numbers" { convert = function(x) return tonumber(x), x .. " is not a number" end } assert.has_error(function() parser:parse{"foo"} end, "foo is not a number") end) end) argparse-0.5.0/spec/pparse_spec.lua0000644000175000017500000000163312632007121016310 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to :pparse()", function() it("returns true and result on success", function() local parser = Parser() parser:option "-s --server" local ok, args = parser:pparse{"--server", "foo"} assert.is_true(ok) assert.same({server = "foo"}, args) end) it("returns false and bare error message on failure", function() local parser = Parser() parser:argument "foo" local ok, errmsg = parser:pparse{} assert.is_false(ok) assert.equal("missing argument 'foo'", errmsg) end) it("rethrows errors from callbacks", function() local parser = Parser() parser:flag "--foo" :action(function() error("some error message") end) assert.error_matches(function() parser:pparse{"--foo"} end, "some error message") end) end) argparse-0.5.0/spec/help_spec.lua0000644000175000017500000001144112632007121015744 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to help message generation", function() it("creates correct help message for empty parser", function() local parser = Parser "foo" assert.equal([[ Usage: foo [-h] Options: -h, --help Show this help message and exit.]], parser:get_help()) end) it("does not create extra help options when :prepare is called several times", function() local parser = Parser "foo" assert.equal([[ Usage: foo [-h] Options: -h, --help Show this help message and exit.]], parser:get_help()) end) it("uses custom help option ", function() local parser = Parser "foo" :add_help "/?" assert.equal([[ Usage: foo [/?] Options: /? Show this help message and exit.]], parser:get_help()) end) it("uses description and epilog", function() local parser = Parser("foo", "A description.", "An epilog.") assert.equal([[ Usage: foo [-h] A description. Options: -h, --help Show this help message and exit. An epilog.]], parser:get_help()) end) it("creates correct help message for arguments", function() local parser = Parser "foo" parser:argument "first" parser:argument "second-and-third" :args "2" parser:argument "maybe-fourth" :args "?" parser:argument("others", "Optional. ") :args "*" assert.equal([[ Usage: foo [-h] [] [] ... Arguments: first second-and-third maybe-fourth others Optional. Options: -h, --help Show this help message and exit.]], parser:get_help()) end) it("creates correct help message for options", function() local parser = Parser "foo" parser:flag "-q" "--quiet" parser:option "--from" :count "1" :target "server" parser:option "--config" assert.equal([[ Usage: foo [-q] --from [--config ] [-h] Options: -q, --quiet --from --config -h, --help Show this help message and exit.]], parser:get_help()) end) it("adds margin for multiline descriptions", function() local parser = Parser "foo" parser:flag "-v" :count "0-2" :target "verbosity" :description [[ Sets verbosity level. -v: Report all warnings. -vv: Report all debugging information. ]] assert.equal([[ Usage: foo [-v] [-h] Options: -v Sets verbosity level. -v: Report all warnings. -vv: Report all debugging information. -h, --help Show this help message and exit.]], parser:get_help()) end) it("shows default values", function() local parser = Parser "foo" parser:option "-o" :default "a.out" parser:option "-p" :default "8080" :description "Port." assert.equal([[ Usage: foo [-o ] [-p

] [-h] Options: -o default: a.out -p

Port. (default: 8080) -h, --help Show this help message and exit.]], parser:get_help()) end) it("does not show default value when show_default == false", function() local parser = Parser "foo" parser:option "-o" :default "a.out" :show_default(false) parser:option "-p" :default "8080" :show_default(false) :description "Port. " assert.equal([[ Usage: foo [-o ] [-p

] [-h] Options: -o -p

Port. -h, --help Show this help message and exit.]], parser:get_help()) end) it("creates correct help message for commands", function() local parser = Parser "foo" parser:flag "-q --quiet" local run = parser:command "run" :description "Run! " run:option "--where" assert.equal([[ Usage: foo [-q] [-h] ... Options: -q, --quiet -h, --help Show this help message and exit. Commands: run Run! ]], parser:get_help()) end) it("creates correct help message for subcommands", function() local parser = Parser "foo" parser:flag "-q" "--quiet" local run = parser:command "run" run:option "--where" assert.equal([[ Usage: foo run [--where ] [-h] Options: --where -h, --help Show this help message and exit.]], run:get_help()) end) it("uses message provided by user", function() local parser = Parser "foo" :help "I don't like your format of help messages" parser:flag "-q" "--quiet" assert.equal([[ I don't like your format of help messages]], parser:get_help()) end) end) argparse-0.5.0/spec/default_spec.lua0000644000175000017500000001070212632007121016437 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to default values", function() describe("default values for arguments", function() it("handles default argument correctly", function() local parser = Parser() parser:argument "foo" :default "bar" local args = parser:parse{} assert.same({foo = "bar"}, args) args = parser:parse{"baz"} assert.same({foo = "baz"}, args) end) it("handles default argument for multi-argument correctly", function() local parser = Parser() parser:argument "foo" { args = 3, default = "bar", defmode = "arg" } local args = parser:parse{"baz"} assert.same({foo = {"baz", "bar", "bar"}}, args) end) it("handles default value for multi-argument correctly", function() local parser = Parser() parser:argument "foo" { args = 3, default = "bar" } local args = parser:parse{} assert.same({foo = {"bar", "bar", "bar"}}, args) end) it("does not use default values if not needed", function() local parser = Parser() parser:argument "foo" { args = "1-2", default = "bar" } local args = parser:parse({"baz"}) assert.same({foo = {"baz"}}, args) end) end) describe("default values for options", function() it("handles option with default value correctly", function() local parser = Parser() parser:option "-o" "--output" :default "a.out" :defmode "unused" local args = parser:parse{} assert.same({output = "a.out"}, args) args = parser:parse{"--output", "foo.txt"} assert.same({output = "foo.txt"}, args) assert.has_error(function() parser:parse{"-o"} end, "option '-o' requires an argument") end) it("handles option with default value for multi-argument option correctly", function() local parser = Parser() parser:option("-s --several", "Two or three things", "foo", nil, "2-3") local args = parser:parse{} assert.same({several = {"foo", "foo"}}, args) end) it("handles option with default value and argument", function() local parser = Parser() parser:option "-o" "--output" { default = "a.out", defmode = "arg+count" } local args = parser:parse{} assert.same({output = "a.out"}, args) args = parser:parse{"-o"} assert.same({output = "a.out"}, args) args = parser:parse{"-o", "value"} assert.same({output = "value"}, args) end) it("handles option with default argument correctly", function() local parser = Parser() parser:option "-p" "--protected" :target "password" :default "password" :defmode "arg" local args = parser:parse{"-p"} assert.same({password = "password"}, args) end) it("doesn't use default argument if option is not invoked", function() local parser = Parser() parser:option "-f" "--foo" { default = "bar", defmode = "arg" } local args = parser:parse{} assert.same({}, args) end) it("handles default multi-argument correctly", function() local parser = Parser() parser:option "-f" "--foo" { args = 3, default = "bar", defmode = "arg" } local args = parser:parse({"--foo=baz"}) assert.same({foo = {"baz", "bar", "bar"}}, args) end) it("does not use default values if not needed", function() local parser = Parser() parser:option "-f" "--foo" { args = "1-2", default = "bar", defmode = "arg" } local args = parser:parse({"-f", "baz"}) assert.same({foo = {"baz"}}, args) end) it("handles multi-count options with default value correctly", function() local parser = Parser() parser:option "-f" "--foo" { count = "*", default = "bar", defmode = "arg + count" } local args = parser:parse({"-f", "--foo=baz", "--foo"}) assert.same({foo = {"bar", "baz", "bar"}}, args) end) end) end) argparse-0.5.0/spec/script.lua0000755000175000017500000000141712632007121015313 0ustar vsevavseva#!/usr/bin/env lua local Parser = require "argparse" local parser = Parser() :description "A testing program. " :require_command(false) parser:argument "input" parser:flag "-v" "--verbose" :description "Sets verbosity level. " :target "verbosity" :count "0-2" local install = parser:command "install" :description "Install a rock. " install:argument "rock" :description "Name of the rock. " install:argument "version" :description "Version of the rock. " :args "?" install:option "-f" "--from" :description "Fetch the rock from this server. " :target "server" parser:get_usage() parser:get_help() local args = parser:parse() print(args.input) print(args.verbosity) print(args.install) print(args.rock) print(args.version) print(args.server) argparse-0.5.0/spec/integrity_spec.lua0000644000175000017500000001106012632007121017027 0ustar vsevavsevalocal script = "./spec/script.lua" local script_cmd = "lua" if package.loaded["luacov.runner"] then script_cmd = script_cmd .. " -lluacov" end script_cmd = script_cmd .. " " .. script local function get_output(args) local handler = io.popen(script_cmd .. " " .. args .. " 2>&1", "r") local output = handler:read("*a") handler:close() return output end describe("tests related to CLI behaviour #unsafe", function() describe("error messages", function() it("generates correct error message without arguments", function() assert.equal([[ Usage: ]]..script..[[ [-v] [-h] [] ... Error: missing argument 'input' ]], get_output("")) end) it("generates correct error message with too many arguments", function() assert.equal([[ Usage: ]]..script..[[ [-v] [-h] [] ... Error: unknown command 'bar' ]], get_output("foo bar")) end) it("generates correct error message with unexpected argument", function() assert.equal([[ Usage: ]]..script..[[ [-v] [-h] [] ... Error: option '--verbose' does not take arguments ]], get_output("--verbose=true")) end) it("generates correct error message with unexpected option", function() assert.equal([[ Usage: ]]..script..[[ [-v] [-h] [] ... Error: unknown option '-q' Did you mean one of these: '-h' '-v'? ]], get_output("-vq")) end) it("generates correct error message and tip with unexpected command", function() assert.equal([[ Usage: ]]..script..[[ [-v] [-h] [] ... Error: unknown command 'nstall' Did you mean 'install'? ]], get_output("foo nstall")) end) it("generates correct error message without arguments in command", function() assert.equal([[ Usage: ]]..script..[[ install [-f ] [-h] [] Error: missing argument 'rock' ]], get_output("foo install")) end) it("generates correct error message and tip in command", function() assert.equal([[ Usage: ]]..script..[[ install [-f ] [-h] [] Error: unknown option '--form' Did you mean '--from'? ]], get_output("foo install bar --form=there")) end) end) describe("help messages", function() it("generates correct help message", function() assert.equal([[ Usage: ]]..script..[[ [-v] [-h] [] ... A testing program. Arguments: input Options: -v, --verbose Sets verbosity level. -h, --help Show this help message and exit. Commands: install Install a rock. ]], get_output("--help")) end) it("generates correct help message for command", function() assert.equal([[ Usage: ]]..script..[[ install [-f ] [-h] [] Install a rock. Arguments: rock Name of the rock. version Version of the rock. Options: -f , --from Fetch the rock from this server. -h, --help Show this help message and exit. ]], get_output("foo install --help")) end) end) describe("data flow", function() it("works with one argument", function() local handler = io.popen(script_cmd .. " foo 2>&1", "r") assert.equal("foo", handler:read "*l") assert.equal("0", handler:read "*l") handler:close() end) it("works with one argument and a flag", function() local handler = io.popen(script_cmd .. " -v foo --verbose 2>&1", "r") assert.equal("foo", handler:read "*l") assert.equal("2", handler:read "*l") handler:close() end) it("works with command", function() local handler = io.popen(script_cmd .. " foo -v install bar 2>&1", "r") assert.equal("foo", handler:read "*l") assert.equal("1", handler:read "*l") assert.equal("true", handler:read "*l") assert.equal("bar", handler:read "*l") assert.equal("nil", handler:read "*l") assert.equal("nil", handler:read "*l") handler:close() end) it("works with command and options", function() local handler = io.popen(script_cmd .. " foo --verbose install bar 0.1 --from=there -vv 2>&1", "r") assert.equal("foo", handler:read "*l") assert.equal("2", handler:read "*l") assert.equal("true", handler:read "*l") assert.equal("bar", handler:read "*l") assert.equal("0.1", handler:read "*l") assert.equal("there", handler:read "*l") handler:close() end) end) end) argparse-0.5.0/spec/commands_spec.lua0000644000175000017500000000446312632007121016623 0ustar vsevavsevalocal Parser = require "argparse" getmetatable(Parser()).error = function(_, msg) error(msg) end describe("tests related to commands", function() it("handles commands after arguments", function() local parser = Parser "name" parser:argument "file" parser:command "create" parser:command "remove" local args = parser:parse{"temp.txt", "remove"} assert.same({file = "temp.txt", remove = true}, args) end) it("switches context properly", function() local parser = Parser "name" :add_help(false) local install = parser:command "install" install:flag "-q" "--quiet" local args = parser:parse{"install", "-q"} assert.same({install = true, quiet = true}, args) assert.has_error(function() parser:parse{"-q", "install"} end, "unknown option '-q'") end) it("uses command_target property to save command name", function() local parser = Parser "name" :add_help(false) :command_target("command") local install = parser:command "install" install:flag "-q" "--quiet" local args = parser:parse{"install", "-q"} assert.same({install = true, quiet = true, command = "install"}, args) end) it("allows to continue passing old options", function() local parser = Parser "name" parser:flag "-v" "--verbose" { count = "*" } parser:command "install" local args = parser:parse{"-vv", "install", "--verbose"} assert.same({install = true, verbose = 3}, args) end) it("handles nested commands", function() local parser = Parser "name" local foo = parser:command "foo" foo:command "bar" foo:command "baz" local args = parser:parse{"foo", "bar"} assert.same({foo = true, bar = true}, args) end) it("handles no commands depending on parser.require_command", function() local parser = Parser "name" parser:command "install" assert.has_error(function() parser:parse{} end, "a command is required") parser:require_command(false) local args = parser:parse{} assert.same({}, args) end) it("Detects wrong commands", function() local parser = Parser "name" parser:command "install" assert.has_error(function() parser:parse{"run"} end, "unknown command 'run'") end) end) argparse-0.5.0/docsrc/0000755000175000017500000000000012632007121013621 5ustar vsevavsevaargparse-0.5.0/docsrc/options.rst0000644000175000017500000000740512632007121016054 0ustar vsevavsevaAdding and configuring options ============================== Options can be added using ``:option(name, description, default, convert, args, count)`` method. It returns an Option instance, which can be configured in the same way as Parsers. The ``name`` property is required. An option can have several aliases, which can be set as space separated substrings in its name or by continuously setting ``name`` property. .. code-block:: lua :linenos: -- These lines are equivalent: parser:option "-f" "--from" parser:option "-f --from" :: $ lua script.lua --from there $ lua script.lua --from=there $ lua script.lua -f there $ lua script.lua -fthere .. code-block:: lua { from = "there" } For an option, default index used to store arguments passed to it is the first "long" alias (an alias starting with two control characters, typically hyphens) or just the first alias, without control characters. Hyphens in the default index are replaced with underscores. In the following table it is assumed that ``local args = parser:parse()`` has been executed. ======================== ============================== Option's aliases Location of option's arguments ======================== ============================== ``-o`` ``args.o`` ``-o`` ``--output`` ``args.output`` ``-s`` ``--from-server`` ``args.from_server`` ======================== ============================== As with arguments, the index can be explicitly set using ``target`` property. Flags ----- Flags are almost identical to options, except that they don't take an argument by default. .. code-block:: lua :linenos: parser:flag("-q --quiet") :: $ lua script.lua -q .. code-block:: lua { quiet = true } Control characters ------------------ The first characters of all aliases of all options of a parser form the set of control characters, used to distinguish options from arguments. Typically the set only consists of a hyphen. Setting number of consumed arguments ------------------------------------ Just as arguments, options can be configured to take several command line arguments. .. code-block:: lua :linenos: parser:option "--pair" :args(2) parser:option "--optional" :args "?" :: $ lua script.lua --pair foo bar .. code-block:: lua { pair = {"foo", "bar"} } :: $ lua script.lua --pair foo bar --optional .. code-block:: lua { pair = {"foo", "bar"}, optional = {} } :: $ lua script.lua --optional=baz .. code-block:: lua { optional = {"baz"} } Note that the data passed to ``optional`` option is stored in an array. That is necessary to distinguish whether the option was invoked without an argument or it was not invoked at all. Setting number of invocations ----------------------------- For options, it is possible to control how many times they can be used. argparse uses ``count`` property to set how many times an option can be invoked. The value of the property is interpreted in the same way ``args`` is. .. code-block:: lua :linenos: parser:option("-e --exclude") :count "*" :: $ lua script.lua -eFOO -eBAR .. code-block:: lua { exclude = {"FOO", "BAR"} } If an option can be used more than once and it can consume more than one argument, the data is stored as an array of invocations, each being an array of arguments. As a special case, if an option can be used more than once and it consumes no arguments (e.g. it's a flag), than the number of invocations is stored in the associated field of the result table. .. code-block:: lua :linenos: parser:flag("-v --verbose", "Sets verbosity level.") :count "0-2" :target "verbosity" :: $ lua script.lua -vv .. code-block:: lua { verbosity = 2 } argparse-0.5.0/docsrc/callbacks.rst0000644000175000017500000001243212632007121016274 0ustar vsevavsevaCallbacks ========= Converters ---------- argparse can perform automatic validation and conversion on arguments. If ``convert`` property of an element is a function, it will be applied to all the arguments passed to it. The function should return ``nil`` and, optionally, an error message if conversion failed. Standard ``tonumber`` and ``io.open`` functions work exactly like that. .. code-block:: lua :linenos: parser:argument "input" :convert(io.open) parser:option "-t --times" :convert(tonumber) :: $ lua script.lua foo.txt -t5 .. code-block:: lua { input = file_object, times = 5 } :: $ lua script.lua nonexistent.txt :: Usage: script.lua [-t ] [-h] Error: nonexistent.txt: No such file or directory :: $ lua script.lua foo.txt --times=many :: Usage: script.lua [-t ] [-h] Error: malformed argument 'many' Table converters ^^^^^^^^^^^^^^^^ If convert property of an element is a table, arguments passed to it will be used as keys. If a key is missing, an error is raised. .. code-block:: lua :linenos: parser:argument "choice" :convert { foo = "Something foo-related", bar = "Something bar-related" } :: $ lua script.lua bar .. code-block:: lua { choice = "Something bar-related" } :: $ lua script.lua baz :: Usage: script.lua [-h] Error: malformed argument 'baz' Actions ------- .. _actions: Argument and option actions ^^^^^^^^^^^^^^^^^^^^^^^^^^^ argparse uses action callbacks to process invocations of arguments and options. Default actions simply put passed arguments into the result table as a single value or insert into an array depending on number of arguments the option can take and how many times it can be used. A custom action can be set using ``action`` property. An action must be a function. and will be called after each invocation of the option or the argument it is assigned to. Four arguments are passed: result table, target index in that table, an argument or an array of arguments passed by user, and overwrite flag used when an option is invoked too many times. Converters are applied before actions. Initial value to be stored at target index in the result table can be set using ``init`` property, or also using ``default`` property if the value is not a string. .. code-block:: lua :linenos: parser:option("--exceptions"):args("*"):action(function(args, _, exceptions) for _, exception in ipairs(exceptions) do table.insert(args.exceptions, exception) end end):init({"foo", "bar"}) parser:flag("--no-exceptions"):action(function() args.exceptions = {} end) :: $ lua script.lua --exceptions x y --exceptions z t .. code-block:: lua { exceptions = { "foo", "bar", "x", "y", "z", "t" } } :: $ lua script.lua --exceptions x y --no-exceptions .. code-block:: lua { exceptions = {} } Actions can also be used when a flag needs to print some message and exit without parsing remaining arguments. .. code-block:: lua :linenos: parser:flag("-v --version"):action(function() print("script v1.0.0") os.exit(0) end) :: $ lua script.lua -v :: script v1.0.0 Built-in actions ^^^^^^^^^^^^^^^^ These actions can be referred to by their string names when setting ``action`` property: =========== ======================================================= Name Description =========== ======================================================= store Stores argument or arguments at target index. store_true Stores ``true`` at target index. store_false Stores ``false`` at target index. count Increments number at target index. append Appends argument or arguments to table at target index. concat Appends arguments one by one to table at target index. =========== ======================================================= Examples using ``store_false`` and ``concat`` actions: .. code-block:: lua :linenos: parser:flag("--candy") parser:flag("--no-candy"):target("candy"):action("store_false") parser:flag("--rain", "Enable rain", false) parser:option("--exceptions"):args("*"):action("concat"):init({"foo", "bar"}) :: $ lua script.lua .. code-block:: lua { rain = false } :: $ lua script.lua --candy .. code-block:: lua { candy = true, rain = false } :: $ lua script.lua --no-candy --rain .. code-block:: lua { candy = false, rain = true } :: $ lua script.lua --exceptions x y --exceptions z t .. code-block:: lua { exceptions = { "foo", "bar", "x", "y", "z", "t" }, rain = false } Command actions ^^^^^^^^^^^^^^^ Actions for parsers and commands are simply callbacks invoked after parsing, with result table and command name as the arguments. Actions for nested commands are called first. .. code-block:: lua :linenos: local install = parser:command("install"):action(function(args, name) print("Running " .. name) -- Use args here ) parser:action(function(args) print("Callbacks are fun!") end) :: $ lua script.lua install :: Running install Callbacks are fun! argparse-0.5.0/docsrc/commands.rst0000644000175000017500000000515612632007121016163 0ustar vsevavsevaAdding and configuring commands =============================== A command is a subparser invoked when its name is passed as an argument. For example, in `git `_ CLI ``add``, ``commit``, ``push``, etc. are commands. Each command has its own set of arguments and options, but inherits options of its parent. Commands can be added using ``:command(name, description, epilog)`` method. Just as options, commands can have several aliases. .. code-block:: lua :linenos: parser:command "install i" If a command it used, ``true`` is stored in the corresponding field of the result table. :: $ lua script.lua install .. code-block:: lua { install = true } A typo will result in an appropriate error message. :: $ lua script.lua instal :: Usage: script.lua [-h] ... Error: unknown command 'instal' Did you mean 'install'? Getting name of selected command -------------------------------- Use ``command_target`` property of the parser to store the name of used command in a field of the result table. .. code-block:: lua :linenos: parser:command_target("command") parser:command("install") parser:command("remove") :: $ lua script.lua install .. code-block:: lua { install = true, command = "install" } Adding elements to commands --------------------------- The Command class is a subclass of the Parser class, so all the Parser's methods for adding elements work on commands, too. .. code-block:: lua :linenos: local install = parser:command "install" install:argument "rock" install:option "-f --from" :: $ lua script.lua install foo --from=bar .. code-block:: lua { install = true, rock = "foo", from = "bar" } Commands have their own usage and help messages. :: $ lua script.lua install :: Usage: script.lua install [-f ] [-h] Error: too few arguments :: $ lua script.lua install --help :: Usage: script.lua install [-f ] [-h] Arguments: rock Options: -f , --from -h, --help Show this help message and exit. Making a command optional ------------------------- By default, if a parser has commands, using one of them is obligatory. .. code-block:: lua :linenos: local parser = argparse() parser:command "install" :: $ lua script.lua :: Usage: script.lua [-h] ... Error: a command is required This can be changed using ``require_command`` property. .. code-block:: lua :linenos: local parser = argparse() :require_command(false) parser:command "install" argparse-0.5.0/docsrc/defaults.rst0000644000175000017500000000430312632007121016162 0ustar vsevavsevaDefault values ============== For elements such as arguments and options, if ``default`` property is set to a string, its value is stored in case the element was not used (if it's not a string, it'll be used as ``init`` property instead, see :ref:`actions`). .. code-block:: lua :linenos: parser:option("-o --output", "Output file.", "a.out") -- Equivalent: parser:option "-o" "--output" :description "Output file." :default "a.out" :: $ lua script.lua .. code-block:: lua { output = "a.out" } The existence of a default value is reflected in help message, unless ``show_default`` property is set to ``false``. :: $ lua script.lua --help :: Usage: script.lua [-o ] [-h] Options: -o , --output Output file. (default: a.out) -h, --help Show this help message and exit. Note that invocation without required arguments is still an error. :: $ lua script.lua -o :: Usage: script.lua [-o ] [-h] Error: too few arguments Default mode ------------ ``defmode`` property regulates how argparse should use the default value of an element. If ``defmode`` contains ``u`` (for unused), the default value will be automatically passed to the element if it was not invoked at all. This is the default behavior. If ``defmode`` contains ``a`` (for argument), the default value will be automatically passed to the element if not enough arguments were passed, or not enough invocations were made. Consider the difference: .. code-block:: lua :linenos: parser:option "-o" :default "a.out" parser:option "-p" :default "password" :defmode "arg" :: $ lua script.lua -h :: Usage: script.lua [-o ] [-p [

]] [-h] Options: -o default: a.out -p [

] default: password -h, --help Show this help message and exit. :: $ lua script.lua .. code-block:: lua { o = "a.out" } :: $ lua script.lua -p .. code-block:: lua { o = "a.out", p = "password" } :: $ lua script.lua -o :: Usage: script.lua [-o ] [-p [

]] [-h] Error: too few arguments argparse-0.5.0/docsrc/arguments.rst0000644000175000017500000000373412632007121016367 0ustar vsevavsevaAdding and configuring arguments ================================ Positional arguments can be added using ``:argument(name, description, default, convert, args)`` method. It returns an Argument instance, which can be configured in the same way as Parsers. The ``name`` property is required. .. code-block:: lua :linenos: parser:argument "input" :: $ lua script.lua foo .. code-block:: lua { input = "foo" } The data passed to the argument is stored in the result table at index ``input`` because it is the argument's name. The index can be changed using ``target`` property. Setting number of consumed arguments ------------------------------------ ``args`` property sets how many command line arguments the argument consumes. Its value is interpreted as follows: ================================================= ============================= Value Interpretation ================================================= ============================= Number ``N`` Exactly ``N`` arguments String ``A-B``, where ``A`` and ``B`` are numbers From ``A`` to ``B`` arguments String ``N+``, where ``N`` is a number ``N`` or more arguments String ``?`` An optional argument String ``*`` Any number of arguments String ``+`` At least one argument ================================================= ============================= If more than one argument can be consumed, a table is used to store the data. .. code-block:: lua :linenos: parser:argument("pair", "A pair of arguments.") :args(2) parser:argument("optional", "An optional argument.") :args "?" :: $ lua script.lua foo bar .. code-block:: lua { pair = {"foo", "bar"} } :: $ lua script.lua foo bar baz .. code-block:: lua { pair = {"foo", "bar"}, optional = "baz" } argparse-0.5.0/docsrc/parsers.rst0000644000175000017500000000643512632007121016042 0ustar vsevavsevaCreating and using parsers ========================== The ``argparse`` module is a function which, when called, creates an instance of the Parser class. .. code-block:: lua :linenos: -- script.lua local argparse = require "argparse" local parser = argparse() ``parser`` is now an empty parser which does not recognize any command line arguments or options. Parsing command line arguments ------------------------------ ``:parse([args])`` method of the Parser class returns a table with processed data from the command line or ``args`` array. .. code-block:: lua :linenos: local args = parser:parse() print(args) -- Assuming print is patched to handle tables nicely. When executed, this script prints ``{}`` because the parser is empty and no command line arguments were supplied. Error handling ^^^^^^^^^^^^^^ If the provided command line arguments are not recognized by the parser, it will print an error message and call ``os.exit(1)``. :: $ lua script.lua foo :: Usage: script.lua [-h] Error: too many arguments If halting the program is undesirable, ``:pparse([args])`` method should be used. It returns boolean flag indicating success of parsing and result or error message. An error can raised manually using ``:error()`` method. .. code-block:: lua :linenos: parser:error("manual argument validation failed") :: Usage: script.lua [-h] Error: manual argument validation failed Help option ^^^^^^^^^^^ As the automatically generated usage message states, there is a help option ``-h`` added to any parser by default. When a help option is used, parser will print a help message and call ``os.exit(0)``. :: $ lua script.lua -h :: Usage: script.lua [-h] Options: -h, --help Show this help message and exit. Typo autocorrection ^^^^^^^^^^^^^^^^^^^ When an option is not recognized by the parser, but there is an option with a similar name, a suggestion is automatically added to the error message. :: $ lua script.lua --hepl :: Usage: script.lua [-h] Error: unknown option '--hepl' Did you mean '--help'? Configuring parsers ------------------- Parsers have several properties affecting their behavior. For example, ``description`` and ``epilog`` properties set the text to be displayed in the help message after the usage message and after the listings of options and arguments, respectively. Another is ``name``, which overwrites the name of the program which is used in the usage message (default value is inferred from command line arguments). There are several ways to set properties. The first is to chain setter methods of Parser object. .. code-block:: lua :linenos: local parser = argparse() :name "script" :description "A testing script." :epilog "For more info, see http://example.com" The second is to call a parser with a table containing some properties. .. code-block:: lua :linenos: local parser = argparse() { name = "script", description = "A testing script.", epilog "For more info, see http://example.com." } Finally, ``name``. ``description`` and ``epilog`` properties can be passed as arguments when calling a parser. .. code-block:: lua :linenos: local parser = argparse("script", "A testing script.", "For more info, see http://example.com.") argparse-0.5.0/docsrc/index.rst0000644000175000017500000000042012632007121015456 0ustar vsevavsevaArgparse tutorial ================= Contents: .. toctree:: parsers arguments options mutexes commands defaults callbacks misc This is a tutorial for `argparse `_, a feature-rich command line parser for Lua. argparse-0.5.0/docsrc/mutexes.rst0000644000175000017500000000100012632007121016034 0ustar vsevavsevaMutually exclusive groups ========================= A group of options can be marked as mutually exclusive using ``:mutex(option, ...)`` method of the Parser class. .. code-block:: lua :linenos: parser:mutex( parser:flag "-q --quiet", parser:flag "-v --verbose" ) If more than one element of a mutually exclusive group is used, an error is raised. :: $ lua script.lua -qv :: Usage: script.lua ([-q] | [-v]) [-h] Error: option '-v' can not be used together with option '-q' argparse-0.5.0/docsrc/conf.py0000644000175000017500000002027112632007121015122 0ustar vsevavseva# -*- coding: utf-8 -*- # # argparse documentation build configuration file, created by # sphinx-quickstart on Sat Jun 20 14:03:24 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = [] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'argparse' copyright = u'2013 - 2015, Peter Melnichenko' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.5.0' # The full version, including alpha/beta/rc tags. release = '0.5.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. if os.environ.get('READTHEDOCS', None) != 'True': try: import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] except ImportError: pass # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = "argparse 0.5.0 tutorial" # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'argparsetutorial' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'argparse.tex', u'argparse tutorial', u'Peter Melnichenko', 'manual') ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'argparse', u'argparse tutorial', [u'Peter Melnichenko'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'argparse', u'argparse tutorial', u'Peter Melnichenko', 'argparse', 'Command line parser for Lua.', 'Miscellaneous') ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False argparse-0.5.0/docsrc/misc.rst0000644000175000017500000001467512632007121015323 0ustar vsevavsevaMiscellaneous ============= Generating and overwriting help and usage messages -------------------------------------------------- The usage and help messages of parsers and commands can be generated on demand using ``:get_usage()`` and ``:get_help()`` methods, and overridden using ``help`` and ``usage`` properties. Overwriting default help option ------------------------------- If the property ``add_help`` of a parser is set to ``false``, no help option will be added to it. Otherwise, the value of the field will be used to configure it. .. code-block:: lua :linenos: local parser = argparse() :add_help "/?" :: $ lua script.lua /? :: Usage: script.lua [/?] Options: /? Show this help message and exit. Setting argument placeholder ---------------------------- For options and arguments, ``argname`` property controls the placeholder for the argument in the usage message. .. code-block:: lua :linenos: parser:option "-f" "--from" :argname "" :: $ lua script.lua --help :: Usage: script.lua [-f ] [-h] Options: -f , --from -h, --help Show this help message and exit. ``argname`` can be an array of placeholders. .. code-block:: lua :linenos: parser:option "--pair" :args(2) :argname {"", ""} :: $ lua script.lua --help :: Usage: script.lua [--pair ] [-h] Options: --pair -h, --help Show this help message and exit. Disabling option handling ------------------------- When ``handle_options`` property of a parser or a command is set to ``false``, all options will be passed verbatim to the argument list, as if the input included double-hyphens. .. code-block:: lua :linenos: parser:handle_options(false) parser:argument "input" :args "*" parser:option "-f" "--foo" :args "*" :: $ lua script.lua bar -f --foo bar .. code-block:: lua { input = {"bar", "-f", "--foo", "bar"} } Prohibiting overuse of options ------------------------------ By default, if an option is invoked too many times, latest invocations overwrite the data passed earlier. .. code-block:: lua :linenos: parser:option "-o --output" :: $ lua script.lua -oFOO -oBAR .. code-block:: lua { output = "BAR" } Set ``overwrite`` property to ``false`` to prohibit this behavior. .. code-block:: lua :linenos: parser:option "-o --output" :overwrite(false) :: $ lua script.lua -oFOO -oBAR :: Usage: script.lua [-o ] [-h] Error: option '-o' must be used at most 1 time Parsing algorithm ----------------- argparse interprets command line arguments in the following way: ============= ================================================================================================================ Argument Interpretation ============= ================================================================================================================ ``foo`` An argument of an option or a positional argument. ``--foo`` An option. ``--foo=bar`` An option and its argument. The option must be able to take arguments. ``-f`` An option. ``-abcdef`` Letters are interpreted as options. If one of them can take an argument, the rest of the string is passed to it. ``--`` The rest of the command line arguments will be interpreted as positional arguments. ============= ================================================================================================================ Property lists -------------- Parser properties ^^^^^^^^^^^^^^^^^ Properties that can be set as arguments when calling or constructing a parser, in this order: =============== ====== Property Type =============== ====== ``name`` String ``description`` String ``epilog`` String =============== ====== Other properties: =================== ========================== Property Type =================== ========================== ``usage`` String ``help`` String ``require_command`` Boolean ``handle_options`` Boolean ``add_help`` Boolean or string or table ``command_target`` String =================== ========================== Command properties ^^^^^^^^^^^^^^^^^^ Properties that can be set as arguments when calling or constructing a command, in this order: =============== ====== Property Type =============== ====== ``name`` String ``description`` String ``epilog`` String =============== ====== Other properties: =================== ========================== Property Type =================== ========================== ``target`` String ``usage`` String ``help`` String ``require_command`` Boolean ``handle_options`` Boolean ``action`` Function ``add_help`` Boolean or string or table ``command_target`` String =================== ========================== Argument properties ^^^^^^^^^^^^^^^^^^^ Properties that can be set as arguments when calling or constructing an argument, in this order: =============== ================= Property Type =============== ================= ``name`` String ``description`` String ``default`` Any ``convert`` Function or table ``args`` Number or string =============== ================= Other properties: =================== =============== Property Type =================== =============== ``target`` String ``defmode`` String ``show_default`` Boolean ``argname`` String or table ``action`` Function or string ``init`` Any =================== =============== Option and flag properties ^^^^^^^^^^^^^^^^^^^^^^^^^^ Properties that can be set as arguments when calling or constructing an option or a flag, in this order: =============== ================= Property Type =============== ================= ``name`` String ``description`` String ``default`` Any ``convert`` Function or table ``args`` Number or string ``count`` Number or string =============== ================= Other properties: =================== ================== Property Type =================== ================== ``target`` String ``defmode`` String ``show_default`` Boolean ``overwrite`` Booleans ``argname`` String or table ``action`` Function or string ``init`` Any =================== ================== argparse-0.5.0/.travis.yml0000644000175000017500000000072012632007121014454 0ustar vsevavsevalanguage: python sudo: false env: - LUA="lua 5.1" - LUA="lua 5.2" - LUA="lua 5.3" - LUA="luajit 2.0" before_install: - pip install hererocks - hererocks l -r^ --$LUA - export PATH=$PATH:$PWD/l/bin - luarocks install luacheck - luarocks install dkjson --deps-mode=none - luarocks install busted - luarocks install luacov-coveralls install: - luarocks make script: - luacheck src spec - busted -c after_success: - luacov-coveralls argparse-0.5.0/src/0000755000175000017500000000000012632007121013133 5ustar vsevavsevaargparse-0.5.0/src/argparse.lua0000644000175000017500000007075612632007121015461 0ustar vsevavseva-- The MIT License (MIT) -- Copyright (c) 2013 - 2015 Peter Melnichenko -- Permission is hereby granted, free of charge, to any person obtaining a copy of -- this software and associated documentation files (the "Software"), to deal in -- the Software without restriction, including without limitation the rights to -- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -- the Software, and to permit persons to whom the Software is furnished to do so, -- subject to the following conditions: -- The above copyright notice and this permission notice shall be included in all -- copies or substantial portions of the Software. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. local function deep_update(t1, t2) for k, v in pairs(t2) do if type(v) == "table" then v = deep_update({}, v) end t1[k] = v end return t1 end -- A property is a tuple {name, callback}. -- properties.args is number of properties that can be set as arguments -- when calling an object. local function class(prototype, properties, parent) -- Class is the metatable of its instances. local cl = {} cl.__index = cl if parent then cl.__prototype = deep_update(deep_update({}, parent.__prototype), prototype) else cl.__prototype = prototype end if properties then local names = {} -- Create setter methods and fill set of property names. for _, property in ipairs(properties) do local name, callback = property[1], property[2] cl[name] = function(self, value) if not callback(self, value) then self["_" .. name] = value end return self end names[name] = true end function cl.__call(self, ...) -- When calling an object, if the first argument is a table, -- interpret keys as property names, else delegate arguments -- to corresponding setters in order. if type((...)) == "table" then for name, value in pairs((...)) do if names[name] then self[name](self, value) end end else local nargs = select("#", ...) for i, property in ipairs(properties) do if i > nargs or i > properties.args then break end local arg = select(i, ...) if arg ~= nil then self[property[1]](self, arg) end end end return self end end -- If indexing class fails, fallback to its parent. local class_metatable = {} class_metatable.__index = parent function class_metatable.__call(self, ...) -- Calling a class returns its instance. -- Arguments are delegated to the instance. local object = deep_update({}, self.__prototype) setmetatable(object, self) return object(...) end return setmetatable(cl, class_metatable) end local function typecheck(name, types, value) for _, type_ in ipairs(types) do if type(value) == type_ then return true end end error(("bad property '%s' (%s expected, got %s)"):format(name, table.concat(types, " or "), type(value))) end local function typechecked(name, ...) local types = {...} return {name, function(_, value) typecheck(name, types, value) end} end local multiname = {"name", function(self, value) typecheck("name", {"string"}, value) for alias in value:gmatch("%S+") do self._name = self._name or alias table.insert(self._aliases, alias) end -- Do not set _name as with other properties. return true end} local function parse_boundaries(str) if tonumber(str) then return tonumber(str), tonumber(str) end if str == "*" then return 0, math.huge end if str == "+" then return 1, math.huge end if str == "?" then return 0, 1 end if str:match "^%d+%-%d+$" then local min, max = str:match "^(%d+)%-(%d+)$" return tonumber(min), tonumber(max) end if str:match "^%d+%+$" then local min = str:match "^(%d+)%+$" return tonumber(min), math.huge end end local function boundaries(name) return {name, function(self, value) typecheck(name, {"number", "string"}, value) local min, max = parse_boundaries(value) if not min then error(("bad property '%s'"):format(name)) end self["_min" .. name], self["_max" .. name] = min, max end} end local actions = {} local option_action = {"action", function(_, value) typecheck("action", {"function", "string"}, value) if type(value) == "string" and not actions[value] then error(("unknown action '%s'"):format(value)) end end} local option_init = {"init", function(self) self._has_init = true end} local option_default = {"default", function(self, value) if type(value) ~= "string" then self._init = value self._has_init = true return true end end} local add_help = {"add_help", function(self, value) typecheck("add_help", {"boolean", "string", "table"}, value) if self._has_help then table.remove(self._options) self._has_help = false end if value then local help = self:flag() :description "Show this help message and exit." :action(function() print(self:get_help()) os.exit(0) end) if value ~= true then help = help(value) end if not help._name then help "-h" "--help" end self._has_help = true end end} local Parser = class({ _arguments = {}, _options = {}, _commands = {}, _mutexes = {}, _require_command = true, _handle_options = true }, { args = 3, typechecked("name", "string"), typechecked("description", "string"), typechecked("epilog", "string"), typechecked("usage", "string"), typechecked("help", "string"), typechecked("require_command", "boolean"), typechecked("handle_options", "boolean"), typechecked("action", "function"), typechecked("command_target", "string"), add_help }) local Command = class({ _aliases = {} }, { args = 3, multiname, typechecked("description", "string"), typechecked("epilog", "string"), typechecked("target", "string"), typechecked("usage", "string"), typechecked("help", "string"), typechecked("require_command", "boolean"), typechecked("handle_options", "boolean"), typechecked("action", "function"), typechecked("command_target", "string"), add_help }, Parser) local Argument = class({ _minargs = 1, _maxargs = 1, _mincount = 1, _maxcount = 1, _defmode = "unused", _show_default = true }, { args = 5, typechecked("name", "string"), typechecked("description", "string"), option_default, typechecked("convert", "function", "table"), boundaries("args"), typechecked("target", "string"), typechecked("defmode", "string"), typechecked("show_default", "boolean"), typechecked("argname", "string", "table"), option_action, option_init }) local Option = class({ _aliases = {}, _mincount = 0, _overwrite = true }, { args = 6, multiname, typechecked("description", "string"), option_default, typechecked("convert", "function", "table"), boundaries("args"), boundaries("count"), typechecked("target", "string"), typechecked("defmode", "string"), typechecked("show_default", "boolean"), typechecked("overwrite", "boolean"), typechecked("argname", "string", "table"), option_action, option_init }, Argument) function Argument:_get_argument_list() local buf = {} local i = 1 while i <= math.min(self._minargs, 3) do local argname = self:_get_argname(i) if self._default and self._defmode:find "a" then argname = "[" .. argname .. "]" end table.insert(buf, argname) i = i+1 end while i <= math.min(self._maxargs, 3) do table.insert(buf, "[" .. self:_get_argname(i) .. "]") i = i+1 if self._maxargs == math.huge then break end end if i < self._maxargs then table.insert(buf, "...") end return buf end function Argument:_get_usage() local usage = table.concat(self:_get_argument_list(), " ") if self._default and self._defmode:find "u" then if self._maxargs > 1 or (self._minargs == 1 and not self._defmode:find "a") then usage = "[" .. usage .. "]" end end return usage end function actions.store_true(result, target) result[target] = true end function actions.store_false(result, target) result[target] = false end function actions.store(result, target, argument) result[target] = argument end function actions.count(result, target, _, overwrite) if not overwrite then result[target] = result[target] + 1 end end function actions.append(result, target, argument, overwrite) result[target] = result[target] or {} table.insert(result[target], argument) if overwrite then table.remove(result[target], 1) end end function actions.concat(result, target, arguments, overwrite) if overwrite then error("'concat' action can't handle too many invocations") end result[target] = result[target] or {} for _, argument in ipairs(arguments) do table.insert(result[target], argument) end end function Argument:_get_action() local action, init if self._maxcount == 1 then if self._maxargs == 0 then action, init = "store_true", nil else action, init = "store", nil end else if self._maxargs == 0 then action, init = "count", 0 else action, init = "append", {} end end if self._action then action = self._action end if self._has_init then init = self._init end if type(action) == "string" then action = actions[action] end return action, init end -- Returns placeholder for `narg`-th argument. function Argument:_get_argname(narg) local argname = self._argname or self:_get_default_argname() if type(argname) == "table" then return argname[narg] else return argname end end function Argument:_get_default_argname() return "<" .. self._name .. ">" end function Option:_get_default_argname() return "<" .. self:_get_default_target() .. ">" end -- Returns label to be shown in the help message. function Argument:_get_label() return self._name end function Option:_get_label() local variants = {} local argument_list = self:_get_argument_list() table.insert(argument_list, 1, nil) for _, alias in ipairs(self._aliases) do argument_list[1] = alias table.insert(variants, table.concat(argument_list, " ")) end return table.concat(variants, ", ") end function Command:_get_label() return table.concat(self._aliases, ", ") end function Argument:_get_description() if self._default and self._show_default then if self._description then return ("%s (default: %s)"):format(self._description, self._default) else return ("default: %s"):format(self._default) end else return self._description or "" end end function Command:_get_description() return self._description or "" end function Option:_get_usage() local usage = self:_get_argument_list() table.insert(usage, 1, self._name) usage = table.concat(usage, " ") if self._mincount == 0 or self._default then usage = "[" .. usage .. "]" end return usage end function Argument:_get_default_target() return self._name end function Option:_get_default_target() local res for _, alias in ipairs(self._aliases) do if alias:sub(1, 1) == alias:sub(2, 2) then res = alias:sub(3) break end end res = res or self._name:sub(2) return (res:gsub("-", "_")) end function Option:_is_vararg() return self._maxargs ~= self._minargs end function Parser:_get_fullname() local parent = self._parent local buf = {self._name} while parent do table.insert(buf, 1, parent._name) parent = parent._parent end return table.concat(buf, " ") end function Parser:_update_charset(charset) charset = charset or {} for _, command in ipairs(self._commands) do command:_update_charset(charset) end for _, option in ipairs(self._options) do for _, alias in ipairs(option._aliases) do charset[alias:sub(1, 1)] = true end end return charset end function Parser:argument(...) local argument = Argument(...) table.insert(self._arguments, argument) return argument end function Parser:option(...) local option = Option(...) if self._has_help then table.insert(self._options, #self._options, option) else table.insert(self._options, option) end return option end function Parser:flag(...) return self:option():args(0)(...) end function Parser:command(...) local command = Command():add_help(true)(...) command._parent = self table.insert(self._commands, command) return command end function Parser:mutex(...) local options = {...} for i, option in ipairs(options) do assert(getmetatable(option) == Option, ("bad argument #%d to 'mutex' (Option expected)"):format(i)) end table.insert(self._mutexes, options) return self end local max_usage_width = 70 local usage_welcome = "Usage: " function Parser:get_usage() if self._usage then return self._usage end local lines = {usage_welcome .. self:_get_fullname()} local function add(s) if #lines[#lines]+1+#s <= max_usage_width then lines[#lines] = lines[#lines] .. " " .. s else lines[#lines+1] = (" "):rep(#usage_welcome) .. s end end -- This can definitely be refactored into something cleaner local mutex_options = {} local vararg_mutexes = {} -- First, put mutexes which do not contain vararg options and remember those which do for _, mutex in ipairs(self._mutexes) do local buf = {} local is_vararg = false for _, option in ipairs(mutex) do if option:_is_vararg() then is_vararg = true end table.insert(buf, option:_get_usage()) mutex_options[option] = true end local repr = "(" .. table.concat(buf, " | ") .. ")" if is_vararg then table.insert(vararg_mutexes, repr) else add(repr) end end -- Second, put regular options for _, option in ipairs(self._options) do if not mutex_options[option] and not option:_is_vararg() then add(option:_get_usage()) end end -- Put positional arguments for _, argument in ipairs(self._arguments) do add(argument:_get_usage()) end -- Put mutexes containing vararg options for _, mutex_repr in ipairs(vararg_mutexes) do add(mutex_repr) end for _, option in ipairs(self._options) do if not mutex_options[option] and option:_is_vararg() then add(option:_get_usage()) end end if #self._commands > 0 then if self._require_command then add("") else add("[]") end add("...") end return table.concat(lines, "\n") end local margin_len = 3 local margin_len2 = 25 local margin = (" "):rep(margin_len) local margin2 = (" "):rep(margin_len2) local function make_two_columns(s1, s2) if s2 == "" then return margin .. s1 end s2 = s2:gsub("\n", "\n" .. margin2) if #s1 < (margin_len2-margin_len) then return margin .. s1 .. (" "):rep(margin_len2-margin_len-#s1) .. s2 else return margin .. s1 .. "\n" .. margin2 .. s2 end end function Parser:get_help() if self._help then return self._help end local blocks = {self:get_usage()} if self._description then table.insert(blocks, self._description) end local labels = {"Arguments:", "Options:", "Commands:"} for i, elements in ipairs{self._arguments, self._options, self._commands} do if #elements > 0 then local buf = {labels[i]} for _, element in ipairs(elements) do table.insert(buf, make_two_columns(element:_get_label(), element:_get_description())) end table.insert(blocks, table.concat(buf, "\n")) end end if self._epilog then table.insert(blocks, self._epilog) end return table.concat(blocks, "\n\n") end local function get_tip(context, wrong_name) local context_pool = {} local possible_name local possible_names = {} for name in pairs(context) do if type(name) == "string" then for i = 1, #name do possible_name = name:sub(1, i - 1) .. name:sub(i + 1) if not context_pool[possible_name] then context_pool[possible_name] = {} end table.insert(context_pool[possible_name], name) end end end for i = 1, #wrong_name + 1 do possible_name = wrong_name:sub(1, i - 1) .. wrong_name:sub(i + 1) if context[possible_name] then possible_names[possible_name] = true elseif context_pool[possible_name] then for _, name in ipairs(context_pool[possible_name]) do possible_names[name] = true end end end local first = next(possible_names) if first then if next(possible_names, first) then local possible_names_arr = {} for name in pairs(possible_names) do table.insert(possible_names_arr, "'" .. name .. "'") end table.sort(possible_names_arr) return "\nDid you mean one of these: " .. table.concat(possible_names_arr, " ") .. "?" else return "\nDid you mean '" .. first .. "'?" end else return "" end end local ElementState = class({ invocations = 0 }) function ElementState:__call(state, element) self.state = state self.result = state.result self.element = element self.target = element._target or element:_get_default_target() self.action, self.result[self.target] = element:_get_action() return self end function ElementState:error(fmt, ...) self.state:error(fmt, ...) end function ElementState:convert(argument) local converter = self.element._convert if converter then local ok, err if type(converter) == "function" then ok, err = converter(argument) else ok = converter[argument] end if ok == nil then self:error(err and "%s" or "malformed argument '%s'", err or argument) end argument = ok end return argument end function ElementState:default(mode) return self.element._defmode:find(mode) and self.element._default end local function bound(noun, min, max, is_max) local res = "" if min ~= max then res = "at " .. (is_max and "most" or "least") .. " " end local number = is_max and max or min return res .. tostring(number) .. " " .. noun .. (number == 1 and "" or "s") end function ElementState:invoke(alias) self.open = true self.name = ("%s '%s'"):format(alias and "option" or "argument", alias or self.element._name) self.overwrite = false if self.invocations >= self.element._maxcount then if self.element._overwrite then self.overwrite = true else self:error("%s must be used %s", self.name, bound("time", self.element._mincount, self.element._maxcount, true)) end else self.invocations = self.invocations + 1 end self.args = {} if self.element._maxargs <= 0 then self:close() end return self.open end function ElementState:pass(argument) argument = self:convert(argument) table.insert(self.args, argument) if #self.args >= self.element._maxargs then self:close() end return self.open end function ElementState:complete_invocation() while #self.args < self.element._minargs do self:pass(self.element._default) end end function ElementState:close() if self.open then self.open = false if #self.args < self.element._minargs then if self:default("a") then self:complete_invocation() else if #self.args == 0 then if getmetatable(self.element) == Argument then self:error("missing %s", self.name) elseif self.element._maxargs == 1 then self:error("%s requires an argument", self.name) end end self:error("%s requires %s", self.name, bound("argument", self.element._minargs, self.element._maxargs)) end end local args = self.args if self.element._maxargs <= 1 then args = args[1] end if self.element._maxargs == 1 and self.element._minargs == 0 and self.element._mincount ~= self.element._maxcount then args = self.args end self.action(self.result, self.target, args, self.overwrite) end end local ParseState = class({ result = {}, options = {}, arguments = {}, argument_i = 1, element_to_mutexes = {}, mutex_to_used_option = {}, command_actions = {} }) function ParseState:__call(parser, error_handler) self.parser = parser self.error_handler = error_handler self.charset = parser:_update_charset() self:switch(parser) return self end function ParseState:error(fmt, ...) self.error_handler(self.parser, fmt:format(...)) end function ParseState:switch(parser) self.parser = parser if parser._action then table.insert(self.command_actions, {action = parser._action, name = parser._name}) end for _, option in ipairs(parser._options) do option = ElementState(self, option) table.insert(self.options, option) for _, alias in ipairs(option.element._aliases) do self.options[alias] = option end end for _, mutex in ipairs(parser._mutexes) do for _, option in ipairs(mutex) do if not self.element_to_mutexes[option] then self.element_to_mutexes[option] = {} end table.insert(self.element_to_mutexes[option], mutex) end end for _, argument in ipairs(parser._arguments) do argument = ElementState(self, argument) table.insert(self.arguments, argument) argument:invoke() end self.handle_options = parser._handle_options self.argument = self.arguments[self.argument_i] self.commands = parser._commands for _, command in ipairs(self.commands) do for _, alias in ipairs(command._aliases) do self.commands[alias] = command end end end function ParseState:get_option(name) local option = self.options[name] if not option then self:error("unknown option '%s'%s", name, get_tip(self.options, name)) else return option end end function ParseState:get_command(name) local command = self.commands[name] if not command then if #self.commands > 0 then self:error("unknown command '%s'%s", name, get_tip(self.commands, name)) else self:error("too many arguments") end else return command end end function ParseState:invoke(option, name) self:close() if self.element_to_mutexes[option.element] then for _, mutex in ipairs(self.element_to_mutexes[option.element]) do local used_option = self.mutex_to_used_option[mutex] if used_option and used_option ~= option then self:error("option '%s' can not be used together with %s", name, used_option.name) else self.mutex_to_used_option[mutex] = option end end end if option:invoke(name) then self.option = option end end function ParseState:pass(arg) if self.option then if not self.option:pass(arg) then self.option = nil end elseif self.argument then if not self.argument:pass(arg) then self.argument_i = self.argument_i + 1 self.argument = self.arguments[self.argument_i] end else local command = self:get_command(arg) self.result[command._target or command._name] = true if self.parser._command_target then self.result[self.parser._command_target] = command._name end self:switch(command) end end function ParseState:close() if self.option then self.option:close() self.option = nil end end function ParseState:finalize() self:close() for i = self.argument_i, #self.arguments do local argument = self.arguments[i] if #argument.args == 0 and argument:default("u") then argument:complete_invocation() else argument:close() end end if self.parser._require_command and #self.commands > 0 then self:error("a command is required") end for _, option in ipairs(self.options) do local name = option.name or ("option '%s'"):format(option.element._name) if option.invocations == 0 then if option:default("u") then option:invoke(name) option:complete_invocation() option:close() end end local mincount = option.element._mincount if option.invocations < mincount then if option:default("a") then while option.invocations < mincount do option:invoke(name) option:close() end elseif option.invocations == 0 then self:error("missing %s", name) else self:error("%s must be used %s", name, bound("time", mincount, option.element._maxcount)) end end end for i = #self.command_actions, 1, -1 do self.command_actions[i].action(self.result, self.command_actions[i].name) end end function ParseState:parse(args) for _, arg in ipairs(args) do local plain = true if self.handle_options then local first = arg:sub(1, 1) if self.charset[first] then if #arg > 1 then plain = false if arg:sub(2, 2) == first then if #arg == 2 then self:close() self.handle_options = false else local equals = arg:find "=" if equals then local name = arg:sub(1, equals - 1) local option = self:get_option(name) if option.element._maxargs <= 0 then self:error("option '%s' does not take arguments", name) end self:invoke(option, name) self:pass(arg:sub(equals + 1)) else local option = self:get_option(arg) self:invoke(option, arg) end end else for i = 2, #arg do local name = first .. arg:sub(i, i) local option = self:get_option(name) self:invoke(option, name) if i ~= #arg and option.element._maxargs > 0 then self:pass(arg:sub(i + 1)) break end end end end end end if plain then self:pass(arg) end end self:finalize() return self.result end function Parser:error(msg) io.stderr:write(("%s\n\nError: %s\n"):format(self:get_usage(), msg)) os.exit(1) end -- Compatibility with strict.lua and other checkers: local default_cmdline = rawget(_G, "arg") or {} function Parser:_parse(args, error_handler) return ParseState(self, error_handler):parse(args or default_cmdline) end function Parser:parse(args) return self:_parse(args, self.error) end local function xpcall_error_handler(err) return tostring(err) .. "\noriginal " .. debug.traceback("", 2):sub(2) end function Parser:pparse(args) local parse_error local ok, result = xpcall(function() return self:_parse(args, function(_, err) parse_error = err error(err, 0) end) end, xpcall_error_handler) if ok then return true, result elseif not parse_error then error(result, 0) else return false, parse_error end end return function(...) return Parser(default_cmdline[0]):add_help(true)(...) end argparse-0.5.0/.luacheckrc0000644000175000017500000000005312632007121014447 0ustar vsevavsevastd = "min" files["spec/"].std = "+busted"