pythonpython-3.xargparseignore-case

Case insensitive argparse choices without losing case information in choices list


I have a very similar question but the solutions provided do not fit, since I have a slightly different use case. For the sake of simplicity I'll adapt my question to the one already answered.

Is it possible to check argparse choices in case-insensitive manner, without losing the case-sensitive information?

import argparse
choices = ["ADASYN", "BorderlineSMOTE", "KMeansSMOTE"]
parser = argparse.ArgumentParser()
parser.add_argument("-p", choices=choices)
print(parser.parse_args(["-p", "adasyn"]))

this leads to:

choices.py: error: argument -p: invalid choice: 'adasyn' (choose from 'ADASYN', 'BorderlineSMOTE', 'KMeansSMOTE')

I obviously could use type.lower, but then my list of choices needs to be in lower case and I lose a lot of readability for -h (help output). On the other hand I want my user to be able to use different casing as long as the string .lower() for both is the same.

The solution provided by @hpaulj (similar question) does not work in my case. It only enables the argument input to have arbitrary casing. So if my choices list is lowercase AdAsyN would work, but if I keep the choices list in CamelCase, the uppercase letters in the choices list have to be uppercase for the argument as well.

Does anyone have a solution for me, where I can keep the casing of my choices list, while completely ignoring casing for the user input?


Solution

  • You can customize type function to perform search in choices list instead simple type conversion. This looks a little bit hackish but works OK.

    def make_type(choices):
        def find_choice(choice):
            for key, item in enumerate([choice.lower() for choice in choices]):
                if choice.lower() == item:
                    return choices[key]
            else:
                return choice
        return find_choice
    
    choices = ["ADASYN", "BorderlineSMOTE", "KMeansSMOTE"]    
    parser = argparse.ArgumentParser()
    parser.add_argument("-p", choices=choices, type=make_type(choices))
    
    
    print(parser.parse_args("-p adasyn".split()))
    # Namespace(p='ADASYN')
    
    print(parser.parse_args("-p ADASYN".split()))
    # Namespace(p='ADASYN')
    
    print(parser.parse_args("-p aDaSyN".split()))
    # Namespace(p='ADASYN')
    
    print(parser.parse_args("-p adasyn1".split()))
    # usage: argsaction.py [-h] [-p {ADASYN,BorderlineSMOTE,KMeansSMOTE}]
    # argsaction.py: error: argument -p: invalid choice: 'adasyn1' (choose from 'ADASYN', 'BorderlineSMOTE', 'KMeansSMOTE')
    

    Closure function taken from this answer.
    Answer based on @hpaulj comment.