[PATCH v4 1/2] parsemail: Convert to a management command

Andrew Donnellan andrew.donnellan at au1.ibm.com
Mon Sep 5 13:58:18 AEST 2016


On 04/09/16 10:12, Stephen Finucane wrote:
> From: Stephen Finucane <stephen.finucane at intel.com>

git commit --reset-author time? :) (S-O-B too.)

> Management comands allow applications to register their own actions with
> 'manage.py'. This provides some advantages, like automatically
> configuring Django (removing the need for 'django.setup' calls) and
> removing the need to set the PYTHON_PATH. The 'parsemail' script is a
> natural fit for this type of application. Migrate 'parsemail' to a
> management command.
>
> This includes some extensive work on logging configuration, as logging
> is moved from code into settings. In addition, it removes a lot of the
> customizable logging previously introduced in the parsemail command, in
> favour of modifications to the settings files.

Excellent, this simplifies things nicely!

> diff --git a/patchwork/management/commands/parsemail.py b/patchwork/management/commands/parsemail.py
> new file mode 100644
> index 0000000..65525ad
> --- /dev/null
> +++ b/patchwork/management/commands/parsemail.py
> @@ -0,0 +1,81 @@
> +# Patchwork - automated patch tracking system
> +# Copyright (C) 2016 Intel Corporation
> +#
> +# This file is part of the Patchwork package.
> +#
> +# Patchwork is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 2 of the License, or
> +# (at your option) any later version.
> +#
> +# Patchwork is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with Patchwork; if not, write to the Free Software
> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> +
> +import argparse
> +from email import message_from_file
> +import logging
> +from optparse import make_option
> +import sys
> +
> +import django
> +from django.core.management import base
> +
> +from patchwork.parser import parse_mail
> +
> +logger = logging.getLogger(__name__)
> +
> +
> +class Command(base.BaseCommand):
> +    help = 'Parse an mbox file and store any patch/comment found.'
> +
> +    if django.VERSION < (1, 8):
> +        args = '<infile>'
> +        option_list = base.BaseCommand.option_list + (
> +            make_option(
> +                '--list-id',
> +                help='mailing list ID. If not supplied, this will be '
> +                'extracted from the mail headers.'),
> +        )
> +    else:
> +        def add_arguments(self, parser):
> +            parser.add_argument(
> +                'infile',
> +                nargs='?',
> +                type=argparse.FileType('r'),
> +                default=sys.stdin,
> +                help='input mbox file (a filename or stdin)')
> +            parser.add_argument(
> +                '--list-id',
> +                action='store_true',

No parameters? This looks wrong...

> +                help='mailing list ID. If not supplied, this will be '
> +                'extracted from the mail headers.')
> +
> +    def handle(self, *args, **options):
> +        path = (args[0] if args else
> +                options['infile'] if 'infile' in options else None)

argparse is giving you a file, not a string. As you've given a default 
in the argument, this should always return an open file, I'm pretty sure.

> +        stdin = options.get('stdin', sys.stdin)

What's this for?

> +
> +        # Attempt to parse the path if provided, and fallback to stdin if not
> +        if path and not isinstance(path, file):
> +            logger.info('Parsing mail loaded by filename')
> +            with open(path, 'r+') as file_:
> +                mail = message_from_file(file_)
> +        else:
> +            logger.info('Parsing mail loaded from stdin')
> +            mail = message_from_file(stdin)
> +

As noted in my other email this is all broken.

I'd rewrite all this as follows (lightly tested on Py2 and 3):

     def handle(self, *args, **options):
        infile = options['infile']
        if infile == sys.stdin:
            logger.info('Parsing mail loaded from stdin')
        else:
            logger.info('Parsing mail loaded by filename')
        mail = message_from_file(infile)

> diff --git a/patchwork/tests/test_management.py b/patchwork/tests/test_management.py
> new file mode 100644
> index 0000000..a3f4720
> --- /dev/null
> +++ b/patchwork/tests/test_management.py
> @@ -0,0 +1,51 @@
> +# Patchwork - automated patch tracking system
> +# Copyright (C) 2016 Stephen Finucane <stephenfinucane at hotmail.com>
> +#
> +# This file is part of the Patchwork package.
> +#
> +# Patchwork is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 2 of the License, or
> +# (at your option) any later version.
> +#
> +# Patchwork is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with Patchwork; if not, write to the Free Software
> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> +
> +import os
> +
> +from django.core.management import call_command
> +from django.test import TestCase
> +from django.utils.six import StringIO
> +
> +from patchwork.tests import TEST_MAIL_DIR
> +
> +
> +class ParsemailTest(TestCase):
> +    def test_invalid_path(self):
> +        with self.assertRaises(IOError):
> +            call_command('parsemail', 'xyz123random')
> +
> +    def test_path_failure(self):
> +        # we haven't created a project yet, so this will fail
> +        with self.assertRaises(SystemExit) as exc:
> +            call_command('parsemail',
> +                         os.path.join(TEST_MAIL_DIR,
> +                                      '0001-git-pull-request.mbox'))
> +
> +        self.assertEqual(exc.exception.code, 1)
> +
> +    def test_stdin_failure(self):
> +        # we haven't created a project yet, so this will fail
> +        with open(os.path.join(TEST_MAIL_DIR,
> +                               '0001-git-pull-request.mbox')) as file_:
> +            with self.assertRaises(SystemExit) as exc:
> +                call_command('parsemail',
> +                             stdin=file_)
> +
> +            self.assertEqual(exc.exception.code, 1)

Can we test the --list-id option at all?

-- 
Andrew Donnellan              OzLabs, ADL Canberra
andrew.donnellan at au1.ibm.com  IBM Australia Limited



More information about the Patchwork mailing list