fix(email): drop the empty entry of an address group

Merged
CH
13d ago
+38-11

partition_email() reports To: undisclosed-recipients:; as sent_to=[""]. This change drops entries without an address and gives None, as a missing header does.

That header is an RFC 5322 group with no members, which mail clients write whenever every recipient is in Bcc. email.utils.getaddresses() returns it as ("", ""), and the four address properties in email.py format that pair into an empty string. cc_recipient and bcc_recipient do the same:

To: undisclosed-recipients:;   Cc: Team: bob@example.com, carol@example.com;   Bcc: undisclosed-recipients:;
08d407e:     sent_to=['']   cc_recipient=['bob@example.com', 'carol@example.com']   bcc_recipient=['']
this change: sent_to=None   cc_recipient=['bob@example.com', 'carol@example.com']   bcc_recipient=None

One helper, _format_addresses(), now drops entries without an address for all four properties, and returns None when none are left, as a missing header already does. A group with members keeps only their addresses. The filter also covers a header that the strict parser in Python 3.12.3 rejects, such as alice@example.com <bob@example.com>. It also comes back as ("", ""), so it gives None now, as in #4407. CHANGELOG entry under 0.27.31, with the version bumped.

Testing

$ python -m pytest -q -p no:cacheprovider test_unstructured/partition/test_email.py
77 passed, 1 skipped in 3.81s
# baseline at 08d407e: 76 passed, 1 skipped, so the delta is the new test
# email.py reverted, test kept: 1 failed, 76 passed, 1 skipped (test_partition_email_drops_the_empty_entry_of_an_address_group)
$ ruff check unstructured/partition/email.py test_unstructured/partition/test_email.py
All checks passed!
$ ruff format --check unstructured/partition/email.py test_unstructured/partition/test_email.py
2 files already formatted
# ruff 0.15.0, Python 3.12
# not run: scripts/version-sync.sh -c, which needs an origin remote; test_msg.py, which fails 4 tests here with or without this change because the extras are not installed

No issue exists for this. #4407 dropped the same empty entries inside a larger parser fallback and was closed by its author before review. This is only that filter, on the code #4543 left. @cragwolfe, you merged #4543 on these properties. I picked None over [] because a missing header already gives None, so callers test one value. Say if [] fits better and I will switch it.