Skip to content

Commit 86b4ea6

Browse files
authored
feat: add option for user to provide list of words not to capitalize (#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
1 parent d12620f commit 86b4ea6

File tree

9 files changed

+177
-9
lines changed

9 files changed

+177
-9
lines changed

docs/source/configuration.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
How to Configure docformatter
32
=============================
43

docs/source/requirements.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ the requirement falls in, the type of requirement, and whether
163163
' docformatter_4.5.1', ' One-line docstrings may end in any of the following punctuation marks [. ! ?]', ' Derived', ' May', ' Yes'
164164
' docformatter_4.5.2', ' One-line docstrings will have the first word capitalized.', ' Derived', ' Shall', ' Yes'
165165
' 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]'
166+
' 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]'
166167
' docformatter_4.5.3', ' Shall not place a newline after the first line of a wrapped one-line docstring.' ' Derived', ' Shall', ' Yes [PR #179]'
167168
' PEP_257_5','**Multi-line docstrings:**'
168169
' PEP_257_5.1',' A summary is just like a one-line docstring.',' Convention',' Shall',' Yes'

docs/source/usage.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ help output provides a summary of these options:
4040
-c, --check only check and report incorrectly formatted files
4141
-r, --recursive drill down directories recursively
4242
-e, --exclude in recursive mode, exclude directories and files by names
43+
-n, --non-cap list of words not to capitalize when they appear as the
44+
first word in the summary
4345
4446
--wrap-summaries length
4547
wrap long summary lines at this length; set

src/docformatter/configuration.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ def do_parse_arguments(self) -> None:
127127
default=self.flargs_dct.get("exclude", None),
128128
help="in recursive mode, exclude directories and files by names",
129129
)
130+
self.parser.add_argument(
131+
"-n",
132+
"--non-cap",
133+
action="store",
134+
nargs="*",
135+
default=self.flargs_dct.get("non-cap", None),
136+
help="list of words not to capitalize when they appear as the "
137+
"first word in the summary",
138+
)
130139
self.parser.add_argument(
131140
"--wrap-summaries",
132141
default=int(self.flargs_dct.get("wrap-summaries", 79)),

src/docformatter/format.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,15 +430,17 @@ def _do_format_oneline_docstring(
430430
beginning = f"{open_quote}\n{indentation}"
431431
ending = f'\n{indentation}"""'
432432
summary_wrapped = _syntax.wrap_summary(
433-
_strings.normalize_summary(contents),
433+
_strings.normalize_summary(contents, self.args.non_cap),
434434
wrap_length=self.args.wrap_summaries,
435435
initial_indent=indentation,
436436
subsequent_indent=indentation,
437437
).strip()
438438
return f"{beginning}{summary_wrapped}{ending}"
439439
else:
440440
summary_wrapped = _syntax.wrap_summary(
441-
open_quote + _strings.normalize_summary(contents) + '"""',
441+
open_quote
442+
+ _strings.normalize_summary(contents, self.args.non_cap)
443+
+ '"""',
442444
wrap_length=self.args.wrap_summaries,
443445
initial_indent=indentation,
444446
subsequent_indent=indentation,
@@ -488,7 +490,7 @@ def _do_format_multiline_docstring(
488490
"\n" + indentation if self.args.pre_summary_newline else ""
489491
)
490492
summary = _syntax.wrap_summary(
491-
_strings.normalize_summary(summary),
493+
_strings.normalize_summary(summary, self.args.non_cap),
492494
wrap_length=self.args.wrap_summaries,
493495
initial_indent=initial_indent,
494496
subsequent_indent=indentation,

src/docformatter/strings.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424
"""This module provides docformatter string functions."""
2525

2626

27-
import contextlib
2827
# Standard Library Imports
28+
import contextlib
2929
import re
30+
from typing import List
3031

3132

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

101102

102-
def normalize_summary(summary: str) -> str:
103-
"""Return normalized docstring summary."""
103+
def normalize_summary(summary: str, noncap: List[str] = None) -> str:
104+
"""Return normalized docstring summary.
105+
106+
A normalized docstring summary will have the first word capitalized and
107+
a period at the end.
108+
109+
Parameters
110+
----------
111+
summary : str
112+
The summary string.
113+
noncap : list
114+
A user-provided list of words not to capitalize when they appear as
115+
the first word in the summary.
116+
117+
Returns
118+
-------
119+
summary : str
120+
The normalized summary string.
121+
"""
122+
if noncap is None:
123+
noncap = []
124+
104125
# Remove trailing whitespace
105126
summary = summary.rstrip()
106127

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

123148
return summary

tests/conftest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ def test_args(args):
158158
"--exclude",
159159
nargs="*",
160160
)
161+
parser.add_argument(
162+
"-n",
163+
"--non-cap",
164+
nargs="*",
165+
)
161166
parser.add_argument(
162167
"--wrap-summaries",
163168
default=79,

tests/test_configuration_functions.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,106 @@ def test_exclude_from_setup_cfg(self,temporary_setup_cfg,config,):
380380
"diff": "true",
381381
"exclude": '["src/", "tests/"]'
382382
}
383+
384+
@pytest.mark.unit
385+
def test_non_capitalize_words(self, capsys):
386+
"""Read list of words not to capitalize.
387+
388+
See issue #193.
389+
"""
390+
argb = [
391+
"/path/to/docformatter",
392+
"-n",
393+
"qBittorrent",
394+
"eBay",
395+
"iPad",
396+
"-c",
397+
"",
398+
]
399+
400+
uut = Configurater(argb)
401+
uut.do_parse_arguments()
402+
403+
assert uut.args.non_cap == ["qBittorrent", "eBay", "iPad"]
404+
405+
406+
@pytest.mark.unit
407+
@pytest.mark.parametrize(
408+
"config",
409+
[
410+
"""\
411+
[tool.docformatter]
412+
check = true
413+
diff = true
414+
recursive = true
415+
non-cap = ["qBittorrent", "iPad", "iOS", "eBay"]
416+
"""
417+
],
418+
)
419+
def test_non_cap_from_pyproject_toml(self,temporary_pyproject_toml,
420+
config,):
421+
"""Read list of words not to capitalize from pyproject.toml.
422+
423+
See issue #193.
424+
"""
425+
argb = [
426+
"/path/to/docformatter",
427+
"-c",
428+
"--config",
429+
"/tmp/pyproject.toml",
430+
"",
431+
]
432+
433+
uut = Configurater(argb)
434+
uut.do_parse_arguments()
435+
436+
assert uut.args.check
437+
assert not uut.args.in_place
438+
assert uut.args_lst == argb
439+
assert uut.config_file == "/tmp/pyproject.toml"
440+
assert uut.flargs_dct == {
441+
"recursive": "True",
442+
"check": "True",
443+
"diff": "True",
444+
"non-cap": ["qBittorrent", "iPad", "iOS", "eBay"]
445+
}
446+
447+
@pytest.mark.unit
448+
@pytest.mark.parametrize(
449+
"config",
450+
[
451+
"""\
452+
[docformatter]
453+
check = true
454+
diff = true
455+
recursive = true
456+
non-cap = ["qBittorrent", "iPad", "iOS", "eBay"]
457+
"""
458+
],
459+
)
460+
def test_non_cap_from_setup_cfg(self,temporary_setup_cfg,config,):
461+
"""Read list of words not to capitalize from setup.cfg.
462+
463+
See issue #193.
464+
"""
465+
argb = [
466+
"/path/to/docformatter",
467+
"-c",
468+
"--config",
469+
"/tmp/setup.cfg",
470+
"",
471+
]
472+
473+
uut = Configurater(argb)
474+
uut.do_parse_arguments()
475+
476+
assert uut.args.check
477+
assert not uut.args.in_place
478+
assert uut.args_lst == argb
479+
assert uut.config_file == "/tmp/setup.cfg"
480+
assert uut.flargs_dct == {
481+
"recursive": "true",
482+
"check": "true",
483+
"diff": "true",
484+
"non-cap": '["qBittorrent", "iPad", "iOS", "eBay"]'
485+
}

tests/test_format_docstring.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,28 @@ def test_format_docstring_with_no_period(self, test_args, args):
215215
'''.strip(),
216216
)
217217

218+
@pytest.mark.unit
219+
@pytest.mark.parametrize("args", [["--non-cap", "eBay", 'iPad', "-c", ""]])
220+
def test_format_docstring_with_non_cap_words(self, test_args, args):
221+
"""Capitalize words not found in the non_cap list.
222+
223+
See issue #193.
224+
"""
225+
uut = Formatter(
226+
test_args,
227+
sys.stderr,
228+
sys.stdin,
229+
sys.stdout,
230+
)
231+
232+
assert '"""eBay kinda suss."""' == uut._do_format_docstring(
233+
INDENTATION,
234+
'''\
235+
"""
236+
eBay kinda suss
237+
"""
238+
''')
239+
218240
@pytest.mark.unit
219241
@pytest.mark.parametrize("args", [[""]])
220242
def test_format_docstring_with_single_quotes(self, test_args, args):

0 commit comments

Comments
 (0)