Débloquez le potentiel d'argparse de Python : sous-commandes et actions personnalisées. Concevez des interfaces CLI avancées, améliorant l'expérience utilisateur.
Argparse Python Avancé : Maîtriser les sous-commandes et les classes d'action personnalisées
Le module argparse
de Python est un outil puissant pour créer des interfaces de ligne de commande (CLI). Si son utilisation de base est relativement simple, argparse
offre des fonctionnalités avancées qui permettent de créer des CLI sophistiquées et conviviales. Ce billet de blog explore deux de ces fonctionnalités avancées : les sous-commandes et les classes d'action personnalisées.
Pourquoi Argparse Avancé ?
Pour les scripts simples avec seulement quelques options, les fonctionnalités de base d'argparse
peuvent suffire. Cependant, à mesure que vos scripts gagnent en complexité et en fonctionnalités, une CLI plus structurée et organisée devient essentielle. Les fonctionnalités avancées d'argparse
aident Ă :
- Améliorer l'expérience utilisateur : Fournir une interface claire et intuitive aux utilisateurs.
- Améliorer la maintenabilité du code : Organiser votre code en modules logiques, le rendant plus facile à comprendre et à maintenir.
- Augmenter les fonctionnalités : Supporter des flux de travail complexes et de multiples opérations au sein d'un seul script.
- Promouvoir la réutilisabilité : Créer des composants réutilisables qui peuvent être appliqués à différentes parties de votre application.
Sous-commandes : Organiser les CLI complexes
Les sous-commandes sont un moyen de regrouper des commandes connexes sous une seule commande principale. C'est particulièrement utile pour les applications qui effectuent une variété de tâches distinctes. Pensez à Git, par exemple. Il utilise les sous-commandes de manière extensive : git commit
, git push
, git pull
, et ainsi de suite. Chaque sous-commande a son propre ensemble d'arguments et d'options.
Implémentation des sous-commandes avec argparse
Pour implémenter des sous-commandes avec argparse
, vous utilisez la méthode add_subparsers()
. Voici un exemple de base :
import argparse
# Create the main parser
parser = argparse.ArgumentParser(description='Un exemple simple avec des sous-commandes')
# Create the subparser
subparsers = parser.add_subparsers(dest='command', help='Commandes disponibles')
# Create the 'add' subcommand
add_parser = subparsers.add_parser('add', help='Additionner deux nombres')
add_parser.add_argument('x', type=int, help='Premier nombre')
add_parser.add_argument('y', type=int, help='Deuxième nombre')
# Create the 'subtract' subcommand
subtract_parser = subparsers.add_parser('subtract', help='Soustraire deux nombres')
subtract_parser.add_argument('x', type=int, help='Premier nombre')
subtract_parser.add_argument('y', type=int, help='Deuxième nombre')
# Parse the arguments
args = parser.parse_args()
# Perform the action based on the subcommand
if args.command == 'add':
result = args.x + args.y
print(f'La somme est : {result}')
elif args.command == 'subtract':
result = args.x - args.y
print(f'La différence est : {result}')
else:
parser.print_help()
Dans cet exemple :
- Nous créons un analyseur principal à l'aide de
argparse.ArgumentParser()
. - Nous ajoutons un sous-analyseur Ă l'aide de
parser.add_subparsers(dest='command', help='Commandes disponibles')
. L'argumentdest
spécifie l'attribut qui stockera le nom de la sous-commande choisie. - Nous créons deux sous-commandes, 'add' et 'subtract', à l'aide de
subparsers.add_parser()
. - Chaque sous-commande a son propre ensemble d'arguments (
x
ety
). - Nous analysons les arguments Ă l'aide de
parser.parse_args()
. - Nous vérifions la valeur de
args.command
pour déterminer quelle sous-commande a été choisie, puis effectuons l'action correspondante.
Pour exécuter ce script, vous utiliseriez des commandes comme :
python your_script.py add 5 3
python your_script.py subtract 10 2
Techniques avancées de sous-commandes
1. Utilisation de fonctions pour gérer les sous-commandes
Une approche plus organisée consiste à définir des fonctions distinctes pour gérer chaque sous-commande. Cela améliore la lisibilité et la maintenabilité du code.
import argparse
def add(args):
result = args.x + args.y
print(f'La somme est : {result}')
def subtract(args):
result = args.x - args.y
print(f'La différence est : {result}')
# Create the main parser
parser = argparse.ArgumentParser(description='Un exemple simple avec des sous-commandes')
# Create the subparser
subparsers = parser.add_subparsers(dest='command', help='Commandes disponibles')
# Create the 'add' subcommand
add_parser = subparsers.add_parser('add', help='Additionner deux nombres')
add_parser.add_argument('x', type=int, help='Premier nombre')
add_parser.add_argument('y', type=int, help='Deuxième nombre')
add_parser.set_defaults(func=add)
# Create the 'subtract' subcommand
subtract_parser = subparsers.add_parser('subtract', help='Soustraire deux nombres')
subtract_parser.add_argument('x', type=int, help='Premier nombre')
subtract_parser.add_argument('y', type=int, help='Deuxième nombre')
subtract_parser.set_defaults(func=subtract)
# Parse the arguments
args = parser.parse_args()
# Call the function associated with the subcommand
if hasattr(args, 'func'):
args.func(args)
else:
parser.print_help()
Ici, nous utilisons set_defaults(func=...)
pour associer une fonction à chaque sous-commande. Ensuite, après l'analyse, nous appelons la fonction appropriée si elle existe.
2. Sous-commandes imbriquées
Vous pouvez imbriquer des sous-commandes pour créer des CLI encore plus complexes et hiérarchiques. Par exemple, considérez une CLI pour la gestion des ressources cloud :
cloud compute instance create --name my-instance --region us-east-1
cloud storage bucket list --project my-project
Dans cet exemple, cloud
est la commande principale, compute
et storage
sont des sous-commandes, et instance
et bucket
sont des sous-sous-commandes.
3. Exemple concret : Outil d'internationalisation
Imaginez un outil pour gérer les traductions dans une application multilingue. Vous pourriez utiliser des sous-commandes pour organiser les différentes opérations :
translation tool add-language --code fr_FR --name "French (France)"
translation tool extract-strings --source-dir src
translation tool translate --target-language es_ES --source-file strings.pot
Cette approche permet une séparation claire des préoccupations et rend l'outil plus facile à utiliser et à maintenir, surtout lorsqu'il s'agit de nombreuses langues et de flux de travail de traduction applicables dans différents pays.
Classes d'action personnalisées : Adapter l'analyse des arguments
argparse
vous permet de définir des classes d'action personnalisées pour adapter la manière dont les arguments sont traités. C'est utile pour les scénarios où le comportement par défaut de traitement des arguments n'est pas suffisant. Une classe d'action est une classe qui hérite de argparse.Action
et surcharge la méthode __call__
.
Création d'une classe d'action personnalisée
Voici un exemple de classe d'action personnalisée qui convertit un argument en majuscules :
import argparse
class ToUpper(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if isinstance(values, list):
setattr(namespace, self.dest, [v.upper() for v in values])
else:
setattr(namespace, self.dest, values.upper())
# Create the parser
parser = argparse.ArgumentParser(description='Exemple avec action personnalisée')
# Add an argument with the custom action
parser.add_argument('--name', action=ToUpper, help='Nom Ă convertir en majuscules')
parser.add_argument('--cities', action=ToUpper, nargs='+', help='Liste de villes Ă convertir en majuscules')
# Parse the arguments
args = parser.parse_args()
# Print the result
if args.name:
print(f'Nom : {args.name}')
if args.cities:
print(f'Villes : {args.cities}')
Dans cet exemple :
- Nous définissons une classe
ToUpper
qui hérite deargparse.Action
. - La méthode
__call__
est appelée lorsque l'argument est rencontré. Elle prend les arguments suivants :parser
: L'objetArgumentParser
.namespace
: L'objet d'espace de noms où les arguments analysés sont stockés.values
: La ou les valeurs de l'argument.option_string
: La chaîne d'option qui a été utilisée pour invoquer cette action (par exemple,--name
).
- À l'intérieur de la méthode
__call__
, nous convertissons la valeur en majuscules Ă l'aide devalues.upper()
et la stockons dans l'espace de noms Ă l'aide desetattr(namespace, self.dest, values.upper())
. - Nous ajoutons un argument Ă l'analyseur Ă l'aide de
parser.add_argument('--name', action=ToUpper, help='Nom Ă convertir en majuscules')
. Nous spécifions l'argumentaction
comme notre classe d'action personnalisée.
Pour exécuter ce script, vous utiliseriez des commandes comme :
python your_script.py --name john
python your_script.py --cities london paris tokyo
Cas d'utilisation des classes d'action personnalisées
1. Validation des entrées
Vous pouvez utiliser des classes d'action personnalisées pour valider les entrées et lever une erreur si l'entrée est invalide. Par exemple, vous pourriez créer une classe d'action qui vérifie si un fichier existe ou si un nombre se situe dans une plage spécifique.
import argparse
import os
class FileMustExist(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if not os.path.exists(values):
raise argparse.ArgumentTypeError(f'Fichier introuvable : {values}')
setattr(namespace, self.dest, values)
parser = argparse.ArgumentParser(description='Exemple de validation de l\'existence d\'un fichier.')
parser.add_argument('--input-file', action=FileMustExist, help='Chemin vers le fichier d\'entrée.')
args = parser.parse_args()
print(f'Fichier d\'entrée : {args.input_file}')
2. Conversion d'unités
Vous pouvez utiliser des classes d'action personnalisées pour convertir des unités. Par exemple, vous pourriez créer une classe d'action qui convertit les températures de Celsius en Fahrenheit.
3. Gestion de structures de données complexes
Si vous avez besoin d'analyser des arguments en structures de données complexes (par exemple, des dictionnaires, des listes d'objets), vous pouvez utiliser des classes d'action personnalisées pour gérer la logique d'analyse.
4. Exemple : Gestion des fuseaux horaires
Considérez une application qui doit gérer les dates et les heures dans différents fuseaux horaires. Une classe d'action personnalisée pourrait être utilisée pour analyser une chaîne de date et la convertir en un fuseau horaire spécifique à l'aide de bibliothèques comme pytz
.
import argparse
import datetime
import pytz
class TimeZoneConverter(argparse.Action):
def __init__(self, option_strings, dest, timezone=None, **kwargs):
super().__init__(option_strings, dest, **kwargs)
self.timezone = timezone
def __call__(self, parser, namespace, values, option_string=None):
try:
dt = datetime.datetime.fromisoformat(values)
if self.timezone:
tz = pytz.timezone(self.timezone)
dt = tz.localize(dt)
setattr(namespace, self.dest, dt)
except ValueError:
raise argparse.ArgumentTypeError(f"Format date/heure invalide. Utilisez le format ISO (AAAA-MM-JJTHH:MM:SS) : {values}")
except pytz.exceptions.UnknownTimeZoneError:
raise argparse.ArgumentTypeError(f"Fuseau horaire invalide : {self.timezone}")
parser = argparse.ArgumentParser(description='Exemple avec conversion de fuseau horaire.')
parser.add_argument('--event-time', action=TimeZoneConverter, timezone='America/Los_Angeles', help='Heure de l\'événement au format ISO (AAAA-MM-JJTHH:MM:SS). Convertit vers le fuseau horaire America/Los_Angeles.')
args = parser.parse_args(['--event-time', '2024-10-27T10:00:00'])
print(f'Heure de l\'événement (Los Angeles) : {args.event_time}')
Cet exemple montre comment les actions personnalisées peuvent gérer les conversions de fuseaux horaires à l'aide de la bibliothèque pytz
, démontrant une utilisation plus sophistiquée et pertinente à l'échelle mondiale.
Meilleures pratiques pour l'utilisation d'Argparse Avancé
- Restez simple : Ne compliquez pas inutilement votre CLI. Utilisez les sous-commandes et les actions personnalisées uniquement lorsque cela est nécessaire.
- Fournissez des messages d'aide clairs : Rédigez des messages d'aide clairs et concis pour chaque commande et argument. Utilisez l'argument
help
dansadd_argument()
de manière extensive. - Validez les entrées : Validez toujours les entrées utilisateur pour prévenir les erreurs et les vulnérabilités de sécurité.
- Utilisez des conventions de nommage cohérentes : Suivez des conventions de nommage cohérentes pour les commandes, les arguments et les options. Envisagez d'utiliser le kebab-case (
--my-option
) pour les noms d'options longs. - Testez minutieusement : Testez votre CLI minutieusement avec différentes entrées et scénarios.
- Documentez votre CLI : Fournissez une documentation complète pour votre CLI, y compris des exemples d'utilisation de chaque commande et argument. Des outils comme Sphinx peuvent générer de la documentation à partir de votre code.
- Pensez à la localisation : Si votre CLI est destinée à un public mondial, envisagez de localiser vos messages d'aide et tout autre texte destiné à l'utilisateur.
Conclusion
Les sous-commandes et les classes d'action personnalisées sont des outils puissants pour créer des CLI sophistiquées et conviviales avec argparse
. En maîtrisant ces fonctionnalités avancées, vous pouvez construire des applications en ligne de commande robustes, maintenables et évolutives qui répondent aux besoins d'une base d'utilisateurs diversifiée. De la gestion d'applications multilingues à la gestion des fuseaux horaires à travers le monde, les possibilités sont vastes. Adoptez ces techniques pour élever votre scripting Python et le développement d'outils en ligne de commande au niveau supérieur.