[PATCH 1/4] trivial: Cleanup of 'bin/parsemail'

Stephen Finucane stephen.finucane at intel.com
Fri Nov 13 14:48:04 AEDT 2015


Do some cleanup of the script, removing excess whitespace and imports,
localising variables where possible etc. This file still isn't py3
compatible but it's getting there.

Signed-off-by: Stephen Finucane <stephen.finucane at intel.com>
---
 patchwork/bin/parsemail.py | 167 ++++++++++++++++++++++++++-------------------
 1 file changed, 97 insertions(+), 70 deletions(-)

diff --git a/patchwork/bin/parsemail.py b/patchwork/bin/parsemail.py
index c15564e..e05f036 100755
--- a/patchwork/bin/parsemail.py
+++ b/patchwork/bin/parsemail.py
@@ -19,34 +19,35 @@
 # along with Patchwork; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-import sys
-import re
-import datetime
-import time
-import operator
 import codecs
+import datetime
 from email import message_from_file
 from email.header import Header, decode_header
 from email.utils import parsedate_tz, mktime_tz
 import logging
+import operator
+import re
+import sys
 
-from patchwork.parser import parse_patch
-from patchwork.models import Patch, Project, Person, Comment, State, \
-        get_default_initial_patch_state
 import django
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.utils.log import AdminEmailHandler
 
+from patchwork.models import (
+    Patch, Project, Person, Comment, State, get_default_initial_patch_state)
+from patchwork.parser import parse_patch
+
 list_id_headers = ['List-ID', 'X-Mailing-List', 'X-list']
 
-whitespace_re = re.compile('\s+')
+
 def normalise_space(str):
+    whitespace_re = re.compile(r'\s+')
     return whitespace_re.sub(' ', str).strip()
 
-def clean_header(header):
-    """ Decode (possibly non-ascii) headers """
 
+def clean_header(header):
+    """Decode (possibly non-ascii) headers."""
     def decode(fragment):
         (frag_str, frag_encoding) = fragment
         if frag_encoding:
@@ -57,10 +58,11 @@ def clean_header(header):
 
     return normalise_space(u' '.join(fragments))
 
+
 def find_project(mail):
     project = None
-    listid_res = [re.compile('.*<([^>]+)>.*', re.S),
-                  re.compile('^([\S]+)$', re.S)]
+    listid_res = [re.compile(r'.*<([^>]+)>.*', re.S),
+                  re.compile(r'^([\S]+)$', re.S)]
 
     for header in list_id_headers:
         if header in mail:
@@ -76,13 +78,14 @@ def find_project(mail):
             listid = match.group(1)
 
             try:
-                project = Project.objects.get(listid = listid)
+                project = Project.objects.get(listid=listid)
                 break
             except Project.DoesNotExist:
                 pass
 
     return project
 
+
 def find_author(mail):
 
     from_header = clean_header(mail.get('From'))
@@ -93,13 +96,13 @@ def find_author(mail):
     #    from re.match().groups()
     from_res = [
         # for "Firstname Lastname" <example at example.com> style addresses
-       (re.compile('"?(.*?)"?\s*<([^>]+)>'), (lambda g: (g[0], g[1]))),
+        (re.compile(r'"?(.*?)"?\s*<([^>]+)>'), (lambda g: (g[0], g[1]))),
 
-       # for example at example.com (Firstname Lastname) style addresses
-       (re.compile('"?(.*?)"?\s*\(([^\)]+)\)'), (lambda g: (g[1], g[0]))),
+        # for example at example.com (Firstname Lastname) style addresses
+        (re.compile(r'"?(.*?)"?\s*\(([^\)]+)\)'), (lambda g: (g[1], g[0]))),
 
-       # everything else
-       (re.compile('(.*)'), (lambda g: (None, g[0]))),
+        # everything else
+        (re.compile(r'(.*)'), (lambda g: (None, g[0]))),
     ]
 
     for regex, fn in from_res:
@@ -118,35 +121,39 @@ def find_author(mail):
     new_person = False
 
     try:
-        person = Person.objects.get(email__iexact = email)
+        person = Person.objects.get(email__iexact=email)
     except Person.DoesNotExist:
-        person = Person(name = name, email = email)
+        person = Person(name=name, email=email)
         new_person = True
 
     return (person, new_person)
 
+
 def mail_date(mail):
     t = parsedate_tz(mail.get('Date', ''))
     if not t:
         return datetime.datetime.utcnow()
     return datetime.datetime.utcfromtimestamp(mktime_tz(t))
 
