Python CLI-Parser improvement for “one of those options”











up vote
2
down vote

favorite












I was wondering if there was a better way to implement my cli parser.



For context:
I need to pass to my script either a file in one of two formats or two files in a specific format.



It may be easier to understand like this:



file.ext1 OR file.ext2 OR (file_1.ext2 AND file_2.ext2)





I've used python argparse add_mutually_exclusive_group successfully and it looks like this:



import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"--ext1",
type = __existant_file,
metavar = "FILE",
help = "input file (format ext1)"
)
group.add_argument(
"--ext2",
type = __existant_file,
metavar = "FILE",
help = "input file (format ext2)"
)
group.add_argument(
"--paired",
nargs = 2,
type = __existant_file,
metavar = ("MATE_1", "MATE_2"),
help = "input file (two files format ext2)"
)

args = parser.parse_args()
print(args)

if args.ext1 is not None:
file = args.ext1
elif args.ext2 is not None:
file = args2.ext2
else:
file = args.paired[0]
file2 = args.paired[1]


Which is used as:



python script.py --ext1 file
OR
python script.py --ext2 file
OR
python script.py --paired file_1 file_2


Which is working but not really smooth. Do you have any lead of how I can improve the CLI parser ?










share|improve this question
























  • @Graipher Well, I do believe this is exactly what I asked, my code is working but I'd like improvement. Maybe it's not clear tho, I'll rewrite a bit
    – Plopp
    Sep 4 at 14:03








  • 2




    @Graipher Oh I didn't though I needed a working example as I wasn't expecting any code back but more a general idea, I've edited it :)
    – Plopp
    Sep 4 at 14:20








  • 1




    We got a very good meta thread about how to improve question quality if you're interested :-)
    – Mast
    Sep 4 at 14:31










  • depending on the specific case, a simpler cli interface might be preferable: just pass one or two files as positional parameters (raise ArgumentError if given files in a wrong format). Using docopt, you would specify the alternatives on two/three lines. Here's a variant with the two line usage: command <file.ext1> $ command <file1.ext2> [<file2.ext2>]
    – jfs
    Sep 4 at 19:05















up vote
2
down vote

favorite












I was wondering if there was a better way to implement my cli parser.



For context:
I need to pass to my script either a file in one of two formats or two files in a specific format.



It may be easier to understand like this:



file.ext1 OR file.ext2 OR (file_1.ext2 AND file_2.ext2)





I've used python argparse add_mutually_exclusive_group successfully and it looks like this:



import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"--ext1",
type = __existant_file,
metavar = "FILE",
help = "input file (format ext1)"
)
group.add_argument(
"--ext2",
type = __existant_file,
metavar = "FILE",
help = "input file (format ext2)"
)
group.add_argument(
"--paired",
nargs = 2,
type = __existant_file,
metavar = ("MATE_1", "MATE_2"),
help = "input file (two files format ext2)"
)

args = parser.parse_args()
print(args)

if args.ext1 is not None:
file = args.ext1
elif args.ext2 is not None:
file = args2.ext2
else:
file = args.paired[0]
file2 = args.paired[1]


Which is used as:



python script.py --ext1 file
OR
python script.py --ext2 file
OR
python script.py --paired file_1 file_2


Which is working but not really smooth. Do you have any lead of how I can improve the CLI parser ?










share|improve this question
























  • @Graipher Well, I do believe this is exactly what I asked, my code is working but I'd like improvement. Maybe it's not clear tho, I'll rewrite a bit
    – Plopp
    Sep 4 at 14:03








  • 2




    @Graipher Oh I didn't though I needed a working example as I wasn't expecting any code back but more a general idea, I've edited it :)
    – Plopp
    Sep 4 at 14:20








  • 1




    We got a very good meta thread about how to improve question quality if you're interested :-)
    – Mast
    Sep 4 at 14:31










  • depending on the specific case, a simpler cli interface might be preferable: just pass one or two files as positional parameters (raise ArgumentError if given files in a wrong format). Using docopt, you would specify the alternatives on two/three lines. Here's a variant with the two line usage: command <file.ext1> $ command <file1.ext2> [<file2.ext2>]
    – jfs
    Sep 4 at 19:05













up vote
2
down vote

favorite









up vote
2
down vote

favorite











I was wondering if there was a better way to implement my cli parser.



