Skip to content

Add type annotations to a few files #904

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions bpython/_internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
16 changes: 6 additions & 10 deletions bpython/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"""

import argparse
from typing import Tuple
import curtsies
import cwcwidth
import greenlet
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down
76 changes: 39 additions & 37 deletions bpython/autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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.

Expand All @@ -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
Expand All @@ -240,15 +243,15 @@ 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)
changed_line = line[: lpart.start] + match + line[lpart.stop :]
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
Expand All @@ -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"
Expand All @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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_"]
Expand Down Expand Up @@ -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)
Expand All @@ -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__"):
Expand All @@ -416,15 +419,15 @@ 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):
return dir(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_"]
Expand All @@ -445,15 +448,15 @@ 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):
return match[:-1]


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"]
Expand All @@ -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.
Expand Down Expand Up @@ -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"]
Expand All @@ -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_"]
Expand All @@ -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"]
Expand Down Expand Up @@ -607,22 +610,21 @@ 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"]
history = kwargs["history"]

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
Expand Down
Loading