2 votes

Comment ajouter des options positionnelles pour des arguments existants dans argparse

J'ai affaire à un (Python 3.x) script (écrit par quelqu'un d'autre) où l'entrée et la sortie sont actuellement spécifiées avec des arguments optionnels marqués comme ceci :

parser.add_argument('-i', '--input', nargs='?', type = argparse.FileType('r'),
                    default=sys.stdin, dest='inputfile')
parser.add_argument('-o', '--output-file', nargs='?', type=argparse.FileType('w'),
                    default=sys.stdout, dest='outputfile')

J'aimerais mettre à jour ce script afin que le fichier d'entrée et le fichier de sortie puissent être spécifiés en tant qu'arguments positionnels tout en conservant les arguments drapeau existants pour une compatibilité ascendante. J'aimerais également gérer intelligemment les conflits potentiels qui pourraient résulter du mélange de l'argument drapeau avec l'argument positionnel (c'est-à-dire que si un seul des arguments de l'option -i o -o est donné, alors un seul argument positionnel est automatiquement passé à l'autre et deux arguments positionnels soulèvent une erreur de redondance alors que si les deux -i y -o sont donnés, alors tout argument positionnel soulève l'erreur de redondance).

Note : le script tel qu'il est écrit actuellement n'accepte pas d'arguments positionnels, bien qu'il accepte d'autres drapeaux, certains avec des arguments d'autres sans, en plus de ceux liés au fichier d'entrée et de sortie.

Est-ce possible avec argparse (et si oui, comment) ou dois-je réécrire l'analyse des arguments en utilisant autre chose (et si oui, que suggérez-vous) ?

1voto

hpaulj Points 6132

S'en tenir à la FileType sera gênante. C'est type ouvre ou crée le fichier. Vous aurez donc potentiellement 4 fichiers ouverts alors que vous n'en voulez que 2. Mais si l'un de ces fichiers est stdin o out vous ne voulez pas le fermer. Et vous ne pouvez pas gérer un positional qui peut être soit en lecture soit en écriture selon les autres arguments donnés.

Vous pourriez essayer de définir 4 arguments de chaîne par défaut, 2 marqués, et 2 nargs='?' positionnel. Donnez-leur différents dest . Vous pouvez ensuite appliquer votre intelligence aux 4 valeurs possibles. La valeur par défaut None devrait être une indication suffisamment claire qu'une valeur n'a pas été fournie. Une fois que vous avez choisi les deux noms de fichiers, vous pouvez les ouvrir et les utiliser. Les versions récentes de Python recommandent d'utiliser with bien que cela puisse être gênant lorsqu'un fichier est déjà ouvert (par ex. sys.stdin ).

Je ne pense pas que vous devriez essayer d'implémenter cette logique au sein de argparse . Faites-le après l'analyse syntaxique.

1voto

rpspringuel Points 189

Pour ceux qui souhaitent une réponse plus explicite, voici comment j'ai finalement mis en œuvre la suggestion de @hpaulj :

J'ai d'abord défini un groupe d'arguments pour les arguments des fichiers d'entrée et de sortie :

files = parser.add_argument_group('file arguments:',description='These arguments can also be provided as postional arguments, in which case the input file comes first.')
files.add_argument('-i', '--input', nargs='?',
                    help='Source of the words to be syllabified.  If None or -, then input will be read from stdin.',
                    dest='inputfile')
files.add_argument('-o', '--output-file', nargs='?',
                    help='Destination of the syllabified words.  If None or -, then ouput will be written to stdout.',
                    dest='outputfile')
files.add_argument('fileone',nargs='?',
                     help=argparse.SUPPRESS)
files.add_argument('filetwo',nargs='?',
                     help=argparse.SUPPRESS)

Cela m'a permis de garder les arguments ensemble, séparément des autres arguments du programme, et m'a donné un peu plus de contrôle sur la façon dont le texte d'aide apparaîtrait pour qu'il ait le plus de sens possible.

Ensuite, après avoir analysé les arguments ( args = parser.parse_args() ), j'ai ajouté la logique suivante pour déterminer quelles étaient les entrées et sorties correctes et pour ouvrir les fichiers ou stdin et stdout comme il convient :

if (args.inputfile == None or args.inputfile == '-'):
    if (args.outputfile == None or args.outputfile == '-'):
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = sys.stdin
            output = sys.stdout
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            try:
                input = open(args.fileone,'r')
                output = sys.stdout
            except:
                input = sys.stdin
                output = open(args.fileone,'w')
        else:
            input = open(args.fileone,'r')
            output = open(args.filetwo,'w')
    else:
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = sys.stdin
            output = open(args.outputfile,'w')
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.fileone,'r')
            output = open(args.outputfile,'w')
        else:
            print("Error: too many files")
            print("Both -o and positional output file given")
            sys.exit(1)
else:
    if (args.outputfile == None or args.outputfile == '-'):
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.inputfile,'r')
            output = sys.stdout
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.inputfile,'r')
            output = open(args.fileone,'w')
        else:
            print("Error: too many files")
            print("Both -i and positional input file give")
            sys.exit(1)
    else:
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.inputfile,'r')
            output = open(args.outputfile,'w')
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            print("Error: too many files")
            print("Both -i and -o given with a positional file")
            sys.exit(1)
        else:
            print("Error: too many files")
            print("Both -i and -o given with positional files")
            sys.exit(1)

Comme vous pouvez le voir, j'ai décidé d'accepter à la fois la version par défaut de l'option None y - comme possibilités de se référer à stdin/stdout. Cela reproduit le comportement de la fonction FileType qui a également accepté - de cette manière.

La seule ambiguïté qui subsiste est que "None" (c'est-à-dire une chaîne de caractères du mot "None") n'est pas la même chose que None (c'est-à-dire la classe NoneType) et les messages d'aide pourraient être interprétés comme impliquant que -i None doit faire référence à stdin. Cependant, je me suis dit que la différence ici devrait être suffisamment évidente pour la plupart des utilisateurs de python pour que je ne complique pas davantage la logique pour tenir compte de cette possibilité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X