From aba1e9028f6c8d152b12462418023fd660aa86d2 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 10 Oct 2019 18:09:25 +0200 Subject: [PATCH] Add type annotations for utils.docutils --- mypy.ini | 4 ++++ qutebrowser/utils/docutils.py | 44 +++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/mypy.ini b/mypy.ini index 3e6e1766f..fca45122e 100644 --- a/mypy.ini +++ b/mypy.ini @@ -117,3 +117,7 @@ disallow_incomplete_defs = True [mypy-qutebrowser.utils.debug] disallow_untyped_defs = True disallow_incomplete_defs = True + +[mypy-qutebrowser.utils.docutils] +disallow_untyped_defs = True +disallow_incomplete_defs = True diff --git a/qutebrowser/utils/docutils.py b/qutebrowser/utils/docutils.py index cf346fb81..94c348292 100644 --- a/qutebrowser/utils/docutils.py +++ b/qutebrowser/utils/docutils.py @@ -25,18 +25,19 @@ import inspect import os.path import collections import enum +import typing import qutebrowser from qutebrowser.utils import log, utils -def is_git_repo(): +def is_git_repo() -> bool: """Check if we're running from a git repository.""" gitfolder = os.path.join(qutebrowser.basedir, os.path.pardir, '.git') return os.path.isdir(gitfolder) -def docs_up_to_date(path): +def docs_up_to_date(path: str) -> bool: """Check if the generated html documentation is up to date. Args: @@ -79,17 +80,18 @@ class DocstringParser: State = enum.Enum('State', ['short', 'desc', 'desc_hidden', 'arg_start', 'arg_inside', 'misc']) - def __init__(self, func): + def __init__(self, func: typing.Callable) -> None: """Constructor. Args: func: The function to parse the docstring for. """ self._state = self.State.short - self._cur_arg_name = None - self._short_desc_parts = [] - self._long_desc_parts = [] - self.arg_descs = collections.OrderedDict() + self._cur_arg_name = None # type: typing.Optional[str] + self._short_desc_parts = [] # type: typing.List[str] + self._long_desc_parts = [] # type: typing.List[str] + self.arg_descs = collections.OrderedDict( + ) # type: typing.Dict[str, typing.Union[str, typing.List[str]]] doc = inspect.getdoc(func) handlers = { self.State.short: self._parse_short, @@ -104,7 +106,8 @@ class DocstringParser: log.commands.warning( "Function {}() from {} has no docstring".format( utils.qualname(func), - inspect.getsourcefile(func))) + # https://github.com/python/typeshed/pull/3295 + inspect.getsourcefile(func))) # type: ignore self.long_desc = "" self.short_desc = "" return @@ -121,25 +124,25 @@ class DocstringParser: self.long_desc = ' '.join(self._long_desc_parts) self.short_desc = ' '.join(self._short_desc_parts) - def _process_arg(self, line): + def _process_arg(self, line: str) -> None: """Helper method to process a line like 'fooarg: Blah blub'.""" self._cur_arg_name, argdesc = line.split(':', maxsplit=1) self._cur_arg_name = self._cur_arg_name.strip().lstrip('*') self.arg_descs[self._cur_arg_name] = [argdesc.strip()] - def _skip(self, line): + def _skip(self, line: str) -> None: """Handler to ignore everything until we get 'Args:'.""" if line.startswith('Args:'): self._state = self.State.arg_start - def _parse_short(self, line): + def _parse_short(self, line: str) -> None: """Parse the short description (first block) in the docstring.""" if not line: self._state = self.State.desc else: self._short_desc_parts.append(line.strip()) - def _parse_desc(self, line): + def _parse_desc(self, line: str) -> None: """Parse the long description in the docstring.""" if line.startswith('Args:'): self._state = self.State.arg_start @@ -148,22 +151,27 @@ class DocstringParser: elif line.strip(): self._long_desc_parts.append(line.strip()) - def _parse_arg_start(self, line): + def _parse_arg_start(self, line: str) -> None: """Parse first argument line.""" self._process_arg(line) self._state = self.State.arg_inside - def _parse_arg_inside(self, line): + def _parse_arg_inside(self, line: str) -> bool: """Parse subsequent argument lines.""" argname = self._cur_arg_name + assert argname is not None + + descs = self.arg_descs[argname] + assert isinstance(descs, list) + if re.fullmatch(r'[A-Z][a-z]+:', line): - if not self.arg_descs[argname][-1].strip(): - self.arg_descs[argname] = self.arg_descs[argname][:-1] + if not descs[-1].strip(): + del descs[-1] return True elif not line.strip(): - self.arg_descs[argname].append('\n\n') + descs.append('\n\n') elif line[4:].startswith(' '): - self.arg_descs[argname].append(line.strip() + '\n') + descs.append(line.strip() + '\n') else: self._process_arg(line) return False