For context:
I need to pass to my script either a file in one of two formats or two files in a specific format.



It may be easier to understand like this:



file.ext1 OR file.ext2 OR (file_1.ext2 AND file_2.ext2)





I've used python argparse add_mutually_exclusive_group successfully and it looks like this:



import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"--ext1",
type = __existant_file,
metavar = "FILE",
help = "input file (format ext1)"
)
group.add_argument(
"--ext2",
type = __existant_file,
metavar = "FILE",
help = "input file (format ext2)"
)
group.add_argument(
"--paired",
nargs = 2,
type = __existant_file,
metavar = ("MATE_1", "MATE_2"),
help = "input file (two files format ext2)"
)

args = parser.parse_args()
print(args)

if args.ext1 is not None:
file = args.ext1
elif args.ext2 is not None:
file = args2.ext2
else:
file = args.paired[0]
file2 = args.paired[1]


Which is used as:



python script.py --ext1 file
OR
python script.py --ext2 file
OR
python script.py --paired file_1 file_2


Which is working but not really smooth. Do you have any lead of how I can improve the CLI parser ?










share|improve this question















I was wondering if there was a better way to implement my cli parser.



For context:
I need to pass to my script either a file in one of two formats or two files in a specific format.



It may be easier to understand like this:



file.ext1 OR file.ext2 OR (file_1.ext2 AND file_2.ext2)





I've used python argparse add_mutually_exclusive_group successfully and it looks like this:



import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"--ext1",
type = __existant_file,
metavar = "FILE",
help = "input file (format ext1)"
)
group.add_argument(
"--ext2",
type = __existant_file,
metavar = "FILE",
help = "input file (format ext2)"
)
group.add_argument(
"--paired",
nargs = 2,
type = __existant_file,
metavar = ("MATE_1", "MATE_2"),
help = "input file (two files format ext2)"
)

args = parser.parse_args()
print(args)

if args.ext1 is not None:
file = args.ext1
elif args.ext2 is not None:
file = args2.ext2
else:
file = args.paired[0]
file2 = args.paired[1]


Which is used as:



python script.py --ext1 file
OR
python script.py --ext2 file
OR
python script.py --paired file_1 file_2


Which is working but not really smooth. Do you have any lead of how I can improve the CLI parser ?







python console






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 4 at 14:22

























asked Sep 4 at 13:57









Plopp

112




112












  • @Graipher Well, I do believe this is exactly what I asked, my code is working but I'd like improvement. Maybe it's not clear tho, I'll rewrite a bit
    – Plopp
    Sep 4 at 14:03








  • 2




    @Graipher Oh I didn't though I needed a working example as I wasn't expecting any code back but more a general idea, I've edited it :)
    – Plopp
    Sep 4 at 14:20








  • 1




    We got a very good meta thread about how to improve question quality if you're interested :-)
    – Mast
    Sep 4 at 14:31










  • depending on the specific case, a simpler cli interface might be preferable: just pass one or two files as positional parameters (raise ArgumentError if given files in a wrong format). Using docopt, you would specify the alternatives on two/three lines. Here's a variant with the two line usage: command <file.ext1> $ command <file1.ext2> [<file2.ext2>]
    – jfs
    Sep 4 at 19:05


















  • @Graipher Well, I do believe this is exactly what I asked, my code is working but I'd like improvement. Maybe it's not clear tho, I'll rewrite a bit
    – Plopp
    Sep 4 at 14:03








  • 2




    @Graipher Oh I didn't though I needed a working example as I wasn't expecting any code back but more a general idea, I've edited it :)
    – Plopp
    Sep 4 at 14:20








  • 1




    We got a very good meta thread about how to improve question quality if you're interested :-)
    – Mast
    Sep 4 at 14:31










  • depending on the specific case, a simpler cli interface might be preferable: just pass one or two files as positional parameters (raise ArgumentError if given files in a wrong format). Using docopt, you would specify the alternatives on two/three lines. Here's a variant with the two line usage: command <file.ext1> $ command <file1.ext2> [<file2.ext2>]
    – jfs
    Sep 4 at 19:05
















@Graipher Well, I do believe this is exactly what I asked, my code is working but I'd like improvement. Maybe it's not clear tho, I'll rewrite a bit
– Plopp
Sep 4 at 14:03