+
 def mail_headers(mail):
     return reduce(operator.__concat__,
-            ['%s: %s\n' % (k, Header(v, header_name = k, \
-                    continuation_ws = '\t').encode()) \
-                for (k, v) in mail.items()])
+                  ['%s: %s\n' % (k, Header(v, header_name=k,
+                                           continuation_ws='\t').encode())
+                   for (k, v) in mail.items()])
+
 
 def find_pull_request(content):
-    git_re = re.compile('^The following changes since commit.*' +
-                        '^are available in the git repository at:\n'
-                        '^\s*([\S]+://[^\n]+)$',
-                           re.DOTALL | re.MULTILINE)
+    git_re = re.compile(r'^The following changes since commit.*' +
+                        r'^are available in the git repository at:\n'
+                        r'^\s*([\S]+://[^\n]+)$',
+                        re.DOTALL | re.MULTILINE)
     match = git_re.search(content)
     if match:
         return match.group(1)
     return None
 
+
 def try_decode(payload, charset):
     try:
         payload = unicode(payload, charset)
@@ -154,6 +161,7 @@ def try_decode(payload, charset):
         return None
     return payload
 
+
 def find_content(project, mail):
     patchbuf = None
     commentbuf = ''
@@ -173,7 +181,7 @@ def find_content(project, mail):
             # ignore it and fallback to our standard set.
             if charset is not None:
                 try:
-                    codec = codecs.lookup(charset)
+                    codecs.lookup(charset)
                 except LookupError:
                     charset = None
 
@@ -214,27 +222,27 @@ def find_content(project, mail):
 
     if pullurl or patchbuf:
         name = clean_subject(mail.get('Subject'), [project.linkname])
-        patch = Patch(name = name, pull_url = pullurl, content = patchbuf,
-                    date = mail_date(mail), headers = mail_headers(mail))
+        patch = Patch(name=name, pull_url=pullurl, content=patchbuf,
+                      date=mail_date(mail), headers=mail_headers(mail))
 
     if commentbuf:
         # If this is a new patch, we defer setting comment.patch until
         # patch has been saved by the caller
         if patch:
-            comment = Comment(date = mail_date(mail),
-                    content = clean_content(commentbuf),
-                    headers = mail_headers(mail))
-
+            comment = Comment(date=mail_date(mail),
+                              content=clean_content(commentbuf),
+                              headers=mail_headers(mail))
         else:
             cpatch = find_patch_for_comment(project, mail)
             if not cpatch:
                 return (None, None)
-            comment = Comment(patch = cpatch, date = mail_date(mail),
-                    content = clean_content(commentbuf),
-                    headers = mail_headers(mail))
+            comment = Comment(patch=cpatch, date=mail_date(mail),
+                              content=clean_content(commentbuf),
+                              headers=mail_headers(mail))
 
     return (patch, comment)
 
+
 def find_patch_for_comment(project, mail):
     # construct a list of possible reply message ids
     refs = []
@@ -253,47 +261,50 @@ def find_patch_for_comment(project, mail):
 
         # first, check for a direct reply
         try:
-            patch = Patch.objects.get(project = project, msgid = ref)
+            patch = Patch.objects.get(project=project, msgid=ref)
             return patch
         except Patch.DoesNotExist:
             pass
 
         # see if we have comments that refer to a patch
         try:
-            comment = Comment.objects.get(patch__project = project, msgid = ref)
+            comment = Comment.objects.get(patch__project=project, msgid=ref)
             return comment.patch
         except Comment.DoesNotExist:
             pass
 
-
     return None
 
-split_re = re.compile('[,\s]+')
 
 def split_prefixes(prefix):
-    """ Turn a prefix string into a list of prefix tokens """
-
+    """Turn a prefix string into a list of prefix tokens."""
+    split_re = re.compile(r'[,\s]+')
     matches = split_re.split(prefix)
-    return [ s for s in matches if s != '' ]
-
-re_re = re.compile('^(re|fwd?)[:\s]\s*', re.I)
-prefix_re = re.compile('^\[([^\]]*)\]\s*(.*)$')
 
-def clean_subject(subject, drop_prefixes = None):
-    """ Clean a Subject: header from an incoming patch.
+    return [s for s in matches if s != '']
 
-    Removes Re: and Fwd: strings, as well as [PATCH]-style prefixes. By
-    default, only [PATCH] is removed, and we keep any other bracketed data
-    in the subject. If drop_prefixes is provided, remove those too,
-    comparing case-insensitively."""
 
