Skip to content

vendor: thirteen re.sub sites pass a regex flag as the positional count, capping substitutions at 8 #796

Description

@JarryShaw

Describe the bug

Thirteen re.sub call sites in pcapkit/vendor/ pass a regex flag where the fourth positional parameter is count. Enumerated by AST rather than grep, on 2d1c8675d:

pcapkit/vendor/default.py:343              pcapkit/vendor/ipv6/router_alert.py:55
pcapkit/vendor/hip/eddsa_curve.py:59       pcapkit/vendor/ipv6/tagger_id.py:55
pcapkit/vendor/http/frame.py:64            pcapkit/vendor/reg/ethertype.py:95
pcapkit/vendor/http/method.py:144          pcapkit/vendor/tcp/flags.py:139
pcapkit/vendor/http/status_code.py:148     pcapkit/vendor/tcp/mp_tcp_option.py:65
pcapkit/vendor/ipv4/router_alert.py:58     pcapkit/vendor/tcp/option.py:78
pcapkit/vendor/ipv6/option.py:89

re.sub(pattern, repl, string, count=0, flags=0) — so re.MULTILINE, which is 8, is read as "replace at most 8 occurrences". Measured:

>>> re.MULTILINE
8
>>> re.sub(r'\r*\n', ' ', 'a\n' * 12, re.MULTILINE)      # flag as positional
'a a a a a a a a a\na\na\na\n'                            # <- stops after 8
>>> re.sub(r'\r*\n', ' ', 'a\n' * 12, flags=re.MULTILINE)
'a a a a a a a a a a a a '

Two defects in one line:

  1. The count is a real limit. Any IANA description with more than eight newlines keeps its ninth onward, so the generated const module gets embedded newlines where it should have spaces. That output is committed to the tree, so the damage is visible rather than theoretical.
  2. The flag never applies. For r'\r*\n' that happens not to matter — no ^/$ in the pattern — so the intent was already a no-op. Check each site; if any pattern uses an anchor, its behaviour is wrong today too.

Also a deprecation: DeprecationWarning: 'count' is passed as positional argument fires on 3.13+, and it becomes a TypeError in a later release, so these break on a future interpreter regardless of semantics.

Expected behavior

flags=re.MULTILINE, or drop the flag where the pattern has no anchor — cleaner than passing one that does nothing.

Additional context

Correcting this issue's own first version, which said "six". A single-line grep found five; the other eight put the flag on a continuation line (), re.MULTILINE))) and were invisible to it. The AST sweep above is the reliable count.

Found by the cross-review on #789, which had just fixed the same shape in re.split at httpv1.py:310/:346. A re-grep found zero remaining re.split sites, so these are the rest of the family.

Two notes for whoever takes it: eight of the thirteen sit on lines also carrying '%s %s' % and a # pylint: disable=consider-using-f-string, so it overlaps #792's convention work — coordinate or sequence them. And regenerating to show the consequence will fetch live IANA registries and move unrelated rows; #792 records the hand-edit-plus-prove-equivalence route around that.

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

    bugfixPull requests that fix a defect (fix: subject prefix)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions