Skip to content

Commit

Permalink
add: Allows key(word) for **kwargs
Browse files Browse the repository at this point in the history
  • Loading branch information
jshwi committed Jun 21, 2022
1 parent 53538a2 commit a1fbae2
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 22 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

[Unreleased](https://github.com/jshwi/docsig/compare/v0.5.0...HEAD)
------------------------------------------------------------------------
### Added
- Allows `key(word)` for `**kwargs`

### Changed
- Updates syntax highlighting

Expand Down
19 changes: 14 additions & 5 deletions docsig/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ def get_files(root: _Path, paths: _t.List[_Path]) -> None:
get_files(path, paths)


def _compare_args(arg: _t.Optional[str], doc: _t.Optional[str]) -> bool:
def _compare_args(
arg: _t.Optional[str], doc: _t.Optional[str], kind: str
) -> bool:
if kind in ("key", "keyword") and arg is not None:
return arg[:2] == "**"

if isinstance(arg, str):
arg = arg.replace("*", "")

Expand All @@ -62,11 +67,15 @@ def construct_func(func: _Function, report: _Report) -> _FuncStr:
):
longest = max([len(func.signature.args), len(func.docstring.args)])
arg = _get_index(count, func.signature.args)
doc = _get_index(count, func.docstring.args)
if _compare_args(arg, doc):
func_str.add_param(arg, doc)
doc_info = _get_index(count, func.docstring.args)
if doc_info is not None:
kind, doc = doc_info
else:
kind, doc = "param", None
if _compare_args(arg, doc, kind):
func_str.add_param(arg, doc, kind)
else:
func_str.add_param(arg, doc, failed=True)
func_str.add_param(arg, doc, kind, failed=True)
report.order(arg, doc)
report.incorrect(arg, doc)

Expand Down
38 changes: 26 additions & 12 deletions docsig/_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
================
"""
import ast as _ast
import re
import typing as _t

from ._utils import get_index as _get_index


class Docstring:
"""Represents docstring parameters.
Expand All @@ -18,28 +17,43 @@ class Docstring:
def __init__(self, func: _ast.FunctionDef) -> None:
self._docstring: _t.Optional[str] = _ast.get_docstring(func)
self._is_doc = bool(self._docstring is not None)
self._args: _t.Tuple[_t.Optional[str], ...] = tuple()
self._args: _t.List[_t.Tuple[str, _t.Optional[str]]] = []
self._returns = False
if self._is_doc:
if self._docstring is not None:
self._parse_docstring()

def _parse_docstring(self):
self._returns = bool(":return:" in self._docstring)
self._args = tuple(
_get_index(1, s.split())
for s in self._docstring.split(":")
if s.startswith("param")
)
keys = 0
for i in re.findall(":(.*?): ", self._docstring):
string = i.split()
if 0 < len(string) <= 2:
if len(string) == 1:
key, value = string[0], None
else:
key, value = string[0], string[1]

if key == "return":
self._returns = True

elif key not in ("raise", "raises"):
if key in ("key", "keyword"):
if keys == 1:
continue

keys = 1
value = "(**)"

self._args.append((key, value))

@property
def is_doc(self) -> bool:
"""Check that docstring exists."""
return self._is_doc

@property
def args(self) -> _t.Tuple[_t.Optional[str], ...]:
def args(self) -> _t.Tuple[_t.Tuple[str, _t.Optional[str]], ...]:
"""Docstring args."""
return self._args
return tuple(self._args)

@property
def returns(self) -> bool:
Expand Down
3 changes: 2 additions & 1 deletion docsig/_repr.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def add_param(
self,
arg: _t.Optional[str],
doc: _t.Optional[str],
kind: str,
failed: bool = False,
) -> None:
"""Add parameters to docstring.
Expand All @@ -61,7 +62,7 @@ def add_param(
"""
self.set_mark(failed)
self.data += f"{self._mark}{arg}"
self._docstring += f"\n{self.TAB}:param {doc}: {self._mark}"
self._docstring += f"\n{self.TAB}:{kind} {doc}: {self._mark}"

