continue
-def _find_series_by_markers(project, mail):
+def _find_series_by_markers(project, mail, author):
"""Find a patch's series using series markers and sender.
Identify suitable series for a patch using a combination of the
still won't help us if someone spams the mailing list with
duplicate series but that's a tricky situation for anyone to parse.
"""
- author = find_author(mail)
subject = mail.get('Subject')
name, prefixes = clean_subject(subject, [project.linkname])
return
-def find_series(project, mail):
+def find_series(project, mail, author):
"""Find a series, if any, for a given patch.
Args:
if series:
return series
- return _find_series_by_markers(project, mail)
+ return _find_series_by_markers(project, mail, author)
-def find_author(mail):
+def get_or_create_author(mail):
from_header = clean_header(mail.get('From'))
if not from_header:
if name is not None:
name = name.strip()[:255]
- try:
- person = Person.objects.get(email__iexact=email)
- if name: # use the latest provided name
- person.name = name
- except Person.DoesNotExist:
- person = Person(name=name, email=email)
+ # this correctly handles the case where we lose the race to create
+ # the person and another process beats us to it. (If the record
+ # does not exist, g_o_c invokes _create_object_from_params which
+ # catches the IntegrityError and repeats the SELECT.)
+ person = Person.objects.get_or_create(email__iexact=email,
+ defaults={'name': name,
+ 'email': email})[0]
+
+ if name: # use the latest provided name
+ person.name = name
+ person.save()
return person
raise ValueError("Broken 'Message-Id' header")
msgid = msgid[:255]
- author = find_author(mail)
subject = mail.get('Subject')
name, prefixes = clean_subject(subject, [project.linkname])
is_comment = subject_check(subject)
if not is_comment and (diff or pull_url): # patches or pull requests
# we delay the saving until we know we have a patch.
- author.save()
+ author = get_or_create_author(mail)
delegate = find_delegate_by_header(mail)
if not delegate and diff:
# series to match against.
series = None
if n:
- series = find_series(project, mail)
+ series = find_series(project, mail, author)
else:
x = n = 1
is_cover_letter = True
if is_cover_letter:
- author.save()
+ author = get_or_create_author(mail)
# we don't use 'find_series' here as a cover letter will
# always be the first item in a thread, thus the references
if not submission:
return
- author.save()
+ author = get_or_create_author(mail)
comment = Comment(
submission=submission,
from patchwork.models import Person
from patchwork.models import State
from patchwork.parser import clean_subject
-from patchwork.parser import find_author
+from patchwork.parser import get_or_create_author
from patchwork.parser import find_patch_content as find_content
from patchwork.parser import find_project
from patchwork.parser import find_series
def _test_encoding(self, from_header, sender_name, sender_email):
email = self._create_email(from_header)
- person = find_author(email)
+ person = get_or_create_author(email)
person.save()
# ensure it was parsed correctly
def test_empty(self):
email = self._create_email('')
with self.assertRaises(ValueError):
- find_author(email)
+ get_or_create_author(email)
def test_ascii_encoding(self):
from_header = 'example user <user@example.com>'
class SenderCorrelationTest(TestCase):
- """Validate correct behavior of the find_author case.
+ """Validate correct behavior of the get_or_create_author case.
Relies of checking the internal state of a Django model object.
'test\n'
return message_from_string(mail)
- def test_non_existing_sender(self):
- sender = 'Non-existing Sender <nonexisting@example.com>'
- mail = self._create_email(sender)
-
- # don't create the person - attempt to find immediately
- person = find_author(mail)
- self.assertEqual(person._state.adding, True)
- self.assertEqual(person.id, None)
-
def test_existing_sender(self):
sender = 'Existing Sender <existing@example.com>'
mail = self._create_email(sender)
# create the person first
- person_a = find_author(mail)
+ person_a = get_or_create_author(mail)
person_a.save()
# then attempt to parse email with the same 'From' line
- person_b = find_author(mail)
+ person_b = get_or_create_author(mail)
self.assertEqual(person_b._state.adding, False)
self.assertEqual(person_b.id, person_a.id)
mail = self._create_email(sender)
# create the person first
- person_a = find_author(mail)
+ person_a = get_or_create_author(mail)
person_a.save()
# then attempt to parse email with a new 'From' line
mail = self._create_email('existing@example.com')
- person_b = find_author(mail)
+ person_b = get_or_create_author(mail)
self.assertEqual(person_b._state.adding, False)
self.assertEqual(person_b.id, person_a.id)
sender = 'Existing Sender <existing@example.com>'
mail = self._create_email(sender)
- person_a = find_author(mail)
+ person_a = get_or_create_author(mail)
person_a.save()
mail = self._create_email(sender.upper())
- person_b = find_author(mail)
+ person_b = get_or_create_author(mail)
self.assertEqual(person_b._state.adding, False)
self.assertEqual(person_b.id, person_a.id)
email = self._create_email(msgid)
project = create_project()
- self.assertIsNone(find_series(project, email))
+ self.assertIsNone(find_series(project, email,
+ get_or_create_author(email)))
def test_first_reply(self):
msgid_a = make_msgid()
# assume msgid_a was already handled
ref = create_series_reference(msgid=msgid_a)
- series = find_series(ref.series.project, email)
+ series = find_series(ref.series.project, email,
+ get_or_create_author(email))
self.assertEqual(series, ref.series)
def test_nested_series(self):
# ...and the "first patch" of this new series
msgid = make_msgid()
email = self._create_email(msgid, msgids)
- series = find_series(project, email)
+ series = find_series(project, email, get_or_create_author(email))
# this should link to the second series - not the first
self.assertEqual(len(msgids), 4 + 1) # old series + new cover