diff mbox

[v2,4/4] pwclient: use argparse's error() function for bad input

Message ID 1445038743-50857-4-git-send-email-computersforpeace@gmail.com
State Accepted
Delegated to: Stephen Finucane
Headers show

Commit Message

Brian Norris Oct. 16, 2015, 11:39 p.m. UTC
This reduces the boilerplate we need and provides a more consistent help
output. e.g.:

   $ pwclient update -s FOO -c 1337 314159 1234567
   usage: pwclient update [--help] [-h HASH] [-p PROJECT] [-c COMMIT-REF]
                          [-s STATE] [-a {yes,no}]
                          [ID [ID ...]]
   pwclient update: error: Declining update with COMMIT-REF on multiple IDs

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
---
v2: new in this series, per Mike's suggestion

 patchwork/bin/pwclient | 20 +++++---------------
 1 file changed, 5 insertions(+), 15 deletions(-)

Comments

Mike Frysinger Oct. 17, 2015, 2:21 a.m. UTC | #1
Reviewed-by: Mike Frysinger <vapier@chromium.org>
-mike
Mike Frysinger Oct. 17, 2015, 2:25 a.m. UTC | #2
On 16 Oct 2015 16:39, Brian Norris wrote:
>          commit_str = args.get('c')

if you feel like cleaning things up, i find the args behavior weird and
non-standard.  it does:
	args = action_parser.parse_args()
	args = dict(vars(args))
	action = args.get('subcmd')

normally argparse code does:
	args = action_parser.parse_args()
	action = args.subcmd

i'm not sure why this dict/get style was picked.  maybe Bernhard can
shed some light here.
-mike
Bernhard Reutner-Fischer Oct. 19, 2015, 7:57 a.m. UTC | #3
On October 17, 2015 4:25:54 AM GMT+02:00, Mike Frysinger <vapier@gentoo.org> wrote:
>On 16 Oct 2015 16:39, Brian Norris wrote:
>>          commit_str = args.get('c')
>
>if you feel like cleaning things up, i find the args behavior weird and
>non-standard.  it does:
>	args = action_parser.parse_args()
>	args = dict(vars(args))
>	action = args.get('subcmd')
>
>normally argparse code does:
>	args = action_parser.parse_args()
>	action = args.subcmd
>
>i'm not sure why this dict/get style was picked.  maybe Bernhard can
>shed some light here.

At this point all we need is a dict and I found it easier and shorter to type.
I don't have a strong opinion about it, so if it's more pythonic to rephrase it, please do.

I do remember to have gone through hoops to handle --help and --hash at the same time AND allowing for recursive printing of all help (I considered that a feature back then) but I do not remember if or why my attempt to use a conflict_handler didn't work out. Could be there was a bug in argparse back then or maybe I just goofed it.

Thanks,
>-mike
Mike Frysinger Oct. 19, 2015, 5:56 p.m. UTC | #4
On 19 Oct 2015 09:57, Bernhard Reutner-Fischer wrote:
> On October 17, 2015 4:25:54 AM GMT+02:00, Mike Frysinger wrote:
> >On 16 Oct 2015 16:39, Brian Norris wrote:
> >>          commit_str = args.get('c')
> >
> >if you feel like cleaning things up, i find the args behavior weird and
> >non-standard.  it does:
> >	args = action_parser.parse_args()
> >	args = dict(vars(args))
> >	action = args.get('subcmd')
> >
> >normally argparse code does:
> >	args = action_parser.parse_args()
> >	action = args.subcmd
> >
> >i'm not sure why this dict/get style was picked.  maybe Bernhard can
> >shed some light here.
> 
> At this point all we need is a dict and I found it easier and shorter to type.

i'm not sure how this is shorter:
	args.get('subcmd')
than this:
	args.subcmd

it's also more dangerous: get will not throw an error if the key
doesn't exist.  so if you did something like:
	args.get('submcd')
you might not notice -- python would return None in the default case
you didn't typo and everytime you typod.  but if you did:
	args.submcd
then python would an exception as soon as that code is run.  since
argparse has a default=None, args.subcmd would return None if the
user hadn't specified anything.

> I do remember to have gone through hoops to handle --help and --hash at the same time AND allowing for recursive printing of all help (I considered that a feature back then) but I do not remember if or why my attempt to use a conflict_handler didn't work out. Could be there was a bug in argparse back then or maybe I just goofed it.

Brian has posted some CLs to clean that up by using some more of the
argparse infrastructure.
-mike
Stephen Finucane Oct. 20, 2015, 12:14 a.m. UTC | #5
> This reduces the boilerplate we need and provides a more consistent help
> output. e.g.:
> 
>    $ pwclient update -s FOO -c 1337 314159 1234567
>    usage: pwclient update [--help] [-h HASH] [-p PROJECT] [-c COMMIT-REF]
>                           [-s STATE] [-a {yes,no}]
>                           [ID [ID ...]]
>    pwclient update: error: Declining update with COMMIT-REF on multiple IDs
> 
> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
> ---
> v2: new in this series, per Mike's suggestion

Yeah, this is a good improvement.

Reviewed-by: Stephen Finucane <stephen.finucane@intel.com>
Stephen Finucane Oct. 20, 2015, 12:57 a.m. UTC | #6
> > This reduces the boilerplate we need and provides a more consistent help

> > output. e.g.:

> >

