Initial commit
This commit is contained in:
151
skills/argparse-patterns/scripts/convert-to-click.sh
Executable file
151
skills/argparse-patterns/scripts/convert-to-click.sh
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env bash
|
||||
# Convert argparse code to Click decorators
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Convert argparse parser to Click decorators
|
||||
|
||||
Usage: $(basename "$0") ARGPARSE_FILE [OUTPUT_FILE]
|
||||
|
||||
Performs basic conversion from argparse to Click:
|
||||
- ArgumentParser → @click.group() or @click.command()
|
||||
- add_argument() → @click.option() or @click.argument()
|
||||
- add_subparsers() → @group.command()
|
||||
- choices=[] → type=click.Choice([])
|
||||
- action='store_true' → is_flag=True
|
||||
|
||||
Note: This is a basic converter. Manual refinement may be needed.
|
||||
|
||||
Examples:
|
||||
$(basename "$0") mycli.py mycli_click.py
|
||||
$(basename "$0") basic-parser.py
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
ARGPARSE_FILE="$1"
|
||||
OUTPUT_FILE="${2:-}"
|
||||
|
||||
if [ ! -f "$ARGPARSE_FILE" ]; then
|
||||
echo "Error: File not found: $ARGPARSE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Converting argparse to Click: $ARGPARSE_FILE"
|
||||
|
||||
convert_to_click() {
|
||||
cat <<'EOF'
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Converted from argparse to Click
|
||||
|
||||
This is a basic conversion. You may need to adjust:
|
||||
- Argument order and grouping
|
||||
- Type conversions
|
||||
- Custom validators
|
||||
- Error handling
|
||||
"""
|
||||
|
||||
import click
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.version_option(version='1.0.0')
|
||||
@click.pass_context
|
||||
def cli(ctx):
|
||||
"""CLI tool converted from argparse"""
|
||||
ctx.ensure_object(dict)
|
||||
|
||||
|
||||
# Convert your subcommands here
|
||||
# Example pattern:
|
||||
#
|
||||
# @cli.command()
|
||||
# @click.argument('target')
|
||||
# @click.option('--env', type=click.Choice(['dev', 'staging', 'prod']), default='dev')
|
||||
# @click.option('--force', is_flag=True, help='Force operation')
|
||||
# def deploy(target, env, force):
|
||||
# """Deploy to environment"""
|
||||
# click.echo(f"Deploying {target} to {env}")
|
||||
# if force:
|
||||
# click.echo("Force mode enabled")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli()
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "# Detected argparse patterns:"
|
||||
echo ""
|
||||
|
||||
# Detect subcommands
|
||||
if grep -q "add_subparsers(" "$ARGPARSE_FILE"; then
|
||||
echo "# Subcommands found:"
|
||||
grep -oP "add_parser\('\K[^']+(?=')" "$ARGPARSE_FILE" | while read -r cmd; do
|
||||
echo "# - $cmd"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Detect arguments
|
||||
if grep -q "add_argument(" "$ARGPARSE_FILE"; then
|
||||
echo "# Arguments found:"
|
||||
grep "add_argument(" "$ARGPARSE_FILE" | grep -oP "'[^']+'" | head -n1 | while read -r arg; do
|
||||
echo "# $arg"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Detect choices
|
||||
if grep -q "choices=" "$ARGPARSE_FILE"; then
|
||||
echo "# Choices found (convert to click.Choice):"
|
||||
grep -oP "choices=\[\K[^\]]+(?=\])" "$ARGPARSE_FILE" | while read -r choices; do
|
||||
echo "# [$choices]"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Provide conversion hints
|
||||
cat <<'EOF'
|
||||
|
||||
# Conversion Guide:
|
||||
#
|
||||
# argparse → Click
|
||||
# ----------------------------------|--------------------------------
|
||||
# parser.add_argument('arg') → @click.argument('arg')
|
||||
# parser.add_argument('--opt') → @click.option('--opt')
|
||||
# action='store_true' → is_flag=True
|
||||
# choices=['a', 'b'] → type=click.Choice(['a', 'b'])
|
||||
# type=int → type=int
|
||||
# required=True → required=True
|
||||
# default='value' → default='value'
|
||||
# help='...' → help='...'
|
||||
#
|
||||
# For nested subcommands:
|
||||
# Use @group.command() decorator
|
||||
#
|
||||
# For more info: https://click.palletsprojects.com/
|
||||
EOF
|
||||
}
|
||||
|
||||
# Output
|
||||
if [ -n "$OUTPUT_FILE" ]; then
|
||||
convert_to_click > "$OUTPUT_FILE"
|
||||
chmod +x "$OUTPUT_FILE"
|
||||
echo "Converted to Click: $OUTPUT_FILE"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Review the generated file"
|
||||
echo " 2. Add your command implementations"
|
||||
echo " 3. Install Click: pip install click"
|
||||
echo " 4. Test: python $OUTPUT_FILE --help"
|
||||
else
|
||||
convert_to_click
|
||||
fi
|
||||
213
skills/argparse-patterns/scripts/generate-parser.sh
Executable file
213
skills/argparse-patterns/scripts/generate-parser.sh
Executable file
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generate argparse parser from specification
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TEMPLATES_DIR="$(dirname "$SCRIPT_DIR")/templates"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Generate argparse parser from specification
|
||||
|
||||
Usage: $(basename "$0") [OPTIONS]
|
||||
|
||||
Options:
|
||||
-n, --name NAME Parser name (required)
|
||||
-d, --description DESC Parser description
|
||||
-s, --subcommands Include subcommands
|
||||
-c, --choices Include choice validation
|
||||
-g, --groups Include argument groups
|
||||
-o, --output FILE Output file (default: stdout)
|
||||
-h, --help Show this help
|
||||
|
||||
Examples:
|
||||
$(basename "$0") -n mycli -d "My CLI tool" -o mycli.py
|
||||
$(basename "$0") -n deploy -s -c -o deploy.py
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
NAME=""
|
||||
DESCRIPTION=""
|
||||
SUBCOMMANDS=false
|
||||
CHOICES=false
|
||||
GROUPS=false
|
||||
OUTPUT=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-n|--name)
|
||||
NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
-d|--description)
|
||||
DESCRIPTION="$2"
|
||||
shift 2
|
||||
;;
|
||||
-s|--subcommands)
|
||||
SUBCOMMANDS=true
|
||||
shift
|
||||
;;
|
||||
-c|--choices)
|
||||
CHOICES=true
|
||||
shift
|
||||
;;
|
||||
-g|--groups)
|
||||
GROUPS=true
|
||||
shift
|
||||
;;
|
||||
-o|--output)
|
||||
OUTPUT="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown option $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$NAME" ]; then
|
||||
echo "Error: --name is required"
|
||||
usage
|
||||
fi
|
||||
|
||||
# Set defaults
|
||||
DESCRIPTION="${DESCRIPTION:-$NAME CLI tool}"
|
||||
|
||||
# Generate parser
|
||||
generate_parser() {
|
||||
cat <<EOF
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
$DESCRIPTION
|
||||
|
||||
Generated by generate-parser.sh
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='$DESCRIPTION',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--version',
|
||||
action='version',
|
||||
version='1.0.0'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--verbose', '-v',
|
||||
action='store_true',
|
||||
help='Enable verbose output'
|
||||
)
|
||||
|
||||
EOF
|
||||
|
||||
if [ "$GROUPS" = true ]; then
|
||||
cat <<EOF
|
||||
# Configuration group
|
||||
config_group = parser.add_argument_group(
|
||||
'configuration',
|
||||
'Configuration options'
|
||||
)
|
||||
|
||||
config_group.add_argument(
|
||||
'--config',
|
||||
help='Configuration file'
|
||||
)
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ "$SUBCOMMANDS" = true ]; then
|
||||
cat <<EOF
|
||||
# Create subparsers
|
||||
subparsers = parser.add_subparsers(
|
||||
dest='command',
|
||||
help='Available commands',
|
||||
required=True
|
||||
)
|
||||
|
||||
# Example subcommand
|
||||
cmd_parser = subparsers.add_parser(
|
||||
'run',
|
||||
help='Run the application'
|
||||
)
|
||||
|
||||
cmd_parser.add_argument(
|
||||
'target',
|
||||
help='Target to run'
|
||||
)
|
||||
|
||||
EOF
|
||||
|
||||
if [ "$CHOICES" = true ]; then
|
||||
cat <<EOF
|
||||
cmd_parser.add_argument(
|
||||
'--env',
|
||||
choices=['development', 'staging', 'production'],
|
||||
default='development',
|
||||
help='Environment (default: %(default)s)'
|
||||
)
|
||||
|
||||
EOF
|
||||
fi
|
||||
else
|
||||
cat <<EOF
|
||||
# Arguments
|
||||
parser.add_argument(
|
||||
'target',
|
||||
help='Target to process'
|
||||
)
|
||||
|
||||
EOF
|
||||
|
||||
if [ "$CHOICES" = true ]; then
|
||||
cat <<EOF
|
||||
parser.add_argument(
|
||||
'--env',
|
||||
choices=['development', 'staging', 'production'],
|
||||
default='development',
|
||||
help='Environment (default: %(default)s)'
|
||||
)
|
||||
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
# Parse arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
# Display configuration
|
||||
if args.verbose:
|
||||
print("Verbose mode enabled")
|
||||
print(f"Arguments: {args}")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
EOF
|
||||
}
|
||||
|
||||
# Output
|
||||
if [ -n "$OUTPUT" ]; then
|
||||
generate_parser > "$OUTPUT"
|
||||
chmod +x "$OUTPUT"
|
||||
echo "Generated parser: $OUTPUT"
|
||||
else
|
||||
generate_parser
|
||||
fi
|
||||
149
skills/argparse-patterns/scripts/test-parser.sh
Executable file
149
skills/argparse-patterns/scripts/test-parser.sh
Executable file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env bash
|
||||
# Test argparse parser with various argument combinations
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Test argparse parser with various arguments
|
||||
|
||||
Usage: $(basename "$0") PARSER_FILE
|
||||
|
||||
Tests:
|
||||
- Help display (--help)
|
||||
- Version display (--version)
|
||||
- Missing required arguments
|
||||
- Invalid choices
|
||||
- Type validation
|
||||
- Subcommands (if present)
|
||||
|
||||
Examples:
|
||||
$(basename "$0") mycli.py
|
||||
$(basename "$0") ../templates/basic-parser.py
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
PARSER_FILE="$1"
|
||||
|
||||
if [ ! -f "$PARSER_FILE" ]; then
|
||||
echo "Error: File not found: $PARSER_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make executable if needed
|
||||
if [ ! -x "$PARSER_FILE" ]; then
|
||||
chmod +x "$PARSER_FILE"
|
||||
fi
|
||||
|
||||
echo "Testing argparse parser: $PARSER_FILE"
|
||||
echo ""
|
||||
|
||||
PASSED=0
|
||||
FAILED=0
|
||||
|
||||
run_test() {
|
||||
local description="$1"
|
||||
shift
|
||||
local expected_result="$1"
|
||||
shift
|
||||
|
||||
echo -n "Testing: $description ... "
|
||||
|
||||
if "$PARSER_FILE" "$@" >/dev/null 2>&1; then
|
||||
result="success"
|
||||
else
|
||||
result="failure"
|
||||
fi
|
||||
|
||||
if [ "$result" = "$expected_result" ]; then
|
||||
echo "✓ PASS"
|
||||
((PASSED++))
|
||||
else
|
||||
echo "✗ FAIL (expected $expected_result, got $result)"
|
||||
((FAILED++))
|
||||
fi
|
||||
}
|
||||
|
||||
# Test --help
|
||||
run_test "Help display" "success" --help
|
||||
|
||||
# Test --version
|
||||
if grep -q "action='version'" "$PARSER_FILE"; then
|
||||
run_test "Version display" "success" --version
|
||||
fi
|
||||
|
||||
# Test with no arguments
|
||||
run_test "No arguments" "failure"
|
||||
|
||||
# Test invalid option
|
||||
run_test "Invalid option" "failure" --invalid-option
|
||||
|
||||
# Detect and test subcommands
|
||||
if grep -q "add_subparsers(" "$PARSER_FILE"; then
|
||||
echo ""
|
||||
echo "Subcommands detected, testing subcommand patterns..."
|
||||
|
||||
# Try to extract subcommand names
|
||||
subcommands=$(grep -oP "add_parser\('\K[^']+(?=')" "$PARSER_FILE" || true)
|
||||
|
||||
if [ -n "$subcommands" ]; then
|
||||
for cmd in $subcommands; do
|
||||
run_test "Subcommand: $cmd --help" "success" "$cmd" --help
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test choices if present
|
||||
if grep -q "choices=\[" "$PARSER_FILE"; then
|
||||
echo ""
|
||||
echo "Choices validation detected, testing..."
|
||||
|
||||
# Extract valid and invalid choices
|
||||
valid_choice=$(grep -oP "choices=\[\s*'([^']+)" "$PARSER_FILE" | head -n1 | grep -oP "'[^']+'" | tr -d "'" || echo "valid")
|
||||
invalid_choice="invalid_choice_12345"
|
||||
|
||||
if grep -q "add_subparsers(" "$PARSER_FILE" && [ -n "$subcommands" ]; then
|
||||
first_cmd=$(echo "$subcommands" | head -n1)
|
||||
run_test "Valid choice" "success" "$first_cmd" target --env "$valid_choice" 2>/dev/null || true
|
||||
run_test "Invalid choice" "failure" "$first_cmd" target --env "$invalid_choice" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test type validation if present
|
||||
if grep -q "type=int" "$PARSER_FILE"; then
|
||||
echo ""
|
||||
echo "Type validation detected, testing..."
|
||||
|
||||
run_test "Valid integer" "success" --port 8080 2>/dev/null || true
|
||||
run_test "Invalid integer" "failure" --port invalid 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Test boolean flags if present
|
||||
if grep -q "action='store_true'" "$PARSER_FILE"; then
|
||||
echo ""
|
||||
echo "Boolean flags detected, testing..."
|
||||
|
||||
run_test "Boolean flag present" "success" --verbose 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "Test Summary:"
|
||||
echo " Passed: $PASSED"
|
||||
echo " Failed: $FAILED"
|
||||
echo " Total: $((PASSED + FAILED))"
|
||||
|
||||
if [ $FAILED -eq 0 ]; then
|
||||
echo ""
|
||||
echo "✓ All tests passed"
|
||||
exit 0
|
||||
else
|
||||
echo ""
|
||||
echo "✗ Some tests failed"
|
||||
exit 1
|
||||
fi
|
||||
173
skills/argparse-patterns/scripts/validate-parser.sh
Executable file
173
skills/argparse-patterns/scripts/validate-parser.sh
Executable file
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env bash
|
||||
# Validate argparse parser structure and completeness
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Validate argparse parser structure
|
||||
|
||||
Usage: $(basename "$0") PARSER_FILE
|
||||
|
||||
Checks:
|
||||
- Valid Python syntax
|
||||
- Imports argparse
|
||||
- Creates ArgumentParser
|
||||
- Has main() function
|
||||
- Calls parse_args()
|
||||
- Has proper shebang
|
||||
- Has help text
|
||||
- Has version info
|
||||
|
||||
Examples:
|
||||
$(basename "$0") mycli.py
|
||||
$(basename "$0") ../templates/basic-parser.py
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
PARSER_FILE="$1"
|
||||
|
||||
if [ ! -f "$PARSER_FILE" ]; then
|
||||
echo "Error: File not found: $PARSER_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Validating argparse parser: $PARSER_FILE"
|
||||
echo ""
|
||||
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
|
||||
# Check shebang
|
||||
if head -n1 "$PARSER_FILE" | grep -q '^#!/usr/bin/env python'; then
|
||||
echo "✓ Has proper Python shebang"
|
||||
else
|
||||
echo "✗ Missing or invalid shebang"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check syntax
|
||||
if python3 -m py_compile "$PARSER_FILE" 2>/dev/null; then
|
||||
echo "✓ Valid Python syntax"
|
||||
else
|
||||
echo "✗ Invalid Python syntax"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check imports
|
||||
if grep -q "import argparse" "$PARSER_FILE"; then
|
||||
echo "✓ Imports argparse"
|
||||
else
|
||||
echo "✗ Does not import argparse"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check ArgumentParser creation
|
||||
if grep -q "ArgumentParser(" "$PARSER_FILE"; then
|
||||
echo "✓ Creates ArgumentParser"
|
||||
else
|
||||
echo "✗ Does not create ArgumentParser"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check main function
|
||||
if grep -q "^def main(" "$PARSER_FILE"; then
|
||||
echo "✓ Has main() function"
|
||||
else
|
||||
echo "⚠ No main() function found"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check parse_args call
|
||||
if grep -q "\.parse_args()" "$PARSER_FILE"; then
|
||||
echo "✓ Calls parse_args()"
|
||||
else
|
||||
echo "✗ Does not call parse_args()"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check version
|
||||
if grep -q "action='version'" "$PARSER_FILE"; then
|
||||
echo "✓ Has version info"
|
||||
else
|
||||
echo "⚠ No version info found"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check help text
|
||||
if grep -q "help=" "$PARSER_FILE"; then
|
||||
echo "✓ Has help text for arguments"
|
||||
else
|
||||
echo "⚠ No help text found"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check description
|
||||
if grep -q "description=" "$PARSER_FILE"; then
|
||||
echo "✓ Has parser description"
|
||||
else
|
||||
echo "⚠ No parser description"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check if executable
|
||||
if [ -x "$PARSER_FILE" ]; then
|
||||
echo "✓ File is executable"
|
||||
else
|
||||
echo "⚠ File is not executable (run: chmod +x $PARSER_FILE)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check subparsers if present
|
||||
if grep -q "add_subparsers(" "$PARSER_FILE"; then
|
||||
echo "✓ Has subparsers"
|
||||
|
||||
# Check if dest is set
|
||||
if grep -q "add_subparsers(.*dest=" "$PARSER_FILE"; then
|
||||
echo " ✓ Subparsers have dest set"
|
||||
else
|
||||
echo " ⚠ Subparsers missing dest parameter"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for choices
|
||||
if grep -q "choices=" "$PARSER_FILE"; then
|
||||
echo "✓ Uses choices for validation"
|
||||
fi
|
||||
|
||||
# Check for type coercion
|
||||
if grep -q "type=" "$PARSER_FILE"; then
|
||||
echo "✓ Uses type coercion"
|
||||
fi
|
||||
|
||||
# Check for argument groups
|
||||
if grep -q "add_argument_group(" "$PARSER_FILE"; then
|
||||
echo "✓ Uses argument groups"
|
||||
fi
|
||||
|
||||
# Check for mutually exclusive groups
|
||||
if grep -q "add_mutually_exclusive_group(" "$PARSER_FILE"; then
|
||||
echo "✓ Uses mutually exclusive groups"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "Validation Summary:"
|
||||
echo " Errors: $ERRORS"
|
||||
echo " Warnings: $WARNINGS"
|
||||
|
||||
if [ $ERRORS -eq 0 ]; then
|
||||
echo ""
|
||||
echo "✓ Parser validation passed"
|
||||
exit 0
|
||||
else
|
||||
echo ""
|
||||
echo "✗ Parser validation failed"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user