Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:04:14 +08:00
commit 70c36b5eff
248 changed files with 47482 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
#!/usr/bin/env node
/**
* argparse patterns translated to commander.js
*
* Shows equivalent patterns between Python argparse and Node.js commander
*
* Usage:
* npm install commander
* node argparse-to-commander.ts deploy production --force
*/
import { Command, Option } from 'commander';
const program = new Command();
// ===== Basic Configuration (like ArgumentParser) =====
program
.name('mycli')
.description('A powerful CLI tool')
.version('1.0.0');
// ===== Subcommands (like add_subparsers) =====
// Init command (like subparsers.add_parser('init'))
program
.command('init')
.description('Initialize a new project')
.option('-t, --template <type>', 'project template', 'basic')
.option('-p, --path <path>', 'project path', '.')
.action((options) => {
console.log(`Initializing project with ${options.template} template...`);
console.log(`Path: ${options.path}`);
});
// Deploy command with choices (like choices=[...])
program
.command('deploy <environment>')
.description('Deploy to specified environment')
.addOption(
new Option('-m, --mode <mode>', 'deployment mode')
.choices(['fast', 'safe', 'rollback'])
.default('safe')
)
.option('-f, --force', 'force deployment', false)
.action((environment, options) => {
console.log(`Deploying to ${environment} in ${options.mode} mode`);
if (options.force) {
console.log('Warning: Force mode enabled');
}
});
// ===== Nested Subcommands (like nested add_subparsers) =====
const config = program
.command('config')
.description('Manage configuration');
config
.command('get <key>')
.description('Get configuration value')
.action((key) => {
console.log(`Getting config: ${key}`);
});
config
.command('set <key> <value>')
.description('Set configuration value')
.option('-f, --force', 'overwrite existing value')
.action((key, value, options) => {
console.log(`Setting ${key} = ${value}`);
if (options.force) {
console.log('(Overwriting existing value)');
}
});
config
.command('list')
.description('List all configuration values')
.option('--format <format>', 'output format', 'text')
.action((options) => {
console.log(`Listing configuration (format: ${options.format})`);
});
// ===== Boolean Flags (like action='store_true') =====
program
.command('build')
.description('Build the project')
.option('--verbose', 'enable verbose output')
.option('--debug', 'enable debug mode')
.option('--no-cache', 'disable cache (enabled by default)')
.action((options) => {
console.log('Building project...');
console.log(`Verbose: ${options.verbose || false}`);
console.log(`Debug: ${options.debug || false}`);
console.log(`Cache: ${options.cache}`);
});
// ===== Type Coercion (like type=int, type=float) =====
program
.command('server')
.description('Start server')
.option('-p, --port <number>', 'server port', parseInt, 8080)
.option('-t, --timeout <seconds>', 'timeout in seconds', parseFloat, 30.0)
.option('-w, --workers <number>', 'number of workers', parseInt, 4)
.action((options) => {
console.log(`Starting server on port ${options.port}`);
console.log(`Timeout: ${options.timeout}s`);
console.log(`Workers: ${options.workers}`);
});
// ===== Variadic Arguments (like nargs='+') =====
program
.command('process <files...>')
.description('Process multiple files')
.option('--format <format>', 'output format', 'json')
.action((files, options) => {
console.log(`Processing ${files.length} file(s):`);
files.forEach((file) => console.log(` - ${file}`));
console.log(`Output format: ${options.format}`);
});
// ===== Mutually Exclusive Options =====
// Note: Commander doesn't have built-in mutually exclusive groups
// You need to validate manually
program
.command('export')
.description('Export data')
.option('--json <file>', 'export as JSON')
.option('--yaml <file>', 'export as YAML')
.option('--xml <file>', 'export as XML')
.action((options) => {
const formats = [options.json, options.yaml, options.xml].filter(Boolean);
if (formats.length > 1) {
console.error('Error: --json, --yaml, and --xml are mutually exclusive');
process.exit(1);
}
if (options.json) {
console.log(`Exporting as JSON to ${options.json}`);
} else if (options.yaml) {
console.log(`Exporting as YAML to ${options.yaml}`);
} else if (options.xml) {
console.log(`Exporting as XML to ${options.xml}`);
}
});
// ===== Required Options (like required=True) =====
program
.command('login')
.description('Login to service')
.requiredOption('--username <username>', 'username for authentication')
.requiredOption('--password <password>', 'password for authentication')
.option('--token <token>', 'authentication token (alternative to password)')
.action((options) => {
console.log(`Logging in as ${options.username}`);
});
// ===== Custom Validation =====
function validatePort(value: string): number {
const port = parseInt(value, 10);
if (isNaN(port) || port < 1 || port > 65535) {
throw new Error(`Invalid port: ${value} (must be 1-65535)`);
}
return port;
}
program
.command('connect')
.description('Connect to server')
.option('-p, --port <number>', 'server port', validatePort, 8080)
.action((options) => {
console.log(`Connecting to port ${options.port}`);
});
// ===== Argument Groups (display organization) =====
// Note: Commander doesn't have argument groups for help display
// You can organize with comments or separate commands
// ===== Parse Arguments =====
program.parse();
/**
* COMPARISON SUMMARY:
*
* argparse Pattern | commander.js Equivalent
* ---------------------------------|--------------------------------
* ArgumentParser() | new Command()
* add_argument() | .option() or .argument()
* add_subparsers() | .command()
* choices=[...] | .choices([...])
* action='store_true' | .option('--flag')
* action='store_false' | .option('--no-flag')
* type=int | parseInt
* type=float | parseFloat
* nargs='+' | <arg...>
* nargs='*' | [arg...]
* required=True | .requiredOption()
* default=value | option(..., default)
* help='...' | .description('...')
* mutually_exclusive_group() | Manual validation
* add_argument_group() | Organize with subcommands
*/

