Skip to content

Commit 96b1835

Browse files
authored
Sections no header (#3)
* make section name optional * remove unnecessary attributes * change definition of section start to be non-empty line * add changelog and increment version
1 parent 41ebe63 commit 96b1835

7 files changed

Lines changed: 63 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## [Unreleased]
44

5+
## [v1.0.1] - 2023-01-03
6+
7+
### Fixed
8+
9+
- Fixed definition of a section start to be a non-empty line rather than based
10+
on whether it has a named header like
11+
512
## [v1.0.0] - 2023-01-02
613

714
### Added
@@ -63,3 +70,4 @@
6370

6471
[//]: # "Release links"
6572
[v1.0.0]: https://github.com/jdkandersson/flake8-docstrings-complete/releases/v1.0.0
73+
[v1.0.1]: https://github.com/jdkandersson/flake8-docstrings-complete/releases/v1.0.1

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,3 +1495,4 @@ Section information is extracted using the following algorithm:
14951495
- Check that argument, exceptions and attributes have non-empty description.
14961496
- Check that arguments, exceptions and attributes are only documented once.
14971497
- Check that arguments, exceptions and attributes have meaningful descriptions.
1498+
- Check other other PEP257 conventions

flake8_docstrings_complete/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def _check_returns(
9292
return_nodes_with_value = list(node for node in return_nodes if node.value is not None)
9393

9494
# Check for return statements with value and no returns section in docstring
95-
if return_nodes_with_value and not docstr_info.returns:
95+
if return_nodes_with_value and not docstr_info.returns_sections:
9696
yield from (
9797
types_.Problem(node.lineno, node.col_offset, RETURNS_SECTION_NOT_IN_DOCSTR_MSG)
9898
for node in return_nodes_with_value
@@ -107,7 +107,7 @@ def _check_returns(
107107
)
108108

109109
# Check for returns section in docstring in function that does not return a value
110-
if not return_nodes_with_value and docstr_info.returns:
110+
if not return_nodes_with_value and docstr_info.returns_sections:
111111
yield types_.Problem(
112112
docstr_node.lineno, docstr_node.col_offset, RETURNS_SECTION_IN_DOCSTR_MSG
113113
)
@@ -131,7 +131,7 @@ def _check_yields(
131131
yield_nodes_with_value = list(node for node in yield_nodes if node.value is not None)
132132

133133
# Check for yield statements with value and no yields section in docstring
134-
if yield_nodes_with_value and not docstr_info.yields:
134+
if yield_nodes_with_value and not docstr_info.yields_sections:
135135
yield from (
136136
types_.Problem(node.lineno, node.col_offset, YIELDS_SECTION_NOT_IN_DOCSTR_MSG)
137137
for node in yield_nodes_with_value
@@ -146,7 +146,7 @@ def _check_yields(
146146
)
147147

148148
# Check for yields section in docstring in function that does not yield a value
149-
if not yield_nodes_with_value and docstr_info.yields:
149+
if not yield_nodes_with_value and docstr_info.yields_sections:
150150
yield types_.Problem(
151151
docstr_node.lineno, docstr_node.col_offset, YIELDS_SECTION_IN_DOCSTR_MSG
152152
)

flake8_docstrings_complete/docstring.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class _Section(NamedTuple):
1717
sub-sections.
1818
"""
1919

20-
name: str
20+
name: str | None
2121
subs: tuple[str, ...]
2222

2323

@@ -31,9 +31,7 @@ class Docstring(NamedTuple):
3131
attrs: The attributes described in the docstring. None if the docstring doesn't have the
3232
attrs section.
3333
attrs_sections: All the attributes sections.
34-
returns: Whether the docstring has the returns section.
3534
returns_sections: All the returns sections.
36-
yields: Whether the docstring has the yields section.
3735
yields_sections: All the yields sections.
3836
raises: The exceptions described in the docstring. None if the docstring doesn't have the
3937
raises section.
@@ -44,9 +42,7 @@ class Docstring(NamedTuple):
4442
args_sections: tuple[str, ...] = ()
4543
attrs: tuple[str, ...] | None = None
4644
attrs_sections: tuple[str, ...] = ()
47-
returns: bool = False
4845
returns_sections: tuple[str, ...] = ()
49-
yields: bool = False
5046
yields_sections: tuple[str, ...] = ()
5147
raises: tuple[str, ...] | None = None
5248
raises_sections: tuple[str, ...] = ()
@@ -60,7 +56,7 @@ class Docstring(NamedTuple):
6056
"raises": {"raises", "raise"},
6157
}
6258
_WHITESPACE_REGEX = r"\s*"
63-
_SECTION_START_PATTERN = re.compile(rf"{_WHITESPACE_REGEX}(\w+):")
59+
_SECTION_NAME_PATTERN = re.compile(rf"{_WHITESPACE_REGEX}(\w+):")
6460
_SUB_SECTION_PATTERN = re.compile(rf"{_WHITESPACE_REGEX}(\w+)( \(.*\))?:")
6561
_SECTION_END_PATTERN = re.compile(rf"{_WHITESPACE_REGEX}$")
6662

@@ -86,16 +82,19 @@ def _get_sections(lines: Iterable[str]) -> Iterator[_Section]:
8682
with contextlib.suppress(StopIteration):
8783
while True:
8884
# Find the start of the next section
89-
section_name = next(
90-
filter(None, (_SECTION_START_PATTERN.match(line) for line in lines))
91-
).group(1)
85+
section_start = next(line for line in lines if line.strip())
86+
section_name_match = _SECTION_NAME_PATTERN.match(section_start)
87+
section_name = section_name_match.group(1) if section_name_match else None
88+
9289
# Get all the lines of the section
9390
section_lines = itertools.takewhile(
9491
lambda line: _SECTION_END_PATTERN.match(line) is None, lines
9592
)
93+
9694
# Retrieve sub section from section lines
9795
sub_section_matches = (_SUB_SECTION_PATTERN.match(line) for line in section_lines)
9896
sub_sections = (match.group(1) for match in sub_section_matches if match is not None)
97+
9998
yield _Section(name=section_name, subs=tuple(sub_sections))
10099

101100

@@ -111,7 +110,11 @@ def _get_section_by_name(name: str, sections: Iterable[_Section]) -> _Section |
111110
"""
112111
sections = iter(sections)
113112
return next(
114-
(section for section in sections if section.name.lower() in _SECTION_NAMES[name]),
113+
(
114+
section
115+
for section in sections
116+
if section.name is not None and section.name.lower() in _SECTION_NAMES[name]
117+
),
115118
None,
116119
)
117120

@@ -128,7 +131,9 @@ def _get_all_section_names_by_name(name: str, sections: Iterable[_Section]) -> I
128131
"""
129132
sections = iter(sections)
130133
yield from (
131-
section.name for section in sections if section.name.lower() in _SECTION_NAMES[name]
134+
section.name
135+
for section in sections
136+
if section.name is not None and section.name.lower() in _SECTION_NAMES[name]
132137
)
133138

134139

@@ -152,9 +157,7 @@ def parse(value: str) -> Docstring:
152157
args_sections=tuple(_get_all_section_names_by_name(name="args", sections=sections)),
153158
attrs=attrs_section.subs if attrs_section is not None else None,
154159
attrs_sections=tuple(_get_all_section_names_by_name(name="attrs", sections=sections)),
155-
returns=_get_section_by_name("returns", sections) is not None,
156160
returns_sections=tuple(_get_all_section_names_by_name(name="returns", sections=sections)),
157-
yields=_get_section_by_name("yields", sections) is not None,
158161
yields_sections=tuple(_get_all_section_names_by_name(name="yields", sections=sections)),
159162
raises=raises_section.subs if raises_section is not None else None,
160163
raises_sections=tuple(_get_all_section_names_by_name(name="raises", sections=sections)),

poetry.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "flake8-docstrings-complete"
3-
version = "1.0.0"
3+
version = "1.0.1"
44
description = "A linter that checks docstrings are complete"
55
authors = ["David Andersson <david@jdkandersson.com>"]
66
license = "Apache 2.0"

tests/test_docstring.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,24 @@
1515
[
1616
pytest.param((), (), id="empty"),
1717
pytest.param(("",), (), id="single not a section"),
18-
pytest.param(("not a section",), (), id="single not a section no colon"),
19-
pytest.param(("not a section:",), (), id="single not a section not after first word"),
18+
pytest.param((" ",), (), id="single not a section whitespace"),
19+
pytest.param(("\t",), (), id="single not a section alternate whitespace"),
20+
pytest.param(
21+
("line 1",), (docstring._Section(None, ()),), id="single line section no name"
22+
),
23+
pytest.param(
24+
("line 1", "line 2"), (docstring._Section(None, ()),), id="multi line section no name"
25+
),
26+
pytest.param(
27+
("line 1", "name_1:"),
28+
(docstring._Section(None, ("name_1",)),),
29+
id="multi line section no name second like name",
30+
),
31+
pytest.param(
32+
("line 1:",),
33+
(docstring._Section(None, ()),),
34+
id="single section no name colon after first word",
35+
),
2036
pytest.param(("name_1:",), (docstring._Section("name_1", ()),), id="single section"),
2137
pytest.param(
2238
(" name_1:",),
@@ -307,7 +323,7 @@ def test__get_sections(
307323
308324
Returns:
309325
""",
310-
docstring.Docstring(returns=True, returns_sections=("Returns",)),
326+
docstring.Docstring(returns_sections=("Returns",)),
311327
id="returns empty",
312328
),
313329
pytest.param(
@@ -316,15 +332,15 @@ def test__get_sections(
316332
Returns:
317333
The return value.
318334
""",
319-
docstring.Docstring(returns=True, returns_sections=("Returns",)),
335+
docstring.Docstring(returns_sections=("Returns",)),
320336
id="returns single line",
321337
),
322338
pytest.param(
323339
"""short description
324340
325341
Return:
326342
""",
327-
docstring.Docstring(returns=True, returns_sections=("Return",)),
343+
docstring.Docstring(returns_sections=("Return",)),
328344
id="returns alternate",
329345
),
330346
pytest.param(
@@ -334,7 +350,7 @@ def test__get_sections(
334350
335351
Returns:
336352
""",
337-
docstring.Docstring(returns=True, returns_sections=("Returns", "Returns")),
353+
docstring.Docstring(returns_sections=("Returns", "Returns")),
338354
id="multiple returns",
339355
),
340356
pytest.param(
@@ -344,15 +360,15 @@ def test__get_sections(
344360
345361
Return:
346362
""",
347-
docstring.Docstring(returns=True, returns_sections=("Returns", "Return")),
363+
docstring.Docstring(returns_sections=("Returns", "Return")),
348364
id="multiple returns alternate",
349365
),
350366
pytest.param(
351367
"""short description
352368
353369
Yields:
354370
""",
355-
docstring.Docstring(yields=True, yields_sections=("Yields",)),
371+
docstring.Docstring(yields_sections=("Yields",)),
356372
id="yields empty",
357373
),
358374
pytest.param(
@@ -361,15 +377,15 @@ def test__get_sections(
361377
Yields:
362378
The return value.
363379
""",
364-
docstring.Docstring(yields=True, yields_sections=("Yields",)),
380+
docstring.Docstring(yields_sections=("Yields",)),
365381
id="yields single line",
366382
),
367383
pytest.param(
368384
"""short description
369385
370386
Yield:
371387
""",
372-
docstring.Docstring(yields=True, yields_sections=("Yield",)),
388+
docstring.Docstring(yields_sections=("Yield",)),
373389
id="yields alternate",
374390
),
375391
pytest.param(
@@ -379,7 +395,7 @@ def test__get_sections(
379395
380396
Yields:
381397
""",
382-
docstring.Docstring(yields=True, yields_sections=("Yields", "Yields")),
398+
docstring.Docstring(yields_sections=("Yields", "Yields")),
383399
id="multiple yields",
384400
),
385401
pytest.param(
@@ -389,7 +405,7 @@ def test__get_sections(
389405
390406
Yield:
391407
""",
392-
docstring.Docstring(yields=True, yields_sections=("Yields", "Yield")),
408+
docstring.Docstring(yields_sections=("Yields", "Yield")),
393409
id="multiple yields alternate",
394410
),
395411
pytest.param(
@@ -479,9 +495,7 @@ def test__get_sections(
479495
args_sections=("Args",),
480496
attrs=("attr_1",),
481497
attrs_sections=("Attrs",),
482-
returns=True,
483498
returns_sections=("Returns",),
484-
yields=True,
485499
yields_sections=("Yields",),
486500
raises=("exc_1",),
487501
raises_sections=("Raises",),

0 commit comments

Comments
 (0)