Skip to content

literal() still misses tuple-unpack rebinding and del of the prefix lists #101

Description

@realmarcin

literal() in tests/test_fleet_page.py walks the tree for Assign, AugAssign and AnnAssign, and separately flags subscript assignment and mutating method calls. That kills the five shapes in #99. These still survive, each leaving the test reading the original literal while the module uses something else:

mutant why it survives worth closing
VOC, _X = [...], 1 tuple-unpack target is a Tuple, not a Name, so getattr(target, "id") is None yesVOC, PREF = load() is a natural refactor
del VOC[0] / del VOC[:] ast.Delete is not examined at all yes — cheap to add
VOC[0] += "X" the AugAssign branch tests node.target.id, so a Subscript target is skipped. Asymmetric: plain VOC[0] = is caught moderate
VOC[0], _x = "BOGUS", 1 subscript store nested inside a Tuple target low
print((VOC := [...])) ast.NamedExpr not handled low
for VOC in [...], with ... as VOC at module level neither is Assign low
globals()["VOC"] = ..., exec(...), list.append(VOC, x) dynamic no — accept

Suggested fix

Collect stores rather than top-level target ids, which covers the first five in one pass:

for node in ast.walk(tree):
    targets = (node.targets if isinstance(node, (ast.Assign, ast.Delete))
               else [node.target] if isinstance(node, (ast.AugAssign, ast.AnnAssign, ast.NamedExpr))
               else [])
    for target in targets:
        for sub in ast.walk(target):
            if isinstance(sub, ast.Name) and sub.id == name and isinstance(sub.ctx, (ast.Store, ast.Del)):
                ...
            elif isinstance(sub, ast.Subscript) and getattr(sub.value, "id", None) == name:
                mutations.append("subscript assignment")

The ctx guard matters: in VOC[0] = x the Name node is a Load, so without it the subscript store would be miscounted as a rebinding.

Also worth a line each

  • assertGreater(len(value), 10) does not check the value is a list. VOC = "abcdefghijklmnop" — a 16-character string — survives, as does a duplicated entry inside the literal. assertIsInstance(value, list) plus a duplicate check is cheap.
  • A bare VOC: list with no value makes ast.literal_eval(None) raise ValueError: malformed node or string rather than a clean assertion. It still fails, just untidily.

None of these shapes exists in either script today. The real fix remains #97 — make the modules importable so the tests read the actual objects instead of parsing text for them; every entry above then becomes moot.

Found by an adversarial review of #100.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions