Skip to content

fix: convert positional-only parameters on methods, not just functions - #21

Open
JarryShaw wants to merge 1 commit into
masterfrom
fix/classdef-cls-ctx-name-value
Open

JarryShaw wants to merge 1 commit into
masterfrom
fix/classdef-cls-ctx-name-value

Conversation

@JarryShaw

Copy link
Copy Markdown
Member

Fixes #20.

The bug

_process_classdef passes cls_ctx=name.name:

# <Name: ...>
name = node.name
...
self._process_suite_node(suite, cls_ctx=name.name)

parso.python.tree.Class.name already returns the Name leaf — as the comment on the line above says — and a parso Name keeps its string on .value. So every class body raised AttributeError: 'Name' object has no attribute 'name'.

Because bpc_utils' walker catches exceptions per node, the process still exited 0. Positional-only markers therefore survived into the output while the caller was told the conversion had succeeded — which matters for the subprocess.check_call integration the README recommends.

Plain module-level functions were unaffected, which is why this stayed hidden.

The fix

cls_ctx=name.value. That this is the intended value is clear from the surrounding contract:

  • _process_suite_node(self, node, *, cls_ctx: Optional[str] = None) (poseur.py:442)
  • stored as self._cls_ctx # type: Optional[str] (poseur.py:438)
  • consumed by return self.mangle(self._cls_ctx, name) (poseur.py:1076)

Test

test_classdef covers the no-op case, the crash case, and the converted output. It fails on master:

$ python -m unittest test.TestPoseur.test_classdef
  File ".../poseur.py", line 744, in _process_classdef
    self._process_suite_node(suite, cls_ctx=name.name)
AttributeError: 'Name' object has no attribute 'name'
FAILED (errors=1)

and passes with the fix, along with the rest of the suite:

$ python -m unittest test
Ran 9 tests in 0.240s
OK

tests/sample.py contains no class statement at all, so the golden-file suite could never have caught this. The new test is written inline rather than by extending sample.py/sample.txt, to keep the fixtures untouched.

Real-world check

pcapkit/corekit/io.py from PyPCAPKit has 12 positional-only parameters spread across class bodies. With this change, walrus then poseur convert it into something CPython 3.7 parses cleanly:

$ python -m walrus --no-archive io.py && python -m poseur --no-archive io.py
$ grep -c ', /)' io.py ; grep -c ':=' io.py
0
0
$ python3.7 -c "import ast; ast.parse(open('io.py').read()); print('parses under 3.7')"
parses under 3.7

Verified on CPython 3.7.16 with parso 0.6.2 and bpc-utils 0.10.1.

`_process_classdef` passed `cls_ctx=name.name`, but `Class.name` already
returns the parso `Name` leaf, whose string lives on `.value`. Every class
body therefore raised `AttributeError: 'Name' object has no attribute
'name'`, and since `bpc_utils` catches per node the process still exited 0 --
so the markers survived into the output and callers wrapping poseur in
`subprocess.check_call` saw success.

`_process_suite_node` declares `cls_ctx: Optional[str]`, stores it as
`Optional[str]`, and hands it to `mangle()`, so `.value` is what was meant.

Added `test_classdef`, which reproduces the crash without this change.
`tests/sample.py` has no `class` at all, which is why the golden-file suite
never caught it.

Fixes #20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Positional-only parameters on methods are silently not converted: AttributeError in _process_classdef, exit code 0

1 participant