> >    $ pwclient update -s FOO -c 1337 314159 1234567

> >    usage: pwclient update [--help] [-h HASH] [-p PROJECT] [-c COMMIT-REF]

> >                           [-s STATE] [-a {yes,no}]

> >                           [ID [ID ...]]

> >    pwclient update: error: Declining update with COMMIT-REF on multiple

> IDs

> >

> > Signed-off-by: Brian Norris <computersforpeace@gmail.com>

> > ---

> > v2: new in this series, per Mike's suggestion

> 

> Yeah, this is a good improvement.

> 

> Reviewed-by: Stephen Finucane <stephen.finucane@intel.com>


Merged.
Brian Norris Oct. 20, 2015, 11:58 p.m. UTC | #7
On Mon, Oct 19, 2015 at 01:56:35PM -0400, Mike Frysinger wrote:
> On 19 Oct 2015 09:57, Bernhard Reutner-Fischer wrote:
> > On October 17, 2015 4:25:54 AM GMT+02:00, Mike Frysinger wrote:
> > >On 16 Oct 2015 16:39, Brian Norris wrote:
> > >>          commit_str = args.get('c')
> > >
> > >if you feel like cleaning things up, i find the args behavior weird and
> > >non-standard.  it does:
> > >	args = action_parser.parse_args()
> > >	args = dict(vars(args))
> > >	action = args.get('subcmd')
> > >
> > >normally argparse code does:
> > >	args = action_parser.parse_args()
> > >	action = args.subcmd

I took a quick look at what it takes to switch. A few questions.

> > >i'm not sure why this dict/get style was picked.  maybe Bernhard can
> > >shed some light here.
> > 
> > At this point all we need is a dict and I found it easier and shorter to type.
> 
> i'm not sure how this is shorter:
> 	args.get('subcmd')
> than this:
> 	args.subcmd
> 
> it's also more dangerous: get will not throw an error if the key
> doesn't exist.  so if you did something like:
> 	args.get('submcd')
> you might not notice -- python would return None in the default case
> you didn't typo and everytime you typod.  but if you did:
> 	args.submcd
> then python would an exception as soon as that code is run.  since
> argparse has a default=None, args.subcmd would return None if the
> user hadn't specified anything.

Those benefits all seem nice to me.

> > I do remember to have gone through hoops to handle --help and --hash
> > at the same time AND allowing for recursive printing of all help (I
> > considered that a feature back then) but I do not remember if or why
> > my attempt to use a conflict_handler didn't work out. Could be there
> > was a bug in argparse back then or maybe I just goofed it.
> 
> Brian has posted some CLs to clean that up by using some more of the
> argparse infrastructure.

Right, so that part is fixed AFAICT. I wrote that stuff so long ago, I
don't actually remember the details about the conflict_handler now :)

Anyway, if we stop converting to a dict, then we have to be a little
more careful on how we treat various arguments when implementing common
code. e.g., we can't do this, since not all subcommands have the
"archived" flag:

	archived_str = args.a

	...

	if archived_str:
		# do something

Maybe I'm not being creative (or pythonic) enough to figure out what the
elegant solution would be that doesn't involve accidentally referencing
attributes that only exist on some subcommands. Do we just go with
hasattr()? Or getattr()? (But then we're still back to the same problem
of typos...)

Brian
diff mbox

Patch

diff --git a/patchwork/bin/pwclient b/patchwork/bin/pwclient
index 9d34d541f3a8..76dd97b969ec 100755
--- a/patchwork/bin/pwclient
+++ b/patchwork/bin/pwclient
@@ -493,10 +493,8 @@  def main():
 
     if args.get('hash') and len(args.get('id')):
         # mimic mutual exclusive group
-        sys.stderr.write("Error: [-h HASH] and [ID [ID ...]] " +
-          "are mutually exlusive\n")
-        locals()[action + '_parser'].print_help()
-        sys.exit(1)
+        locals()[action + '_parser'].error(
+                "[-h HASH] and [ID [ID ...]] are mutually exlusive")
 
     # set defaults
     filt = Filter()
@@ -515,11 +513,7 @@  def main():
     if args.get('c'):
         # update multiple IDs with a single commit-hash does not make sense
         if action == 'update' and patch_ids and len(patch_ids) > 1:
-            sys.stderr.write(
-              "Declining update with COMMIT-REF on multiple IDs\n"
-            )
-            update_parser.print_help()
-            sys.exit(1)
+            update_parser.error("Declining update with COMMIT-REF on multiple IDs")
         commit_str = args.get('c')
 
     if state_str is None and archived_str is None and action == 'update':
@@ -529,9 +523,7 @@  def main():
         try:
             filt.add("max_count", args.get('n'))
         except:
-            sys.stderr.write("Invalid maximum count '%s'\n" % args.get('n'))
-            action_parser.print_help()
-            sys.exit(1)
+            action_parser.error("Invalid maximum count '%s'" % args.get('n'))
 
     do_signoff = args.get('signoff')
 
@@ -572,9 +564,7 @@  def main():
         try:
             project_str = config.get('options', 'default')
         except:
-            sys.stderr.write("No default project configured in ~/.pwclientrc\n")
-            action_parser.print_help()
-            sys.exit(1)
+            action_parser.error("No default project configured in ~/.pwclientrc")
 
     if not config.has_section(project_str):
         sys.stderr.write('No section for project %s in ~/.pwclientrc\n' % project_str)