liblore 0.8.1__tar.gz → 0.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {liblore-0.8.1 → liblore-0.8.2}/PKG-INFO +1 -1
- {liblore-0.8.1 → liblore-0.8.2}/pyproject.toml +1 -1
- {liblore-0.8.1 → liblore-0.8.2}/src/liblore/__init__.py +1 -1
- {liblore-0.8.1 → liblore-0.8.2}/src/liblore/utils.py +7 -0
- {liblore-0.8.1 → liblore-0.8.2}/src/liblore.egg-info/PKG-INFO +1 -1
- {liblore-0.8.1 → liblore-0.8.2}/tests/test_thread.py +51 -0
- {liblore-0.8.1 → liblore-0.8.2}/LICENSES/GPL-2.0-or-later.txt +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/README.md +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/setup.cfg +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/src/liblore/node.py +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/src/liblore/py.typed +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/src/liblore.egg-info/SOURCES.txt +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/src/liblore.egg-info/dependency_links.txt +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/src/liblore.egg-info/requires.txt +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/src/liblore.egg-info/top_level.txt +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/tests/test_auth_headers.py +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/tests/test_cache.py +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/tests/test_email_utils.py +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/tests/test_formatting.py +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/tests/test_mbox.py +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/tests/test_message.py +0 -0
- {liblore-0.8.1 → liblore-0.8.2}/tests/test_node.py +0 -0
|
@@ -394,6 +394,13 @@ def get_strict_thread(
|
|
|
394
394
|
for hval in msg.get_all(hdr_name, []):
|
|
395
395
|
raw_refs += re.findall(r'<([^>]+)>', str(hval))
|
|
396
396
|
|
|
397
|
+
# Some tooling emits messages that reference their own Message-ID.
|
|
398
|
+
# A self-reference is never meaningful for threading, so drop it
|
|
399
|
+
# before it can be mistaken for a parent.
|
|
400
|
+
if c_msgid in raw_refs:
|
|
401
|
+
logger.debug('Ignoring self-reference in %s', c_msgid)
|
|
402
|
+
raw_refs = [ref for ref in raw_refs if ref != c_msgid]
|
|
403
|
+
|
|
397
404
|
# If noparent, pretend our target message has no references
|
|
398
405
|
if noparent and msgid == c_msgid:
|
|
399
406
|
logger.info('Breaking thread to remove parents of %s', msgid)
|
|
@@ -47,6 +47,57 @@ class TestGetStrictThread:
|
|
|
47
47
|
assert 'child@x.com' in ids
|
|
48
48
|
assert 'grandchild@x.com' in ids
|
|
49
49
|
|
|
50
|
+
def test_noparent_self_referencing_cover(self, make_msg: MsgFactory) -> None:
|
|
51
|
+
# Some tooling emits a cover letter whose In-Reply-To/References point
|
|
52
|
+
# at its own Message-ID. Breaking the thread there must not treat the
|
|
53
|
+
# cover's own msgid as a reference to ignore, or every patch that
|
|
54
|
+
# legitimately replies to the cover gets dropped.
|
|
55
|
+
cover = make_msg(
|
|
56
|
+
msgid='cover@x.com',
|
|
57
|
+
subject='[PATCH 0/2] Something',
|
|
58
|
+
in_reply_to='cover@x.com',
|
|
59
|
+
references=['cover@x.com'],
|
|
60
|
+
)
|
|
61
|
+
patch1 = make_msg(
|
|
62
|
+
msgid='p1@x.com',
|
|
63
|
+
subject='[PATCH 1/2] First',
|
|
64
|
+
in_reply_to='cover@x.com',
|
|
65
|
+
references=['cover@x.com'],
|
|
66
|
+
)
|
|
67
|
+
patch2 = make_msg(
|
|
68
|
+
msgid='p2@x.com',
|
|
69
|
+
subject='[PATCH 2/2] Second',
|
|
70
|
+
in_reply_to='cover@x.com',
|
|
71
|
+
references=['cover@x.com'],
|
|
72
|
+
)
|
|
73
|
+
review = make_msg(
|
|
74
|
+
msgid='review@x.com',
|
|
75
|
+
subject='Re: [PATCH 2/2] Second',
|
|
76
|
+
in_reply_to='p2@x.com',
|
|
77
|
+
references=['cover@x.com', 'p2@x.com'],
|
|
78
|
+
)
|
|
79
|
+
want = {'cover@x.com', 'p1@x.com', 'p2@x.com', 'review@x.com'}
|
|
80
|
+
# Cover first in the list is the worst case: without the fix only the
|
|
81
|
+
# cover survives. Ordering must not matter, so check both.
|
|
82
|
+
for msgs in ([cover, patch1, patch2, review], [patch1, cover, patch2, review]):
|
|
83
|
+
result = get_strict_thread(msgs, 'cover@x.com', noparent=True)
|
|
84
|
+
assert result is not None
|
|
85
|
+
assert {get_clean_msgid(m) for m in result} == want
|
|
86
|
+
|
|
87
|
+
def test_self_reference_is_not_a_parent(self, make_msg: MsgFactory) -> None:
|
|
88
|
+
# A self-referencing message must not be treated as a reply to itself,
|
|
89
|
+
# even without noparent: it is a thread root, and starting the walk at
|
|
90
|
+
# its child still has to pull it in.
|
|
91
|
+
root = make_msg(
|
|
92
|
+
msgid='root@x.com',
|
|
93
|
+
in_reply_to='root@x.com',
|
|
94
|
+
references=['root@x.com'],
|
|
95
|
+
)
|
|
96
|
+
reply = make_msg(msgid='reply@x.com', in_reply_to='root@x.com')
|
|
97
|
+
result = get_strict_thread([root, reply], 'reply@x.com')
|
|
98
|
+
assert result is not None
|
|
99
|
+
assert {get_clean_msgid(m) for m in result} == {'root@x.com', 'reply@x.com'}
|
|
100
|
+
|
|
50
101
|
def test_references_chain(self, make_msg: MsgFactory) -> None:
|
|
51
102
|
msg1 = make_msg(msgid='a@x.com')
|
|
52
103
|
msg2 = make_msg(msgid='b@x.com', references=['a@x.com'])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|