@Graipher Well, I do believe this is exactly what I asked, my code is working but I'd like improvement. Maybe it's not clear tho, I'll rewrite a bit
– Plopp
Sep 4 at 14:03






2




2




@Graipher Oh I didn't though I needed a working example as I wasn't expecting any code back but more a general idea, I've edited it :)
– Plopp
Sep 4 at 14:20






@Graipher Oh I didn't though I needed a working example as I wasn't expecting any code back but more a general idea, I've edited it :)
– Plopp
Sep 4 at 14:20






1




1




We got a very good meta thread about how to improve question quality if you're interested :-)
– Mast
Sep 4 at 14:31




We got a very good meta thread about how to improve question quality if you're interested :-)
– Mast
Sep 4 at 14:31












depending on the specific case, a simpler cli interface might be preferable: just pass one or two files as positional parameters (raise ArgumentError if given files in a wrong format). Using docopt, you would specify the alternatives on two/three lines. Here's a variant with the two line usage: command <file.ext1> $ command <file1.ext2> [<file2.ext2>]
– jfs
Sep 4 at 19:05




depending on the specific case, a simpler cli interface might be preferable: just pass one or two files as positional parameters (raise ArgumentError if given files in a wrong format). Using docopt, you would specify the alternatives on two/three lines. Here's a variant with the two line usage: command <file.ext1> $ command <file1.ext2> [<file2.ext2>]
– jfs
Sep 4 at 19:05










1 Answer
1






active

oldest

votes

















up vote
0
down vote













Okay, I found a better way



import argparse
parser = argparse.ArgumentParser()

class RequiredLen(argparse.Action):
def __call__(self, parser, args, values, option_string=None):
if not 1 <= len(values) <= 2:
msg = f"argument {self.dest} requires 1 or 2 arguments"
raise argparse.ArgumentTypeError(msg)
setattr(args, self.dest, values)
# Just give me 1 or 2 files
parser.add_argument(
"--paired",
nargs = '+',
action = RequiredLen,
required = True,
help = "input file(s)"
)
# Specify either ext1 or ext2 format
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"--ext1",
action = "store_const",
dest = "format",
const = "ext1",
help = "input is ext1 format"
)
group.add_argument(
"--ext2",
action = "store_const",
dest = "format",
const = "ext2",
help = "input is ext2 format"
)
args = parser.parse_args()
print(args)


This way I can later just use something like:



def process_ext1(l: list):
print("ext1 file(s)")
for i in l:
print(i)

def process_ext2(l: list):
print("ext2 file(s)")
for i in l:
print(i)

process_format = { 'ext1': process_ext1, 'ext2': process_ext2 }
process_format[args.format](args.paired)





