Click CLI Framework
Click is a Python package for creating beautiful command-line interfaces with minimal code. Uses decorators for a clean, composable API. Powers Flask, AWS CLI, and many other tools.
Use Case
Use Click when you need to:
- Build CLI tools in Python
- Create nested command groups
- Handle complex option parsing
- Generate automatic help pages
Installation
1pip install click
Code
Basic Command
1import click
2
3@click.command()
4@click.option('--name', default='World', help='Name to greet')
5@click.option('--count', default=1, help='Number of greetings')
6def hello(name, count):
7 """Simple program that greets NAME COUNT times."""
8 for _ in range(count):
9 click.echo(f'Hello, {name}!')
10
11if __name__ == '__main__':
12 hello()
Examples
Example 1: Options and Arguments
1import click
2
3@click.command()
4@click.option('--verbose', '-v', is_flag=True, help='Verbose output')
5@click.option('--output', '-o', type=click.Path(), help='Output file')
6@click.option('--format', type=click.Choice(['json', 'yaml', 'text']),
7 default='text', help='Output format')
8@click.argument('input_file', type=click.Path(exists=True))
9def process(verbose, output, format, input_file):
10 """Process INPUT_FILE and optionally save to OUTPUT."""
11 if verbose:
12 click.echo(f'Processing {input_file}...')
13
14 # Process file
15 result = f"Processed {input_file} as {format}"
16
17 if output:
18 with open(output, 'w') as f:
19 f.write(result)
20 click.echo(f'Saved to {output}')
21 else:
22 click.echo(result)
23
24if __name__ == '__main__':
25 process()
Usage:
1$ python app.py input.txt
2Processed input.txt as text
3
4$ python app.py input.txt -v -o output.txt --format json
5Processing input.txt...
6Saved to output.txt
Example 2: Command Groups (Subcommands)
1import click
2
3@click.group()
4def cli():
5 """My CLI application with subcommands."""
6 pass
7
8@cli.command()
9def init():
10 """Initialize the application."""
11 click.echo('Initializing...')
12
13@cli.command()
14@click.option('--port', default=8000, help='Port number')
15def serve(port):
16 """Start the server."""
17 click.echo(f'Starting server on port {port}...')
18
19@cli.group()
20def config():
21 """Manage configuration."""
22 pass
23
24@config.command('get')
25@click.argument('key')
26def config_get(key):
27 """Get configuration value."""
28 click.echo(f'Getting config: {key}')
29
30@config.command('set')
31@click.argument('key')
32@click.argument('value')
33def config_set(key, value):
34 """Set configuration value."""
35 click.echo(f'Setting {key} = {value}')
36
37if __name__ == '__main__':
38 cli()
Usage:
1$ python app.py init
2Initializing...
3
4$ python app.py serve --port 3000
5Starting server on port 3000...
6
7$ python app.py config get database.host
8Getting config: database.host
9
10$ python app.py config set database.host localhost
11Setting database.host = localhost
Example 3: Prompts and Confirmation
1import click
2
3@click.command()
4@click.option('--name', prompt='Your name', help='Name to greet')
5@click.option('--password', prompt=True, hide_input=True,
6 confirmation_prompt=True, help='Password')
7@click.confirmation_option(prompt='Are you sure you want to continue?')
8def secure_hello(name, password):
9 """Secure greeting with prompts."""
10 click.echo(f'Hello, {name}!')
11 click.echo('Password accepted')
12
13if __name__ == '__main__':
14 secure_hello()
Usage:
1$ python app.py
2Your name: John
3Password:
4Repeat for confirmation:
5Are you sure you want to continue? [y/N]: y
6Hello, John!
7Password accepted
Example 4: Progress Bars and Styling
1import click
2import time
3
4@click.command()
5@click.option('--items', default=100, help='Number of items')
6def process_items(items):
7 """Process items with progress bar."""
8
9 # Progress bar
10 with click.progressbar(range(items), label='Processing') as bar:
11 for item in bar:
12 time.sleep(0.01) # Simulate work
13
14 # Styled output
15 click.secho('Success!', fg='green', bold=True)
16 click.secho('Warning: Some items skipped', fg='yellow')
17 click.secho('Error details', fg='red', err=True)
18
19 # Formatted output
20 click.echo(click.style('Processed: ', fg='blue') +
21 click.style(str(items), fg='cyan', bold=True))
22
23if __name__ == '__main__':
24 process_items()
Example 5: Context and Shared State
1import click
2
3@click.group()
4@click.option('--debug/--no-debug', default=False)
5@click.pass_context
6def cli(ctx, debug):
7 """CLI with shared context."""
8 ctx.ensure_object(dict)
9 ctx.obj['DEBUG'] = debug
10
11@cli.command()
12@click.pass_context
13def status(ctx):
14 """Show status."""
15 if ctx.obj['DEBUG']:
16 click.echo('Debug mode is ON')
17 click.echo('Status: OK')
18
19@cli.command()
20@click.option('--config', type=click.File('r'))
21@click.pass_context
22def run(ctx, config):
23 """Run with configuration."""
24 if ctx.obj['DEBUG']:
25 click.echo('Debug: Loading config...')
26
27 if config:
28 content = config.read()
29 click.echo(f'Config loaded: {len(content)} bytes')
30
31if __name__ == '__main__':
32 cli(obj={})
Example 6: Custom Types and Validation
1import click
2
3class EmailType(click.ParamType):
4 name = "email"
5
6 def convert(self, value, param, ctx):
7 if '@' not in value:
8 self.fail(f'{value} is not a valid email', param, ctx)
9 return value
10
11EMAIL = EmailType()
12
13@click.command()
14@click.option('--email', type=EMAIL, required=True)
15@click.option('--age', type=click.IntRange(0, 120), required=True)
16@click.option('--score', type=click.FloatRange(0.0, 100.0))
17def register(email, age, score):
18 """Register user with validation."""
19 click.echo(f'Email: {email}')
20 click.echo(f'Age: {age}')
21 if score:
22 click.echo(f'Score: {score}')
23
24if __name__ == '__main__':
25 register()
Example 7: File Handling
1import click
2
3@click.command()
4@click.argument('input', type=click.File('r'))
5@click.argument('output', type=click.File('w'))
6@click.option('--uppercase', is_flag=True)
7def convert(input, output, uppercase):
8 """Convert INPUT file to OUTPUT file."""
9 content = input.read()
10
11 if uppercase:
12 content = content.upper()
13
14 output.write(content)
15 click.echo(f'Converted {input.name} -> {output.name}')
16
17if __name__ == '__main__':
18 convert()
Usage:
1$ python app.py input.txt output.txt --uppercase
2Converted input.txt -> output.txt
3
4# Can use stdin/stdout
5$ echo "hello" | python app.py - -
6HELLO
Common Patterns
Environment Variables
1@click.command()
2@click.option('--api-key', envvar='API_KEY', required=True)
3def api_call(api_key):
4 """Call API with key from env or flag."""
5 click.echo(f'Using API key: {api_key[:4]}...')
Multiple Values
1@click.command()
2@click.option('--tag', multiple=True)
3def tag_items(tag):
4 """Add multiple tags."""
5 for t in tag:
6 click.echo(f'Tag: {t}')
7
8# Usage: python app.py --tag python --tag cli --tag tool
Callbacks
1def validate_port(ctx, param, value):
2 if value < 1024:
3 raise click.BadParameter('Port must be >= 1024')
4 return value
5
6@click.command()
7@click.option('--port', callback=validate_port, type=int)
8def serve(port):
9 click.echo(f'Port: {port}')
Notes
- Arguments are required by default, options are optional
- Use
@click.pass_contextto share state between commands click.echo()is better thanprint()(handles encoding)- Progress bars work automatically with iterables
- Click handles
--helpautomatically
Gotchas/Warnings
- ⚠️ Arguments vs Options: Arguments are positional, options have flags
- ⚠️ Order matters: Decorators are applied bottom-to-top
- ⚠️ File objects: Click handles opening/closing automatically
- ⚠️ Context: Use
@click.pass_contextto access shared state
comments powered by Disqus