diff --git a/pytool/cmd.py b/pytool/cmd.py index ecf8277..40a6ebd 100644 --- a/pytool/cmd.py +++ b/pytool/cmd.py @@ -3,11 +3,11 @@ line utilities. """ -import sys -import signal import functools +import signal +import sys +from typing import Callable, Optional -import six from pytool.lang import UNSET # Handle the optional configargparse lib @@ -145,7 +145,7 @@ def __init__(self): self.set_opts() self.opt("--help", action="help", help="display this help and exit") - def parser_opts(self): + def parser_opts(self) -> dict: """Subclasses should override this method to return a dictionary of additional arguments to the parser instance. @@ -161,7 +161,7 @@ def parser_opts(self): """ return dict() - def set_opts(self): + def set_opts(self) -> None: """Subclasses should override this method to configure the command line arguments and options. @@ -178,13 +178,13 @@ def run(self): """ pass - def opt(self, *args, **kwargs): + def opt(self, *args, **kwargs) -> None: """Add an option to this command. This takes the same arguments as :meth:`ArgumentParser.add_argument`. """ self.parser.add_argument(*args, **kwargs) - def run(self): + def run(self) -> None: """Subclasses should override this method to start the command process. In other words, this is where the magic happens. @@ -196,7 +196,7 @@ def run(self): self.parser.print_help() sys.exit(1) - def describe(self, description): + def describe(self, description: str) -> None: """ Describe the command in more detail. This will be displayed in addition to the argument help. @@ -227,7 +227,14 @@ def run(self): # And use the raw class so it doesn't strip our formatting self.parser.formatter_class = CommandFormatter - def subcommand(self, name, opt_func=None, run_func=None, *args, **kwargs): + def subcommand( + self, + name: str, + opt_func: Optional[Callable] = None, + run_func=None, + *args, + **kwargs, + ) -> None: """ Add a subcommand `name` with setup `opt_func` and main `run_func` to the argument parser. @@ -315,21 +322,18 @@ def run(self): return parser @classmethod - def console_script(cls): + def console_script(cls) -> None: """Method used to start the command when launched from a distutils console script. """ cls().start(sys.argv[1:]) - def start(self, args): + def start(self, args: list[str]) -> None: """Starts a command and registers single handlers.""" - if six.PY3 and sys.version_info >= (3, 7): - # Unfortunately this doesn't work and I don't know why... will fix - # it later. - # self.args = self.parser.parse_intermixed_args(args) - self.args = self.parser.parse_args(args) - else: - self.args = self.parser.parse_args(args) + # Unfortunately this doesn't work and I don't know why... will fix + # it later. + # self.args = self.parser.parse_intermixed_args(args) + self.args = self.parser.parse_args(args) signal_handler(RELOAD_SIGNAL, self.reload) signal_handler(STOP_SIGNAL, self.stop) if self.subparsers and self.args.command: @@ -373,7 +377,7 @@ def __init__(self, *args, **kwargs): super(CommandFormatter, self).__init__(*args, **kwargs) -def opt(*args, **kwargs): +def opt(*args, **kwargs) -> Callable: """ Factory function for creating :class:`Command.opt` bindings at the class level for reuse in subcommands. diff --git a/pytool/lang.py b/pytool/lang.py index 422eea0..8bebde6 100644 --- a/pytool/lang.py +++ b/pytool/lang.py @@ -2,15 +2,12 @@ This module contains items that are "missing" from the Python standard library, that do miscelleneous things. """ -import re import copy +import functools import inspect +import re import weakref -import functools - -import six -from six.moves import range - +from typing import TypeVar __all__ = [ "get_name", @@ -23,7 +20,7 @@ ] -def get_name(frame): +def get_name(frame) -> str: """Gets the name of the passed frame. :warning: It's very important to delete a stack frame after you're done @@ -69,7 +66,7 @@ def get_name(frame): pass if module: - if not isinstance(module, six.string_types): + if not isinstance(module, (str, bytes)): module = module.__name__ if name != "": return "%s.%s" % (module, name) @@ -114,7 +111,10 @@ def __get__(self, instance, owner): )() -def singleton(klass): +_Singleton = TypeVar("_Singleton", bound=object) + + +def singleton(klass: _Singleton) -> _Singleton: """Wraps a class to create a singleton version of it. :param klass: Class to decorate @@ -161,7 +161,7 @@ def __new__(cls, *args, **kwargs): return type(cls_name, (object,), cls_dict) -def hashed_singleton(klass): +def hashed_singleton(klass: _Singleton) -> _Singleton: """Wraps a class to create a hashed singleton version of it. A hashed singleton is like a singleton in that there will be only a single instance of the class for each call signature. @@ -227,7 +227,7 @@ def __init__(self, *args, **kwargs): # Make new method that controls singleton behavior def __new__(cls, *args, **kwargs): - hashable_kwargs = tuple(sorted(six.iteritems(kwargs))) + hashable_kwargs = tuple(sorted(kwargs.items())) signature = (args, hashable_kwargs) if signature not in cls._singletons: @@ -276,8 +276,7 @@ def __repr__(cls): return "UNSET" -@six.add_metaclass(_UNSETMeta) -class UNSET(object): +class UNSET(object, metaclass=_UNSETMeta): """Special class that evaluates to ``bool(False)``, but can be distinctly identified as seperate from ``None`` or ``False``. This class can and should be used without instantiation. @@ -443,7 +442,7 @@ def __getattribute__(self, name): # Allow for dict-like key access and traversal def __getitem__(self, item): - if isinstance(item, six.string_types) and "." in item: + if isinstance(item, (str, bytes)) and "." in item: return self.traverse(item.split(".")) try: return self.__getattribute__(item)