diff --git a/bpython/_internal.py b/bpython/_internal.py index 4545862c..35cd0bf8 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 e3095e17..69df4ba9 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,20 +54,17 @@ 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__, - sys.version.split()[0], - sys.executable, + base, __version__, sys.version.split()[0], sys.executable, ) -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) @@ -139,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 975a2622..3f5219ab 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,13 @@ 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 +232,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 +243,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 +251,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 +260,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 +269,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 +278,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 +311,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 +344,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 +380,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 +399,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 +419,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 +427,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 +448,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 +456,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 +468,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 +503,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 +529,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 +563,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 +610,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"] @@ -621,8 +624,7 @@ def matches(self, cursor_offset, line, **kwargs): 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/cli.py b/bpython/cli.py index 8add9f74..baa2059d 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/config.py b/bpython/config.py index 48686610..87783629 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 2bd34833..a21e157f 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 ) @@ -35,16 +35,14 @@ def __init__(self, config, locals_, banner, interp=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 f38304e9..724d6d56 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 ba9acf4d..8a787b1a 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 ad991abe..79443554 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 64abff3e..a6342d76 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 1f63b137..4923f439 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: