Initial commit
This commit is contained in:
201
skills/argparse-patterns/templates/argparse-to-commander.ts
Normal file
201
skills/argparse-patterns/templates/argparse-to-commander.ts
Normal 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
|
||||
*/
|
||||
243
skills/argparse-patterns/templates/argument-groups.py
Executable file
243
skills/argparse-patterns/templates/argument-groups.py
Executable 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())
|
||||
93
skills/argparse-patterns/templates/basic-parser.py
Executable file
93
skills/argparse-patterns/templates/basic-parser.py
Executable 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())
|
||||
162
skills/argparse-patterns/templates/boolean-flags.py
Executable file
162
skills/argparse-patterns/templates/boolean-flags.py
Executable 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())
|
||||
197
skills/argparse-patterns/templates/choices-validation.py
Executable file
197
skills/argparse-patterns/templates/choices-validation.py
Executable 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())
|
||||
188
skills/argparse-patterns/templates/custom-actions.py
Executable file
188
skills/argparse-patterns/templates/custom-actions.py
Executable 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())
|
||||
175
skills/argparse-patterns/templates/mutually-exclusive.py
Executable file
175
skills/argparse-patterns/templates/mutually-exclusive.py
Executable 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())
|
||||
219
skills/argparse-patterns/templates/nested-subparser.py
Executable file
219
skills/argparse-patterns/templates/nested-subparser.py
Executable 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)
|
||||
123
skills/argparse-patterns/templates/subparser-pattern.py
Executable file
123
skills/argparse-patterns/templates/subparser-pattern.py
Executable 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)
|
||||
257
skills/argparse-patterns/templates/type-coercion.py
Executable file
257
skills/argparse-patterns/templates/type-coercion.py
Executable 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())
|
||||
164
skills/argparse-patterns/templates/variadic-args.py
Executable file
164
skills/argparse-patterns/templates/variadic-args.py
Executable 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())
|
||||
Reference in New Issue
Block a user