Skip to content

gh-66889: Report which character made an address invalid - #158154

Open
v0ropaev wants to merge 2 commits into
python:mainfrom
v0ropaev:gh-66889-header-parser-defect
Open

v0ropaev wants to merge 2 commits into
python:mainfrom
v0ropaev:gh-66889-header-parser-defect

Conversation

@v0ropaev

@v0ropaev v0ropaev commented Sep 25, 2026 •

Copy link
Copy Markdown

Closes #66889.

Being upfront about one thing first: the original message on this issue did not survive the move from bugs.python.org — what is on GitHub is only the metadata block, so the title is all we have of what @bitdancer meant. I went by the title plus what the parser actually does today.

For abc@xyz.c:om the parser does report a defect, but only a generic one on the enclosing list:

>>> a, _ = _header_value_parser.get_address_list('abc@xyz.c:om')
>>> [str(d) for d in a.all_defects]
['invalid address in address-list']

Nothing says what was wrong or where. The parser already knows: it builds a misplaced-special terminal for the stray : and then throws that knowledge away. This attaches an InvalidHeaderDefect to that terminal, so the character is named:

['invalid address in address-list', "misplaced special character ':'"]

One defect per misplaced character, which for a header with several bad addresses is the difference between "something is wrong somewhere" and a list you can point at.

There is a second, separate change in here, and it has its own news entry because it stands alone. TokenList.all_defects was sum((x.all_defects for x in self), self.defects), which builds a new list at every node, so collecting defects is quadratic in their number. It now appends to one list. Measured on headers of 200, 800 and 3200 malformed addresses:

                    main        this branch
   200 addresses    0.0008 s    0.0005 s
   800 addresses    0.0058 s    0.0017 s
  3200 addresses    0.0620 s    0.0068 s

Four times the input costs roughly ten times the collection on main and roughly four times here. The branch is also collecting twice as many defects, since it now reports the specific one too.

./python.exe -m test test_email is 1,822 passing. Reverting Lib/email/_header_value_parser.py while keeping the tests fails five of them.

If the missing report @bitdancer had in mind was something other than this, say so and I will rework it — the title is genuinely all I had to go on.

…invalid mailbox

get_invalid_mailbox() builds a 'misplaced-special' ValueTerminal for every
stray special character but leaves its .defects empty, so the only record of
the problem is the generic 'invalid address in address-list' (or 'invalid
mailbox in mailbox-list') defect on the enclosing token list, which does not
say which character was misplaced.

Attach an InvalidHeaderDefect naming the character to the terminal itself.
It is picked up by all_defects of every enclosing token, so defect lists for
malformed address headers gain one entry per misplaced character; the three
tests that spell those lists out are updated accordingly.
TokenList.all_defects built its result with sum(), which rebuilds the whole
accumulated list once per child token, so gathering the defects of a parse
tree costs time quadratic in the number of defects it holds.  Until now that
was mostly hidden, because the containers that collect many defects are rare;
attaching a defect to every misplaced special character makes an ordinary
malformed address header hit it.  Parsing 'To: ' + '@'*32000 goes from 0.0154
to 1.08 seconds, and the pre-existing worst case, a long run of backslashes in
an obs-local-part, already takes 4.45 seconds at the same size on main.

Extend one list instead.  Terminal.all_defects already returns a fresh copy,
so returning a copy from an empty TokenList rather than the .defects list
itself also makes the two agree.
@v0ropaev

Copy link
Copy Markdown
Author

Closing this myself. @picnixz pointed out on #158152 that I have been opening too many at once and that old issues nobody has revived are the wrong ones to pick up unasked — this is one of those, so it should not be sitting in the queue. The branch stays on my fork if the issue ever comes back to life.

@v0ropaev v0ropaev closed this Sep 25, 2026
@v0ropaev v0ropaev reopened this Sep 25, 2026
@v0ropaev

v0ropaev commented Sep 25, 2026 •

Copy link
Copy Markdown
Author

Reopened. Closing this was my own overreaction to being told I had too many open, not something anyone asked for.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

email's header_value_parser missing defect report for 'abc@xyz.c:om'

1 participant