From 98617816e59f2c1a0ad874c1a270e177dafde364 Mon Sep 17 00:00:00 2001 From: supremestdoggo <83146042+supremestdoggo@users.noreply.github.com> Date: Fri, 6 Aug 2021 21:42:02 -0400 Subject: [PATCH 1/2] Add type annotations to a few files --- bpython/_internal.py | 6 ++-- bpython/args.py | 7 ++-- bpython/autocomplete.py | 71 +++++++++++++++++++++-------------------- bpython/cli.py | 21 ++++++------ bpython/curtsies.py | 2 +- 5 files changed, 55 insertions(+), 52 deletions(-) diff --git a/bpython/_internal.py b/bpython/_internal.py index 4545862c4..35cd0bf80 100644 --- a/bpython/_internal.py +++ b/bpython/_internal.py @@ -8,20 +8,20 @@ class _Helper: - def __init__(self): + def __init__(self) -> None: if hasattr(pydoc.Helper, "output"): # See issue #228 self.helper = pydoc.Helper(sys.stdin, None) else: self.helper = pydoc.Helper(sys.stdin, sys.stdout) - def __repr__(self): + def __repr__(self) -> str: return ( "Type help() for interactive help, " "or help(object) for help about object." ) - def __call__(self, *args, **kwargs): + def __call__(self, *args, **kwargs) -> None: self.helper(*args, **kwargs) diff --git a/bpython/args.py b/bpython/args.py index e3095e17c..7895d84ad 100644 --- a/bpython/args.py +++ b/bpython/args.py @@ -26,6 +26,7 @@ """ import argparse +from typing import Tuple import curtsies import cwcwidth import greenlet @@ -53,7 +54,7 @@ def error(self, msg): raise ArgumentParserFailed() -def version_banner(base="bpython"): +def version_banner(base="bpython") -> str: return _("{} version {} on top of Python {} {}").format( base, __version__, @@ -62,11 +63,11 @@ def version_banner(base="bpython"): ) -def copyright_banner(): +def copyright_banner() -> str: return _("{} See AUTHORS.rst for details.").format(__copyright__) -def parse(args, extras=None, ignore_stdin=False): +def parse(args, extras=None, ignore_stdin=False) -> Tuple: """Receive an argument list - if None, use sys.argv - parse all args and take appropriate action. Also receive optional extra argument: this should be a tuple of (title, description, callback) diff --git a/bpython/autocomplete.py b/bpython/autocomplete.py index 975a2622c..0697e4906 100644 --- a/bpython/autocomplete.py +++ b/bpython/autocomplete.py @@ -33,6 +33,7 @@ import builtins from enum import Enum +from typing import Any, Dict, Iterator, List, Match, NoReturn, Set, Union from . import inspection from . import line as lineparts from .line import LinePart @@ -48,7 +49,7 @@ class AutocompleteModes(Enum): FUZZY = "fuzzy" @classmethod - def from_string(cls, value): + def from_string(cls, value) -> Union[Any, None]: if value.upper() in cls.__members__: return cls.__members__[value.upper()] return None @@ -161,11 +162,11 @@ def from_string(cls, value): KEYWORDS = frozenset(keyword.kwlist) -def after_last_dot(name): +def after_last_dot(name: str) -> str: return name.rstrip(".").rsplit(".")[-1] -def few_enough_underscores(current, match): +def few_enough_underscores(current, match) -> bool: """Returns whether match should be shown based on current if current is _, True if match starts with 0 or 1 underscore @@ -179,19 +180,19 @@ def few_enough_underscores(current, match): return not match.startswith("_") -def method_match_none(word, size, text): +def method_match_none(word, size, text) -> False: return False -def method_match_simple(word, size, text): +def method_match_simple(word, size, text) -> bool: return word[:size] == text -def method_match_substring(word, size, text): +def method_match_substring(word, size, text) -> bool: return text in word -def method_match_fuzzy(word, size, text): +def method_match_fuzzy(word, size, text) -> Union[Match, None]: s = r".*%s.*" % ".*".join(list(text)) return re.search(s, word) @@ -207,11 +208,11 @@ def method_match_fuzzy(word, size, text): class BaseCompletionType: """Describes different completion types""" - def __init__(self, shown_before_tab=True, mode=AutocompleteModes.SIMPLE): + def __init__(self, shown_before_tab: bool=True, mode=AutocompleteModes.SIMPLE) -> None: self._shown_before_tab = shown_before_tab self.method_match = MODES_MAP[mode] - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> NoReturn: """Returns a list of possible matches given a line and cursor, or None if this completion type isn't applicable. @@ -229,7 +230,7 @@ def matches(self, cursor_offset, line, **kwargs): """ raise NotImplementedError - def locate(self, cursor_offset, line): + def locate(self, cursor_offset, line) -> NoReturn: """Returns a Linepart namedtuple instance or None given cursor and line A Linepart namedtuple contains a start, stop, and word. None is @@ -240,7 +241,7 @@ def locate(self, cursor_offset, line): def format(self, word): return word - def substitute(self, cursor_offset, line, match): + def substitute(self, cursor_offset, line, match) -> NoReturn: """Returns a cursor offset and line with match swapped in""" lpart = self.locate(cursor_offset, line) offset = lpart.start + len(match) @@ -248,7 +249,7 @@ def substitute(self, cursor_offset, line, match): return offset, changed_line @property - def shown_before_tab(self): + def shown_before_tab(self) -> bool: """Whether suggestions should be shown before the user hits tab, or only once that has happened.""" return self._shown_before_tab @@ -257,7 +258,7 @@ def shown_before_tab(self): class CumulativeCompleter(BaseCompletionType): """Returns combined matches from several completers""" - def __init__(self, completers, mode=AutocompleteModes.SIMPLE): + def __init__(self, completers, mode=AutocompleteModes.SIMPLE) -> None: if not completers: raise ValueError( "CumulativeCompleter requires at least one completer" @@ -266,7 +267,7 @@ def __init__(self, completers, mode=AutocompleteModes.SIMPLE): super().__init__(True, mode) - def locate(self, current_offset, line): + def locate(self, current_offset, line) -> Union[None, NoReturn]: for completer in self._completers: return_value = completer.locate(current_offset, line) if return_value is not None: @@ -275,7 +276,7 @@ def locate(self, current_offset, line): def format(self, word): return self._completers[0].format(word) - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[None, Set]: return_value = None all_matches = set() for completer in self._completers: @@ -308,10 +309,10 @@ class FilenameCompletion(BaseCompletionType): def __init__(self, mode=AutocompleteModes.SIMPLE): super().__init__(False, mode) - def safe_glob(self, pathname): + def safe_glob(self, pathname) -> Iterator: return glob.iglob(glob.escape(pathname) + "*") - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[None, set]: cs = lineparts.current_string(cursor_offset, line) if cs is None: return None @@ -341,7 +342,7 @@ class AttrCompletion(BaseCompletionType): attr_matches_re = LazyReCompile(r"(\w+(\.\w+)*)\.(\w*)") - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[None, Dict]: if "locals_" not in kwargs: return None locals_ = kwargs["locals_"] @@ -377,7 +378,7 @@ def locate(self, current_offset, line): def format(self, word): return after_last_dot(word) - def attr_matches(self, text, namespace): + def attr_matches(self, text, namespace) -> List: """Taken from rlcompleter.py and bent to my will.""" m = self.attr_matches_re.match(text) @@ -396,7 +397,7 @@ def attr_matches(self, text, namespace): matches = self.attr_lookup(obj, expr, attr) return matches - def attr_lookup(self, obj, expr, attr): + def attr_lookup(self, obj, expr, attr) -> List: """Second half of attr_matches.""" words = self.list_attributes(obj) if inspection.hasattr_safe(obj, "__class__"): @@ -416,7 +417,7 @@ def attr_lookup(self, obj, expr, attr): matches.append(f"{expr}.{word}") return matches - def list_attributes(self, obj): + def list_attributes(self, obj) -> List[str]: # TODO: re-implement dir using getattr_static to avoid using # AttrCleaner here? with inspection.AttrCleaner(obj): @@ -424,7 +425,7 @@ def list_attributes(self, obj): class DictKeyCompletion(BaseCompletionType): - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[None, Dict]: if "locals_" not in kwargs: return None locals_ = kwargs["locals_"] @@ -445,7 +446,7 @@ def matches(self, cursor_offset, line, **kwargs): else: return None - def locate(self, current_offset, line): + def locate(self, current_offset, line) -> Union[LinePart, None]: return lineparts.current_dict_key(current_offset, line) def format(self, match): @@ -453,7 +454,7 @@ def format(self, match): class MagicMethodCompletion(BaseCompletionType): - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[None, Dict]: if "current_block" not in kwargs: return None current_block = kwargs["current_block"] @@ -465,12 +466,12 @@ def matches(self, cursor_offset, line, **kwargs): return None return {name for name in MAGIC_METHODS if name.startswith(r.word)} - def locate(self, current_offset, line): + def locate(self, current_offset, line) -> Union[LinePart, None]: return lineparts.current_method_definition_name(current_offset, line) class GlobalCompletion(BaseCompletionType): - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[Set, None]: """Compute matches when text is a simple name. Return a list of all keywords, built-in functions and names currently defined in self.namespace that match. @@ -500,12 +501,12 @@ def matches(self, cursor_offset, line, **kwargs): matches.add(_callable_postfix(val, word)) return matches if matches else None - def locate(self, current_offset, line): + def locate(self, current_offset, line) -> Union[LinePart, None]: return lineparts.current_single_word(current_offset, line) class ParameterNameCompletion(BaseCompletionType): - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[None, Dict]: if "argspec" not in kwargs: return None argspec = kwargs["argspec"] @@ -526,16 +527,16 @@ def matches(self, cursor_offset, line, **kwargs): ) return matches if matches else None - def locate(self, current_offset, line): + def locate(self, current_offset, line) -> Union[LinePart, None]: return lineparts.current_word(current_offset, line) class ExpressionAttributeCompletion(AttrCompletion): # could replace attr completion as a more general case with some work - def locate(self, current_offset, line): + def locate(self, current_offset, line) -> Union[LinePart, None]: return lineparts.current_expression_attribute(current_offset, line) - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[Set, Dict, None]: if "locals_" not in kwargs: return None locals_ = kwargs["locals_"] @@ -560,14 +561,14 @@ def matches(self, cursor_offset, line, **kwargs): except ImportError: class MultilineJediCompletion(BaseCompletionType): - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> None: return None else: class JediCompletion(BaseCompletionType): - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[None, Dict]: if "history" not in kwargs: return None history = kwargs["history"] @@ -607,13 +608,13 @@ def matches(self, cursor_offset, line, **kwargs): # case-sensitive matches only return {m for m in matches if m.startswith(first_letter)} - def locate(self, cursor_offset, line): + def locate(self, cursor_offset, line) -> LinePart: start = self._orig_start end = cursor_offset return LinePart(start, end, line[start:end]) class MultilineJediCompletion(JediCompletion): - def matches(self, cursor_offset, line, **kwargs): + def matches(self, cursor_offset, line, **kwargs) -> Union[Dict, None]: if "current_block" not in kwargs or "history" not in kwargs: return None current_block = kwargs["current_block"] diff --git a/bpython/cli.py b/bpython/cli.py index 8add9f743..baa2059da 100644 --- a/bpython/cli.py +++ b/bpython/cli.py @@ -50,6 +50,7 @@ import struct import sys import time +from typing import Iterator, NoReturn import unicodedata from dataclasses import dataclass @@ -97,7 +98,7 @@ class ShowListState: wl: int = 0 -def calculate_screen_lines(tokens, width, cursor=0): +def calculate_screen_lines(tokens, width, cursor=0) -> int: """Given a stream of tokens and a screen width plus an optional initial cursor position, return the amount of needed lines on the screen.""" @@ -130,31 +131,31 @@ class FakeStream: provided.""" def __init__(self, interface, get_dest): - self.encoding = getpreferredencoding() + self.encoding: str = getpreferredencoding() self.interface = interface self.get_dest = get_dest @forward_if_not_current - def write(self, s): + def write(self, s) -> None: self.interface.write(s) @forward_if_not_current - def writelines(self, l): + def writelines(self, l) -> None: for s in l: self.write(s) - def isatty(self): + def isatty(self) -> True: # some third party (amongst them mercurial) depend on this return True - def flush(self): + def flush(self) -> None: self.interface.flush() class FakeStdin: """Provide a fake stdin type for things like raw_input() etc.""" - def __init__(self, interface): + def __init__(self, interface) -> None: """Take the curses Repl on init and assume it provides a get_key method which, fortunately, it does.""" @@ -162,19 +163,19 @@ def __init__(self, interface): self.interface = interface self.buffer = list() - def __iter__(self): + def __iter__(self) -> Iterator: return iter(self.readlines()) def flush(self): """Flush the internal buffer. This is a no-op. Flushing stdin doesn't make any sense anyway.""" - def write(self, value): + def write(self, value) -> NoReturn: # XXX IPython expects sys.stdin.write to exist, there will no doubt be # others, so here's a hack to keep them happy raise OSError(errno.EBADF, "sys.stdin is read-only") - def isatty(self): + def isatty(self) -> True: return True def readline(self, size=-1): diff --git a/bpython/curtsies.py b/bpython/curtsies.py index 2bd348335..139bbae36 100644 --- a/bpython/curtsies.py +++ b/bpython/curtsies.py @@ -20,7 +20,7 @@ class FullCurtsiesRepl(BaseRepl): - def __init__(self, config, locals_, banner, interp=None): + def __init__(self, config, locals_, banner, interp=None) -> None: self.input_generator = curtsies.input.Input( keynames="curtsies", sigint_event=True, paste_threshold=None ) From 06d255ada61105684f4e2b92f09219f9619506e6 Mon Sep 17 00:00:00 2001 From: supremestdoggo <83146042+supremestdoggo@users.noreply.github.com> Date: Thu, 12 Aug 2021 19:45:18 -0400 Subject: [PATCH 2/2] Reformat via black --- bpython/args.py | 9 ++------- bpython/autocomplete.py | 7 ++++--- bpython/config.py | 10 ++-------- bpython/curtsies.py | 14 ++++++-------- bpython/curtsiesfrontend/repl.py | 7 +------ bpython/repl.py | 3 +-- bpython/test/test_autocomplete.py | 3 +-- bpython/test/test_crashers.py | 5 +---- bpython/urwid.py | 7 +------ 9 files changed, 19 insertions(+), 46 deletions(-) diff --git a/bpython/args.py b/bpython/args.py index 7895d84ad..69df4ba96 100644 --- a/bpython/args.py +++ b/bpython/args.py @@ -56,10 +56,7 @@ def error(self, msg): def version_banner(base="bpython") -> str: return _("{} version {} on top of Python {} {}").format( - base, - __version__, - sys.version.split()[0], - sys.executable, + base, __version__, sys.version.split()[0], sys.executable, ) @@ -140,9 +137,7 @@ def callback(group): help=_("Set log level for logging"), ) parser.add_argument( - "--log-output", - "-L", - help=_("Log output file"), + "--log-output", "-L", help=_("Log output file"), ) if extras is not None: diff --git a/bpython/autocomplete.py b/bpython/autocomplete.py index 0697e4906..3f5219aba 100644 --- a/bpython/autocomplete.py +++ b/bpython/autocomplete.py @@ -208,7 +208,9 @@ def method_match_fuzzy(word, size, text) -> Union[Match, None]: class BaseCompletionType: """Describes different completion types""" - def __init__(self, shown_before_tab: bool=True, mode=AutocompleteModes.SIMPLE) -> None: + def __init__( + self, shown_before_tab: bool = True, mode=AutocompleteModes.SIMPLE + ) -> None: self._shown_before_tab = shown_before_tab self.method_match = MODES_MAP[mode] @@ -622,8 +624,7 @@ def matches(self, cursor_offset, line, **kwargs) -> Union[Dict, None]: if "\n" in current_block: assert cursor_offset <= len(line), "{!r} {!r}".format( - cursor_offset, - line, + cursor_offset, line, ) results = super().matches(cursor_offset, line, history=history) return results diff --git a/bpython/config.py b/bpython/config.py index 48686610e..877836291 100644 --- a/bpython/config.py +++ b/bpython/config.py @@ -187,14 +187,8 @@ class Config: "up_one_line": "C-p", "yank_from_buffer": "C-y", }, - "cli": { - "suggestion_width": 0.8, - "trim_prompts": False, - }, - "curtsies": { - "list_above": False, - "right_arrow_completion": True, - }, + "cli": {"suggestion_width": 0.8, "trim_prompts": False,}, + "curtsies": {"list_above": False, "right_arrow_completion": True,}, } def __init__(self, config_path: Path) -> None: diff --git a/bpython/curtsies.py b/bpython/curtsies.py index 139bbae36..a21e157f1 100644 --- a/bpython/curtsies.py +++ b/bpython/curtsies.py @@ -35,16 +35,14 @@ def __init__(self, config, locals_, banner, interp=None) -> None: self._request_refresh_callback = self.input_generator.event_trigger( events.RefreshRequestEvent ) - self._schedule_refresh_callback = ( - self.input_generator.scheduled_event_trigger( - events.ScheduledRefreshRequestEvent - ) + self._schedule_refresh_callback = self.input_generator.scheduled_event_trigger( + events.ScheduledRefreshRequestEvent ) - self._request_reload_callback = ( - self.input_generator.threadsafe_event_trigger(events.ReloadEvent) + self._request_reload_callback = self.input_generator.threadsafe_event_trigger( + events.ReloadEvent ) - self._interrupting_refresh_callback = ( - self.input_generator.threadsafe_event_trigger(lambda: None) + self._interrupting_refresh_callback = self.input_generator.threadsafe_event_trigger( + lambda: None ) self._request_undo_callback = self.input_generator.event_trigger( events.UndoEvent diff --git a/bpython/curtsiesfrontend/repl.py b/bpython/curtsiesfrontend/repl.py index f38304e9a..724d6d560 100644 --- a/bpython/curtsiesfrontend/repl.py +++ b/bpython/curtsiesfrontend/repl.py @@ -307,12 +307,7 @@ class BaseRepl(Repl): """ def __init__( - self, - config, - locals_=None, - banner=None, - interp=None, - orig_tcattrs=None, + self, config, locals_=None, banner=None, interp=None, orig_tcattrs=None, ): """ locals_ is a mapping of locals to pass into the interpreter diff --git a/bpython/repl.py b/bpython/repl.py index ba9acf4d8..8a787b1a7 100644 --- a/bpython/repl.py +++ b/bpython/repl.py @@ -445,8 +445,7 @@ def __init__(self, interp, config): self.paster = PasteHelper(self.config.pastebin_helper) else: self.paster = PastePinnwand( - self.config.pastebin_url, - self.config.pastebin_expiry, + self.config.pastebin_url, self.config.pastebin_expiry, ) @property diff --git a/bpython/test/test_autocomplete.py b/bpython/test/test_autocomplete.py index ad991abe4..79443554e 100644 --- a/bpython/test/test_autocomplete.py +++ b/bpython/test/test_autocomplete.py @@ -257,8 +257,7 @@ def test_custom_get_attribute_not_invoked(self): def test_slots_not_crash(self): com = autocomplete.AttrCompletion() self.assertSetEqual( - com.matches(2, "A.", locals_={"A": Slots}), - {"A.b", "A.a"}, + com.matches(2, "A.", locals_={"A": Slots}), {"A.b", "A.a"}, ) diff --git a/bpython/test/test_crashers.py b/bpython/test/test_crashers.py index 64abff3e3..a6342d760 100644 --- a/bpython/test/test_crashers.py +++ b/bpython/test/test_crashers.py @@ -100,10 +100,7 @@ def processExited(self, reason): str(TEST_CONFIG), "-q", # prevents version greeting ), - env={ - "TERM": "vt100", - "LANG": os.environ.get("LANG", "C.UTF-8"), - }, + env={"TERM": "vt100", "LANG": os.environ.get("LANG", "C.UTF-8"),}, usePTY=(master, slave, os.ttyname(slave)), ) return result diff --git a/bpython/urwid.py b/bpython/urwid.py index 1f63b1370..4923f4397 100644 --- a/bpython/urwid.py +++ b/bpython/urwid.py @@ -1143,12 +1143,7 @@ def options_callback(group): # TODO: maybe support displays other than raw_display? config, options, exec_args = bpargs.parse( - args, - ( - "Urwid options", - None, - options_callback, - ), + args, ("Urwid options", None, options_callback,), ) if options.help_reactors: