summaryrefslogtreecommitdiff
path: root/cmd2/utils.py
diff options
context:
space:
mode:
authorxNinjaKittyx <xNinjaKittyx@users.noreply.github.com>2020-12-15 17:21:33 -0800
committerxNinjaKittyx <xNinjaKittyx@users.noreply.github.com>2020-12-15 18:20:13 -0800
commit9aa54a5b27468d61337528cb1e1b5b9b11a80978 (patch)
tree567693115cc101efb9254a96d96d80e9f9ccd557 /cmd2/utils.py
parent03c65c60b39e369958b056c5c844d36d515c8a63 (diff)
downloadcmd2-git-ci_improvements.tar.gz
Adds pre-commit config to run various lintersci_improvements
This ads black, isort, pyupgrade, and flake8 to pre-commit-config.yaml There are also some small changes to travis.yml and tasks.py to reduce some repeated configurations that should be consolidated into setup.cfg. Most other changes are automated by the linter scripts.
Diffstat (limited to 'cmd2/utils.py')
-rw-r--r--cmd2/utils.py103
1 files changed, 62 insertions, 41 deletions
diff --git a/cmd2/utils.py b/cmd2/utils.py
index b58cdb96..d2dd5b18 100644
--- a/cmd2/utils.py
+++ b/cmd2/utils.py
@@ -12,10 +12,9 @@ import re
import subprocess
import sys
import threading
-
import unicodedata
from enum import Enum
-from typing import Any, Callable, Dict, IO, Iterable, List, Optional, TextIO, Type, Union
+from typing import IO, Any, Callable, Dict, Iterable, List, Optional, TextIO, Type, Union
from . import constants
@@ -88,6 +87,7 @@ class CompletionError(Exception):
- A previous command line argument that determines the data set being completed is invalid
- Tab completion hints
"""
+
def __init__(self, *args, apply_style: bool = True, **kwargs):
"""
Initializer for CompletionError
@@ -103,13 +103,20 @@ class CompletionError(Exception):
class Settable:
"""Used to configure a cmd2 instance member to be settable via the set command in the CLI"""
- def __init__(self, name: str, val_type: Callable, description: str, *,
- onchange_cb: Callable[[str, Any, Any], Any] = None,
- choices: Iterable = None,
- choices_function: Optional[Callable] = None,
- choices_method: Optional[Callable] = None,
- completer_function: Optional[Callable] = None,
- completer_method: Optional[Callable] = None):
+
+ def __init__(
+ self,
+ name: str,
+ val_type: Callable,
+ description: str,
+ *,
+ onchange_cb: Callable[[str, Any, Any], Any] = None,
+ choices: Iterable = None,
+ choices_function: Optional[Callable] = None,
+ choices_method: Optional[Callable] = None,
+ completer_function: Optional[Callable] = None,
+ completer_method: Optional[Callable] = None
+ ):
"""
Settable Initializer
@@ -158,8 +165,7 @@ class Settable:
self.completer_method = completer_method
-def namedtuple_with_defaults(typename: str, field_names: Union[str, List[str]],
- default_values: collections_abc.Iterable = ()):
+def namedtuple_with_defaults(typename: str, field_names: Union[str, List[str]], default_values: collections_abc.Iterable = ()):
"""
Convenience function for defining a namedtuple with default values
@@ -446,8 +452,8 @@ class StdSim:
Class to simulate behavior of sys.stdout or sys.stderr.
Stores contents in internal buffer and optionally echos to the inner stream it is simulating.
"""
- def __init__(self, inner_stream, *, echo: bool = False,
- encoding: str = 'utf-8', errors: str = 'replace') -> None:
+
+ def __init__(self, inner_stream, *, echo: bool = False, encoding: str = 'utf-8', errors: str = 'replace') -> None:
"""
StdSim Initializer
:param inner_stream: the wrapped stream. Should be a TextIO or StdSim instance.
@@ -530,6 +536,7 @@ class ByteBuf:
"""
Used by StdSim to write binary data and stores the actual bytes written
"""
+
# Used to know when to flush the StdSim
NEWLINES = [b'\n', b'\r']
@@ -560,8 +567,8 @@ class ProcReader:
Used to capture stdout and stderr from a Popen process if any of those were set to subprocess.PIPE.
If neither are pipes, then the process will run normally and no output will be captured.
"""
- def __init__(self, proc: subprocess.Popen, stdout: Union[StdSim, TextIO],
- stderr: Union[StdSim, TextIO]) -> None:
+
+ def __init__(self, proc: subprocess.Popen, stdout: Union[StdSim, TextIO], stderr: Union[StdSim, TextIO]) -> None:
"""
ProcReader initializer
:param proc: the Popen process being read from
@@ -572,11 +579,9 @@ class ProcReader:
self._stdout = stdout
self._stderr = stderr
- self._out_thread = threading.Thread(name='out_thread', target=self._reader_thread_func,
- kwargs={'read_stdout': True})
+ self._out_thread = threading.Thread(name='out_thread', target=self._reader_thread_func, kwargs={'read_stdout': True})
- self._err_thread = threading.Thread(name='out_thread', target=self._reader_thread_func,
- kwargs={'read_stdout': False})
+ self._err_thread = threading.Thread(name='out_thread', target=self._reader_thread_func, kwargs={'read_stdout': False})
# Start the reader threads for pipes only
if self._proc.stdout is not None:
@@ -587,6 +592,7 @@ class ProcReader:
def send_sigint(self) -> None:
"""Send a SIGINT to the process similar to if <Ctrl>+C were pressed"""
import signal
+
if sys.platform.startswith('win'):
# cmd2 started the Windows process in a new process group. Therefore
# a CTRL_C_EVENT can't be sent to it. Send a CTRL_BREAK_EVENT instead.
@@ -664,6 +670,7 @@ class ContextFlag:
while a critical code section has set the flag to True. Because signal handling is always done on the
main thread, this class is not thread-safe since there is no need.
"""
+
def __init__(self) -> None:
# When this flag has a positive value, it is considered set.
# When it is 0, it is not set. It should never go below 0.
@@ -683,8 +690,14 @@ class ContextFlag:
class RedirectionSavedState:
"""Created by each command to store information required to restore state after redirection"""
- def __init__(self, self_stdout: Union[StdSim, IO[str]], sys_stdout: Union[StdSim, IO[str]],
- pipe_proc_reader: Optional[ProcReader], saved_redirecting: bool) -> None:
+
+ def __init__(
+ self,
+ self_stdout: Union[StdSim, IO[str]],
+ sys_stdout: Union[StdSim, IO[str]],
+ pipe_proc_reader: Optional[ProcReader],
+ saved_redirecting: bool,
+ ) -> None:
"""
RedirectionSavedState initializer
:param self_stdout: saved value of Cmd.stdout
@@ -722,13 +735,21 @@ def basic_complete(text: str, line: str, begidx: int, endidx: int, match_against
class TextAlignment(Enum):
"""Horizontal text alignment"""
+
LEFT = 1
CENTER = 2
RIGHT = 3
-def align_text(text: str, alignment: TextAlignment, *, fill_char: str = ' ',
- width: Optional[int] = None, tab_width: int = 4, truncate: bool = False) -> str:
+def align_text(
+ text: str,
+ alignment: TextAlignment,
+ *,
+ fill_char: str = ' ',
+ width: Optional[int] = None,
+ tab_width: int = 4,
+ truncate: bool = False
+) -> str:
"""
Align text for display within a given width. Supports characters with display widths greater than 1.
ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
@@ -809,7 +830,7 @@ def align_text(text: str, alignment: TextAlignment, *, fill_char: str = ' ',
line_width = ansi.style_aware_wcswidth(line)
if line_width == -1:
- raise(ValueError("Text to align contains an unprintable character"))
+ raise (ValueError("Text to align contains an unprintable character"))
# Get the styles in this line
line_styles = get_styles_in_text(line)
@@ -860,8 +881,9 @@ def align_text(text: str, alignment: TextAlignment, *, fill_char: str = ' ',
return text_buf.getvalue()
-def align_left(text: str, *, fill_char: str = ' ', width: Optional[int] = None,
- tab_width: int = 4, truncate: bool = False) -> str:
+def align_left(
+ text: str, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False
+) -> str:
"""
Left align text for display within a given width. Supports characters with display widths greater than 1.
ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
@@ -879,12 +901,12 @@ def align_left(text: str, *, fill_char: str = ' ', width: Optional[int] = None,
:raises: ValueError if text or fill_char contains an unprintable character
:raises: ValueError if width is less than 1
"""
- return align_text(text, TextAlignment.LEFT, fill_char=fill_char, width=width,
- tab_width=tab_width, truncate=truncate)
+ return align_text(text, TextAlignment.LEFT, fill_char=fill_char, width=width, tab_width=tab_width, truncate=truncate)
-def align_center(text: str, *, fill_char: str = ' ', width: Optional[int] = None,
- tab_width: int = 4, truncate: bool = False) -> str:
+def align_center(
+ text: str, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False
+) -> str:
"""
Center text for display within a given width. Supports characters with display widths greater than 1.
ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
@@ -902,12 +924,12 @@ def align_center(text: str, *, fill_char: str = ' ', width: Optional[int] = None
:raises: ValueError if text or fill_char contains an unprintable character
:raises: ValueError if width is less than 1
"""
- return align_text(text, TextAlignment.CENTER, fill_char=fill_char, width=width,
- tab_width=tab_width, truncate=truncate)
+ return align_text(text, TextAlignment.CENTER, fill_char=fill_char, width=width, tab_width=tab_width, truncate=truncate)
-def align_right(text: str, *, fill_char: str = ' ', width: Optional[int] = None,
- tab_width: int = 4, truncate: bool = False) -> str:
+def align_right(
+ text: str, *, fill_char: str = ' ', width: Optional[int] = None, tab_width: int = 4, truncate: bool = False
+) -> str:
"""
Right align text for display within a given width. Supports characters with display widths greater than 1.
ANSI style sequences do not count toward the display width. If text has line breaks, then each line is aligned
@@ -925,8 +947,7 @@ def align_right(text: str, *, fill_char: str = ' ', width: Optional[int] = None,
:raises: ValueError if text or fill_char contains an unprintable character
:raises: ValueError if width is less than 1
"""
- return align_text(text, TextAlignment.RIGHT, fill_char=fill_char, width=width,
- tab_width=tab_width, truncate=truncate)
+ return align_text(text, TextAlignment.RIGHT, fill_char=fill_char, width=width, tab_width=tab_width, truncate=truncate)
def truncate_line(line: str, max_width: int, *, tab_width: int = 4) -> str:
@@ -951,6 +972,7 @@ def truncate_line(line: str, max_width: int, *, tab_width: int = 4) -> str:
:raises: ValueError if max_width is less than 1
"""
import io
+
from . import ansi
# Handle tabs
@@ -1077,16 +1099,15 @@ def get_defining_class(meth) -> Type:
"""
if isinstance(meth, functools.partial):
return get_defining_class(meth.func)
- if inspect.ismethod(meth) or (inspect.isbuiltin(meth)
- and getattr(meth, '__self__') is not None
- and getattr(meth.__self__, '__class__')):
+ if inspect.ismethod(meth) or (
+ inspect.isbuiltin(meth) and getattr(meth, '__self__') is not None and getattr(meth.__self__, '__class__')
+ ):
for cls in inspect.getmro(meth.__self__.__class__):
if meth.__name__ in cls.__dict__:
return cls
meth = getattr(meth, '__func__', meth) # fallback to __qualname__ parsing
if inspect.isfunction(meth):
- cls = getattr(inspect.getmodule(meth),
- meth.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
+ cls = getattr(inspect.getmodule(meth), meth.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
if isinstance(cls, type):
return cls
return getattr(meth, '__objclass__', None) # handle special descriptor objects