View File

@@ -0,0 +1,243 @@
#!/usr/bin/env python3
"""
Argument groups for better organization and help output.
Usage:
python argument-groups.py --host 192.168.1.1 --port 8080 --ssl
python argument-groups.py --db-host localhost --db-port 5432 --db-name mydb
python argument-groups.py --log-level debug --log-file app.log
"""
import argparse
import sys
def main():
parser = argparse.ArgumentParser(
description='Organized arguments with groups',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# ===== Server Configuration Group =====
server_group = parser.add_argument_group(
'server configuration',
'Options for configuring the web server'
)
server_group.add_argument(
'--host',
default='127.0.0.1',
help='Server host address (default: %(default)s)'
)
server_group.add_argument(
'--port', '-p',
type=int,
default=8080,
help='Server port (default: %(default)s)'
)
server_group.add_argument(
'--workers',
type=int,
default=4,
help='Number of worker processes (default: %(default)s)'
)
server_group.add_argument(
'--ssl',
action='store_true',
help='Enable SSL/TLS'
)
server_group.add_argument(
'--cert',
help='Path to SSL certificate (required if --ssl is set)'
)
server_group.add_argument(
'--key',
help='Path to SSL private key (required if --ssl is set)'
)
# ===== Database Configuration Group =====
db_group = parser.add_argument_group(
'database configuration',
'Options for database connection'
)
db_group.add_argument(
'--db-host',
default='localhost',
help='Database host (default: %(default)s)'
)
db_group.add_argument(
'--db-port',
type=int,
default=5432,
help='Database port (default: %(default)s)'
)
db_group.add_argument(
'--db-name',
required=True,
help='Database name (required)'
)
db_group.add_argument(
'--db-user',
help='Database username'
)
db_group.add_argument(
'--db-password',
help='Database password'
)
db_group.add_argument(
'--db-pool-size',
type=int,
default=10,
help='Database connection pool size (default: %(default)s)'
)
# ===== Logging Configuration Group =====
log_group = parser.add_argument_group(
'logging configuration',
'Options for logging and monitoring'
)
log_group.add_argument(
'--log-level',
choices=['debug', 'info', 'warning', 'error', 'critical'],
default='info',
help='Logging level (default: %(default)s)'
)
log_group.add_argument(
'--log-file',
help='Log to file instead of stdout'
)
log_group.add_argument(
'--log-format',
choices=['text', 'json'],
default='text',
help='Log format (default: %(default)s)'
)
log_group.add_argument(
'--access-log',
action='store_true',
help='Enable access logging'
)
# ===== Cache Configuration Group =====
cache_group = parser.add_argument_group(
'cache configuration',
'Options for caching layer'
)
cache_group.add_argument(
'--cache-backend',
choices=['redis', 'memcached', 'memory'],
default='memory',
help='Cache backend (default: %(default)s)'
)
cache_group.add_argument(
'--cache-host',
default='localhost',
help='Cache server host (default: %(default)s)'
)
cache_group.add_argument(
'--cache-port',
type=int,
default=6379,
help='Cache server port (default: %(default)s)'
)
cache_group.add_argument(
'--cache-ttl',
type=int,
default=300,
help='Default cache TTL in seconds (default: %(default)s)'
)
# ===== Security Configuration Group =====
security_group = parser.add_argument_group(
'security configuration',
'Security and authentication options'
)
security_group.add_argument(
'--auth-required',
action='store_true',
help='Require authentication for all requests'
)
security_group.add_argument(
'--jwt-secret',
help='JWT secret key'
)
security_group.add_argument(
'--cors-origins',
nargs='+',
help='Allowed CORS origins'
)
security_group.add_argument(
'--rate-limit',
type=int,
default=100,
help='Rate limit (requests per minute, default: %(default)s)'
)
# Parse arguments
args = parser.parse_args()
# Validate SSL configuration
if args.ssl and (not args.cert or not args.key):
parser.error("--cert and --key are required when --ssl is enabled")
# Display configuration
print("Configuration Summary:")
print("\nServer:")
print(f" Host: {args.host}:{args.port}")
print(f" Workers: {args.workers}")
print(f" SSL: {'Enabled' if args.ssl else 'Disabled'}")
if args.ssl:
print(f" Certificate: {args.cert}")
print(f" Key: {args.key}")
print("\nDatabase:")
print(f" Host: {args.db_host}:{args.db_port}")
print(f" Database: {args.db_name}")
print(f" User: {args.db_user or '(not set)'}")
print(f" Pool Size: {args.db_pool_size}")
print("\nLogging:")
print(f" Level: {args.log_level}")
print(f" File: {args.log_file or 'stdout'}")
print(f" Format: {args.log_format}")
print(f" Access Log: {'Enabled' if args.access_log else 'Disabled'}")
print("\nCache:")
print(f" Backend: {args.cache_backend}")
print(f" Host: {args.cache_host}:{args.cache_port}")
print(f" TTL: {args.cache_ttl}s")
print("\nSecurity:")
print(f" Auth Required: {'Yes' if args.auth_required else 'No'}")
print(f" CORS Origins: {args.cors_origins or '(not set)'}")
print(f" Rate Limit: {args.rate_limit} req/min")
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""
Basic argparse parser with common argument types.
Usage:
python basic-parser.py --help
python basic-parser.py deploy app1 --env production --force
python basic-parser.py deploy app2 --env staging --timeout 60
"""
import argparse
import sys
def main():
parser = argparse.ArgumentParser(
description='Deploy application to specified environment',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
Examples:
%(prog)s deploy my-app --env production
%(prog)s deploy my-app --env staging --force
%(prog)s deploy my-app --env dev --timeout 120
'''
)
# Version info
parser.add_argument(
'--version',
action='version',
version='%(prog)s 1.0.0'
)
# Required positional argument
parser.add_argument(
'action',
help='Action to perform'
)
parser.add_argument(
'app_name',
help='Name of the application to deploy'
)
# Optional arguments with different types
parser.add_argument(
'--env', '-e',
default='development',
help='Deployment environment (default: %(default)s)'
)
parser.add_argument(
'--timeout', '-t',
type=int,
default=30,
help='Timeout in seconds (default: %(default)s)'
)
# Boolean flag
parser.add_argument(
'--force', '-f',
action='store_true',
help='Force deployment without confirmation'
)
# Verbose flag (count occurrences)
parser.add_argument(
'--verbose', '-v',
action='count',
default=0,
help='Increase verbosity (-v, -vv, -vvv)'
)
# Parse arguments
args = parser.parse_args()
# Use parsed arguments
print(f"Action: {args.action}")
print(f"App Name: {args.app_name}")
print(f"Environment: {args.env}")
print(f"Timeout: {args.timeout}s")
print(f"Force: {args.force}")
print(f"Verbosity Level: {args.verbose}")
# Example validation
if args.timeout < 1:
parser.error("Timeout must be at least 1 second")
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,162 @@
#!/usr/bin/env python3
"""
Boolean flag patterns with store_true, store_false, and count actions.
Usage:
python boolean-flags.py --verbose
python boolean-flags.py -vvv --debug --force
python boolean-flags.py --no-cache --quiet
"""
import argparse
import sys
def main():
parser = argparse.ArgumentParser(
description='Boolean flag patterns',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# ===== store_true (False by default) =====
parser.add_argument(
'--verbose',
action='store_true',
help='Enable verbose output'
)
parser.add_argument(
'--debug',
action='store_true',
help='Enable debug mode'
)
parser.add_argument(
'--force', '-f',
action='store_true',
help='Force operation without confirmation'
)
parser.add_argument(
'--dry-run',
action='store_true',
help='Perform a dry run without making changes'
)
# ===== store_false (True by default) =====
parser.add_argument(
'--no-cache',
action='store_false',
dest='cache',
help='Disable caching (enabled by default)'
)
parser.add_argument(
'--no-color',
action='store_false',
dest='color',
help='Disable colored output (enabled by default)'
)
# ===== count action (count occurrences) =====
parser.add_argument(
'-v',
action='count',
default=0,
dest='verbosity',
help='Increase verbosity (-v, -vv, -vvv)'
)
parser.add_argument(
'-q', '--quiet',
action='count',
default=0,
help='Decrease verbosity (-q, -qq, -qqq)'
)
# ===== store_const action =====
parser.add_argument(
'--fast',
action='store_const',
const='fast',
dest='mode',
help='Use fast mode'
)
parser.add_argument(
'--safe',
action='store_const',
const='safe',
dest='mode',
help='Use safe mode (default)'
)
parser.set_defaults(mode='safe')
# ===== Combined short flags =====
parser.add_argument(
'-a', '--all',
action='store_true',
help='Process all items'
)
parser.add_argument(
'-r', '--recursive',
action='store_true',
help='Process recursively'
)
parser.add_argument(
'-i', '--interactive',
action='store_true',
help='Run in interactive mode'
)
# Parse arguments
args = parser.parse_args()
# Calculate effective verbosity
effective_verbosity = args.verbosity - args.quiet
# Display configuration
print("Boolean Flags Configuration:")
print(f" Verbose: {args.verbose}")
print(f" Debug: {args.debug}")
print(f" Force: {args.force}")
print(f" Dry Run: {args.dry_run}")
print(f" Cache: {args.cache}")
print(f" Color: {args.color}")
print(f" Verbosity Level: {effective_verbosity}")
print(f" Mode: {args.mode}")
print(f" All: {args.all}")
print(f" Recursive: {args.recursive}")
print(f" Interactive: {args.interactive}")
# Example usage based on flags
if args.debug:
print("\nDebug mode enabled - showing detailed information")
if args.dry_run:
print("\nDry run mode - no changes will be made")
if effective_verbosity > 0:
print(f"\nVerbosity level: {effective_verbosity}")
if effective_verbosity >= 3:
print("Maximum verbosity - showing everything")
elif effective_verbosity < 0:
print(f"\nQuiet level: {abs(effective_verbosity)}")
if args.force:
print("\nForce mode - skipping confirmations")
if not args.cache:
print("\nCache disabled")
if not args.color:
print("\nColor output disabled")
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,197 @@
#!/usr/bin/env python3
"""
Argument choices and custom validation patterns.
Usage:
python choices-validation.py --log-level debug
python choices-validation.py --port 8080 --host 192.168.1.1
python choices-validation.py --region us-east-1 --instance-type t2.micro
"""
import argparse
import re
import sys
from pathlib import Path
def validate_port(value):
"""Custom validator for port numbers."""
try:
ivalue = int(value)
except ValueError:
raise argparse.ArgumentTypeError(f"{value} is not a valid integer")
if ivalue < 1 or ivalue > 65535:
raise argparse.ArgumentTypeError(
f"{value} is not a valid port (must be 1-65535)"
)
return ivalue
def validate_ip(value):
"""Custom validator for IP addresses."""
pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
if not re.match(pattern, value):
raise argparse.ArgumentTypeError(f"{value} is not a valid IP address")
# Check each octet is 0-255
octets = [int(x) for x in value.split('.')]
if any(o < 0 or o > 255 for o in octets):
raise argparse.ArgumentTypeError(
f"{value} contains invalid octets (must be 0-255)"
)
return value
def validate_email(value):
"""Custom validator for email addresses."""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(pattern, value):
raise argparse.ArgumentTypeError(f"{value} is not a valid email address")
return value
def validate_path_exists(value):
"""Custom validator to check if path exists."""
path = Path(value)
if not path.exists():
raise argparse.ArgumentTypeError(f"Path does not exist: {value}")
return path
def validate_range(min_val, max_val):
"""Factory function for range validators."""
def validator(value):
try:
ivalue = int(value)
except ValueError:
raise argparse.ArgumentTypeError(f"{value} is not a valid integer")
if ivalue < min_val or ivalue > max_val:
raise argparse.ArgumentTypeError(
f"{value} must be between {min_val} and {max_val}"
)
return ivalue
return validator
def main():
parser = argparse.ArgumentParser(
description='Demonstrate choices and validation patterns',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# ===== String Choices =====
parser.add_argument(
'--log-level',
choices=['debug', 'info', 'warning', 'error', 'critical'],
default='info',
help='Logging level (default: %(default)s)'
)
parser.add_argument(
'--region',
choices=[
'us-east-1', 'us-west-1', 'us-west-2',
'eu-west-1', 'eu-central-1',
'ap-southeast-1', 'ap-northeast-1'
],
default='us-east-1',
help='AWS region (default: %(default)s)'
)
parser.add_argument(
'--format',
choices=['json', 'yaml', 'toml', 'xml'],
default='json',
help='Output format (default: %(default)s)'
)
# ===== Custom Validators =====
parser.add_argument(
'--port',
type=validate_port,
default=8080,
help='Server port (1-65535, default: %(default)s)'
)
parser.add_argument(
'--host',
type=validate_ip,
default='127.0.0.1',
help='Server host IP (default: %(default)s)'
)
parser.add_argument(
'--email',
type=validate_email,
help='Email address for notifications'
)
parser.add_argument(
'--config',
type=validate_path_exists,
help='Path to configuration file (must exist)'
)
# ===== Range Validators =====
parser.add_argument(
'--workers',
type=validate_range(1, 32),
default=4,
help='Number of worker processes (1-32, default: %(default)s)'
)
parser.add_argument(
'--timeout',
type=validate_range(1, 3600),
default=30,
help='Request timeout in seconds (1-3600, default: %(default)s)'
)
# ===== Integer Choices =====
parser.add_argument(
'--instance-type',
choices=['t2.micro', 't2.small', 't2.medium', 't3.large'],
default='t2.micro',
help='EC2 instance type (default: %(default)s)'
)
# ===== Type Coercion =====
parser.add_argument(
'--memory',
type=float,
default=1.0,
help='Memory limit in GB (default: %(default)s)'
)
parser.add_argument(
'--retry-count',
type=int,
default=3,
help='Number of retries (default: %(default)s)'
)
# Parse arguments
args = parser.parse_args()
# Display parsed values
print("Configuration:")
print(f" Log Level: {args.log_level}")
print(f" Region: {args.region}")
print(f" Format: {args.format}")
print(f" Port: {args.port}")
print(f" Host: {args.host}")
print(f" Email: {args.email}")
print(f" Config: {args.config}")
print(f" Workers: {args.workers}")
print(f" Timeout: {args.timeout}s")
print(f" Instance Type: {args.instance_type}")
print(f" Memory: {args.memory}GB")
print(f" Retry Count: {args.retry_count}")
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,188 @@
#!/usr/bin/env python3
"""
Custom action classes for advanced argument processing.
Usage:
python custom-actions.py --env-file .env
python custom-actions.py --key API_KEY --key DB_URL
python custom-actions.py --range 1-10 --range 20-30
"""
import argparse
import sys
from pathlib import Path
class LoadEnvFileAction(argparse.Action):
"""Custom action to load environment variables from file."""
def __call__(self, parser, namespace, values, option_string=None):
env_file = Path(values)
if not env_file.exists():
parser.error(f"Environment file does not exist: {values}")
env_vars = {}
with open(env_file, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#'):
if '=' in line:
key, value = line.split('=', 1)
env_vars[key.strip()] = value.strip()
setattr(namespace, self.dest, env_vars)
class KeyValueAction(argparse.Action):
"""Custom action to parse key=value pairs."""
def __call__(self, parser, namespace, values, option_string=None):
if '=' not in values:
parser.error(f"Argument must be in key=value format: {values}")
key, value = values.split('=', 1)
items = getattr(namespace, self.dest, None) or {}
items[key] = value
setattr(namespace, self.dest, items)
class RangeAction(argparse.Action):
"""Custom action to parse ranges like 1-10."""
def __call__(self, parser, namespace, values, option_string=None):
if '-' not in values:
parser.error(f"Range must be in format start-end: {values}")
try:
start, end = values.split('-')
start = int(start)
end = int(end)
except ValueError:
parser.error(f"Invalid range format: {values}")
if start > end:
parser.error(f"Start must be less than or equal to end: {values}")
ranges = getattr(namespace, self.dest, None) or []
ranges.append((start, end))
setattr(namespace, self.dest, ranges)
class AppendUniqueAction(argparse.Action):
"""Custom action to append unique values only."""
def __call__(self, parser, namespace, values, option_string=None):
items = getattr(namespace, self.dest, None) or []
if values not in items:
items.append(values)
setattr(namespace, self.dest, items)
class ValidateAndStoreAction(argparse.Action):
"""Custom action that validates before storing."""
def __call__(self, parser, namespace, values, option_string=None):
# Custom validation logic
if values.startswith('test-'):
print(f"Warning: Using test value: {values}")
# Transform value
transformed = values.upper()
setattr(namespace, self.dest, transformed)
class IncrementAction(argparse.Action):
"""Custom action to increment a value."""
def __init__(self, option_strings, dest, default=0, **kwargs):
super().__init__(option_strings, dest, nargs=0, default=default, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
current = getattr(namespace, self.dest, self.default)
setattr(namespace, self.dest, current + 1)
def main():
parser = argparse.ArgumentParser(
description='Custom action demonstrations',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# Load environment file
parser.add_argument(
'--env-file',
action=LoadEnvFileAction,
help='Load environment variables from file'
)
# Key-value pairs
parser.add_argument(
'--config', '-c',
action=KeyValueAction,
help='Configuration in key=value format (can be used multiple times)'
)
# Range parsing
parser.add_argument(
'--range', '-r',
action=RangeAction,
help='Range in start-end format (e.g., 1-10)'
)
# Unique append
parser.add_argument(
'--tag',
action=AppendUniqueAction,
help='Add unique tag (duplicates ignored)'
)
# Validate and transform
parser.add_argument(
'--key',
action=ValidateAndStoreAction,
help='Key to transform to uppercase'
)
# Custom increment
parser.add_argument(
'--increment',
action=IncrementAction,
help='Increment counter'
)
# Parse arguments
args = parser.parse_args()
# Display results
print("Custom Actions Results:")
if args.env_file:
print(f"\nEnvironment Variables:")
for key, value in args.env_file.items():
print(f" {key}={value}")
if args.config:
print(f"\nConfiguration:")
for key, value in args.config.items():
print(f" {key}={value}")
if args.range:
print(f"\nRanges:")
for start, end in args.range:
print(f" {start}-{end} (includes {end - start + 1} values)")
if args.tag:
print(f"\nUnique Tags: {', '.join(args.tag)}")
if args.key:
print(f"\nTransformed Key: {args.key}")
if args.increment:
print(f"\nIncrement Count: {args.increment}")
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,175 @@
#!/usr/bin/env python3
"""
Mutually exclusive argument groups.
Usage:
python mutually-exclusive.py --json output.json
python mutually-exclusive.py --yaml output.yaml
python mutually-exclusive.py --verbose
python mutually-exclusive.py --quiet
python mutually-exclusive.py --create resource
python mutually-exclusive.py --delete resource
"""
import argparse
import sys
def main():
parser = argparse.ArgumentParser(
description='Mutually exclusive argument groups',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# ===== Output Format (mutually exclusive) =====
output_group = parser.add_mutually_exclusive_group()
output_group.add_argument(
'--json',
metavar='FILE',
help='Output in JSON format'
)
output_group.add_argument(
'--yaml',
metavar='FILE',
help='Output in YAML format'
)
output_group.add_argument(
'--xml',
metavar='FILE',
help='Output in XML format'
)
# ===== Verbosity (mutually exclusive) =====
verbosity_group = parser.add_mutually_exclusive_group()
verbosity_group.add_argument(
'--verbose', '-v',
action='store_true',
help='Increase verbosity'
)
verbosity_group.add_argument(
'--quiet', '-q',
action='store_true',
help='Suppress output'
)
# ===== Operation Mode (mutually exclusive, required) =====
operation_group = parser.add_mutually_exclusive_group(required=True)
operation_group.add_argument(
'--create',
metavar='RESOURCE',
help='Create a resource'
)
operation_group.add_argument(
'--update',
metavar='RESOURCE',
help='Update a resource'
)
operation_group.add_argument(
'--delete',
metavar='RESOURCE',
help='Delete a resource'
)
operation_group.add_argument(
'--list',
action='store_true',
help='List all resources'
)
# ===== Authentication Method (mutually exclusive) =====
auth_group = parser.add_mutually_exclusive_group()
auth_group.add_argument(
'--token',
metavar='TOKEN',
help='Authenticate with token'
)
auth_group.add_argument(
'--api-key',
metavar='KEY',
help='Authenticate with API key'
)
auth_group.add_argument(
'--credentials',
metavar='FILE',
help='Authenticate with credentials file'
)
# ===== Deployment Strategy (mutually exclusive with default) =====
strategy_group = parser.add_mutually_exclusive_group()
strategy_group.add_argument(
'--rolling',
action='store_true',
help='Use rolling deployment'
)
strategy_group.add_argument(
'--blue-green',
action='store_true',
help='Use blue-green deployment'
)
strategy_group.add_argument(
'--canary',
action='store_true',
help='Use canary deployment'
)
# Set default strategy if none specified
parser.set_defaults(rolling=False, blue_green=False, canary=False)
# Parse arguments
args = parser.parse_args()
# Display configuration
print("Mutually Exclusive Groups Configuration:")
# Output format
if args.json:
print(f" Output Format: JSON to {args.json}")
elif args.yaml:
print(f" Output Format: YAML to {args.yaml}")
elif args.xml:
print(f" Output Format: XML to {args.xml}")
else:
print(" Output Format: None (default stdout)")
# Verbosity
if args.verbose:
print(" Verbosity: Verbose")
elif args.quiet:
print(" Verbosity: Quiet")
else:
print(" Verbosity: Normal")
# Operation
if args.create:
print(f" Operation: Create {args.create}")
elif args.update:
print(f" Operation: Update {args.update}")
elif args.delete:
print(f" Operation: Delete {args.delete}")
elif args.list:
print(" Operation: List resources")
# Authentication
if args.token:
print(f" Auth Method: Token")
elif args.api_key:
print(f" Auth Method: API Key")
elif args.credentials:
print(f" Auth Method: Credentials file ({args.credentials})")
else:
print(" Auth Method: None")
# Deployment strategy
if args.rolling:
print(" Deployment: Rolling")
elif args.blue_green:
print(" Deployment: Blue-Green")
elif args.canary:
print(" Deployment: Canary")
else:
print(" Deployment: Default")
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,219 @@
#!/usr/bin/env python3
"""
Nested subcommands pattern (like git config get/set, kubectl config view).
Usage:
python nested-subparser.py config get database_url
python nested-subparser.py config set api_key abc123
python nested-subparser.py config list
python nested-subparser.py deploy start production --replicas 3
python nested-subparser.py deploy stop production
"""
import argparse
import sys
# Config command handlers
def config_get(args):
"""Get configuration value."""
print(f"Getting config: {args.key}")
# Simulate getting config
print(f"{args.key} = example_value")
def config_set(args):
"""Set configuration value."""
print(f"Setting config: {args.key} = {args.value}")
if args.force:
print("(Overwriting existing value)")
def config_list(args):
"""List all configuration values."""
print(f"Listing all configuration (format: {args.format})")
def config_delete(args):
"""Delete configuration value."""
if not args.force:
response = input(f"Delete {args.key}? (y/n): ")
if response.lower() != 'y':
print("Cancelled")
return 1
print(f"Deleted: {args.key}")
# Deploy command handlers
def deploy_start(args):
"""Start deployment."""
print(f"Starting deployment to {args.environment}")
print(f"Replicas: {args.replicas}")
print(f"Wait: {args.wait}")
def deploy_stop(args):
"""Stop deployment."""
print(f"Stopping deployment in {args.environment}")
def deploy_restart(args):
"""Restart deployment."""
print(f"Restarting deployment in {args.environment}")
if args.hard:
print("(Hard restart)")
def main():
# Main parser
parser = argparse.ArgumentParser(
description='Multi-level CLI tool with nested subcommands',
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument('--version', action='version', version='1.0.0')
# Top-level subparsers
subparsers = parser.add_subparsers(
dest='command',
help='Top-level commands',
required=True
)
# ===== Config command group =====
config_parser = subparsers.add_parser(
'config',
help='Manage configuration',
description='Configuration management commands'
)
# Config subcommands
config_subparsers = config_parser.add_subparsers(
dest='config_command',
help='Config operations',
required=True
)
# config get
config_get_parser = config_subparsers.add_parser(
'get',
help='Get configuration value'
)
config_get_parser.add_argument('key', help='Configuration key')
config_get_parser.set_defaults(func=config_get)
# config set
config_set_parser = config_subparsers.add_parser(
'set',
help='Set configuration value'
)
config_set_parser.add_argument('key', help='Configuration key')
config_set_parser.add_argument('value', help='Configuration value')
config_set_parser.add_argument(
'--force', '-f',
action='store_true',
help='Overwrite existing value'
)
config_set_parser.set_defaults(func=config_set)
# config list
config_list_parser = config_subparsers.add_parser(
'list',
help='List all configuration values'
)
config_list_parser.add_argument(
'--format',
choices=['text', 'json', 'yaml'],
default='text',
help='Output format (default: %(default)s)'
)
config_list_parser.set_defaults(func=config_list)
# config delete
config_delete_parser = config_subparsers.add_parser(
'delete',
help='Delete configuration value'
)
config_delete_parser.add_argument('key', help='Configuration key')
config_delete_parser.add_argument(
'--force', '-f',
action='store_true',
help='Delete without confirmation'
)
config_delete_parser.set_defaults(func=config_delete)
# ===== Deploy command group =====
deploy_parser = subparsers.add_parser(
'deploy',
help='Manage deployments',
description='Deployment management commands'
)
# Deploy subcommands
deploy_subparsers = deploy_parser.add_subparsers(
dest='deploy_command',
help='Deploy operations',
required=True
)
# deploy start
deploy_start_parser = deploy_subparsers.add_parser(
'start',
help='Start deployment'
)
deploy_start_parser.add_argument(
'environment',
choices=['development', 'staging', 'production'],
help='Target environment'
)
deploy_start_parser.add_argument(
'--replicas', '-r',
type=int,
default=1,
help='Number of replicas (default: %(default)s)'
)
deploy_start_parser.add_argument(
'--wait',
action='store_true',
help='Wait for deployment to complete'
)
deploy_start_parser.set_defaults(func=deploy_start)
# deploy stop
deploy_stop_parser = deploy_subparsers.add_parser(
'stop',
help='Stop deployment'
)
deploy_stop_parser.add_argument(
'environment',
choices=['development', 'staging', 'production'],
help='Target environment'
)
deploy_stop_parser.set_defaults(func=deploy_stop)
# deploy restart
deploy_restart_parser = deploy_subparsers.add_parser(
'restart',
help='Restart deployment'
)
deploy_restart_parser.add_argument(
'environment',
choices=['development', 'staging', 'production'],
help='Target environment'
)
deploy_restart_parser.add_argument(
'--hard',
action='store_true',
help='Perform hard restart'
)
deploy_restart_parser.set_defaults(func=deploy_restart)
# Parse arguments
args = parser.parse_args()
# Call the appropriate command function
return args.func(args)
if __name__ == '__main__':
sys.exit(main() or 0)

View File

@@ -0,0 +1,123 @@
#!/usr/bin/env python3
"""
Single-level subcommands pattern (like docker, kubectl).
Usage:
python subparser-pattern.py init --template react
python subparser-pattern.py deploy production --force
python subparser-pattern.py status --format json
"""
import argparse
import sys
def cmd_init(args):
"""Initialize a new project."""
print(f"Initializing project with {args.template} template...")
print(f"Path: {args.path}")
def cmd_deploy(args):
"""Deploy application."""
print(f"Deploying to {args.environment} in {args.mode} mode")
if args.force:
print("Warning: Force mode enabled")
def cmd_status(args):
"""Show deployment status."""
print(f"Status format: {args.format}")
print("Fetching status...")
def main():
# Main parser
parser = argparse.ArgumentParser(
description='Multi-command CLI tool',
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
'--version',
action='version',
version='1.0.0'
)
# Create subparsers
subparsers = parser.add_subparsers(
dest='command',
help='Available commands',
required=True # Python 3.7+
)
# Init command
init_parser = subparsers.add_parser(
'init',
help='Initialize a new project',
description='Initialize a new project with specified template'
)
init_parser.add_argument(
'--template', '-t',
default='basic',
help='Project template (default: %(default)s)'
)
init_parser.add_argument(
'--path', '-p',
default='.',
help='Project path (default: %(default)s)'
)
init_parser.set_defaults(func=cmd_init)
# Deploy command
deploy_parser = subparsers.add_parser(
'deploy',
help='Deploy application to environment',
description='Deploy application to specified environment'
)
deploy_parser.add_argument(
'environment',
choices=['development', 'staging', 'production'],
help='Target environment'
)
deploy_parser.add_argument(
'--force', '-f',
action='store_true',
help='Force deployment without confirmation'
)
deploy_parser.add_argument(
'--mode', '-m',
choices=['fast', 'safe', 'rollback'],
default='safe',
help='Deployment mode (default: %(default)s)'
)
deploy_parser.set_defaults(func=cmd_deploy)
# Status command
status_parser = subparsers.add_parser(
'status',
help='Show deployment status',
description='Display current deployment status'
)
status_parser.add_argument(
'--format',
choices=['text', 'json', 'yaml'],
default='text',
help='Output format (default: %(default)s)'
)
status_parser.add_argument(
'--service',
action='append',
help='Filter by service (can be used multiple times)'
)
status_parser.set_defaults(func=cmd_status)
# Parse arguments
args = parser.parse_args()
# Call the appropriate command function
return args.func(args)
if __name__ == '__main__':
sys.exit(main() or 0)

View File

@@ -0,0 +1,257 @@
#!/usr/bin/env python3
"""
Type coercion and custom type converters.
Usage:
python type-coercion.py --port 8080 --timeout 30.5 --date 2024-01-15
python type-coercion.py --url https://api.example.com --size 1.5GB
"""
import argparse
import sys
from datetime import datetime
from pathlib import Path
import re
def parse_date(value):
"""Parse date in YYYY-MM-DD format."""
try:
return datetime.strptime(value, '%Y-%m-%d').date()
except ValueError:
raise argparse.ArgumentTypeError(
f"Invalid date format: {value} (expected YYYY-MM-DD)"
)
def parse_datetime(value):
"""Parse datetime in ISO format."""
try:
return datetime.fromisoformat(value)
except ValueError:
raise argparse.ArgumentTypeError(
f"Invalid datetime format: {value} (expected ISO format)"
)
def parse_url(value):
"""Parse and validate URL."""
pattern = r'^https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/.*)?$'
if not re.match(pattern, value):
raise argparse.ArgumentTypeError(f"Invalid URL: {value}")
return value
def parse_size(value):
"""Parse size with units (e.g., 1.5GB, 500MB)."""
pattern = r'^(\d+\.?\d*)(B|KB|MB|GB|TB)$'
match = re.match(pattern, value, re.IGNORECASE)
if not match:
raise argparse.ArgumentTypeError(
f"Invalid size format: {value} (expected number with unit)"
)
size, unit = match.groups()
size = float(size)
units = {'B': 1, 'KB': 1024, 'MB': 1024**2, 'GB': 1024**3, 'TB': 1024**4}
return int(size * units[unit.upper()])
def parse_duration(value):
"""Parse duration (e.g., 1h, 30m, 90s)."""
pattern = r'^(\d+)(s|m|h|d)$'
match = re.match(pattern, value, re.IGNORECASE)
if not match:
raise argparse.ArgumentTypeError(
f"Invalid duration format: {value} (expected number with s/m/h/d)"
)
amount, unit = match.groups()
amount = int(amount)
units = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
return amount * units[unit.lower()]
def parse_percentage(value):
"""Parse percentage (0-100)."""
try:
pct = float(value.rstrip('%'))
except ValueError:
raise argparse.ArgumentTypeError(f"Invalid percentage: {value}")
if pct < 0 or pct > 100:
raise argparse.ArgumentTypeError(
f"Percentage must be between 0 and 100: {value}"
)
return pct
def parse_comma_separated(value):
"""Parse comma-separated list."""
return [item.strip() for item in value.split(',') if item.strip()]
def parse_key_value_pairs(value):
"""Parse semicolon-separated key=value pairs."""
pairs = {}
for pair in value.split(';'):
if '=' in pair:
key, val = pair.split('=', 1)
pairs[key.strip()] = val.strip()
return pairs
def main():
parser = argparse.ArgumentParser(
description='Type coercion demonstrations',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# ===== Built-in Types =====
parser.add_argument(
'--port',
type=int,
default=8080,
help='Port number (integer)'
)
parser.add_argument(
'--timeout',
type=float,
default=30.0,
help='Timeout in seconds (float)'
)
parser.add_argument(
'--config',
type=Path,
help='Configuration file path'
)
parser.add_argument(
'--output',
type=argparse.FileType('w'),
help='Output file (opened for writing)'
)
parser.add_argument(
'--input',
type=argparse.FileType('r'),
help='Input file (opened for reading)'
)
# ===== Custom Types =====
parser.add_argument(
'--date',
type=parse_date,
help='Date in YYYY-MM-DD format'
)
parser.add_argument(
'--datetime',
type=parse_datetime,
help='Datetime in ISO format'
)
parser.add_argument(
'--url',
type=parse_url,
help='URL to connect to'
)
parser.add_argument(
'--size',
type=parse_size,
help='Size with unit (e.g., 1.5GB, 500MB)'
)
parser.add_argument(
'--duration',
type=parse_duration,
help='Duration (e.g., 1h, 30m, 90s)'
)
parser.add_argument(
'--percentage',
type=parse_percentage,
help='Percentage (0-100)'
)
parser.add_argument(
'--tags',
type=parse_comma_separated,
help='Comma-separated tags'
)
parser.add_argument(
'--env',
type=parse_key_value_pairs,
help='Environment variables as key=value;key2=value2'
)
# ===== List Types =====
parser.add_argument(
'--ids',
type=int,
nargs='+',
help='List of integer IDs'
)
parser.add_argument(
'--ratios',
type=float,
nargs='*',
help='List of float ratios'
)
# Parse arguments
args = parser.parse_args()
# Display parsed values
print("Type Coercion Results:")
print("\nBuilt-in Types:")
print(f" Port (int): {args.port} - type: {type(args.port).__name__}")
print(f" Timeout (float): {args.timeout} - type: {type(args.timeout).__name__}")
if args.config:
print(f" Config (Path): {args.config} - type: {type(args.config).__name__}")
print("\nCustom Types:")
if args.date:
print(f" Date: {args.date} - type: {type(args.date).__name__}")
if args.datetime:
print(f" Datetime: {args.datetime}")
if args.url:
print(f" URL: {args.url}")
if args.size:
print(f" Size: {args.size} bytes ({args.size / (1024**3):.2f} GB)")
if args.duration:
print(f" Duration: {args.duration} seconds ({args.duration / 3600:.2f} hours)")
if args.percentage is not None:
print(f" Percentage: {args.percentage}%")
if args.tags:
print(f" Tags: {args.tags}")
if args.env:
print(f" Environment:")
for key, value in args.env.items():
print(f" {key} = {value}")
print("\nList Types:")
if args.ids:
print(f" IDs: {args.ids}")
if args.ratios:
print(f" Ratios: {args.ratios}")
# Clean up file handles
if args.output:
args.output.close()
if args.input:
args.input.close()
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,164 @@
#!/usr/bin/env python3
"""
Variadic argument patterns (nargs: ?, *, +, number).
Usage:
python variadic-args.py file1.txt file2.txt file3.txt
python variadic-args.py --output result.json file1.txt file2.txt
python variadic-args.py --include *.py --exclude test_*.py
"""
import argparse
import sys
from pathlib import Path
def main():
parser = argparse.ArgumentParser(
description='Variadic argument patterns',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# ===== nargs='?' (optional, 0 or 1) =====
parser.add_argument(
'--output',
nargs='?',
const='default.json', # Used if flag present but no value
default=None, # Used if flag not present
help='Output file (default: stdout, or default.json if flag present)'
)
parser.add_argument(
'--config',
nargs='?',
const='config.yaml',
help='Configuration file (default: config.yaml if flag present)'
)
# ===== nargs='*' (zero or more) =====
parser.add_argument(
'--include',
nargs='*',
default=[],
help='Include patterns (zero or more)'
)
parser.add_argument(
'--exclude',
nargs='*',
default=[],
help='Exclude patterns (zero or more)'
)
parser.add_argument(
'--tags',
nargs='*',
metavar='TAG',
help='Tags to apply'
)
# ===== nargs='+' (one or more, required) =====
parser.add_argument(
'files',
nargs='+',
type=Path,
help='Input files (at least one required)'
)
parser.add_argument(
'--servers',
nargs='+',
metavar='SERVER',
help='Server addresses (at least one required if specified)'
)
# ===== nargs=N (exact number) =====
parser.add_argument(
'--coordinates',
nargs=2,
type=float,
metavar=('LAT', 'LON'),
help='Coordinates as latitude longitude'
)
parser.add_argument(
'--range',
nargs=2,
type=int,
metavar=('START', 'END'),
help='Range as start end'
)
parser.add_argument(
'--rgb',
nargs=3,
type=int,
metavar=('R', 'G', 'B'),
help='RGB color values (0-255)'
)
# ===== Remainder arguments =====
parser.add_argument(
'--command',
nargs=argparse.REMAINDER,
help='Command and arguments to pass through'
)
# Parse arguments
args = parser.parse_args()
# Display results
print("Variadic Arguments Results:")
print("\nnargs='?' (optional):")
print(f" Output: {args.output}")
print(f" Config: {args.config}")
print("\nnargs='*' (zero or more):")
print(f" Include Patterns: {args.include if args.include else '(none)'}")
print(f" Exclude Patterns: {args.exclude if args.exclude else '(none)'}")
print(f" Tags: {args.tags if args.tags else '(none)'}")
print("\nnargs='+' (one or more):")
print(f" Files ({len(args.files)}):")
for f in args.files:
print(f" - {f}")
if args.servers:
print(f" Servers ({len(args.servers)}):")
for s in args.servers:
print(f" - {s}")
print("\nnargs=N (exact number):")
if args.coordinates:
lat, lon = args.coordinates
print(f" Coordinates: {lat}, {lon}")
if args.range:
start, end = args.range
print(f" Range: {start} to {end}")
if args.rgb:
r, g, b = args.rgb
print(f" RGB Color: rgb({r}, {g}, {b})")
print("\nRemaining arguments:")
if args.command:
print(f" Command: {' '.join(args.command)}")
# Example usage
print("\nExample Processing:")
print(f"Processing {len(args.files)} file(s)...")
if args.include:
print(f"Including patterns: {', '.join(args.include)}")
if args.exclude:
print(f"Excluding patterns: {', '.join(args.exclude)}")
if args.output:
print(f"Output will be written to: {args.output}")
else:
print("Output will be written to: stdout")
return 0
if __name__ == '__main__':
sys.exit(main())