Skip to content

Commit f82d3cd

Browse files
committed
subc: add copyright header, fix pre-commit violations
Signed-off-by: Stephen Brennan <[email protected]>
1 parent f6aecc7 commit f82d3cd

File tree

1 file changed

+70
-27
lines changed

1 file changed

+70
-27
lines changed

yo/subc.py

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,47 @@
1-
#!/usr/bin/env python3
1+
# -*- coding: utf-8 -*-
2+
# Copyright (c) 2025, Oracle and/or its affiliates.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or data
8+
# (collectively the "Software"), free of charge and under any and all copyright
9+
# rights in the Software, and any and all patent rights owned or freely
10+
# licensable by each licensor hereunder covering either (i) the unmodified
11+
# Software as contributed to or provided by such licensor, or (ii) the Larger
12+
# Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
# (b) any piece of software and/or hardware listed in the
16+
# lrgrwrks.txt file if one is included with the Software (each a "Larger
17+
# Work" to which the Software is contributed by such licensors),
18+
#
19+
# without restriction, including without limitation the rights to copy, create
20+
# derivative works of, display, perform, and distribute the Software and make,
21+
# use, sell, offer for sale, import, export, have made, and have sold the
22+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
23+
# either these or other terms.
24+
#
25+
# This license is subject to the following condition: The above copyright notice
26+
# and either this complete permission notice or at a minimum a reference to the
27+
# UPL must be included in all copies or substantial portions of the Software.
28+
#
29+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35+
# SOFTWARE.
236
"""
3-
A simple sub-command library for writing rich CLIs
37+
yo.subc: A simple sub-command library for writing rich CLIs
438
"""
539
import argparse
640
import collections
741
import typing as t
842
from abc import ABC
9-
from abc import abstractproperty
1043
from abc import abstractmethod
44+
from abc import abstractproperty
1145

1246

1347
def _first_different(s1: str, s2: str) -> int:
@@ -51,33 +85,37 @@ def _unique_prefixes(strings: t.Iterable[str]) -> t.Dict[str, t.List[str]]:
5185
}
5286

5387

54-
class _SneakyDict(collections.UserDict):
88+
Tk = t.TypeVar("Tk")
89+
Tv = t.TypeVar("Tv")
90+
91+
92+
class _SneakyDict(collections.UserDict, t.Generic[Tk, Tv]): # type: ignore
5593
"""
5694
A dictionary which can have "hidden" keys that only show up if you know
5795
about them. The keys are just aliases to other keys. They show up with
5896
"getitem" and "contains" operations, but not in list / len operations.
5997
"""
6098

61-
def __init__(self, *args, **kwargs):
99+
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
62100
super().__init__(*args, **kwargs)
63-
self._aliases = {}
101+
self._aliases: t.Dict[Tk, Tk] = {}
64102

65-
def __getitem__(self, key):
103+
def __getitem__(self, key: Tk) -> t.Any:
66104
key = self._aliases.get(key, key)
67105
return super().__getitem__(key)
68106

69-
def __contains__(self, key):
107+
def __contains__(self, key: t.Any) -> bool:
70108
key = self._aliases.get(key, key)
71109
return super().__contains__(key)
72110

73-
def add_aliases(self, alias_map: t.Dict[str, t.List[str]]):
111+
def add_aliases(self, alias_map: t.Dict[Tk, t.List[Tk]]) -> None:
74112
alias_to_name = {a: n for n, l in alias_map.items() for a in l}
75113
self._aliases.update(alias_to_name)
76114

77115

78116
def _wrap_subparser_aliases(
79-
option: argparse._SubParsersAction,
80-
alias_map: t.Dict[str, t.List[str]]
117+
option: argparse._SubParsersAction, # type: ignore
118+
alias_map: t.Dict[str, t.List[str]],
81119
) -> None:
82120
"""
83121
Unfortunately, this mucks around with an internal implementation of
@@ -94,14 +132,13 @@ def _wrap_subparser_aliases(
94132
aliases should be hidden. Thus, use a the _SneakyDict from above to hide the
95133
aliases.
96134
"""
97-
new_choices = _SneakyDict(option.choices)
135+
new_choices: _SneakyDict[str, str] = _SneakyDict(option.choices)
98136
new_choices.add_aliases(alias_map)
99137
option.choices = new_choices # type: ignore
100138
option._name_parser_map = option.choices
101139

