Skip to content

Commit 46c5cb7

Browse files
authored
Fix a bug with ast.assign (#195)
1 parent f141b71 commit 46c5cb7

File tree

4 files changed

+68
-26
lines changed

4 files changed

+68
-26
lines changed

CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Change Log
22

3-
## [unpublished]
3+
## [0.5.14] - 2024-12-26
44

55
- Changed
66

@@ -10,6 +10,10 @@
1010
Python's AST can correctly parse the files
1111
- Added end-to-end test (essentially an integration test)
1212

13+
- Fixed
14+
15+
- A bug in ast.assign
16+
1317
- Full diff
1418
- https://github.com/jsh9/pydoclint/compare/0.5.13...0.5.14
1519

pydoclint/utils/arg.py

+47-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import ast
4+
from typing import Any
45

56
from docstring_parser.common import DocstringAttr, DocstringParam
67

@@ -217,34 +218,56 @@ def fromAstAssign(cls, astAssign: ast.Assign) -> 'ArgList':
217218
for i, target in enumerate(astAssign.targets):
218219
if isinstance(target, ast.Tuple): # such as `a, b = c, d = 1, 2`
219220
for j, item in enumerate(target.elts):
220-
if not isinstance(item, ast.Name):
221-
raise EdgeCaseError(
222-
f'astAssign.targets[{i}].elts[{j}] is of'
223-
f' type {type(item)} instead of ast.Name'
224-
)
225-
226-
infoList.append(Arg(name=item.id, typeHint=''))
227-
elif isinstance(target, ast.Name): # such as `a = 1` or `a = b = 2`
228-
infoList.append(Arg(name=target.id, typeHint=''))
229-
else:
230-
try: # we may not know all potential cases, so we use try/catch
231-
unparsedTarget: str | None = unparseName(target)
232-
assert unparsedTarget is not None # to help mypy understand type
233-
infoList.append(Arg(name=unparsedTarget, typeHint=''))
234-
except Exception as ex:
235-
lineRange: str = (
236-
f'in Line {astAssign.lineno}'
237-
if astAssign.lineno == astAssign.end_lineno
238-
else f'in Lines {astAssign.lineno}-{astAssign.end_lineno}'
239-
)
240-
msg: str = (
241-
f'Edge case encountered {lineRange}.'
242-
f' astAssign.targets[{i}] is of type {type(target)}.'
221+
cls._unparseTargetAndAppendToInfoList(
222+
target=item,
223+
infoList=infoList,
224+
lineNum=astAssign.lineno,
225+
endLineNum=astAssign.end_lineno,
226+
i=i,
227+
j=j,
243228
)
244-
raise EdgeCaseError(msg) from ex
229+
else: # a single element
230+
cls._unparseTargetAndAppendToInfoList(
231+
target=target,
232+
infoList=infoList,
233+
lineNum=astAssign.lineno,
234+
endLineNum=astAssign.end_lineno,
235+
i=i,
236+
j=None,
237+
)
245238

246239
return ArgList(infoList=infoList)
247240

241+
@classmethod
242+
def _unparseTargetAndAppendToInfoList(
243+
cls,
244+
*,
245+
target: Any,
246+
infoList: list[Arg],
247+
lineNum: int | None,
248+
endLineNum: int | None,
249+
i: int,
250+
j: int | None = None,
251+
) -> None:
252+
try: # we may not know all potential cases, so we use try/catch
253+
unparsedTarget: str | None = unparseName(target)
254+
assert unparsedTarget is not None # to help mypy understand type
255+
infoList.append(Arg(name=unparsedTarget, typeHint=''))
256+
except Exception as ex:
257+
lineRange: str = (
258+
f'in Line {lineNum}'
259+
if lineNum == endLineNum
260+
else f'in Lines {lineNum}-{endLineNum}'
261+
)
262+
msg1: str = f'Edge case encountered {lineRange}.'
263+
msg2: str = (
264+
f' astAssign.targets[{i}] is of type {type(target)}.'
265+
if j is None
266+
else f' astAssign.targets[{i}].elts[{j}] is of type {type(target)}.'
267+
)
268+
msg: str = msg1 + msg2
269+
raise EdgeCaseError(msg) from ex
270+
248271
def contains(self, arg: Arg) -> bool:
249272
"""Whether a given `Arg` object exists in the list"""
250273
return arg.name in self.lookup

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = pydoclint
3-
version = 0.5.13
3+
version = 0.5.14
44
description = A Python docstring linter that checks arguments, returns, yields, and raises sections
55
long_description = file: README.md
66
long_description_content_type = text/markdown

tests/data/edge_cases/16_assign_to_attr/cases.py

+15
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,18 @@ def large_drawing(self, obj):
1010
# Non-self attribute should not be type hinted, because this could lead to
1111
# potential ambiguities. See more: https://stackoverflow.com/a/77831273
1212
large_drawing.descr_2: str = 'Drawing'
13+
14+
# The following is from:
15+
# https://github.com/matplotlib/matplotlib/blob/c2d502d219c8c0abe8722279b21f817aeae2058a/lib/matplotlib/backends/backend_agg.py#L510-L521
16+
print_jpg.__doc__, print_tif.__doc__, print_webp.__doc__ = map(
17+
"""
18+
Write the figure to a {} file.
19+
20+
Parameters
21+
----------
22+
filename_or_obj : str or path-like or file-like
23+
The file to write to.
24+
pil_kwargs : dict, optional
25+
Additional keyword arguments that are passed to
26+
`PIL.Image.Image.save` when saving the figure.
27+
""".format, ["JPEG", "TIFF", "WebP"])

0 commit comments

Comments
 (0)