Skip to content

Commit

Permalink
Sections no header (#3)
Browse files Browse the repository at this point in the history
* make section name optional
* remove unnecessary attributes
* change definition of section start to be non-empty line
* add changelog and increment version
  • Loading branch information
jdkandersson authored Jan 3, 2023
1 parent 41ebe63 commit 96b1835
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 37 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## [Unreleased]

## [v1.0.1] - 2023-01-03

### Fixed

- Fixed definition of a section start to be a non-empty line rather than based
on whether it has a named header like

## [v1.0.0] - 2023-01-02

### Added
Expand Down Expand Up @@ -63,3 +70,4 @@

[//]: # "Release links"
[v1.0.0]: https://github.com/jdkandersson/flake8-docstrings-complete/releases/v1.0.0
[v1.0.1]: https://github.com/jdkandersson/flake8-docstrings-complete/releases/v1.0.1
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1495,3 +1495,4 @@ Section information is extracted using the following algorithm:
- Check that argument, exceptions and attributes have non-empty description.
- Check that arguments, exceptions and attributes are only documented once.
- Check that arguments, exceptions and attributes have meaningful descriptions.
- Check other other PEP257 conventions
8 changes: 4 additions & 4 deletions flake8_docstrings_complete/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def _check_returns(
return_nodes_with_value = list(node for node in return_nodes if node.value is not None)

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

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

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

# Check for yields section in docstring in function that does not yield a value
if not yield_nodes_with_value and docstr_info.yields:
if not yield_nodes_with_value and docstr_info.yields_sections:
yield types_.Problem(
docstr_node.lineno, docstr_node.col_offset, YIELDS_SECTION_IN_DOCSTR_MSG
)
Expand Down
29 changes: 16 additions & 13 deletions flake8_docstrings_complete/docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class _Section(NamedTuple):
sub-sections.
"""

name: str
name: str | None
subs: tuple[str, ...]


Expand All @@ -31,9 +31,7 @@ class Docstring(NamedTuple):
attrs: The attributes described in the docstring. None if the docstring doesn't have the
attrs section.
attrs_sections: All the attributes sections.
returns: Whether the docstring has the returns section.
returns_sections: All the returns sections.
yields: Whether the docstring has the yields section.
yields_sections: All the yields sections.
raises: The exceptions described in the docstring. None if the docstring doesn't have the
raises section.
Expand All @@ -44,9 +42,7 @@ class Docstring(NamedTuple):
args_sections: tuple[str, ...] = ()
attrs: tuple[str, ...] | None = None
attrs_sections: tuple[str, ...] = ()
returns: bool = False
returns_sections: tuple[str, ...] = ()
yields: bool = False
yields_sections: tuple[str, ...] = ()
raises: tuple[str, ...] | None = None
raises_sections: tuple[str, ...] = ()
Expand All @@ -60,7 +56,7 @@ class Docstring(NamedTuple):
"raises": {"raises", "raise"},
}
_WHITESPACE_REGEX = r"\s*"
_SECTION_START_PATTERN = re.compile(rf"{_WHITESPACE_REGEX}(\w+):")
_SECTION_NAME_PATTERN = re.compile(rf"{_WHITESPACE_REGEX}(\w+):")
_SUB_SECTION_PATTERN = re.compile(rf"{_WHITESPACE_REGEX}(\w+)( \(.*\))?:")
_SECTION_END_PATTERN = re.compile(rf"{_WHITESPACE_REGEX}$")

Expand All @@ -86,16 +82,19 @@ def _get_sections(lines: Iterable[str]) -> Iterator[_Section]:
with contextlib.suppress(StopIteration):
while True:
# Find the start of the next section
section_name = next(
filter(None, (_SECTION_START_PATTERN.match(line) for line in lines))
).group(1)
section_start = next(line for line in lines if line.strip())
section_name_match = _SECTION_NAME_PATTERN.match(section_start)
section_name = section_name_match.group(1) if section_name_match else None

# Get all the lines of the section
section_lines = itertools.takewhile(
lambda line: _SECTION_END_PATTERN.match(line) is None, lines
)

# Retrieve sub section from section lines
sub_section_matches = (_SUB_SECTION_PATTERN.match(line) for line in section_lines)
sub_sections = (match.group(1) for match in sub_section_matches if match is not None)

yield _Section(name=section_name, subs=tuple(sub_sections))


Expand All @@ -111,7 +110,11 @@ def _get_section_by_name(name: str, sections: Iterable[_Section]) -> _Section |
"""
sections = iter(sections)
return next(
(section for section in sections if section.name.lower() in _SECTION_NAMES[name]),
(
section
for section in sections
if section.name is not None and section.name.lower() in _SECTION_NAMES[name]
),
None,
)

Expand All @@ -128,7 +131,9 @@ def _get_all_section_names_by_name(name: str, sections: Iterable[_Section]) -> I
"""
sections = iter(sections)
yield from (
section.name for section in sections if section.name.lower() in _SECTION_NAMES[name]
section.name
for section in sections
if section.name is not None and section.name.lower() in _SECTION_NAMES[name]
)


Expand All @@ -152,9 +157,7 @@ def parse(value: str) -> Docstring:
args_sections=tuple(_get_all_section_names_by_name(name="args", sections=sections)),
attrs=attrs_section.subs if attrs_section is not None else None,
attrs_sections=tuple(_get_all_section_names_by_name(name="attrs", sections=sections)),
returns=_get_section_by_name("returns", sections) is not None,
returns_sections=tuple(_get_all_section_names_by_name(name="returns", sections=sections)),
yields=_get_section_by_name("yields", sections) is not None,
yields_sections=tuple(_get_all_section_names_by_name(name="yields", sections=sections)),
raises=raises_section.subs if raises_section is not None else None,
raises_sections=tuple(_get_all_section_names_by_name(name="raises", sections=sections)),
Expand Down
10 changes: 5 additions & 5 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "flake8-docstrings-complete"
version = "1.0.0"
version = "1.0.1"
description = "A linter that checks docstrings are complete"
authors = ["David Andersson <[email protected]>"]
license = "Apache 2.0"
Expand Down
42 changes: 28 additions & 14 deletions tests/test_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,24 @@
[
pytest.param((), (), id="empty"),
pytest.param(("",), (), id="single not a section"),
pytest.param(("not a section",), (), id="single not a section no colon"),
pytest.param(("not a section:",), (), id="single not a section not after first word"),
pytest.param((" ",), (), id="single not a section whitespace"),
pytest.param(("\t",), (), id="single not a section alternate whitespace"),
pytest.param(
("line 1",), (docstring._Section(None, ()),), id="single line section no name"
),
pytest.param(
("line 1", "line 2"), (docstring._Section(None, ()),), id="multi line section no name"
),
pytest.param(
("line 1", "name_1:"),
(docstring._Section(None, ("name_1",)),),
id="multi line section no name second like name",
),
pytest.param(
("line 1:",),
(docstring._Section(None, ()),),
id="single section no name colon after first word",
),
pytest.param(("name_1:",), (docstring._Section("name_1", ()),), id="single section"),
pytest.param(
(" name_1:",),
Expand Down Expand Up @@ -307,7 +323,7 @@ def test__get_sections(
Returns:
""",
docstring.Docstring(returns=True, returns_sections=("Returns",)),
docstring.Docstring(returns_sections=("Returns",)),
id="returns empty",
),
pytest.param(
Expand All @@ -316,15 +332,15 @@ def test__get_sections(
Returns:
The return value.
""",
docstring.Docstring(returns=True, returns_sections=("Returns",)),
docstring.Docstring(returns_sections=("Returns",)),
id="returns single line",
),
pytest.param(
"""short description
Return:
""",
docstring.Docstring(returns=True, returns_sections=("Return",)),
docstring.Docstring(returns_sections=("Return",)),
id="returns alternate",
),
pytest.param(
Expand All @@ -334,7 +350,7 @@ def test__get_sections(
Returns:
""",
docstring.Docstring(returns=True, returns_sections=("Returns", "Returns")),
docstring.Docstring(returns_sections=("Returns", "Returns")),
id="multiple returns",
),
pytest.param(
Expand All @@ -344,15 +360,15 @@ def test__get_sections(
Return:
""",
docstring.Docstring(returns=True, returns_sections=("Returns", "Return")),
docstring.Docstring(returns_sections=("Returns", "Return")),
id="multiple returns alternate",
),
pytest.param(
"""short description
Yields:
""",
docstring.Docstring(yields=True, yields_sections=("Yields",)),
docstring.Docstring(yields_sections=("Yields",)),
id="yields empty",
),
pytest.param(
Expand All @@ -361,15 +377,15 @@ def test__get_sections(
Yields:
The return value.
""",
docstring.Docstring(yields=True, yields_sections=("Yields",)),
docstring.Docstring(yields_sections=("Yields",)),
id="yields single line",
),
pytest.param(
"""short description
Yield:
""",
docstring.Docstring(yields=True, yields_sections=("Yield",)),
docstring.Docstring(yields_sections=("Yield",)),
id="yields alternate",
),
pytest.param(
Expand All @@ -379,7 +395,7 @@ def test__get_sections(
Yields:
""",
docstring.Docstring(yields=True, yields_sections=("Yields", "Yields")),
docstring.Docstring(yields_sections=("Yields", "Yields")),
id="multiple yields",
),
pytest.param(
Expand All @@ -389,7 +405,7 @@ def test__get_sections(
Yield:
""",
docstring.Docstring(yields=True, yields_sections=("Yields", "Yield")),
docstring.Docstring(yields_sections=("Yields", "Yield")),
id="multiple yields alternate",
),
pytest.param(
Expand Down Expand Up @@ -479,9 +495,7 @@ def test__get_sections(
args_sections=("Args",),
attrs=("attr_1",),
attrs_sections=("Attrs",),
returns=True,
returns_sections=("Returns",),
yields=True,
yields_sections=("Yields",),
raises=("exc_1",),
raises_sections=("Raises",),
Expand Down

0 comments on commit 96b1835

Please sign in to comment.