def add_return(self, failed: bool = False) -> None:
"""Add return statement to docstring.
Expand Down
84 changes: 84 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,3 +728,87 @@ def method(self) -> int:
@property
def expected(self) -> str:
return ""


@_templates.register
class _PassWithKwargsKey(_BaseTemplate):
@property
def template(self) -> str:
return """
def function(param1, **kwargs) -> None:
\"\"\"Proper docstring.
:param param1: Passes
:key kwarg1: Pass
:keyword kwarg2: Pass
\"\"\"
"""

@property
def expected(self) -> str:
return ""


@_templates.register
class _FailWithKwargsOutOfOrder(_BaseTemplate):
@property
def template(self) -> str:
return """
def function(param1, **kwargs) -> None:
\"\"\"Proper docstring.
:keyword kwarg1: Fail
:keyword kwarg3: Fail
:param param1: Fail
\"\"\"
"""

@property
def expected(self) -> str:
return ""


@_templates.register
class _PassDualColon(_BaseTemplate):
@property
def template(self) -> str:
return """
def function(attachments, sync, **kwargs) -> None:
\"\"\"Proper docstring.
Note: Keyword args (dict) to pass to ``attachments``:
See ``flask_mail.Message.attach``.
* filename: filename of attachment
* content_type: file mimetype
* data: the raw file data
:param attachments: Iterable of kwargs to construct attachment.
:param sync: Don't thread if True: Defaults to False.
:param kwargs: Keyword args to pass to ``Message``:
See ``flask_mail.Message``.
\"\"\"
"""

@property
def expected(self) -> str:
return ""


@_templates.register
class _PassOnlyParams(_BaseTemplate):
@property
def template(self) -> str:
return """
def function(reduce: bool = False) -> _t.Tuple[str, ...]:
\"\"\"Proper docstring.
:param reduce: :func:`~lsfiles.utils._Tree.reduce`
:return: Tuple of `Path` objects or str repr.
\"\"\"
"""

@property
def expected(self) -> str:
return ""
12 changes: 8 additions & 4 deletions whitelist.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
_.data # unused attribute (docsig/_repr.py:32)
_.data # unused attribute (docsig/_repr.py:63)
_.data # unused attribute (docsig/_repr.py:79)
_.data # unused attribute (docsig/_repr.py:85)
_.data # unused attribute (docsig/_repr.py:93)
_.data # unused attribute (docsig/_repr.py:64)
_.data # unused attribute (docsig/_repr.py:80)
_.data # unused attribute (docsig/_repr.py:86)
_.data # unused attribute (docsig/_repr.py:94)
_FailClass # unused class (tests/__init__.py:668)
_FailDupesSum # unused class (tests/__init__.py:471)
_FailIncorrectDoc # unused class (tests/__init__.py:369)
Expand All @@ -22,17 +22,21 @@
_FailRetTypeSig1Sum # unused class (tests/__init__.py:451)
_FailWithArgs # unused class (tests/__init__.py:530)
_FailWithKwargs # unused class (tests/__init__.py:575)
_FailWithKwargsOutOfOrder # unused class (tests/__init__.py:752)
_MultiFail # unused class (tests/__init__.py:601)
_PassClassProperty # unused class (tests/__init__.py:715)
_PassClassSelf # unused class (tests/__init__.py:696)
_PassDualColon # unused class (tests/__init__.py:771)
_PassNoDocstring # unused class (tests/__init__.py:96)
_PassNoParams # unused class (tests/__init__.py:110)
_PassNoRetNoType # unused class (tests/__init__.py:249)
_PassOnlyParams # unused class (tests/__init__.py:799)
_PassParam # unused class (tests/__init__.py:24)
_PassRetType # unused class (tests/__init__.py:170)
_PassUnderscoreParam # unused class (tests/__init__.py:124)
_PassWithArgs # unused class (tests/__init__.py:511)
_PassWithKwargs # unused class (tests/__init__.py:556)
_PassWithKwargsKey # unused class (tests/__init__.py:733)
fixture_init_file # unused function (tests/conftest.py:56)
fixture_main # unused function (tests/conftest.py:18)
fixture_nocolorcapsys # unused function (tests/conftest.py:44)

0 comments on commit a1fbae2

Please sign in to comment.