argparse: Initial minimal implementation of argparse.

CPython's argparse needs around 160k heap just for the module, and has
heaps of dependencies.  This minimal version can run in 16k heap and has
only sys and namedtuple dependencies (which are builtin to MicroPython).
pull/28/merge
Damien George 2015-06-05 23:21:03 +01:00
rodzic 132ab9520e
commit eba0c0ad8d
4 zmienionych plików z 217 dodań i 7 usunięć

Wyświetl plik

@ -0,0 +1,176 @@
"""
Minimal and functional version of CPython's argparse module.
"""
import sys
from _collections import namedtuple
class _ArgError(BaseException):
pass
class _Arg:
def __init__(self, name, dest, action, nargs, const, default, help):
self.name = name
self.dest = dest
self.action = action
self.nargs = nargs
self.const = const
self.default = default
self.help = help
def parse(self, args):
# parse args for this arg
if self.action == "store":
if self.nargs == None:
if args:
return args.pop(0)
else:
raise _ArgError("expecting value for %s" % self.name)
elif self.nargs == "?":
if args:
return args.pop(0)
else:
return self.default
else:
if self.nargs == "*":
n = -1
elif self.nargs == "+":
if not args:
raise _ArgError("expecting value for %s" % self.name)
n = -1
else:
n = int(self.nargs)
ret = []
stop_at_opt = True
while args and n != 0:
if stop_at_opt and args[0].startswith("-") and args[0] != "-":
if args[0] == "--":
stop_at_opt = False
args.pop(0)
else:
break
else:
ret.append(args.pop(0))
n -= 1
if n > 0:
raise _ArgError("expecting value for %s" % self.name)
return ret
elif self.action == "store_const":
return self.const
else:
assert False
class ArgumentParser:
def __init__(self, *, description):
self.description = description
self.opt = []
self.pos = []
def add_argument(self, name, **kwargs):
action = kwargs.get("action", "store")
if action == "store_true":
action = "store_const"
const = True
default = kwargs.get("default", False)
elif action == "store_false":
action = "store_const"
const = False
default = kwargs.get("default", True)
else:
const = kwargs.get("const", None)
default = kwargs.get("default", None)
if name.startswith("-"):
list = self.opt
if name.startswith("--"):
dest = kwargs.get("dest", name[2:])
else:
dest = kwargs.get("dest", name[1:])
else:
list = self.pos
dest = kwargs.get("dest", name)
list.append(
_Arg(name, dest, action, kwargs.get("nargs", None),
const, default, kwargs.get("help", "")))
def usage(self, full):
# print short usage
print("usage: %s [-h]" % sys.argv[0], end="")
def render_arg(arg):
if arg.action == "store":
if arg.nargs is None:
return " %s" % arg.dest
if isinstance(arg.nargs, int):
return " %s(x%d)" % (arg.dest, arg.nargs)
else:
return " %s%s" % (arg.dest, arg.nargs)
else:
return ""
for opt in self.opt:
print(" [%s%s]" % (opt.name, render_arg(opt)), end="")
for pos in self.pos:
print(render_arg(pos), end="")
print()
if not full:
return
# print full information
print()
print(self.description)
if self.pos:
print("\npositional args:")
for pos in self.pos:
print(" %-16s%s" % (pos.name, pos.help))
print("\noptional args:")
print(" -h, --help show this message and exit")
for opt in self.opt:
print(" %-16s%s" % (opt.name + render_arg(opt), opt.help))
def parse_args(self, args=None):
if args is None:
args = sys.argv[1:]
else:
args = args[:]
try:
return self._parse_args(args)
except _ArgError as e:
self.usage(False)
print("error:", e)
sys.exit(2)
def _parse_args(self, args):
# add optional args with defaults
arg_dest = []
arg_vals = []
for opt in self.opt:
arg_dest.append(opt.dest)
arg_vals.append(opt.default)
# parse all args
parsed_pos = False
while args or not parsed_pos:
if args and args[0].startswith("-") and args[0] != "-" and args[0] != "--":
# optional arg
a = args.pop(0)
if a in ("-h", "--help"):
self.usage(True)
sys.exit(0)
found = False
for i, opt in enumerate(self.opt):
if a == opt.name:
arg_vals[i] = opt.parse(args)
found = True
break
if not found:
raise _ArgError("unknown option %s" % a)
else:
# positional arg
if parsed_pos:
raise _ArgError("extra args: %s" % " ".join(args))
for pos in self.pos:
arg_dest.append(pos.dest)
arg_vals.append(pos.parse(args))
parsed_pos = True
# build and return named tuple with arg values
return namedtuple("args", arg_dest)(*arg_vals)

Wyświetl plik

@ -1,3 +1,4 @@
srctype=dummy
type=module
version=0.0.0
srctype = micropython-lib
type = module
version = 0.1
author = Damien George

Wyświetl plik

@ -6,11 +6,11 @@ from setuptools import setup
setup(name='micropython-argparse',
version='0.0.0',
description='Dummy argparse module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
version='0.1',
description='argparse module for MicroPython',
long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.",
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author='Damien George',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',

Wyświetl plik

@ -0,0 +1,33 @@
import argparse
parser = argparse.ArgumentParser(description="command line program")
parser.add_argument("a")
parser.add_argument("b")
parser.add_argument("c")
args = parser.parse_args(["1", "2", "3"])
assert args.a == "1" and args.b == "2" and args.c == "3"
parser = argparse.ArgumentParser()
parser.add_argument("-a", action="store_true")
parser.add_argument("-b", default=123)
args = parser.parse_args([])
assert args.a == False and args.b == 123
args = parser.parse_args(["-a"])
assert args.a == True and args.b == 123
args = parser.parse_args(["-b", "456"])
assert args.a == False and args.b == 456
parser = argparse.ArgumentParser()
parser.add_argument("files", nargs="+")
args = parser.parse_args(["a"])
assert args.files == ["a"]
args = parser.parse_args(["a", "b", "c"])
assert args.files == ["a", "b", "c"]
parser = argparse.ArgumentParser()
parser.add_argument("files1", nargs=2)
parser.add_argument("files2", nargs="*")
args = parser.parse_args(["a", "b"])
assert args.files1 == ["a", "b"] and args.files2 == []
args = parser.parse_args(["a", "b", "c"])
assert args.files1 == ["a", "b"] and args.files2 == ["c"]