+def clean_subject(subject, drop_prefixes=None):
+    """Clean a Subject: header from an incoming patch.
 
+    Removes Re: and Fwd: strings, as well as [PATCH]-style prefixes. By
+    default, only [PATCH] is removed, and we keep any other bracketed
+    data in the subject. If drop_prefixes is provided, remove those
+    too, comparing case-insensitively.
+
+    Args:
+        subject: Subject to be cleaned
+        drop_prefixes: Additional, case-insensitive prefixes to remove
+          from the subject
+    """
+    re_re = re.compile(r'^(re|fwd?)[:\s]\s*', re.I)
+    prefix_re = re.compile(r'^\[([^\]]*)\]\s*(.*)$')
     subject = clean_header(subject)
 
     if drop_prefixes is None:
         drop_prefixes = []
     else:
-        drop_prefixes = [ s.lower() for s in drop_prefixes ]
+        drop_prefixes = [s.lower() for s in drop_prefixes]
 
     drop_prefixes.append('patch')
 
@@ -308,8 +319,8 @@ def clean_subject(subject, drop_prefixes = None):
 
     while match:
         prefix_str = match.group(1)
-        prefixes += [ p for p in split_prefixes(prefix_str) \
-                        if p.lower() not in drop_prefixes]
+        prefixes += [p for p in split_prefixes(prefix_str)
+                     if p.lower() not in drop_prefixes]
 
         subject = match.group(2)
         match = prefix_re.match(subject)
@@ -322,14 +333,20 @@ def clean_subject(subject, drop_prefixes = None):
 
     return subject
 
-sig_re = re.compile('^(-- |_+)\n.*', re.S | re.M)
-def clean_content(str):
-    """ Try to remove signature (-- ) and list footer (_____) cruft """
-    str = sig_re.sub('', str)
-    return str.strip()
+
+def clean_content(content):
+    """Remove cruft from the email message.
+
+    Catch ignature (-- ) and list footer (_____) cruft.
+    """
+    sig_re = re.compile(r'^(-- |_+)\n.*', re.S | re.M)
+    content = sig_re.sub('', content)
+
+    return content.strip()
+
 
 def get_state(state_name):
-    """ Return the state with the given name or the default State """
+    """Return the state with the given name or the default."""
     if state_name:
         try:
             return State.objects.get(name__iexact=state_name)
@@ -337,8 +354,9 @@ def get_state(state_name):
             pass
     return get_default_initial_patch_state()
 
+
 def get_delegate(delegate_email):
-    """ Return the delegate with the given email or None """
+    """Return the delegate with the given email or None."""
     if delegate_email:
         try:
             return User.objects.get(email__iexact=delegate_email)
@@ -346,8 +364,9 @@ def get_delegate(delegate_email):
             pass
     return None
 
-def parse_mail(mail):
 
+def parse_mail(mail):
+    """Parse a mail and add to the database."""
     # some basic sanity checks
     if 'From' not in mail:
         return 0
@@ -360,11 +379,12 @@ def parse_mail(mail):
 
     hint = mail.get('X-Patchwork-Hint', '').lower()
     if hint == 'ignore':
-        return 0;
+        return 0
 
     project = find_project(mail)
+
     if project is None:
-        print "no project found"
+        print("no project found")
         return 0
 
     msgid = mail.get('Message-Id').strip()
@@ -383,7 +403,7 @@ def parse_mail(mail):
         patch.project = project
         patch.state = get_state(mail.get('X-Patchwork-State', '').strip())
         patch.delegate = get_delegate(
-                mail.get('X-Patchwork-Delegate', '').strip())
+            mail.get('X-Patchwork-Delegate', '').strip())
         patch.save()
 
     if comment:
@@ -408,10 +428,15 @@ extra_error_message = '''
 
 '''
 
-# Send emails to settings.ADMINS when encountering errors
+
 def setup_error_handler():
+    """Configure error handler.
+
+    Ensure emails are send to settings.ADMINS when errors are
+    encountered.
+    """
     if settings.DEBUG:
-        return None
+        return
 
     mail_handler = AdminEmailHandler()
     mail_handler.setLevel(logging.ERROR)
@@ -422,6 +447,7 @@ def setup_error_handler():
 
     return logger
 
+
 def main(args):
     django.setup()
     logger = setup_error_handler()
@@ -435,5 +461,6 @@ def main(args):
             })
         raise
 
+
 if __name__ == '__main__':
     sys.exit(main(sys.argv))
-- 
2.0.0



More information about the Patchwork mailing list