Skip to content

Commit

Permalink
feat: add option for user to provide list of words not to capitalize (#…
Browse files Browse the repository at this point in the history
…195)

* feat: add option for list of words not to capitalize

* test: for add option for list of words not to capitalize

* docs: update documentation for option not to capitalize
  • Loading branch information
weibullguy committed Apr 28, 2023
1 parent d12620f commit 86b4ea6
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 9 deletions.
1 change: 0 additions & 1 deletion docs/source/configuration.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

How to Configure docformatter
=============================

Expand Down
1 change: 1 addition & 0 deletions docs/source/requirements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ the requirement falls in, the type of requirement, and whether
' docformatter_4.5.1', ' One-line docstrings may end in any of the following punctuation marks [. ! ?]', ' Derived', ' May', ' Yes'
' docformatter_4.5.2', ' One-line docstrings will have the first word capitalized.', ' Derived', ' Shall', ' Yes'
' docformatter_4.5.2.1', ' First words in one-line docstrings that are variables or filenames shall remain unchanged.', ' Derived', ' Shall', ' Yes [PR #185, #188]'
' docformatter_4.5.2.2', ' First words in one-line docstrings that are user-specified to not be capitalized shall remain unchanged.', ' Derived', ' Shall', ' Yes [PR #194]'
' docformatter_4.5.3', ' Shall not place a newline after the first line of a wrapped one-line docstring.' ' Derived', ' Shall', ' Yes [PR #179]'
' PEP_257_5','**Multi-line docstrings:**'
' PEP_257_5.1',' A summary is just like a one-line docstring.',' Convention',' Shall',' Yes'
Expand Down
2 changes: 2 additions & 0 deletions docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ help output provides a summary of these options:
-c, --check only check and report incorrectly formatted files
-r, --recursive drill down directories recursively
-e, --exclude in recursive mode, exclude directories and files by names
-n, --non-cap list of words not to capitalize when they appear as the
first word in the summary
--wrap-summaries length
wrap long summary lines at this length; set
Expand Down
9 changes: 9 additions & 0 deletions src/docformatter/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@ def do_parse_arguments(self) -> None:
default=self.flargs_dct.get("exclude", None),
help="in recursive mode, exclude directories and files by names",
)
self.parser.add_argument(
"-n",
"--non-cap",
action="store",
nargs="*",
default=self.flargs_dct.get("non-cap", None),
help="list of words not to capitalize when they appear as the "
"first word in the summary",
)
self.parser.add_argument(
"--wrap-summaries",
default=int(self.flargs_dct.get("wrap-summaries", 79)),
Expand Down
8 changes: 5 additions & 3 deletions src/docformatter/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,15 +430,17 @@ def _do_format_oneline_docstring(
beginning = f"{open_quote}\n{indentation}"
ending = f'\n{indentation}"""'
summary_wrapped = _syntax.wrap_summary(
_strings.normalize_summary(contents),
_strings.normalize_summary(contents, self.args.non_cap),
wrap_length=self.args.wrap_summaries,
initial_indent=indentation,
subsequent_indent=indentation,
).strip()
return f"{beginning}{summary_wrapped}{ending}"
else:
summary_wrapped = _syntax.wrap_summary(
open_quote + _strings.normalize_summary(contents) + '"""',
open_quote
+ _strings.normalize_summary(contents, self.args.non_cap)
+ '"""',
wrap_length=self.args.wrap_summaries,
initial_indent=indentation,
subsequent_indent=indentation,
Expand Down Expand Up @@ -488,7 +490,7 @@ def _do_format_multiline_docstring(
"\n" + indentation if self.args.pre_summary_newline else ""
)
summary = _syntax.wrap_summary(
_strings.normalize_summary(summary),
_strings.normalize_summary(summary, self.args.non_cap),
wrap_length=self.args.wrap_summaries,
initial_indent=initial_indent,
subsequent_indent=indentation,
Expand Down
35 changes: 30 additions & 5 deletions src/docformatter/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
"""This module provides docformatter string functions."""


import contextlib
# Standard Library Imports
import contextlib
import re
from typing import List


def find_shortest_indentation(lines):
Expand Down Expand Up @@ -99,8 +100,28 @@ def normalize_line_endings(lines, newline):
return "".join([normalize_line(line, newline) for line in lines])


def normalize_summary(summary: str) -> str:
"""Return normalized docstring summary."""
def normalize_summary(summary: str, noncap: List[str] = None) -> str:
"""Return normalized docstring summary.
A normalized docstring summary will have the first word capitalized and
a period at the end.
Parameters
----------
summary : str
The summary string.
noncap : list
A user-provided list of words not to capitalize when they appear as
the first word in the summary.
Returns
-------
summary : str
The normalized summary string.
"""
if noncap is None:
noncap = []

# Remove trailing whitespace
summary = summary.rstrip()

Expand All @@ -115,9 +136,13 @@ def normalize_summary(summary: str) -> str:
with contextlib.suppress(IndexError):
# Look for underscores, periods in the first word, this would typically
# indicate the first word is a variable name, file name, or some other
# non-standard English word. If none of these exist capitalize the
# non-standard English word. The search the list of user-defined
# words not to capitalize. If none of these exist capitalize the
# first word of the summary.
if all(char not in summary.split(" ", 1)[0] for char in ["_", "."]):
if (
all(char not in summary.split(" ", 1)[0] for char in ["_", "."])
and summary.split(" ", 1)[0] not in noncap
):
summary = summary[0].upper() + summary[1:]

return summary
Expand Down
5 changes: 5 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ def test_args(args):
"--exclude",
nargs="*",
)
parser.add_argument(
"-n",
"--non-cap",
nargs="*",
)
parser.add_argument(
"--wrap-summaries",
default=79,
Expand Down
103 changes: 103 additions & 0 deletions tests/test_configuration_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,106 @@ def test_exclude_from_setup_cfg(self,temporary_setup_cfg,config,):
"diff": "true",
"exclude": '["src/", "tests/"]'
}

@pytest.mark.unit
def test_non_capitalize_words(self, capsys):
"""Read list of words not to capitalize.
See issue #193.
"""
argb = [
"/path/to/docformatter",
"-n",
"qBittorrent",
"eBay",
"iPad",
"-c",
"",
]

uut = Configurater(argb)
uut.do_parse_arguments()

assert uut.args.non_cap == ["qBittorrent", "eBay", "iPad"]


@pytest.mark.unit
@pytest.mark.parametrize(
"config",
[
"""\
[tool.docformatter]
check = true
diff = true
recursive = true
non-cap = ["qBittorrent", "iPad", "iOS", "eBay"]
"""
],
)
def test_non_cap_from_pyproject_toml(self,temporary_pyproject_toml,
config,):
"""Read list of words not to capitalize from pyproject.toml.
See issue #193.
"""
argb = [
"/path/to/docformatter",
"-c",
"--config",
"/tmp/pyproject.toml",
"",
]

uut = Configurater(argb)
uut.do_parse_arguments()

assert uut.args.check
assert not uut.args.in_place
assert uut.args_lst == argb
assert uut.config_file == "/tmp/pyproject.toml"
assert uut.flargs_dct == {
"recursive": "True",
"check": "True",
"diff": "True",
"non-cap": ["qBittorrent", "iPad", "iOS", "eBay"]
}

@pytest.mark.unit
@pytest.mark.parametrize(
"config",
[
"""\
[docformatter]
check = true
diff = true
recursive = true
non-cap = ["qBittorrent", "iPad", "iOS", "eBay"]
"""
],
)
def test_non_cap_from_setup_cfg(self,temporary_setup_cfg,config,):
"""Read list of words not to capitalize from setup.cfg.
See issue #193.
"""
argb = [
"/path/to/docformatter",
"-c",
"--config",
"/tmp/setup.cfg",
"",
]

uut = Configurater(argb)
uut.do_parse_arguments()

assert uut.args.check
assert not uut.args.in_place
assert uut.args_lst == argb
assert uut.config_file == "/tmp/setup.cfg"
assert uut.flargs_dct == {
"recursive": "true",
"check": "true",
"diff": "true",
"non-cap": '["qBittorrent", "iPad", "iOS", "eBay"]'
}
22 changes: 22 additions & 0 deletions tests/test_format_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,28 @@ def test_format_docstring_with_no_period(self, test_args, args):
'''.strip(),
)

@pytest.mark.unit
@pytest.mark.parametrize("args", [["--non-cap", "eBay", 'iPad', "-c", ""]])
def test_format_docstring_with_non_cap_words(self, test_args, args):
"""Capitalize words not found in the non_cap list.
See issue #193.
"""
uut = Formatter(
test_args,
sys.stderr,
sys.stdin,
sys.stdout,
)

assert '"""eBay kinda suss."""' == uut._do_format_docstring(
INDENTATION,
'''\
"""
eBay kinda suss
"""
''')

@pytest.mark.unit
@pytest.mark.parametrize("args", [[""]])
def test_format_docstring_with_single_quotes(self, test_args, args):
Expand Down

0 comments on commit 86b4ea6

Please sign in to comment.