diff --git a/CHANGELOG.md b/CHANGELOG.md index b6e7ecc..199411d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## [unpublished] + +- Fixed + - Fixed a bug where assigning a dict value (such as `abc['something'] = 123`) + would result in EdgeCaseError + ## [0.5.12] - 2024-12-15 - Changed diff --git a/pydoclint/utils/arg.py b/pydoclint/utils/arg.py index 165eb44..13578bf 100644 --- a/pydoclint/utils/arg.py +++ b/pydoclint/utils/arg.py @@ -226,14 +226,22 @@ def fromAstAssign(cls, astAssign: ast.Assign) -> 'ArgList': infoList.append(Arg(name=item.id, typeHint='')) elif isinstance(target, ast.Name): # such as `a = 1` or `a = b = 2` infoList.append(Arg(name=target.id, typeHint='')) - elif isinstance(target, ast.Attribute): # e.g., uvw.xyz = 1 - unparsedTarget: str | None = unparseName(target) - assert unparsedTarget is not None # to help mypy understand type - infoList.append(Arg(name=unparsedTarget, typeHint='')) else: - raise EdgeCaseError( - f'astAssign.targets[{i}] is of type {type(target)}' - ) + try: # we may not know all potential cases, so we use try/catch + unparsedTarget: str | None = unparseName(target) + assert unparsedTarget is not None # to help mypy understand type + infoList.append(Arg(name=unparsedTarget, typeHint='')) + except Exception as ex: + lineRange: str = ( + f'in Line {astAssign.lineno}' + if astAssign.lineno == astAssign.end_lineno + else f'in Lines {astAssign.lineno}-{astAssign.end_lineno}' + ) + msg: str = ( + f'Edge case encountered {lineRange}.' + f' astAssign.targets[{i}] is of type {type(target)}.' + ) + raise EdgeCaseError(msg) from ex return ArgList(infoList=infoList) diff --git a/tests/data/edge_cases/18_assign_to_subscript/case.py b/tests/data/edge_cases/18_assign_to_subscript/case.py new file mode 100644 index 0000000..b4bead6 --- /dev/null +++ b/tests/data/edge_cases/18_assign_to_subscript/case.py @@ -0,0 +1,14 @@ +# This comes from the pip repository: +# https://github.com/pypa/pip/blob/3b91f42e461de3f23e9bed46a8c5695435f930fb/src/pip/_internal/cli/cmdoptions.py#L110-L114 +# and it was encountered by a user in https://github.com/jsh9/pydoclint/issues/190 +# +# It's just supposed to pass without bugs and violations. +# (The previous buggy implementation would raise EdgeCaseError when parsing +# this code.) + + +class PipOption(Option): + TYPES = Option.TYPES + ('path', 'package_name') + TYPE_CHECKER = Option.TYPE_CHECKER.copy() + TYPE_CHECKER['package_name'] = _package_name_option_check + TYPE_CHECKER['path'] = _path_option_check diff --git a/tests/test_main.py b/tests/test_main.py index ed31ac0..510aef8 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1530,6 +1530,7 @@ def testNonAscii() -> None: 'correctly document class attributes.)', ], ), + ('18_assign_to_subscript/case.py', {}, []), ], ) def testEdgeCases(