102140

103141
T = t.TypeVar("T", bound="Command")
104-
F = t.TypeVar("F", bound=argparse.HelpFormatter)
105142

106143

107144
class Command(ABC):
@@ -138,7 +175,7 @@ def main():
138175
"""
139176

140177
@property
141-
def help_formatter_class(self) -> t.Type[F]:
178+
def help_formatter_class(self) -> t.Type[argparse.HelpFormatter]:
142179
return argparse.HelpFormatter
143180

144181
@abstractproperty
@@ -149,14 +186,14 @@ def name(self) -> str:
149186
def description(self) -> str:
150187
"""A field or property which is used as the help/description"""
151188

152-
def add_args(self, parser: argparse.ArgumentParser):
189+
def add_args(self, parser: argparse.ArgumentParser) -> None:
153190
pass # default is no arguments
154191

155192
@abstractmethod
156193
def run(self) -> t.Any:
157194
"""Function which is called for this command."""
158195

159-
def base_run(self, args: argparse.Namespace):
196+
def base_run(self, args: argparse.Namespace) -> t.Any:
160197
self.args = args
161198
return self.run()
162199

@@ -248,7 +285,8 @@ def add_commands(
248285
"""
249286
default_set = False
250287
subparsers = parser.add_subparsers(
251-
help=argparse.SUPPRESS, metavar="SUB-COMMAND",
288+
help=argparse.SUPPRESS,
289+
metavar="SUB-COMMAND",
252290
)
253291
parser.formatter_class = argparse.RawTextHelpFormatter
254292
to_add = list(cls.iter_commands())
@@ -306,7 +344,8 @@ def add_commands(
306344
for subcmd, cmdlist in subcmds.items():
307345
subcmd_parser = subparsers.add_parser(subcmd)
308346
subcmd_subp = subcmd_parser.add_subparsers(
309-
title="sub-command", metavar="SUB-COMMAND",
347+
title="sub-command",
348+
metavar="SUB-COMMAND",
310349
)
311350
sub_names = []
312351
names.append(subcmd)
@@ -330,7 +369,7 @@ def add_commands(
330369
names.extend(cmd_aliases)
331370
aliases.update(cmd_aliases)
332371

333-
inv_aliases = collections.defaultdict(list)
372+
inv_aliases: t.Dict[str, t.List[str]] = collections.defaultdict(list)
334373
if shortest_prefix:
335374
inv_aliases.update(_unique_prefixes(names))
336375
for name, target in aliases.items():
@@ -356,18 +395,20 @@ def add_commands(
356395
parser.epilog = "\n".join(lines[:-1])
357396

358397
if not default_set:
359-
def default_func(*args, **kwargs):
360-
raise Exception('you must select a sub-command')
398+
399+
def default_func(*args: t.Any, **kwargs: t.Any) -> None:
400+
raise Exception("you must select a sub-command")
401+
361402
parser.set_defaults(func=default_func)
362403
return parser
363404

364405
@classmethod
365406
def main(
366-
cls,
367-
description: str,
368-
default: t.Optional[str] = None,
369-
args: t.Optional[t.List[str]] = None,
370-
shortest_prefix: bool = False,
407+
cls,
408+
description: str,
409+
default: t.Optional[str] = None,
410+
args: t.Optional[t.List[str]] = None,
411+
shortest_prefix: bool = False,
371412
) -> t.Any:
372413
"""
373414
Parse arguments and run the selected sub-command.
@@ -390,7 +431,9 @@ def main(
390431
"""
391432
parser = argparse.ArgumentParser(description=description)
392433
cls.add_commands(
393-
parser, default=default, shortest_prefix=shortest_prefix,
434+
parser,
435+
default=default,
436+
shortest_prefix=shortest_prefix,
394437
)
395438
ns = parser.parse_args(args=args)
396439
return ns.func(ns)

0 commit comments

Comments
 (0)