-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
185 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
"""A package to help automatically create command-line interface from configuration or code.""" | ||
|
||
__version__ = "1.4.0" | ||
__version__ = "1.5.0" | ||
|
||
from .cap import ConfigArgumentParser | ||
from .gap import GlobalArgumentParser | ||
from .tap import TypeArgumentParser | ||
|
||
__all__ = ["ConfigArgumentParser", "TypeArgumentParser"] | ||
__all__ = ["ConfigArgumentParser", "TypeArgumentParser", "GlobalArgumentParser"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
"""A module for building command-line interface from globals.""" | ||
|
||
import argparse | ||
import inspect | ||
|
||
|
||
class GlobalArgumentParser: | ||
"""Parser parsing and updating global variables. | ||
Attributes: | ||
parser: An `~argparse.ArgumentParser`. | ||
defaults: A `dict` contains the default arguments. | ||
args: A `dict` contains the parsed arguments. | ||
""" | ||
|
||
def __init__(self): | ||
"""Initialize GlobalArgumentParser.""" | ||
self._init_parser() | ||
self.defaults = {} | ||
self.args = {} | ||
self.help = {} | ||
self._globals = {} | ||
|
||
def _init_parser(self): | ||
self.parser = argparse.ArgumentParser( | ||
formatter_class=argparse.ArgumentDefaultsHelpFormatter | ||
) | ||
|
||
def _read_globals(self, stack=2): | ||
"""Read and parse the attributes of global variables. | ||
Convert attributes to :attr:`defaults`. | ||
""" | ||
self._globals = dict(inspect.getmembers(inspect.stack()[stack][0]))["f_globals"] | ||
self.defaults = { | ||
k: v | ||
for k, v in self._globals.items() | ||
if not k.startswith("_") | ||
and not inspect.ismodule(v) | ||
and not inspect.isclass(v) | ||
and not inspect.isfunction(v) | ||
and not isinstance(v, GlobalArgumentParser) | ||
} | ||
self.help = {k: str(k) for k in self.defaults} | ||
|
||
def _add_arguments(self, shorts=""): | ||
"""Add arguments to parser according to the default. | ||
Args: | ||
shorts: A sequence of short option letters for the leading options. | ||
""" | ||
boolean_to_action = {True: "store_false", False: "store_true"} | ||
for i, (option, value) in enumerate(self.defaults.items()): | ||
flags = [f"--{option.replace('_', '-')}"] | ||
if i < len(shorts): | ||
flags.insert(0, f"-{shorts[i]}") | ||
if isinstance(value, bool): | ||
self.parser.add_argument( | ||
*flags, | ||
action=boolean_to_action[value], | ||
help=self.help[option], | ||
) | ||
else: | ||
self.parser.add_argument( | ||
*flags, default=value, type=type(value), help=self.help[option] | ||
) | ||
|
||
def _parse_args(self, args=None): | ||
"""Convert argument strings to dictionary :attr:`args`. | ||
Return a `dict` containing arguments. | ||
""" | ||
namespace = self.parser.parse_args(args) | ||
self.args = vars(namespace) | ||
return self.args | ||
|
||
def _change_globals(self): | ||
"""Update global variables.""" | ||
self._globals.update(self.args) | ||
|
||
def parse_globals(self, args=None, *, shorts=""): | ||
"""Parse arguments and update global variables. | ||
Args: | ||
args: A list of strings to parse. The default is taken from `sys.argv`. | ||
shorts: A sequence of short option letters for the leading options. | ||
Returns: | ||
A `dict` containing updated arguments. | ||
""" | ||
self._read_globals() | ||
self._add_arguments(shorts=shorts) | ||
self._parse_args(args=args) | ||
self._change_globals() | ||
return self.args |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from configargparser import gap | ||
|
||
a_string = "abc" | ||
a_float = 1.23 | ||
a_boolean = False | ||
an_integer = 0 | ||
|
||
|
||
class TestConfigArgumentParser: | ||
def setup_method(self): | ||
self.args = { | ||
"a_string": a_string, | ||
"a_float": a_float, | ||
"a_boolean": a_boolean, | ||
"an_integer": an_integer, | ||
} | ||
self.parser = gap.GlobalArgumentParser() | ||
self.parser._read_globals(stack=1) | ||
|
||
def teardown_method(self): | ||
self.args = { | ||
"a_string": a_string, | ||
"a_float": a_float, | ||
"a_boolean": a_boolean, | ||
"an_integer": an_integer, | ||
} | ||
self.parser = gap.GlobalArgumentParser() | ||
|
||
def test_read_globals(self): | ||
assert self.parser.defaults == self.args | ||
|
||
def test_parse_args_default(self): | ||
self.parser._add_arguments() | ||
self.parser._parse_args([]) | ||
assert self.parser.args == self.args | ||
|
||
def test_parse_args_separate(self): | ||
self.parser._add_arguments() | ||
self.parser._parse_args("--a-float 1".split()) | ||
assert self.parser.args["a_float"] == 1.0 | ||
self.parser._parse_args(["--a-boolean"]) | ||
assert self.parser.args["a_boolean"] | ||
|
||
def test_parse_args_short(self): | ||
self.parser._add_arguments(shorts="sfb") | ||
self.parser._parse_args("-b -f 1".split()) | ||
assert self.parser.args["a_float"] == 1.0 | ||
assert self.parser.args["a_boolean"] | ||
|
||
def test_update_globals(self): | ||
self.parser.parse_globals("-b -f 1".split(), shorts="sfb") | ||
assert a_string == "abc" | ||
assert a_float == 1.0 | ||
assert a_boolean | ||
assert an_integer == 0 |