superparsing 0.0.0.dev2__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {superparsing-0.0.0.dev2/superparsing.egg-info → superparsing-0.2.0}/PKG-INFO +1 -1
- {superparsing-0.0.0.dev2 → superparsing-0.2.0}/pyproject.toml +1 -1
- superparsing-0.2.0/src/superparsing/__init__.py +6 -0
- superparsing-0.2.0/src/superparsing/core/SubCommand.py +27 -0
- superparsing-0.2.0/src/superparsing/core/SuperFlag.py +53 -0
- superparsing-0.2.0/src/superparsing/core/SuperParseError.py +5 -0
- superparsing-0.2.0/src/superparsing/core/SuperParser.py +157 -0
- superparsing-0.2.0/src/superparsing/core/__init__.py +0 -0
- superparsing-0.2.0/src/superparsing/py.typed +0 -0
- {superparsing-0.0.0.dev2 → superparsing-0.2.0/src/superparsing.egg-info}/PKG-INFO +1 -1
- superparsing-0.2.0/src/superparsing.egg-info/SOURCES.txt +24 -0
- superparsing-0.2.0/src/superparsing.egg-info/top_level.txt +1 -0
- superparsing-0.2.0/tests/test_0.py +94 -0
- superparsing-0.2.0/tests/test_comprehensive_TestParseArgs.py +138 -0
- superparsing-0.2.0/tests/test_comprehensive_TestSubCommand.py +57 -0
- superparsing-0.2.0/tests/test_comprehensive_TestSuperFlag.py +88 -0
- superparsing-0.2.0/tests/test_comprehensive_TestSuperParserBasics.py +53 -0
- superparsing-0.2.0/tests/test_comprehensive_TestSuperParserText.py +81 -0
- superparsing-0.2.0/tests/test_patch.py +29 -0
- superparsing-0.0.0.dev2/make/env.py +0 -85
- superparsing-0.0.0.dev2/superparsing.egg-info/SOURCES.txt +0 -12
- superparsing-0.0.0.dev2/superparsing.egg-info/top_level.txt +0 -1
- superparsing-0.0.0.dev2/tests/test_1984.py +0 -10
- {superparsing-0.0.0.dev2 → superparsing-0.2.0}/LICENSE.txt +0 -0
- {superparsing-0.0.0.dev2 → superparsing-0.2.0}/MANIFEST.in +0 -0
- {superparsing-0.0.0.dev2 → superparsing-0.2.0}/README.rst +0 -0
- {superparsing-0.0.0.dev2 → superparsing-0.2.0}/run_tests.py +0 -0
- {superparsing-0.0.0.dev2 → superparsing-0.2.0}/setup.cfg +0 -0
- {superparsing-0.0.0.dev2 → superparsing-0.2.0/src}/superparsing.egg-info/dependency_links.txt +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from superparsing.core.SubCommand import SubCommand
|
|
2
|
+
from superparsing.core.SuperFlag import SuperFlag
|
|
3
|
+
from superparsing.core.SuperParseError import SuperParseError
|
|
4
|
+
from superparsing.core.SuperParser import SuperParser
|
|
5
|
+
|
|
6
|
+
__all__ = ["SuperParseError", "SuperFlag", "SubCommand", "SuperParser"]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Self
|
|
4
|
+
|
|
5
|
+
__all__ = ["SubCommand"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class SubCommand:
|
|
10
|
+
name: object
|
|
11
|
+
aliases: Iterable[object] = ()
|
|
12
|
+
help: object = ""
|
|
13
|
+
|
|
14
|
+
def _usage(self: Self) -> str:
|
|
15
|
+
return ", ".join(map(str, [self.name] + list(self.aliases)))
|
|
16
|
+
|
|
17
|
+
def intro(self: Self) -> str:
|
|
18
|
+
ans: str
|
|
19
|
+
aliases_: list[str]
|
|
20
|
+
aliases_ = list(map(str, self.aliases))
|
|
21
|
+
ans = str(self.name)
|
|
22
|
+
if not aliases_:
|
|
23
|
+
return ans + ":"
|
|
24
|
+
ans += "("
|
|
25
|
+
ans += ", ".join(aliases_)
|
|
26
|
+
ans += "):"
|
|
27
|
+
return ans
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Optional, Self
|
|
4
|
+
|
|
5
|
+
__all__ = ["SuperFlag"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class SuperFlag:
|
|
10
|
+
keys: Iterable[object] = ()
|
|
11
|
+
help: object = ""
|
|
12
|
+
message: Optional[object] = None
|
|
13
|
+
|
|
14
|
+
def _keys(self: Self) -> list[str]:
|
|
15
|
+
return list(map(str, self.keys))
|
|
16
|
+
|
|
17
|
+
def _message(self: Self) -> Optional[str]:
|
|
18
|
+
if self.message is None:
|
|
19
|
+
return None
|
|
20
|
+
else:
|
|
21
|
+
return str(self.message)
|
|
22
|
+
|
|
23
|
+
def _usage(self: Self) -> str:
|
|
24
|
+
ans: list[str]
|
|
25
|
+
ans = self._keys()
|
|
26
|
+
if ans:
|
|
27
|
+
return "[" + ", ".join(ans) + "]"
|
|
28
|
+
else:
|
|
29
|
+
return ""
|
|
30
|
+
|
|
31
|
+
def intro(self: Self) -> str:
|
|
32
|
+
ans: list[str]
|
|
33
|
+
ans = self._keys()
|
|
34
|
+
if ans:
|
|
35
|
+
return ", ".join(ans) + ":"
|
|
36
|
+
else:
|
|
37
|
+
return ""
|
|
38
|
+
|
|
39
|
+
def longopts(self: Self) -> tuple[str, ...]:
|
|
40
|
+
ans: list[str]
|
|
41
|
+
ans = list()
|
|
42
|
+
for flag in map(str, self.keys):
|
|
43
|
+
if flag.startswith("--"):
|
|
44
|
+
ans.append(flag[2:].split("=")[0])
|
|
45
|
+
return tuple(ans)
|
|
46
|
+
|
|
47
|
+
def shortopts(self: Self) -> str:
|
|
48
|
+
ans: str
|
|
49
|
+
ans = ""
|
|
50
|
+
for flag in map(str, self.keys):
|
|
51
|
+
if len(flag) == 2 and flag[0] == "-":
|
|
52
|
+
ans += flag[1]
|
|
53
|
+
return ans
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import getopt
|
|
2
|
+
import importlib.metadata
|
|
3
|
+
import sys
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from functools import partial
|
|
7
|
+
from typing import Any, Final, Optional, Self
|
|
8
|
+
|
|
9
|
+
from .SubCommand import SubCommand
|
|
10
|
+
from .SuperFlag import SuperFlag
|
|
11
|
+
from .SuperParseError import SuperParseError
|
|
12
|
+
|
|
13
|
+
__all__ = ["SuperParser"]
|
|
14
|
+
|
|
15
|
+
INDENT: Final[int] = 2
|
|
16
|
+
SHIFT: Final[int] = 8
|
|
17
|
+
ARGS_MESSAGE: Final[str] = (
|
|
18
|
+
"These remaining args are parsed again by the chosen subcommand."
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class SuperParser:
|
|
24
|
+
fromfile_prefix_chars: object = field(default="", kw_only=True)
|
|
25
|
+
helpFlag: SuperFlag = field(
|
|
26
|
+
default_factory=partial(SuperFlag, help="print this message and exit"),
|
|
27
|
+
kw_only=True,
|
|
28
|
+
)
|
|
29
|
+
prog: Optional[str] = field(default=None, kw_only=True)
|
|
30
|
+
subCommands: list[SubCommand] = field(default_factory=list, kw_only=True)
|
|
31
|
+
versionFlag: SuperFlag = field(
|
|
32
|
+
default_factory=partial(SuperFlag, help="print version and exit"),
|
|
33
|
+
kw_only=True,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def _keys_message(self: Self) -> Optional[str]:
|
|
37
|
+
ans: str
|
|
38
|
+
shift: int
|
|
39
|
+
help_intro = self.helpFlag.intro()
|
|
40
|
+
version_intro = self.versionFlag.intro()
|
|
41
|
+
if help_intro + version_intro == "":
|
|
42
|
+
return None
|
|
43
|
+
ans = "keys:"
|
|
44
|
+
shift = max(SHIFT, len(help_intro), len(version_intro))
|
|
45
|
+
shift += INDENT
|
|
46
|
+
if help_intro:
|
|
47
|
+
ans += "\n"
|
|
48
|
+
ans += " " * INDENT
|
|
49
|
+
ans += help_intro.ljust(shift)
|
|
50
|
+
ans += str(self.helpFlag.help)
|
|
51
|
+
if version_intro:
|
|
52
|
+
ans += "\n"
|
|
53
|
+
ans += " " * INDENT
|
|
54
|
+
ans += version_intro.ljust(shift)
|
|
55
|
+
ans += str(self.versionFlag.help)
|
|
56
|
+
return ans
|
|
57
|
+
|
|
58
|
+
def _prog(self: Self) -> str:
|
|
59
|
+
if self.prog is None:
|
|
60
|
+
return str(sys.argv[0])
|
|
61
|
+
else:
|
|
62
|
+
return str(self.prog)
|
|
63
|
+
|
|
64
|
+
def _subCommands_message(self: Self) -> Optional[str]:
|
|
65
|
+
intros = list(map(SubCommand.intro, self.subCommands))
|
|
66
|
+
if not intros:
|
|
67
|
+
return None
|
|
68
|
+
ans = "subcommands:"
|
|
69
|
+
shift = max(SHIFT, *map(len, intros))
|
|
70
|
+
shift += INDENT
|
|
71
|
+
for intro, cmd in zip(intros, self.subCommands):
|
|
72
|
+
ans += "\n" + " " * INDENT + intro.ljust(shift) + str(cmd.help)
|
|
73
|
+
return ans
|
|
74
|
+
|
|
75
|
+
def add_subCommand(self: Self, **kwargs: Any) -> SubCommand:
|
|
76
|
+
self.subCommands.append(SubCommand(**kwargs))
|
|
77
|
+
return self.subCommands[-1]
|
|
78
|
+
|
|
79
|
+
def help(self: Self) -> str:
|
|
80
|
+
ans: list[Optional[str]]
|
|
81
|
+
intro = self.helpFlag._message()
|
|
82
|
+
if intro is not None:
|
|
83
|
+
return intro
|
|
84
|
+
ans = list()
|
|
85
|
+
ans.append(self.usage())
|
|
86
|
+
ans.append(self._keys_message())
|
|
87
|
+
ans.append(self._subCommands_message())
|
|
88
|
+
ans.append("args:\n" + " " * INDENT + ARGS_MESSAGE)
|
|
89
|
+
filtered = [part for part in ans if part is not None]
|
|
90
|
+
return "\n\n".join(filtered)
|
|
91
|
+
|
|
92
|
+
def parse_args(
|
|
93
|
+
self: Self, args: Optional[Iterable[object]] = None, /
|
|
94
|
+
) -> list[str]:
|
|
95
|
+
args_: list[str]
|
|
96
|
+
cmd: SubCommand
|
|
97
|
+
longopts: tuple[str, ...]
|
|
98
|
+
optitems: list[tuple[str, str]]
|
|
99
|
+
shortopts: str
|
|
100
|
+
args_ = list()
|
|
101
|
+
for arg in map(str, sys.argv[1:] if args is None else args):
|
|
102
|
+
prefix_chars = str(self.fromfile_prefix_chars)
|
|
103
|
+
if arg == "" or arg[0] not in prefix_chars:
|
|
104
|
+
args_.append(arg)
|
|
105
|
+
continue
|
|
106
|
+
with open(arg[1:], "r") as stream:
|
|
107
|
+
args_.extend(stream.read().splitlines())
|
|
108
|
+
shortopts = self.helpFlag.shortopts() + self.versionFlag.shortopts()
|
|
109
|
+
longopts = self.helpFlag.longopts() + self.versionFlag.longopts()
|
|
110
|
+
try:
|
|
111
|
+
optitems, args_ = getopt.getopt(
|
|
112
|
+
args=args_, shortopts=shortopts, longopts=longopts
|
|
113
|
+
)
|
|
114
|
+
except Exception:
|
|
115
|
+
raise SuperParseError("Failed getopt!")
|
|
116
|
+
if set(self.helpFlag._keys()).intersection(dict(optitems).keys()):
|
|
117
|
+
_print(self.help())
|
|
118
|
+
return list()
|
|
119
|
+
if set(self.versionFlag._keys()).intersection(dict(optitems).keys()):
|
|
120
|
+
_print(self.version())
|
|
121
|
+
return list()
|
|
122
|
+
if not args_:
|
|
123
|
+
raise SuperParseError("Subcommand missing!")
|
|
124
|
+
for cmd in self.subCommands:
|
|
125
|
+
if args_[0] == str(cmd.name):
|
|
126
|
+
return args_
|
|
127
|
+
for cmd in self.subCommands:
|
|
128
|
+
if args_[0] in map(str, cmd.aliases):
|
|
129
|
+
args_[0] = str(cmd.name)
|
|
130
|
+
return args_
|
|
131
|
+
raise SuperParseError("Subcommand %r unknown!" % args_[0])
|
|
132
|
+
|
|
133
|
+
def usage(self: Self) -> str:
|
|
134
|
+
ans: str
|
|
135
|
+
piece: str
|
|
136
|
+
ans = "usage:"
|
|
137
|
+
ans += " " + self._prog()
|
|
138
|
+
for flag in (self.helpFlag, self.versionFlag):
|
|
139
|
+
piece = flag._usage()
|
|
140
|
+
if piece:
|
|
141
|
+
ans += " "
|
|
142
|
+
ans += piece
|
|
143
|
+
ans += " {"
|
|
144
|
+
ans += ", ".join(map(SubCommand._usage, self.subCommands))
|
|
145
|
+
ans += "} [arg ...]"
|
|
146
|
+
return ans
|
|
147
|
+
|
|
148
|
+
def version(self: Self) -> str:
|
|
149
|
+
if self.versionFlag.message is not None:
|
|
150
|
+
return str(self.versionFlag.message)
|
|
151
|
+
else:
|
|
152
|
+
return f"{self._prog()}, version {importlib.metadata.version(self._prog())}"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _print(value: Optional[str]) -> None:
|
|
156
|
+
if value is not None:
|
|
157
|
+
print(value)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.rst
|
|
4
|
+
pyproject.toml
|
|
5
|
+
run_tests.py
|
|
6
|
+
setup.cfg
|
|
7
|
+
src/superparsing/__init__.py
|
|
8
|
+
src/superparsing/py.typed
|
|
9
|
+
src/superparsing.egg-info/PKG-INFO
|
|
10
|
+
src/superparsing.egg-info/SOURCES.txt
|
|
11
|
+
src/superparsing.egg-info/dependency_links.txt
|
|
12
|
+
src/superparsing.egg-info/top_level.txt
|
|
13
|
+
src/superparsing/core/SubCommand.py
|
|
14
|
+
src/superparsing/core/SuperFlag.py
|
|
15
|
+
src/superparsing/core/SuperParseError.py
|
|
16
|
+
src/superparsing/core/SuperParser.py
|
|
17
|
+
src/superparsing/core/__init__.py
|
|
18
|
+
tests/test_0.py
|
|
19
|
+
tests/test_comprehensive_TestParseArgs.py
|
|
20
|
+
tests/test_comprehensive_TestSubCommand.py
|
|
21
|
+
tests/test_comprehensive_TestSuperFlag.py
|
|
22
|
+
tests/test_comprehensive_TestSuperParserBasics.py
|
|
23
|
+
tests/test_comprehensive_TestSuperParserText.py
|
|
24
|
+
tests/test_patch.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
superparsing
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
import unittest
|
|
4
|
+
from typing import Self
|
|
5
|
+
|
|
6
|
+
from superparsing import SubCommand, SuperFlag, SuperParseError, SuperParser
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestSuperFlag(unittest.TestCase):
|
|
10
|
+
|
|
11
|
+
def test_intro(self: Self) -> None:
|
|
12
|
+
flag = SuperFlag(keys=["-h", "--help"])
|
|
13
|
+
self.assertEqual(flag.intro(), "-h, --help:")
|
|
14
|
+
|
|
15
|
+
def test_shortopts(self: Self) -> None:
|
|
16
|
+
flag = SuperFlag(keys=["-h", "-v", "--help"])
|
|
17
|
+
self.assertEqual(flag.shortopts(), "hv")
|
|
18
|
+
|
|
19
|
+
def test_longopts(self: Self) -> None:
|
|
20
|
+
flag = SuperFlag(keys=["-h", "--help", "--version=1"])
|
|
21
|
+
self.assertEqual(flag.longopts(), ("help", "version"))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TestSubCommand(unittest.TestCase):
|
|
25
|
+
|
|
26
|
+
def test_intro_without_alias(self: Self) -> None:
|
|
27
|
+
cmd = SubCommand(name="run")
|
|
28
|
+
self.assertEqual(cmd.intro(), "run:")
|
|
29
|
+
|
|
30
|
+
def test_intro_with_aliases(self: Self) -> None:
|
|
31
|
+
cmd = SubCommand(name="run", aliases=["r", "execute"])
|
|
32
|
+
self.assertEqual(cmd.intro(), "run(r, execute):")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TestSuperParser(unittest.TestCase):
|
|
36
|
+
|
|
37
|
+
def setUp(self: Self) -> None:
|
|
38
|
+
self.parser = SuperParser(
|
|
39
|
+
prog="tool",
|
|
40
|
+
helpFlag=SuperFlag(keys=["-h", "--help"]),
|
|
41
|
+
versionFlag=SuperFlag(keys=["-v", "--version"]),
|
|
42
|
+
)
|
|
43
|
+
self.parser.add_subCommand(
|
|
44
|
+
name="run", aliases=["r"], help="run command"
|
|
45
|
+
)
|
|
46
|
+
self.parser.add_subCommand(name="test", help="test command")
|
|
47
|
+
|
|
48
|
+
def test_usage(self: Self) -> None:
|
|
49
|
+
usage = self.parser.usage()
|
|
50
|
+
self.assertIn("usage:", usage)
|
|
51
|
+
self.assertIn("tool", usage)
|
|
52
|
+
self.assertIn("run, r", usage)
|
|
53
|
+
|
|
54
|
+
def test_help_output(self: Self) -> None:
|
|
55
|
+
text = self.parser.help()
|
|
56
|
+
self.assertIn("usage:", text)
|
|
57
|
+
self.assertIn("subcommands:", text)
|
|
58
|
+
self.assertIn("args:", text)
|
|
59
|
+
|
|
60
|
+
def test_parse_valid_subcommand(self: Self) -> None:
|
|
61
|
+
result = self.parser.parse_args(["run"])
|
|
62
|
+
self.assertEqual(result, ["run"])
|
|
63
|
+
|
|
64
|
+
def test_parse_alias(self: Self) -> None:
|
|
65
|
+
result = self.parser.parse_args(["r"])
|
|
66
|
+
self.assertEqual(result, ["run"])
|
|
67
|
+
|
|
68
|
+
def test_parse_unknown_subcommand(self: Self) -> None:
|
|
69
|
+
with self.assertRaises(SuperParseError):
|
|
70
|
+
self.parser.parse_args(["unknown"])
|
|
71
|
+
|
|
72
|
+
def test_parse_missing_subcommand(self: Self) -> None:
|
|
73
|
+
with self.assertRaises(SuperParseError):
|
|
74
|
+
self.parser.parse_args([])
|
|
75
|
+
|
|
76
|
+
def test_version_custom_message(self: Self) -> None:
|
|
77
|
+
parser = SuperParser(
|
|
78
|
+
prog="tool", versionFlag=SuperFlag(message="custom version")
|
|
79
|
+
)
|
|
80
|
+
self.assertEqual(parser.version(), "custom version")
|
|
81
|
+
|
|
82
|
+
def test_fromfile_prefix_chars(self: Self) -> None:
|
|
83
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
84
|
+
fname = os.path.join(tmpdir, "a.txt")
|
|
85
|
+
with open(fname, "w") as f:
|
|
86
|
+
f.write("run\narg1\narg2")
|
|
87
|
+
parser = SuperParser(prog="tool", fromfile_prefix_chars="@")
|
|
88
|
+
parser.add_subCommand(name="run")
|
|
89
|
+
result = parser.parse_args([f"@{fname}"])
|
|
90
|
+
self.assertEqual(result, ["run", "arg1", "arg2"])
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if __name__ == "__main__":
|
|
94
|
+
unittest.main()
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import io
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import tempfile
|
|
6
|
+
import unittest
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import Self
|
|
9
|
+
from unittest import mock
|
|
10
|
+
|
|
11
|
+
from superparsing import SuperParseError, SuperParser
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def capture_stdout(
|
|
15
|
+
func: Callable[..., object],
|
|
16
|
+
*args: object,
|
|
17
|
+
**kwargs: object,
|
|
18
|
+
) -> tuple[object, str]:
|
|
19
|
+
"""Run ``func`` and return (return_value, captured_stdout_str)."""
|
|
20
|
+
buf = io.StringIO()
|
|
21
|
+
with contextlib.redirect_stdout(buf):
|
|
22
|
+
result = func(*args, **kwargs)
|
|
23
|
+
return result, buf.getvalue()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# SuperParser: parse_args
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
class TestParseArgs(unittest.TestCase):
|
|
30
|
+
def make(self: Self) -> SuperParser:
|
|
31
|
+
p = SuperParser(prog="tool")
|
|
32
|
+
p.helpFlag.keys = ["-h", "--help"]
|
|
33
|
+
p.versionFlag.keys = ["-v", "--version"]
|
|
34
|
+
p.versionFlag.message = "tool 1.0"
|
|
35
|
+
p.add_subCommand(name="build", aliases=["b", "mk"], help="build")
|
|
36
|
+
p.add_subCommand(name="clean", help="clean")
|
|
37
|
+
return p
|
|
38
|
+
|
|
39
|
+
def test_plain_subcommand(self: Self) -> None:
|
|
40
|
+
p = self.make()
|
|
41
|
+
self.assertEqual(p.parse_args(["build"]), ["build"])
|
|
42
|
+
|
|
43
|
+
def test_subcommand_with_trailing_args(self: Self) -> None:
|
|
44
|
+
p = self.make()
|
|
45
|
+
self.assertEqual(
|
|
46
|
+
p.parse_args(["build", "--force", "target"]),
|
|
47
|
+
["build", "--force", "target"],
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def test_alias_is_rewritten_to_canonical_name(self: Self) -> None:
|
|
51
|
+
p = self.make()
|
|
52
|
+
self.assertEqual(p.parse_args(["b", "x"]), ["build", "x"])
|
|
53
|
+
self.assertEqual(p.parse_args(["mk"]), ["build"])
|
|
54
|
+
|
|
55
|
+
def test_getopt_stops_at_first_positional(self: Self) -> None:
|
|
56
|
+
# flags after the subcommand are passed through, NOT consumed as help
|
|
57
|
+
p = self.make()
|
|
58
|
+
_, out = capture_stdout(p.parse_args, ["build", "-h"])
|
|
59
|
+
# parse_args returns the list (help not triggered)
|
|
60
|
+
self.assertEqual(p.parse_args(["build", "-h"]), ["build", "-h"])
|
|
61
|
+
self.assertEqual(out, "")
|
|
62
|
+
|
|
63
|
+
def test_help_short_flag_prints_and_returns_empty(self: Self) -> None:
|
|
64
|
+
p = self.make()
|
|
65
|
+
result, out = capture_stdout(p.parse_args, ["-h"])
|
|
66
|
+
self.assertEqual(result, [])
|
|
67
|
+
self.assertIn("usage: tool", out)
|
|
68
|
+
|
|
69
|
+
def test_help_long_flag(self: Self) -> None:
|
|
70
|
+
p = self.make()
|
|
71
|
+
result, out = capture_stdout(p.parse_args, ["--help"])
|
|
72
|
+
self.assertEqual(result, [])
|
|
73
|
+
self.assertIn("subcommands:", out)
|
|
74
|
+
|
|
75
|
+
def test_version_flag_prints_and_returns_empty(self: Self) -> None:
|
|
76
|
+
p = self.make()
|
|
77
|
+
result, out = capture_stdout(p.parse_args, ["-v"])
|
|
78
|
+
self.assertEqual(result, [])
|
|
79
|
+
self.assertEqual(out.strip(), "tool 1.0")
|
|
80
|
+
|
|
81
|
+
def test_help_takes_precedence_over_version(self: Self) -> None:
|
|
82
|
+
p = self.make()
|
|
83
|
+
result, out = capture_stdout(p.parse_args, ["-v", "-h"])
|
|
84
|
+
self.assertEqual(result, [])
|
|
85
|
+
self.assertIn("usage:", out)
|
|
86
|
+
self.assertNotIn("tool 1.0", out)
|
|
87
|
+
|
|
88
|
+
def test_missing_subcommand_raises(self: Self) -> None:
|
|
89
|
+
p = self.make()
|
|
90
|
+
with self.assertRaises(SuperParseError) as ctx:
|
|
91
|
+
p.parse_args([])
|
|
92
|
+
self.assertIn("Subcommand missing", str(ctx.exception))
|
|
93
|
+
|
|
94
|
+
def test_unknown_subcommand_raises(self: Self) -> None:
|
|
95
|
+
p = self.make()
|
|
96
|
+
with self.assertRaises(SuperParseError) as ctx:
|
|
97
|
+
p.parse_args(["nope"])
|
|
98
|
+
self.assertIn("unknown", str(ctx.exception))
|
|
99
|
+
|
|
100
|
+
def test_reads_sys_argv_when_args_none(self: Self) -> None:
|
|
101
|
+
p = self.make()
|
|
102
|
+
with mock.patch.object(sys, "argv", ["tool", "clean", "now"]):
|
|
103
|
+
self.assertEqual(p.parse_args(), ["clean", "now"])
|
|
104
|
+
|
|
105
|
+
def test_non_string_args_are_coerced(self: Self) -> None:
|
|
106
|
+
p = self.make()
|
|
107
|
+
# map(str, ...) means numeric tokens are stringified before matching
|
|
108
|
+
self.assertEqual(p.parse_args(["build", 5]), ["build", "5"])
|
|
109
|
+
|
|
110
|
+
def test_fromfile_prefix_expands_file(self: Self) -> None:
|
|
111
|
+
p = self.make()
|
|
112
|
+
p.fromfile_prefix_chars = "@"
|
|
113
|
+
with tempfile.TemporaryDirectory() as d:
|
|
114
|
+
path = os.path.join(d, "args.txt")
|
|
115
|
+
with open(path, "w") as fh:
|
|
116
|
+
fh.write("build\n--flag\nvalue\n")
|
|
117
|
+
self.assertEqual(
|
|
118
|
+
p.parse_args(["@" + path]),
|
|
119
|
+
["build", "--flag", "value"],
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def test_fromfile_disabled_by_default(self: Self) -> None:
|
|
123
|
+
# default fromfile_prefix_chars == "" -> "@file" treated literally
|
|
124
|
+
p = self.make()
|
|
125
|
+
with self.assertRaises(SuperParseError):
|
|
126
|
+
# "@whatever" is an unknown subcommand, not a file read
|
|
127
|
+
p.parse_args(["@whatever"])
|
|
128
|
+
|
|
129
|
+
def test_empty_string_arg_is_not_treated_as_file(self: Self) -> None:
|
|
130
|
+
p = self.make()
|
|
131
|
+
p.fromfile_prefix_chars = "@"
|
|
132
|
+
# leading "" must not crash on arg[0]; it's a positional/unknown subcmd
|
|
133
|
+
with self.assertRaises(SuperParseError):
|
|
134
|
+
p.parse_args([""])
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
unittest.main(verbosity=2)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import io
|
|
3
|
+
import unittest
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Self
|
|
6
|
+
|
|
7
|
+
from superparsing import SubCommand
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def capture_stdout(
|
|
11
|
+
func: Callable[..., object],
|
|
12
|
+
*args: object,
|
|
13
|
+
**kwargs: object,
|
|
14
|
+
) -> tuple[object, str]:
|
|
15
|
+
"""Run ``func`` and return (return_value, captured_stdout_str)."""
|
|
16
|
+
buf = io.StringIO()
|
|
17
|
+
with contextlib.redirect_stdout(buf):
|
|
18
|
+
result = func(*args, **kwargs)
|
|
19
|
+
return result, buf.getvalue()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# SubCommand
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
class TestSubCommand(unittest.TestCase):
|
|
26
|
+
def test_defaults(self: Self) -> None:
|
|
27
|
+
c = SubCommand(name="run")
|
|
28
|
+
self.assertEqual(c.name, "run")
|
|
29
|
+
self.assertEqual(tuple(c.aliases), ())
|
|
30
|
+
self.assertEqual(c.help, "")
|
|
31
|
+
|
|
32
|
+
def test_usage_no_aliases(self: Self) -> None:
|
|
33
|
+
self.assertEqual(SubCommand(name="run")._usage(), "run")
|
|
34
|
+
|
|
35
|
+
def test_usage_with_aliases(self: Self) -> None:
|
|
36
|
+
c = SubCommand(name="build", aliases=["b", "make"])
|
|
37
|
+
self.assertEqual(c._usage(), "build, b, make")
|
|
38
|
+
|
|
39
|
+
def test_usage_coerces_non_strings(self: Self) -> None:
|
|
40
|
+
c = SubCommand(name=1, aliases=[2, 3])
|
|
41
|
+
self.assertEqual(c._usage(), "1, 2, 3")
|
|
42
|
+
|
|
43
|
+
def test_intro_no_aliases(self: Self) -> None:
|
|
44
|
+
self.assertEqual(SubCommand(name="run").intro(), "run:")
|
|
45
|
+
|
|
46
|
+
def test_intro_with_aliases(self: Self) -> None:
|
|
47
|
+
c = SubCommand(name="build", aliases=["b", "make"])
|
|
48
|
+
self.assertEqual(c.intro(), "build(b, make):")
|
|
49
|
+
|
|
50
|
+
def test_intro_single_alias(self: Self) -> None:
|
|
51
|
+
self.assertEqual(
|
|
52
|
+
SubCommand(name="build", aliases=["b"]).intro(), "build(b):"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
unittest.main(verbosity=2)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import io
|
|
3
|
+
import unittest
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Self
|
|
6
|
+
|
|
7
|
+
from superparsing import SuperFlag
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def capture_stdout(
|
|
11
|
+
func: Callable[..., object],
|
|
12
|
+
*args: object,
|
|
13
|
+
**kwargs: object,
|
|
14
|
+
) -> tuple[object, str]:
|
|
15
|
+
"""Run ``func`` and return (return_value, captured_stdout_str)."""
|
|
16
|
+
buf = io.StringIO()
|
|
17
|
+
with contextlib.redirect_stdout(buf):
|
|
18
|
+
result = func(*args, **kwargs)
|
|
19
|
+
return result, buf.getvalue()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# SuperFlag
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
class TestSuperFlag(unittest.TestCase):
|
|
26
|
+
def test_defaults(self: Self) -> None:
|
|
27
|
+
f = SuperFlag()
|
|
28
|
+
self.assertEqual(tuple(f.keys), ())
|
|
29
|
+
self.assertEqual(f.help, "")
|
|
30
|
+
self.assertIsNone(f.message)
|
|
31
|
+
|
|
32
|
+
def test_keys_stringifies(self: Self) -> None:
|
|
33
|
+
f = SuperFlag(keys=["-h", "--help"])
|
|
34
|
+
self.assertEqual(f._keys(), ["-h", "--help"])
|
|
35
|
+
|
|
36
|
+
def test_keys_stringifies_non_strings(self: Self) -> None:
|
|
37
|
+
# keys is typed Iterable[object]; non-strings must be coerced via str()
|
|
38
|
+
f = SuperFlag(keys=[1, 2.5, None])
|
|
39
|
+
self.assertEqual(f._keys(), ["1", "2.5", "None"])
|
|
40
|
+
|
|
41
|
+
def test_message_none(self: Self) -> None:
|
|
42
|
+
self.assertIsNone(SuperFlag().message)
|
|
43
|
+
self.assertIsNone(SuperFlag()._message())
|
|
44
|
+
|
|
45
|
+
def test_message_coerced(self: Self) -> None:
|
|
46
|
+
self.assertEqual(SuperFlag(message=42)._message(), "42")
|
|
47
|
+
self.assertEqual(SuperFlag(message="hi")._message(), "hi")
|
|
48
|
+
|
|
49
|
+
def test_usage_with_keys(self: Self) -> None:
|
|
50
|
+
self.assertEqual(
|
|
51
|
+
SuperFlag(keys=["-h", "--help"])._usage(), "[-h, --help]"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def test_usage_empty(self: Self) -> None:
|
|
55
|
+
self.assertEqual(SuperFlag()._usage(), "")
|
|
56
|
+
|
|
57
|
+
def test_intro_with_keys(self: Self) -> None:
|
|
58
|
+
self.assertEqual(
|
|
59
|
+
SuperFlag(keys=["-h", "--help"]).intro(), "-h, --help:"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def test_intro_empty(self: Self) -> None:
|
|
63
|
+
self.assertEqual(SuperFlag().intro(), "")
|
|
64
|
+
|
|
65
|
+
def test_longopts(self: Self) -> None:
|
|
66
|
+
f = SuperFlag(keys=["-h", "--help", "--version"])
|
|
67
|
+
self.assertEqual(f.longopts(), ("help", "version"))
|
|
68
|
+
|
|
69
|
+
def test_longopts_strips_equals(self: Self) -> None:
|
|
70
|
+
f = SuperFlag(keys=["--name=VALUE"])
|
|
71
|
+
self.assertEqual(f.longopts(), ("name",))
|
|
72
|
+
|
|
73
|
+
def test_longopts_ignores_short_and_bare(self: Self) -> None:
|
|
74
|
+
f = SuperFlag(keys=["-h", "h", "help", "--x"])
|
|
75
|
+
self.assertEqual(f.longopts(), ("x",))
|
|
76
|
+
|
|
77
|
+
def test_shortopts(self: Self) -> None:
|
|
78
|
+
f = SuperFlag(keys=["-h", "-v", "--help"])
|
|
79
|
+
self.assertEqual(f.shortopts(), "hv")
|
|
80
|
+
|
|
81
|
+
def test_shortopts_ignores_long_and_malformed(self: Self) -> None:
|
|
82
|
+
f = SuperFlag(keys=["--help", "-ab", "x", "-"])
|
|
83
|
+
# only exactly "-X" (len 2, leading dash) counts
|
|
84
|
+
self.assertEqual(f.shortopts(), "")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
unittest.main(verbosity=2)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import io
|
|
3
|
+
import unittest
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Self
|
|
6
|
+
|
|
7
|
+
from superparsing import SubCommand, SuperParser
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def capture_stdout(
|
|
11
|
+
func: Callable[..., object],
|
|
12
|
+
*args: object,
|
|
13
|
+
**kwargs: object,
|
|
14
|
+
) -> tuple[object, str]:
|
|
15
|
+
"""Run ``func`` and return (return_value, captured_stdout_str)."""
|
|
16
|
+
buf = io.StringIO()
|
|
17
|
+
with contextlib.redirect_stdout(buf):
|
|
18
|
+
result = func(*args, **kwargs)
|
|
19
|
+
return result, buf.getvalue()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TestSuperParserBasics(unittest.TestCase):
|
|
23
|
+
def test_default_flags_have_help_text(self: Self) -> None:
|
|
24
|
+
p = SuperParser()
|
|
25
|
+
self.assertEqual(p.helpFlag.help, "print this message and exit")
|
|
26
|
+
self.assertEqual(p.versionFlag.help, "print version and exit")
|
|
27
|
+
# default flags carry no keys until the user assigns them
|
|
28
|
+
self.assertEqual(tuple(p.helpFlag.keys), ())
|
|
29
|
+
self.assertEqual(tuple(p.versionFlag.keys), ())
|
|
30
|
+
|
|
31
|
+
def test_instances_do_not_share_mutable_state(self: Self) -> None:
|
|
32
|
+
# default_factory must produce fresh objects per instance
|
|
33
|
+
p1 = SuperParser()
|
|
34
|
+
p2 = SuperParser()
|
|
35
|
+
self.assertIsNot(p1.helpFlag, p2.helpFlag)
|
|
36
|
+
self.assertIsNot(p1.versionFlag, p2.versionFlag)
|
|
37
|
+
self.assertIsNot(p1.subCommands, p2.subCommands)
|
|
38
|
+
p1.subCommands.append(SubCommand(name="x"))
|
|
39
|
+
self.assertEqual(p2.subCommands, [])
|
|
40
|
+
|
|
41
|
+
def test_add_subCommand_returns_and_appends(self: Self) -> None:
|
|
42
|
+
p = SuperParser()
|
|
43
|
+
c = p.add_subCommand(name="run", aliases=["r"], help="go")
|
|
44
|
+
self.assertIsInstance(c, SubCommand)
|
|
45
|
+
self.assertIs(p.subCommands[-1], c)
|
|
46
|
+
self.assertEqual(c.name, "run")
|
|
47
|
+
|
|
48
|
+
def test_subcommands_message_none_when_empty(self: Self) -> None:
|
|
49
|
+
self.assertIsNone(SuperParser()._subCommands_message())
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
unittest.main(verbosity=2)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import io
|
|
3
|
+
import unittest
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Self
|
|
6
|
+
|
|
7
|
+
from superparsing import SuperParser
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def capture_stdout(
|
|
11
|
+
func: Callable[..., object],
|
|
12
|
+
*args: object,
|
|
13
|
+
**kwargs: object,
|
|
14
|
+
) -> tuple[object, str]:
|
|
15
|
+
"""Run ``func`` and return (return_value, captured_stdout_str)."""
|
|
16
|
+
buf = io.StringIO()
|
|
17
|
+
with contextlib.redirect_stdout(buf):
|
|
18
|
+
result = func(*args, **kwargs)
|
|
19
|
+
return result, buf.getvalue()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# SuperParser: usage / help / version text
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
class TestSuperParserText(unittest.TestCase):
|
|
26
|
+
def make(self: Self) -> SuperParser:
|
|
27
|
+
p = SuperParser(prog="tool")
|
|
28
|
+
p.helpFlag.keys = ["-h", "--help"]
|
|
29
|
+
p.versionFlag.keys = ["-v", "--version"]
|
|
30
|
+
p.add_subCommand(name="build", aliases=["b"], help="build the project")
|
|
31
|
+
p.add_subCommand(name="clean", help="remove artifacts")
|
|
32
|
+
return p
|
|
33
|
+
|
|
34
|
+
def test_usage_full(self: Self) -> None:
|
|
35
|
+
p = self.make()
|
|
36
|
+
self.assertEqual(
|
|
37
|
+
p.usage(),
|
|
38
|
+
"usage: tool [-h, --help] [-v, --version] {build, b, clean} [arg ...]",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def test_usage_no_flags_no_subcommands(self: Self) -> None:
|
|
42
|
+
p = SuperParser(prog="tool")
|
|
43
|
+
self.assertEqual(p.usage(), "usage: tool {} [arg ...]")
|
|
44
|
+
|
|
45
|
+
def test_help_contains_all_sections(self: Self) -> None:
|
|
46
|
+
p = self.make()
|
|
47
|
+
h = p.help()
|
|
48
|
+
self.assertTrue(h.startswith("usage: tool"))
|
|
49
|
+
self.assertIn("keys:", h)
|
|
50
|
+
self.assertIn("subcommands:", h)
|
|
51
|
+
self.assertIn("args:", h)
|
|
52
|
+
# sections separated by blank lines
|
|
53
|
+
self.assertIn("\n\n", h)
|
|
54
|
+
|
|
55
|
+
def test_help_message_override(self: Self) -> None:
|
|
56
|
+
p = self.make()
|
|
57
|
+
p.helpFlag.message = "custom help blob"
|
|
58
|
+
self.assertEqual(p.help(), "custom help blob")
|
|
59
|
+
|
|
60
|
+
def test_help_omits_missing_sections(self: Self) -> None:
|
|
61
|
+
# no flags, no subcommands -> only usage + args sections
|
|
62
|
+
p = SuperParser(prog="tool")
|
|
63
|
+
h = p.help()
|
|
64
|
+
self.assertIn("usage:", h)
|
|
65
|
+
self.assertIn("args:", h)
|
|
66
|
+
self.assertNotIn("keys:", h)
|
|
67
|
+
self.assertNotIn("subcommands:", h)
|
|
68
|
+
|
|
69
|
+
def test_version_from_message(self: Self) -> None:
|
|
70
|
+
p = SuperParser(prog="tool")
|
|
71
|
+
p.versionFlag.message = "tool 2.3.4"
|
|
72
|
+
self.assertEqual(p.version(), "tool 2.3.4")
|
|
73
|
+
|
|
74
|
+
def test_version_message_coerced(self: Self) -> None:
|
|
75
|
+
p = SuperParser(prog="tool")
|
|
76
|
+
p.versionFlag.message = 99
|
|
77
|
+
self.assertEqual(p.version(), "99")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
unittest.main(verbosity=2)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from typing import Self
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
4
|
+
|
|
5
|
+
from superparsing import SuperFlag, SuperParser
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestSuperParser(unittest.TestCase):
|
|
9
|
+
|
|
10
|
+
def setUp(self: Self) -> None:
|
|
11
|
+
self.parser = SuperParser(
|
|
12
|
+
prog="tool",
|
|
13
|
+
helpFlag=SuperFlag(keys=["-h", "--help"]),
|
|
14
|
+
versionFlag=SuperFlag(keys=["-v", "--version"]),
|
|
15
|
+
)
|
|
16
|
+
self.parser.add_subCommand(
|
|
17
|
+
name="run", aliases=["r"], help="run command"
|
|
18
|
+
)
|
|
19
|
+
self.parser.add_subCommand(name="test", help="test command")
|
|
20
|
+
|
|
21
|
+
@patch("importlib.metadata.version")
|
|
22
|
+
def test_version_default(self: Self, mock_version: MagicMock) -> None:
|
|
23
|
+
mock_version.return_value = "2.0.0"
|
|
24
|
+
parser = SuperParser(prog="tool")
|
|
25
|
+
self.assertEqual(parser.version(), "tool, version 2.0.0")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
unittest.main()
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import json
|
|
3
|
-
import subprocess
|
|
4
|
-
from typing import Any, Optional
|
|
5
|
-
|
|
6
|
-
__all__ = ["main", "run"]
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def env_create(env: str, python: Optional[str]) -> subprocess.CompletedProcess: # type: ignore[type-arg]
|
|
10
|
-
args: list[str]
|
|
11
|
-
args = [
|
|
12
|
-
"conda",
|
|
13
|
-
"create",
|
|
14
|
-
"--name",
|
|
15
|
-
env,
|
|
16
|
-
"--yes",
|
|
17
|
-
"--channel",
|
|
18
|
-
"conda-forge",
|
|
19
|
-
"--override-channels",
|
|
20
|
-
]
|
|
21
|
-
if python is not None:
|
|
22
|
-
args.append("python=" + python)
|
|
23
|
-
return subprocess.run(
|
|
24
|
-
args,
|
|
25
|
-
check=True,
|
|
26
|
-
capture_output=True,
|
|
27
|
-
text=True,
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def env_list() -> list[str]:
|
|
32
|
-
ans: list[str]
|
|
33
|
-
data: Any
|
|
34
|
-
result: subprocess.CompletedProcess # type: ignore[type-arg]
|
|
35
|
-
try:
|
|
36
|
-
result = subprocess.run(
|
|
37
|
-
["conda", "env", "list", "--json"],
|
|
38
|
-
check=True,
|
|
39
|
-
capture_output=True,
|
|
40
|
-
text=True,
|
|
41
|
-
)
|
|
42
|
-
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
43
|
-
return []
|
|
44
|
-
data = json.loads(result.stdout)
|
|
45
|
-
ans = list()
|
|
46
|
-
for details in data["envs_details"].values():
|
|
47
|
-
ans.append(str(details["name"]))
|
|
48
|
-
return ans
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def env_remove(env: str) -> subprocess.CompletedProcess: # type: ignore[type-arg]
|
|
52
|
-
args: list[str]
|
|
53
|
-
args = ["conda", "env", "remove", "-y", "-n", env]
|
|
54
|
-
return subprocess.run(args, check=True)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def main(args: Optional[list[str]] = None, /) -> None:
|
|
58
|
-
kwargs: dict[str, Any]
|
|
59
|
-
parser: argparse.ArgumentParser
|
|
60
|
-
parser = argparse.ArgumentParser(fromfile_prefix_chars="@")
|
|
61
|
-
# parser.add_argument("--errfile", default="-")
|
|
62
|
-
# parser.add_argument("--outfile", default="-")
|
|
63
|
-
parser.add_argument("--python")
|
|
64
|
-
parser.add_argument("--recreate", action="store_true")
|
|
65
|
-
parser.add_argument("envs", default=[], nargs="*")
|
|
66
|
-
kwargs = vars(parser.parse_args(args))
|
|
67
|
-
run(*kwargs.pop("envs"), **kwargs)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def run(
|
|
71
|
-
*envs: str, python: Optional[str] = None, recreate: bool = False
|
|
72
|
-
) -> None:
|
|
73
|
-
env: str
|
|
74
|
-
envs_: list[str]
|
|
75
|
-
envs_ = env_list()
|
|
76
|
-
for env in envs:
|
|
77
|
-
if env in envs_ and not recreate:
|
|
78
|
-
continue
|
|
79
|
-
if env in envs_:
|
|
80
|
-
env_remove(env)
|
|
81
|
-
env_create(env, python=python)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if __name__ == "__main__":
|
|
85
|
-
main()
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
LICENSE.txt
|
|
2
|
-
MANIFEST.in
|
|
3
|
-
README.rst
|
|
4
|
-
pyproject.toml
|
|
5
|
-
run_tests.py
|
|
6
|
-
setup.cfg
|
|
7
|
-
make/env.py
|
|
8
|
-
superparsing.egg-info/PKG-INFO
|
|
9
|
-
superparsing.egg-info/SOURCES.txt
|
|
10
|
-
superparsing.egg-info/dependency_links.txt
|
|
11
|
-
superparsing.egg-info/top_level.txt
|
|
12
|
-
tests/test_1984.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
make
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{superparsing-0.0.0.dev2 → superparsing-0.2.0/src}/superparsing.egg-info/dependency_links.txt
RENAMED
|
File without changes
|