share|improve this answer





















    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f203104%2fpython-cli-parser-improvement-for-one-of-those-options%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    0
    down vote













    Okay, I found a better way



    import argparse
    parser = argparse.ArgumentParser()

    class RequiredLen(argparse.Action):
    def __call__(self, parser, args, values, option_string=None):
    if not 1 <= len(values) <= 2:
    msg = f"argument {self.dest} requires 1 or 2 arguments"
    raise argparse.ArgumentTypeError(msg)
    setattr(args, self.dest, values)
    # Just give me 1 or 2 files
    parser.add_argument(
    "--paired",
    nargs = '+',
    action = RequiredLen,
    required = True,
    help = "input file(s)"
    )
    # Specify either ext1 or ext2 format
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument(
    "--ext1",
    action = "store_const",
    dest = "format",
    const = "ext1",
    help = "input is ext1 format"
    )
    group.add_argument(
    "--ext2",
    action = "store_const",
    dest = "format",
    const = "ext2",
    help = "input is ext2 format"
    )
    args = parser.parse_args()
    print(args)


    This way I can later just use something like:



    def process_ext1(l: list):
    print("ext1 file(s)")
    for i in l:
    print(i)

    def process_ext2(l: list):
    print("ext2 file(s)")
    for i in l:
    print(i)

    process_format = { 'ext1': process_ext1, 'ext2': process_ext2 }
    process_format[args.format](args.paired)





    share|improve this answer

























      up vote
      0
      down vote













      Okay, I found a better way



      import argparse
      parser = argparse.ArgumentParser()

      class RequiredLen(argparse.Action):
      def __call__(self, parser, args, values, option_string=None):
      if not 1 <= len(values) <= 2:
      msg = f"argument {self.dest} requires 1 or 2 arguments"
      raise argparse.ArgumentTypeError(msg)
      setattr(args, self.dest, values)
      # Just give me 1 or 2 files
      parser.add_argument(
      "--paired",
      nargs = '+',
      action = RequiredLen,
      required = True,
      help = "input file(s)"
      )
      # Specify either ext1 or ext2 format
      group = parser.add_mutually_exclusive_group(required=True)
      group.add_argument(
      "--ext1",
      action = "store_const",
      dest = "format",
      const = "ext1",
      help = "input is ext1 format"
      )
      group.add_argument(
      "--ext2",
      action = "store_const",
      dest = "format",
      const = "ext2",
      help = "input is ext2 format"
      )
      args = parser.parse_args()
      print(args)


      This way I can later just use something like:



      def process_ext1(l: list):
      print("ext1 file(s)")
      for i in l:
      print(i)

      def process_ext2(l: list):
      print("ext2 file(s)")
      for i in l:
      print(i)

      process_format = { 'ext1': process_ext1, 'ext2': process_ext2 }
      process_format[args.format](args.paired)





      share|improve this answer























        up vote
        0
        down vote










        up vote
        0
        down vote









        Okay, I found a better way



        import argparse
        parser = argparse.ArgumentParser()

        class RequiredLen(argparse.Action):
        def __call__(self, parser, args, values, option_string=None):
        if not 1 <= len(values) <= 2:
        msg = f"argument {self.dest} requires 1 or 2 arguments"
        raise argparse.ArgumentTypeError(msg)
        setattr(args, self.dest, values)
        # Just give me 1 or 2 files
        parser.add_argument(
        "--paired",
        nargs = '+',
        action = RequiredLen,
        required = True,
        help = "input file(s)"
        )
        # Specify either ext1 or ext2 format
        group = parser.add_mutually_exclusive_group(required=True)
        group.add_argument(
        "--ext1",
        action = "store_const",
        dest = "format",
        const = "ext1",
        help = "input is ext1 format"
        )
        group.add_argument(
        "--ext2",
        action = "store_const",
        dest = "format",
        const = "ext2",
        help = "input is ext2 format"
        )
        args = parser.parse_args()
        print(args)


        This way I can later just use something like:



        def process_ext1(l: list):
        print("ext1 file(s)")
        for i in l:
        print(i)

        def process_ext2(l: list):
        print("ext2 file(s)")
        for i in l:
        print(i)

        process_format = { 'ext1': process_ext1, 'ext2': process_ext2 }
        process_format[args.format](args.paired)





        share|improve this answer












        Okay, I found a better way



        import argparse
        parser = argparse.ArgumentParser()

        class RequiredLen(argparse.Action):
        def __call__(self, parser, args, values, option_string=None):
        if not 1 <= len(values) <= 2:
        msg = f"argument {self.dest} requires 1 or 2 arguments"
        raise argparse.ArgumentTypeError(msg)
        setattr(args, self.dest, values)
        # Just give me 1 or 2 files
        parser.add_argument(
        "--paired",
        nargs = '+',
        action = RequiredLen,
        required = True,
        help = "input file(s)"
        )
        # Specify either ext1 or ext2 format
        group = parser.add_mutually_exclusive_group(required=True)
        group.add_argument(
        "--ext1",
        action = "store_const",
        dest = "format",
        const = "ext1",
        help = "input is ext1 format"
        )
        group.add_argument(
        "--ext2",
        action = "store_const",
        dest = "format",
        const = "ext2",
        help = "input is ext2 format"
        )
        args = parser.parse_args()
        print(args)


        This way I can later just use something like:



        def process_ext1(l: list):
        print("ext1 file(s)")
        for i in l:
        print(i)

        def process_ext2(l: list):
        print("ext2 file(s)")
        for i in l:
        print(i)

        process_format = { 'ext1': process_ext1, 'ext2': process_ext2 }
        process_format[args.format](args.paired)






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Sep 4 at 16:01









        Plopp

        112




        112






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f203104%2fpython-cli-parser-improvement-for-one-of-those-options%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Список кардиналов, возведённых папой римским Каликстом III

            Deduzione

            Mysql.sock missing - “Can't connect to local MySQL server through socket”