How to add multiple argparse arguments

PythonPythonBeginner
Practice Now

Introduction

This tutorial explores how to add and manage multiple command-line arguments in Python using the argparse module. Command-line arguments allow users to customize how programs run without modifying the code itself. The argparse module simplifies this process by providing tools to define, validate, and parse these arguments.

By the end of this tutorial, you will understand how to create Python scripts that can accept various input parameters, making your applications more flexible and user-friendly.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ControlFlowGroup(["Control Flow"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python/ControlFlowGroup -.-> python/conditional_statements("Conditional Statements") python/FunctionsGroup -.-> python/arguments_return("Arguments and Return Values") python/FunctionsGroup -.-> python/default_arguments("Default Arguments") python/FunctionsGroup -.-> python/keyword_arguments("Keyword Arguments") python/FunctionsGroup -.-> python/build_in_functions("Build-in Functions") subgraph Lab Skills python/conditional_statements -.-> lab-451011{{"How to add multiple argparse arguments"}} python/arguments_return -.-> lab-451011{{"How to add multiple argparse arguments"}} python/default_arguments -.-> lab-451011{{"How to add multiple argparse arguments"}} python/keyword_arguments -.-> lab-451011{{"How to add multiple argparse arguments"}} python/build_in_functions -.-> lab-451011{{"How to add multiple argparse arguments"}} end

Understanding Argparse Basics

The first step in working with command-line arguments is understanding the basics of the argparse module.

What is Argparse?

Argparse is a built-in Python module that makes it easy to write user-friendly command-line interfaces. It automatically generates help messages, handles errors, and converts command-line arguments to the appropriate data types for your program.

Creating Your First Script with Argparse

Let's start by creating a simple Python script that uses argparse. We'll create a file named hello.py that accepts a name as a command-line argument.

  1. Open the VSCode editor and create a new file by clicking "File" > "New File"
  2. Save the file as hello.py in the /home/labex/project directory
  3. Add the following code to the file:
import argparse

## Create the parser
parser = argparse.ArgumentParser(description='A simple greeting program')

## Add an argument
parser.add_argument('--name', type=str, default='World', help='Name to greet')

## Parse the arguments
args = parser.parse_args()

## Use the argument
print(f"Hello, {args.name}!")

This script demonstrates the three main steps for using argparse:

  1. Creating an ArgumentParser object
  2. Adding arguments to the parser
  3. Parsing the command-line arguments

Running Your Script

Let's run the script to see how it works:

  1. Open a terminal in the VSCode interface (Terminal > New Terminal)
  2. Run the script with the following command:
python3 hello.py

You should see this output:

Hello, World!

Now let's try passing a name:

python3 hello.py --name Alice

Output:

Hello, Alice!

Understanding Help Messages

One of the benefits of argparse is that it automatically generates help messages. Try running:

python3 hello.py --help

You should see output similar to:

usage: hello.py [-h] [--name NAME]

A simple greeting program

options:
  -h, --help   show this help message and exit
  --name NAME  Name to greet

This help message is automatically generated based on the description we provided when creating the parser and the help text we added to our argument.

Key Components of Argparse

Let's examine the main components in our script:

  • parser = argparse.ArgumentParser(description='...'): Creates a parser with a description
  • parser.add_argument('--name', ...): Adds an argument with the name '--name'
  • type=str: Specifies that the argument should be a string
  • default='World': Sets a default value if the argument isn't provided
  • help='Name to greet': Provides help text for the argument
  • args = parser.parse_args(): Parses the command-line arguments
  • args.name: Accesses the value of the 'name' argument

Now you understand the basics of using argparse to handle command-line arguments in Python.

Adding Multiple Argument Types

Now that you understand the basics, let's explore different types of arguments that can be added to your command-line interface.

Types of Arguments

Argparse supports several types of arguments:

  1. Positional arguments: Required arguments identified by their position
  2. Optional arguments: Arguments prefixed with -- (or -) that may have default values
  3. Flag arguments: Boolean arguments that are either present or absent
  4. Arguments with choices: Arguments limited to specific values

Let's create a new script that demonstrates these different argument types.

  1. Create a new file named calculator.py in the /home/labex/project directory
  2. Add the following code:
import argparse

## Create the parser
parser = argparse.ArgumentParser(description='A simple calculator')

## Add a positional argument
parser.add_argument('operation',
                    choices=['add', 'subtract', 'multiply', 'divide'],
                    help='Math operation to perform')

## Add optional arguments
parser.add_argument('--num1', type=float, required=True,
                    help='First number')
parser.add_argument('--num2', type=float, required=True,
                    help='Second number')

## Add a flag argument
parser.add_argument('--verbose', action='store_true',
                    help='Enable verbose output')

## Parse the arguments
args = parser.parse_args()

## Perform the calculation based on the operation
result = 0
if args.operation == 'add':
    result = args.num1 + args.num2
elif args.operation == 'subtract':
    result = args.num1 - args.num2
elif args.operation == 'multiply':
    result = args.num1 * args.num2
elif args.operation == 'divide':
    if args.num2 == 0:
        print("Error: Cannot divide by zero")
        exit(1)
    result = args.num1 / args.num2

## Display the result
if args.verbose:
    print(f"The result of {args.num1} {args.operation} {args.num2} is: {result}")
else:
    print(f"Result: {result}")

Understanding the New Arguments

Let's examine the new argument types in this script:

  1. Positional argument: operation - must be one of 'add', 'subtract', 'multiply', or 'divide'

    parser.add_argument('operation',
                        choices=['add', 'subtract', 'multiply', 'divide'],
                        help='Math operation to perform')
  2. Optional arguments with required=True: --num1 and --num2 - must be provided by the user

    parser.add_argument('--num1', type=float, required=True,
                        help='First number')
  3. Flag argument: --verbose - when present, enables verbose output

    parser.add_argument('--verbose', action='store_true',
                        help='Enable verbose output')

Testing the Calculator

Run the calculator with different arguments to see how it works:

python3 calculator.py add --num1 5 --num2 3

Output:

Result: 8.0

Try with the verbose flag:

python3 calculator.py multiply --num1 4 --num2 7 --verbose

Output:

The result of 4.0 multiply 7.0 is: 28.0

Try the division operation:

python3 calculator.py divide --num1 10 --num2 2

Output:

Result: 5.0

If you forget a required argument, argparse will show an error:

python3 calculator.py add --num1 5

Output:

usage: calculator.py [-h] [--num1 NUM1] [--num2 NUM2] [--verbose]
                    {add,subtract,multiply,divide}
calculator.py: error: the following arguments are required: --num2

Key Points About Argument Types

  • Positional arguments don't need a prefix and are identified by their position
  • The choices parameter restricts the valid values for an argument
  • The required=True parameter makes an optional argument mandatory
  • The action='store_true' parameter creates a flag that's False by default and True when specified
  • The type parameter converts the input to the specified type (float in our example)

Now you understand how to use different types of arguments with argparse.

Advanced Argument Configurations

Now let's explore more advanced configurations for arguments, including:

  1. Arguments that accept multiple values
  2. Arguments with custom validation
  3. Arguments with default values
  4. Arguments with short names (single-letter aliases)

Creating a File Processing Script

Let's create a script that demonstrates these advanced configurations by building a file processing utility.

  1. Create a new file named file_processor.py in the /home/labex/project directory
  2. Add the following code:
import argparse
import os

def validate_file(filename):
    """Validate that the file exists."""
    if not os.path.exists(filename):
        raise argparse.ArgumentTypeError(f"File {filename} does not exist")
    return filename

## Create the parser
parser = argparse.ArgumentParser(description='Process text files')

## Multiple values
parser.add_argument('-f', '--files',
                    type=validate_file,
                    nargs='+',
                    help='Input files to process')

## Argument with short name
parser.add_argument('-o', '--output',
                    default='output.txt',
                    help='Output file (default: output.txt)')

## Choices with default
parser.add_argument('-m', '--mode',
                    choices=['read', 'count', 'stats'],
                    default='read',
                    help='Processing mode (default: read)')

## Custom validation for a positive integer
def positive_int(value):
    ivalue = int(value)
    if ivalue <= 0:
        raise argparse.ArgumentTypeError(f"{value} is not a positive integer")
    return ivalue

parser.add_argument('-l', '--lines',
                    type=positive_int,
                    default=10,
                    help='Number of lines to process (default: 10)')

## Parse arguments
args = parser.parse_args()

## Process files based on mode
for file in args.files if args.files else []:
    print(f"Processing file: {file}")

    with open(file, 'r') as f:
        content = f.readlines()

        if args.mode == 'read':
            ## Read mode: output first N lines
            print(f"First {args.lines} lines:")
            for i, line in enumerate(content[:args.lines]):
                print(f"{i+1}: {line.strip()}")

        elif args.mode == 'count':
            ## Count mode: count lines, words, chars
            line_count = len(content)
            word_count = sum(len(line.split()) for line in content)
            char_count = sum(len(line) for line in content)

            print(f"Lines: {line_count}")
            print(f"Words: {word_count}")
            print(f"Characters: {char_count}")

        elif args.mode == 'stats':
            ## Stats mode: line length statistics
            if content:
                line_lengths = [len(line.strip()) for line in content]
                avg_length = sum(line_lengths) / len(line_lengths)
                max_length = max(line_lengths)
                min_length = min(line_lengths)

                print(f"Average line length: {avg_length:.2f}")
                print(f"Shortest line: {min_length} characters")
                print(f"Longest line: {max_length} characters")
            else:
                print("File is empty")

    print("-" * 30)

print(f"Results will be saved to: {args.output}")
## Note: In a real application, we would actually write to the output file

Understanding the Advanced Configurations

Let's examine the advanced argument configurations:

  1. Multiple values with nargs='+':

    parser.add_argument('-f', '--files',
                        type=validate_file,
                        nargs='+',
                        help='Input files to process')

    The nargs='+' parameter allows the user to provide one or more file arguments.

  2. Custom validation function:

    def validate_file(filename):
        if not os.path.exists(filename):
            raise argparse.ArgumentTypeError(f"File {filename} does not exist")
        return filename

    This function checks if the specified file exists before proceeding.

  3. Short aliases with -o:

    parser.add_argument('-o', '--output',
                        default='output.txt',
                        help='Output file (default: output.txt)')

    The -o provides a shorter way to specify the --output argument.

  4. Choices with default value:

    parser.add_argument('-m', '--mode',
                        choices=['read', 'count', 'stats'],
                        default='read',
                        help='Processing mode (default: read)')

    This limits the mode to specific values while providing a default.

Creating Sample Test Files

Let's create two sample files to test our script:

  1. Create a sample file named sample1.txt:
echo -e "This is the first line.\nThis is the second line.\nThis is the third line.\nThis is the fourth line.\nThis is the fifth line." > /home/labex/project/sample1.txt
  1. Create another sample file named sample2.txt:
echo -e "Lorem ipsum dolor sit amet.\nConsectetur adipiscing elit.\nSed do eiusmod tempor incididunt.\nUt labore et dolore magna aliqua." > /home/labex/project/sample2.txt

Testing the File Processor

Now let's run the script with different arguments:

  1. Basic usage with one file:
python3 file_processor.py -f sample1.txt

Output:

Processing file: sample1.txt
First 10 lines:
1: This is the first line.
2: This is the second line.
3: This is the third line.
4: This is the fourth line.
5: This is the fifth line.
------------------------------
Results will be saved to: output.txt
  1. Process multiple files:
python3 file_processor.py -f sample1.txt sample2.txt

Output:

Processing file: sample1.txt
First 10 lines:
1: This is the first line.
2: This is the second line.
3: This is the third line.
4: This is the fourth line.
5: This is the fifth line.
------------------------------
Processing file: sample2.txt
First 10 lines:
1: Lorem ipsum dolor sit amet.
2: Consectetur adipiscing elit.
3: Sed do eiusmod tempor incididunt.
4: Ut labore et dolore magna aliqua.
------------------------------
Results will be saved to: output.txt
  1. Use the count mode:
python3 file_processor.py -f sample1.txt -m count

Output:

Processing file: sample1.txt
Lines: 5
Words: 25
Characters: 135
------------------------------
Results will be saved to: output.txt
  1. Use the stats mode:
python3 file_processor.py -f sample2.txt -m stats

Output:

Processing file: sample2.txt
Average line length: 29.25
Shortest line: 22 characters
Longest line: 37 characters
------------------------------
Results will be saved to: output.txt
  1. Limit the number of lines:
python3 file_processor.py -f sample1.txt -l 2

Output:

Processing file: sample1.txt
First 2 lines:
1: This is the first line.
2: This is the second line.
------------------------------
Results will be saved to: output.txt

Key Points About Advanced Configurations

  • nargs='+' allows for multiple argument values
  • Custom validation functions help ensure valid input
  • Short argument names (single-letter aliases) provide convenience
  • Default values simplify usage for common scenarios
  • The choices parameter restricts input to valid options

Now you have learned how to configure advanced argument options in your command-line scripts.

Building a Complete Command-Line Tool

In this step, we'll combine everything we've learned to build a complete command-line tool. Let's create a simple task manager that allows users to add, list, and delete tasks using command-line arguments.

Creating the Task Manager

  1. Create a new file named task_manager.py in the /home/labex/project directory
  2. Add the following code:
import argparse
import json
import os

## File to store tasks
TASKS_FILE = "/home/labex/project/tasks.json"

def load_tasks():
    """Load tasks from the JSON file."""
    if os.path.exists(TASKS_FILE):
        with open(TASKS_FILE, 'r') as f:
            try:
                return json.load(f)
            except json.JSONDecodeError:
                return []
    return []

def save_tasks(tasks):
    """Save tasks to the JSON file."""
    with open(TASKS_FILE, 'w') as f:
        json.dump(tasks, f, indent=2)

def main():
    ## Create the main parser
    parser = argparse.ArgumentParser(description='Task Manager CLI')
    subparsers = parser.add_subparsers(dest='command', help='Command to run')

    ## Add command
    add_parser = subparsers.add_parser('add', help='Add a new task')
    add_parser.add_argument('title', help='Task title')
    add_parser.add_argument('-p', '--priority',
                          choices=['low', 'medium', 'high'],
                          default='medium',
                          help='Task priority (default: medium)')
    add_parser.add_argument('-d', '--due',
                          help='Due date (format: YYYY-MM-DD)')

    ## List command
    list_parser = subparsers.add_parser('list', help='List all tasks')
    list_parser.add_argument('-p', '--priority',
                           choices=['low', 'medium', 'high'],
                           help='Filter tasks by priority')
    list_parser.add_argument('-s', '--sort',
                           choices=['priority', 'title'],
                           default='priority',
                           help='Sort tasks by criteria (default: priority)')

    ## Delete command
    delete_parser = subparsers.add_parser('delete', help='Delete a task')
    delete_parser.add_argument('task_id', type=int, help='Task ID to delete')

    ## Parse arguments
    args = parser.parse_args()

    ## Load existing tasks
    tasks = load_tasks()

    ## Handle commands
    if args.command == 'add':
        ## Add a new task
        new_task = {
            'id': len(tasks) + 1,
            'title': args.title,
            'priority': args.priority,
            'due': args.due
        }
        tasks.append(new_task)
        save_tasks(tasks)
        print(f"Task added: {new_task['title']} (ID: {new_task['id']})")

    elif args.command == 'list':
        ## List tasks
        if not tasks:
            print("No tasks found.")
            return

        ## Filter by priority if specified
        if args.priority:
            filtered_tasks = [t for t in tasks if t['priority'] == args.priority]
        else:
            filtered_tasks = tasks

        ## Sort tasks
        if args.sort == 'priority':
            ## Custom priority sorting
            priority_order = {'high': 0, 'medium': 1, 'low': 2}
            sorted_tasks = sorted(filtered_tasks, key=lambda x: priority_order[x['priority']])
        else:
            ## Sort by title
            sorted_tasks = sorted(filtered_tasks, key=lambda x: x['title'])

        ## Display tasks
        print("ID | Title                 | Priority | Due Date")
        print("-" * 50)
        for task in sorted_tasks:
            due_date = task['due'] if task['due'] else 'N/A'
            print(f"{task['id']:2} | {task['title'][:20]:<20} | {task['priority']:<8} | {due_date}")

    elif args.command == 'delete':
        ## Delete a task
        task_id = args.task_id
        task_found = False

        for i, task in enumerate(tasks):
            if task['id'] == task_id:
                del tasks[i]
                task_found = True
                break

        if task_found:
            save_tasks(tasks)
            print(f"Task {task_id} deleted.")
        else:
            print(f"Task {task_id} not found.")

    else:
        ## No command specified
        parser.print_help()

if __name__ == "__main__":
    main()

Understanding the Task Manager

This script demonstrates several advanced features of argparse:

  1. Subparsers - Creating command-specific argument sets
  2. Command-specific arguments - Different arguments for add, list, and delete commands
  3. Nested validation - Priority choices limited to specific values
  4. Default values - Providing sensible defaults for optional arguments

Testing the Task Manager

Let's run the script with different commands to see how it works:

  1. Adding tasks:
python3 task_manager.py add "Complete Python tutorial" -p high -d 2023-12-31

Output:

Task added: Complete Python tutorial (ID: 1)

Add a few more tasks:

python3 task_manager.py add "Read documentation" -p medium
python3 task_manager.py add "Take a break" -p low -d 2023-12-25
  1. Listing tasks:
python3 task_manager.py list

Output:

ID | Title                 | Priority | Due Date
--------------------------------------------------
1  | Complete Python tutor | high     | 2023-12-31
2  | Read documentation    | medium   | N/A
3  | Take a break          | low      | 2023-12-25
  1. Listing tasks with filtering and sorting:
python3 task_manager.py list -s title

Output:

ID | Title                 | Priority | Due Date
--------------------------------------------------
1  | Complete Python tutor | high     | 2023-12-31
2  | Read documentation    | medium   | N/A
3  | Take a break          | low      | 2023-12-25
  1. Filtering by priority:
python3 task_manager.py list -p high

Output:

ID | Title                 | Priority | Due Date
--------------------------------------------------
1  | Complete Python tutor | high     | 2023-12-31
  1. Deleting a task:
python3 task_manager.py delete 2

Output:

Task 2 deleted.

Verify the task was deleted:

python3 task_manager.py list

Output:

ID | Title                 | Priority | Due Date
--------------------------------------------------
1  | Complete Python tutor | high     | 2023-12-31
3  | Take a break          | low      | 2023-12-25

Getting Help for Subcommands

Argparse automatically generates help for each subcommand:

python3 task_manager.py add --help

Output:

usage: task_manager.py add [-h] [-p {low,medium,high}] [-d DUE] title

positional arguments:
  title                 Task title

options:
  -h, --help            show this help message and exit
  -p {low,medium,high}, --priority {low,medium,high}
                        Task priority (default: medium)
  -d DUE, --due DUE     Due date (format: YYYY-MM-DD)

Key Points About Subparsers

  • Subparsers create command-specific argument sets
  • Each subparser can have its own arguments
  • The dest parameter specifies where the command name will be stored
  • Help messages are automatically generated for each subcommand
  • You can mix positional and optional arguments in subparsers

You now have a fully functional command-line tool that demonstrates the power and flexibility of argparse for handling complex argument scenarios.

Summary

In this tutorial, you learned how to use Python's argparse module to handle command-line arguments effectively. You explored:

  • Basic argparse functionality and creating simple scripts
  • Different types of arguments: positional, optional, flags, and choices
  • Advanced argument configurations like multiple values and custom validation
  • Building a complete command-line tool with subcommands

These skills allow you to create more flexible and user-friendly Python applications that can be configured through command-line arguments without modifying the code.

Command-line argument parsing is an essential skill for Python developers who want to build professional-quality tools and utilities. The argparse module provides a robust and standardized way to implement this functionality in your scripts.

Now you can apply these techniques to your own Python projects to make them more versatile and accessible to users.