From 675704ede626b678949e96622505b050891b452a Mon Sep 17 00:00:00 2001 From: Daniel Pope Date: Wed, 17 Jul 2019 16:57:19 +0100 Subject: [PATCH] Select best imperative by longest prefix match --- src/pydocstyle/checker.py | 8 ++++++-- src/pydocstyle/utils.py | 13 +++++++++++++ src/tests/test_cases/test.py | 16 ++++++++++++++++ src/tests/test_utils.py | 23 +++++++++++++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/tests/test_utils.py diff --git a/src/pydocstyle/checker.py b/src/pydocstyle/checker.py index f4777c5d..a45648fa 100644 --- a/src/pydocstyle/checker.py +++ b/src/pydocstyle/checker.py @@ -13,7 +13,7 @@ from .parser import (Package, Module, Class, NestedClass, Definition, AllError, Method, Function, NestedFunction, Parser, StringIO, ParseError) -from .utils import log, is_blank, pairwise +from .utils import log, is_blank, pairwise, common_prefix_length from .wordlists import IMPERATIVE_VERBS, IMPERATIVE_BLACKLIST, stem @@ -448,8 +448,12 @@ def check_imperative_mood(self, function, docstring): # def context return if correct_forms and check_word not in correct_forms: + best = max( + correct_forms, + key=lambda f: common_prefix_length(check_word, f) + ) return violations.D401( - next(iter(correct_forms)).capitalize(), + best.capitalize(), first_word ) diff --git a/src/pydocstyle/utils.py b/src/pydocstyle/utils.py index f922129b..91af8065 100644 --- a/src/pydocstyle/utils.py +++ b/src/pydocstyle/utils.py @@ -25,3 +25,16 @@ def pairwise( a, b = tee(iterable) _ = next(b, default_value) return zip_longest(a, b, fillvalue=default_value) + + +def common_prefix_length(a: str, b: str) -> int: + """Return the length of the longest common prefix of a and b. + + >>> common_prefix_length('abcd', 'abce') + 3 + + """ + for common, (ca, cb) in enumerate(zip(a, b)): + if ca != cb: + return common + return common + 1 diff --git a/src/tests/test_cases/test.py b/src/tests/test_cases/test.py index 3f6c7958..80762a16 100644 --- a/src/tests/test_cases/test.py +++ b/src/tests/test_cases/test.py @@ -385,6 +385,22 @@ def docstring_ignore_some_violations_but_catch_D401(): # noqa: E501,D400,D415 pass +@expect( + "D401: First line should be in imperative mood " + "('Initiate', not 'Initiates')" +) +def docstring_initiates(): + """Initiates the process.""" + + +@expect( + "D401: First line should be in imperative mood " + "('Initialize', not 'Initializes')" +) +def docstring_initializes(): + """Initializes the process.""" + + @wraps(docstring_bad_ignore_one) def bad_decorated_function(): """Bad (E501) but decorated""" diff --git a/src/tests/test_utils.py b/src/tests/test_utils.py new file mode 100644 index 00000000..df54be40 --- /dev/null +++ b/src/tests/test_utils.py @@ -0,0 +1,23 @@ +"""Unit test for pydocstyle utils. + +Use tox or py.test to run the test suite. +""" +from pydocstyle import utils + + +__all__ = () + + +def test_common_prefix(): + """We can find the common prefix of two strings.""" + assert utils.common_prefix_length('abcd', 'abce') == 3 + + +def test_no_common_prefix(): + """If two strings have no common prefix, return the empty string.""" + assert utils.common_prefix_length('abcd', 'cdef') == 0 + + +def test_differ_length(): + """We can find a common prefix of two strings differing in length.""" + assert utils.common_prefix_length('abcd', 'ab') == 2