Batu Lab NotesPractical developer guides

Extract numbered issue references without matching prefixes

By Batu · English technical notes

Also published in our Blogger archive.

Issue text can contain a useful reference such as #12 alongside strings where a number sign is part of a larger token. The pattern (?<![A-Za-z0-9_])#(\d+) uses a one-character negative lookbehind before #. It therefore extracts 12 and 7, but rejects pre#34 and ABC_#56: in each rejected case, the number sign has an ASCII letter or underscore immediately before it. The capturing group contains only the digits, which is why findall() returns digit strings rather than complete #12 text.

The assertion fixes the intended behavior for this input, and the list printout has stable ordering because findall() reports non-overlapping matches from left to right. Python's standard re module supports fixed-width negative lookbehinds; this one is exactly one character wide. Read the official re.Pattern.findall() documentation, the description of negative lookbehind, and the special sequence reference. These APIs are available in current supported Python 3 versions.

“Prefix” is a policy decision. This version defines an unwanted prefix as an ASCII letter, digit, or underscore. It still permits a reference after punctuation, whitespace, or a non-ASCII character, and it does not check whether an extracted number names an existing issue. If issue notation has repository prefixes, ranges, or Markdown links, specify and test those cases separately.

AI-assistance disclosure: this article was drafted with AI assistance and checked using a synthetic example.

import re

references = re.compile(r"(?<![A-Za-z0-9_])#(\d+)")
text = "Fix #12; keep pre#34, ABC_#56, and collect #7."

issues = references.findall(text)
assert issues == ["12", "7"]

print(issues)
['12', '7']