Initial commit
This commit is contained in:
105
skills/typer-patterns/scripts/convert-argparse.sh
Executable file
105
skills/typer-patterns/scripts/convert-argparse.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
# Helper script to convert argparse CLI to Typer (guidance)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
cat << 'EOF'
|
||||
Converting argparse to Typer
|
||||
=============================
|
||||
|
||||
This script provides guidance on converting argparse CLIs to Typer.
|
||||
|
||||
Common Conversions:
|
||||
-------------------
|
||||
|
||||
1. Argument Parser Setup
|
||||
argparse: parser = ArgumentParser()
|
||||
Typer: app = typer.Typer()
|
||||
|
||||
2. Positional Arguments
|
||||
argparse: parser.add_argument('name')
|
||||
Typer: name: str = typer.Argument(...)
|
||||
|
||||
3. Optional Arguments
|
||||
argparse: parser.add_argument('--flag', '-f')
|
||||
Typer: flag: bool = typer.Option(False, '--flag', '-f')
|
||||
|
||||
4. Required Options
|
||||
argparse: parser.add_argument('--name', required=True)
|
||||
Typer: name: str = typer.Option(...)
|
||||
|
||||
5. Default Values
|
||||
argparse: parser.add_argument('--count', default=10)
|
||||
Typer: count: int = typer.Option(10)
|
||||
|
||||
6. Type Conversion
|
||||
argparse: parser.add_argument('--port', type=int)
|
||||
Typer: port: int = typer.Option(8000)
|
||||
|
||||
7. Choices/Enums
|
||||
argparse: parser.add_argument('--format', choices=['json', 'yaml'])
|
||||
Typer: format: Format = typer.Option(Format.json) # Format is Enum
|
||||
|
||||
8. File Arguments
|
||||
argparse: parser.add_argument('--input', type=argparse.FileType('r'))
|
||||
Typer: input: Path = typer.Option(...)
|
||||
|
||||
9. Help Text
|
||||
argparse: parser.add_argument('--name', help='User name')
|
||||
Typer: name: str = typer.Option(..., help='User name')
|
||||
|
||||
10. Subcommands
|
||||
argparse: subparsers = parser.add_subparsers()
|
||||
Typer: sub_app = typer.Typer(); app.add_typer(sub_app, name='sub')
|
||||
|
||||
Example Conversion:
|
||||
-------------------
|
||||
|
||||
BEFORE (argparse):
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('input', help='Input file')
|
||||
parser.add_argument('--output', '-o', help='Output file')
|
||||
parser.add_argument('--verbose', '-v', action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
AFTER (Typer):
|
||||
app = typer.Typer()
|
||||
|
||||
@app.command()
|
||||
def main(
|
||||
input: Path = typer.Argument(..., help='Input file'),
|
||||
output: Optional[Path] = typer.Option(None, '--output', '-o'),
|
||||
verbose: bool = typer.Option(False, '--verbose', '-v')
|
||||
) -> None:
|
||||
"""Process input file."""
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
app()
|
||||
|
||||
Benefits of Typer:
|
||||
------------------
|
||||
✓ Automatic type validation
|
||||
✓ Better IDE support with type hints
|
||||
✓ Less boilerplate code
|
||||
✓ Built-in help generation
|
||||
✓ Easier testing
|
||||
✓ Rich formatting support
|
||||
|
||||
Next Steps:
|
||||
-----------
|
||||
1. Identify all argparse patterns in your CLI
|
||||
2. Use templates from this skill as reference
|
||||
3. Convert incrementally, one command at a time
|
||||
4. Run validation: ./scripts/validate-types.sh
|
||||
5. Test thoroughly: ./scripts/test-cli.sh
|
||||
|
||||
EOF
|
||||
|
||||
echo -e "${BLUE}For specific conversion help, provide your argparse CLI code.${NC}"
|
||||
113
skills/typer-patterns/scripts/generate-cli.sh
Executable file
113
skills/typer-patterns/scripts/generate-cli.sh
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
# Generate a Typer CLI from template
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Usage
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 <template-name> <output-file> [options]
|
||||
|
||||
Templates:
|
||||
basic - Basic typed command
|
||||
enum - Enum-based options
|
||||
subapp - Sub-application structure
|
||||
factory - Factory pattern
|
||||
validation - Advanced validation
|
||||
|
||||
Options:
|
||||
--app-name NAME Set application name (default: mycli)
|
||||
--help Show this help
|
||||
|
||||
Example:
|
||||
$0 basic my_cli.py --app-name myapp
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
if [ $# -lt 2 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
TEMPLATE="$1"
|
||||
OUTPUT="$2"
|
||||
APP_NAME="mycli"
|
||||
|
||||
shift 2
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--app-name)
|
||||
APP_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Get script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TEMPLATE_DIR="$(dirname "$SCRIPT_DIR")/templates"
|
||||
|
||||
# Map template name to file
|
||||
case "$TEMPLATE" in
|
||||
basic)
|
||||
TEMPLATE_FILE="$TEMPLATE_DIR/basic-typed-command.py"
|
||||
;;
|
||||
enum)
|
||||
TEMPLATE_FILE="$TEMPLATE_DIR/enum-options.py"
|
||||
;;
|
||||
subapp)
|
||||
TEMPLATE_FILE="$TEMPLATE_DIR/sub-app-structure.py"
|
||||
;;
|
||||
factory)
|
||||
TEMPLATE_FILE="$TEMPLATE_DIR/typer-instance.py"
|
||||
;;
|
||||
validation)
|
||||
TEMPLATE_FILE="$TEMPLATE_DIR/advanced-validation.py"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown template: $TEMPLATE"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check if template exists
|
||||
if [ ! -f "$TEMPLATE_FILE" ]; then
|
||||
echo -e "${YELLOW}✗ Template not found: $TEMPLATE_FILE${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy and customize template
|
||||
cp "$TEMPLATE_FILE" "$OUTPUT"
|
||||
|
||||
# Replace app name if not default
|
||||
if [ "$APP_NAME" != "mycli" ]; then
|
||||
sed -i "s/mycli/$APP_NAME/g" "$OUTPUT"
|
||||
sed -i "s/myapp/$APP_NAME/g" "$OUTPUT"
|
||||
fi
|
||||
|
||||
# Make executable
|
||||
chmod +x "$OUTPUT"
|
||||
|
||||
echo -e "${GREEN}✓ Generated CLI: $OUTPUT${NC}"
|
||||
echo " Template: $TEMPLATE"
|
||||
echo " App name: $APP_NAME"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Review and customize the generated file"
|
||||
echo " 2. Install dependencies: pip install typer"
|
||||
echo " 3. Run: python $OUTPUT --help"
|
||||
echo " 4. Validate: ./scripts/validate-types.sh $OUTPUT"
|
||||
138
skills/typer-patterns/scripts/test-cli.sh
Executable file
138
skills/typer-patterns/scripts/test-cli.sh
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/bin/bash
|
||||
# Test Typer CLI functionality
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Check if file provided
|
||||
if [ $# -eq 0 ]; then
|
||||
echo -e "${RED}✗ Usage: $0 <python-file>${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CLI_FILE="$1"
|
||||
|
||||
# Check if file exists
|
||||
if [ ! -f "$CLI_FILE" ]; then
|
||||
echo -e "${RED}✗ File not found: $CLI_FILE${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Testing Typer CLI: $CLI_FILE"
|
||||
echo "========================================"
|
||||
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
|
||||
# Test: Help command
|
||||
echo "Test: Help output"
|
||||
if python "$CLI_FILE" --help > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ Help command works${NC}"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
echo -e "${RED}✗ Help command failed${NC}"
|
||||
((TESTS_FAILED++))
|
||||
fi
|
||||
|
||||
# Test: Version flag (if supported)
|
||||
echo "Test: Version flag"
|
||||
if python "$CLI_FILE" --version > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ Version flag works${NC}"
|
||||
((TESTS_PASSED++))
|
||||
elif grep -q "version" "$CLI_FILE"; then
|
||||
echo -e "${YELLOW}⚠ Version defined but flag not working${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ No version flag (optional)${NC}"
|
||||
fi
|
||||
|
||||
# Test: Check for syntax errors
|
||||
echo "Test: Python syntax"
|
||||
if python -m py_compile "$CLI_FILE" 2>/dev/null; then
|
||||
echo -e "${GREEN}✓ No syntax errors${NC}"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
echo -e "${RED}✗ Syntax errors detected${NC}"
|
||||
((TESTS_FAILED++))
|
||||
fi
|
||||
|
||||
# Test: Type checking with mypy (if available)
|
||||
echo "Test: Type checking"
|
||||
if command -v mypy &> /dev/null; then
|
||||
if mypy "$CLI_FILE" --ignore-missing-imports 2>/dev/null; then
|
||||
echo -e "${GREEN}✓ Type checking passed${NC}"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Type checking warnings/errors${NC}"
|
||||
echo " Run: mypy $CLI_FILE --ignore-missing-imports"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠ mypy not installed (skipping type check)${NC}"
|
||||
fi
|
||||
|
||||
# Test: Linting with ruff (if available)
|
||||
echo "Test: Code linting"
|
||||
if command -v ruff &> /dev/null; then
|
||||
if ruff check "$CLI_FILE" --select E,W,F 2>/dev/null; then
|
||||
echo -e "${GREEN}✓ Linting passed${NC}"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Linting warnings/errors${NC}"
|
||||
echo " Run: ruff check $CLI_FILE"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠ ruff not installed (skipping linting)${NC}"
|
||||
fi
|
||||
|
||||
# Test: Import check
|
||||
echo "Test: Import dependencies"
|
||||
if python -c "import sys; sys.path.insert(0, '.'); exec(open('$CLI_FILE').read().split('if __name__')[0])" 2>/dev/null; then
|
||||
echo -e "${GREEN}✓ All imports successful${NC}"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
echo -e "${RED}✗ Import errors detected${NC}"
|
||||
echo " Check that all dependencies are installed"
|
||||
((TESTS_FAILED++))
|
||||
fi
|
||||
|
||||
# Test: Check for common patterns
|
||||
echo "Test: Typer patterns"
|
||||
PATTERN_ISSUES=0
|
||||
|
||||
if ! grep -q "@app.command()" "$CLI_FILE"; then
|
||||
echo -e "${YELLOW} ⚠ No @app.command() decorators found${NC}"
|
||||
((PATTERN_ISSUES++))
|
||||
fi
|
||||
|
||||
if ! grep -q "typer.Typer()" "$CLI_FILE"; then
|
||||
echo -e "${YELLOW} ⚠ No Typer() instance found${NC}"
|
||||
((PATTERN_ISSUES++))
|
||||
fi
|
||||
|
||||
if ! grep -q "if __name__ == \"__main__\":" "$CLI_FILE"; then
|
||||
echo -e "${YELLOW} ⚠ Missing if __name__ == '__main__' guard${NC}"
|
||||
((PATTERN_ISSUES++))
|
||||
fi
|
||||
|
||||
if [ $PATTERN_ISSUES -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Common patterns found${NC}"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Some patterns missing${NC}"
|
||||
fi
|
||||
|
||||
echo "========================================"
|
||||
echo "Tests passed: $TESTS_PASSED"
|
||||
echo "Tests failed: $TESTS_FAILED"
|
||||
|
||||
if [ $TESTS_FAILED -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ All tests passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}✗ Some tests failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
135
skills/typer-patterns/scripts/validate-skill.sh
Executable file
135
skills/typer-patterns/scripts/validate-skill.sh
Executable file
@@ -0,0 +1,135 @@
|
||||
#!/bin/bash
|
||||
# Validate typer-patterns skill structure
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ERRORS=0
|
||||
|
||||
echo "Validating typer-patterns skill..."
|
||||
echo "========================================"
|
||||
|
||||
# Check SKILL.md exists
|
||||
echo "Checking SKILL.md..."
|
||||
if [ ! -f "$SKILL_DIR/SKILL.md" ]; then
|
||||
echo -e "${RED}✗ SKILL.md not found${NC}"
|
||||
((ERRORS++))
|
||||
else
|
||||
echo -e "${GREEN}✓ SKILL.md exists${NC}"
|
||||
|
||||
# Check frontmatter starts at line 1
|
||||
FIRST_LINE=$(head -n 1 "$SKILL_DIR/SKILL.md")
|
||||
if [ "$FIRST_LINE" != "---" ]; then
|
||||
echo -e "${RED}✗ SKILL.md frontmatter must start at line 1 (found: $FIRST_LINE)${NC}"
|
||||
((ERRORS++))
|
||||
else
|
||||
echo -e "${GREEN}✓ Frontmatter starts at line 1${NC}"
|
||||
fi
|
||||
|
||||
# Check required frontmatter fields
|
||||
if grep -q "^name: " "$SKILL_DIR/SKILL.md"; then
|
||||
echo -e "${GREEN}✓ name field present${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ name field missing${NC}"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
if grep -q "^description: " "$SKILL_DIR/SKILL.md"; then
|
||||
echo -e "${GREEN}✓ description field present${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ description field missing${NC}"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for "Use when" in description
|
||||
if grep "^description: " "$SKILL_DIR/SKILL.md" | grep -q "Use when"; then
|
||||
echo -e "${GREEN}✓ Description contains 'Use when' triggers${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Description should include 'Use when' trigger contexts${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check templates directory
|
||||
echo "Checking templates..."
|
||||
TEMPLATE_COUNT=$(find "$SKILL_DIR/templates" -name "*.py" 2>/dev/null | wc -l)
|
||||
if [ "$TEMPLATE_COUNT" -ge 4 ]; then
|
||||
echo -e "${GREEN}✓ Found $TEMPLATE_COUNT templates (minimum 4)${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Found $TEMPLATE_COUNT templates (need at least 4)${NC}"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check scripts directory
|
||||
echo "Checking scripts..."
|
||||
SCRIPT_COUNT=$(find "$SKILL_DIR/scripts" -name "*.sh" 2>/dev/null | wc -l)
|
||||
if [ "$SCRIPT_COUNT" -ge 3 ]; then
|
||||
echo -e "${GREEN}✓ Found $SCRIPT_COUNT scripts (minimum 3)${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Found $SCRIPT_COUNT scripts (need at least 3)${NC}"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check scripts are executable
|
||||
NONEXEC=$(find "$SKILL_DIR/scripts" -name "*.sh" ! -executable 2>/dev/null | wc -l)
|
||||
if [ "$NONEXEC" -gt 0 ]; then
|
||||
echo -e "${YELLOW}⚠ $NONEXEC scripts are not executable${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✓ All scripts are executable${NC}"
|
||||
fi
|
||||
|
||||
# Check examples directory
|
||||
echo "Checking examples..."
|
||||
EXAMPLE_COUNT=$(find "$SKILL_DIR/examples" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)
|
||||
if [ "$EXAMPLE_COUNT" -ge 3 ]; then
|
||||
echo -e "${GREEN}✓ Found $EXAMPLE_COUNT example directories (minimum 3)${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Found $EXAMPLE_COUNT examples (need at least 3)${NC}"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for README files in examples
|
||||
for example_dir in "$SKILL_DIR/examples"/*; do
|
||||
if [ -d "$example_dir" ]; then
|
||||
example_name=$(basename "$example_dir")
|
||||
if [ -f "$example_dir/README.md" ]; then
|
||||
echo -e "${GREEN}✓ Example $example_name has README.md${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Example $example_name missing README.md${NC}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for hardcoded secrets (basic check)
|
||||
echo "Checking for hardcoded secrets..."
|
||||
if grep -r "sk-[a-zA-Z0-9]" "$SKILL_DIR" 2>/dev/null | grep -v "validate-skill.sh" | grep -q .; then
|
||||
echo -e "${RED}✗ Possible API keys detected${NC}"
|
||||
((ERRORS++))
|
||||
else
|
||||
echo -e "${GREEN}✓ No obvious API keys detected${NC}"
|
||||
fi
|
||||
|
||||
# Check SKILL.md length
|
||||
if [ -f "$SKILL_DIR/SKILL.md" ]; then
|
||||
LINE_COUNT=$(wc -l < "$SKILL_DIR/SKILL.md")
|
||||
if [ "$LINE_COUNT" -gt 150 ]; then
|
||||
echo -e "${YELLOW}⚠ SKILL.md is $LINE_COUNT lines (consider keeping under 150)${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✓ SKILL.md length is reasonable ($LINE_COUNT lines)${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "========================================"
|
||||
|
||||
if [ $ERRORS -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Validation passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}✗ Validation failed with $ERRORS error(s)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
123
skills/typer-patterns/scripts/validate-types.sh
Executable file
123
skills/typer-patterns/scripts/validate-types.sh
Executable file
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
# Validate type hints in Typer CLI files
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check if file provided
|
||||
if [ $# -eq 0 ]; then
|
||||
echo -e "${RED}✗ Usage: $0 <python-file>${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILE="$1"
|
||||
|
||||
# Check if file exists
|
||||
if [ ! -f "$FILE" ]; then
|
||||
echo -e "${RED}✗ File not found: $FILE${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Validating type hints in: $FILE"
|
||||
echo "----------------------------------------"
|
||||
|
||||
ERRORS=0
|
||||
|
||||
# Check for type hints on function parameters
|
||||
echo "Checking function parameter type hints..."
|
||||
UNTYPED_PARAMS=$(grep -n "def " "$FILE" | while read -r line; do
|
||||
LINE_NUM=$(echo "$line" | cut -d: -f1)
|
||||
LINE_CONTENT=$(echo "$line" | cut -d: -f2-)
|
||||
|
||||
# Extract parameter list
|
||||
PARAMS=$(echo "$LINE_CONTENT" | sed -n 's/.*def [^(]*(\(.*\)).*/\1/p')
|
||||
|
||||
# Check if any parameter lacks type hint (excluding self, ctx)
|
||||
if echo "$PARAMS" | grep -qE '[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*=' | \
|
||||
grep -vE '[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*:[[:space:]]*[a-zA-Z]'; then
|
||||
echo " Line $LINE_NUM: Missing type hint"
|
||||
((ERRORS++))
|
||||
fi
|
||||
done)
|
||||
|
||||
if [ -z "$UNTYPED_PARAMS" ]; then
|
||||
echo -e "${GREEN}✓ All parameters have type hints${NC}"
|
||||
else
|
||||
echo -e "${RED}$UNTYPED_PARAMS${NC}"
|
||||
fi
|
||||
|
||||
# Check for return type hints
|
||||
echo "Checking function return type hints..."
|
||||
MISSING_RETURN=$(grep -n "def " "$FILE" | grep -v "-> " | while read -r line; do
|
||||
LINE_NUM=$(echo "$line" | cut -d: -f1)
|
||||
echo " Line $LINE_NUM: Missing return type hint"
|
||||
((ERRORS++))
|
||||
done)
|
||||
|
||||
if [ -z "$MISSING_RETURN" ]; then
|
||||
echo -e "${GREEN}✓ All functions have return type hints${NC}"
|
||||
else
|
||||
echo -e "${RED}$MISSING_RETURN${NC}"
|
||||
fi
|
||||
|
||||
# Check for Typer imports
|
||||
echo "Checking Typer imports..."
|
||||
if ! grep -q "^import typer" "$FILE" && ! grep -q "^from typer import" "$FILE"; then
|
||||
echo -e "${RED}✗ Missing typer import${NC}"
|
||||
((ERRORS++))
|
||||
else
|
||||
echo -e "${GREEN}✓ Typer imported${NC}"
|
||||
fi
|
||||
|
||||
# Check for typing imports when using Optional, Union, etc.
|
||||
echo "Checking typing imports..."
|
||||
if grep -qE "Optional|Union|List|Dict|Tuple" "$FILE"; then
|
||||
if ! grep -q "from typing import" "$FILE"; then
|
||||
echo -e "${YELLOW}⚠ Using typing types but missing typing import${NC}"
|
||||
((ERRORS++))
|
||||
else
|
||||
echo -e "${GREEN}✓ Typing imports present${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠ No typing annotations detected${NC}"
|
||||
fi
|
||||
|
||||
# Check for Path usage
|
||||
echo "Checking Path usage for file parameters..."
|
||||
if grep -qE "file|path|dir" "$FILE" | grep -i "str.*=.*typer"; then
|
||||
echo -e "${YELLOW}⚠ Consider using Path type instead of str for file/path parameters${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✓ No obvious Path type issues${NC}"
|
||||
fi
|
||||
|
||||
# Check for Enum usage
|
||||
echo "Checking for Enum patterns..."
|
||||
if grep -qE "class.*\(str, Enum\)" "$FILE"; then
|
||||
echo -e "${GREEN}✓ Enum classes found${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ No Enum classes detected (consider for constrained choices)${NC}"
|
||||
fi
|
||||
|
||||
# Check for docstrings
|
||||
echo "Checking command docstrings..."
|
||||
MISSING_DOCS=$(grep -A1 "def " "$FILE" | grep -v '"""' | wc -l)
|
||||
if [ "$MISSING_DOCS" -gt 0 ]; then
|
||||
echo -e "${YELLOW}⚠ Some functions may be missing docstrings${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✓ Docstrings appear present${NC}"
|
||||
fi
|
||||
|
||||
echo "----------------------------------------"
|
||||
|
||||
if [ $ERRORS -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Validation passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}✗ Validation failed with $ERRORS error(s)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user