Linting
The lint package provides code analysis functionality for Scriptling scripts. It can detect syntax errors and potential issues without executing the code, making it safe for untrusted input.
Basic Linting
import "github.com/paularlott/scriptling/lint"
// Lint inline code
result := lint.Lint(`x = 1 + 2`, nil)
if result.HasIssues() {
fmt.Println(result.String())
}Linting Files
// Lint a single file
result, err := lint.LintFile("script.py")
if err != nil {
log.Fatal(err)
}
if result.HasErrors {
fmt.Println(result.String())
os.Exit(1)
}
// Lint multiple files
result, err = lint.LintFiles([]string{"a.py", "b.py", "c.py"})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Checked %d files, found %d issues\n",
result.FilesChecked, len(result.Errors))Lint Options
result := lint.Lint(code, &lint.Options{
Filename: "myscript.py", // Used for error messages
})LintError Type
Each issue found is represented as a LintError:
type LintError struct {
File string // Source file name (may be empty)
Line int // 1-based line number
Column int // 1-based column (0 if not available)
Message string // Human-readable description
Severity Severity // "error", "warning", or "info"
Code string // Optional error code
}Result Type
The Result type contains all findings:
type Result struct {
Errors []LintError // All issues found
FilesChecked int // Number of files linted
HasErrors bool // True if any severity="error" found
}Processing Lint Errors
result, _ := lint.LintFile("script.py")
for _, err := range result.Errors {
fmt.Printf("%s:%d: %s (%s)\n",
err.File, err.Line, err.Message, err.Severity)
// Check severity
if err.Severity == lint.SeverityError {
// Handle errors
} else if err.Severity == lint.SeverityWarning {
// Handle warnings
}
}
// Quick checks
if result.HasErrors {
fmt.Println("Script has syntax errors")
}
if result.HasIssues() {
fmt.Println("Script has lint issues")
}JSON Output
For integration with other tools, you can output results as JSON:
import "encoding/json"
result, _ := lint.LintFile("script.py")
output, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(output))Output:
{
"errors": [
{
"file": "script.py",
"line": 3,
"message": "expected token COLON",
"severity": "error",
"code": "parse-error"
}
],
"files_checked": 1,
"has_errors": true
}Use Cases
- Pre-execution validation: Check scripts before running them
- CI/CD integration: Fail pipelines on syntax errors
- Editor integration: Real-time syntax checking
- Security scanning: Validate untrusted scripts without execution
// Example: Validate script before execution
func safeExecute(p *scriptling.Scriptling, code string) (object.Object, error) {
// First, lint the code
result := lint.Lint(code, nil)
if result.HasErrors {
return nil, fmt.Errorf("script has syntax errors:\n%s", result.String())
}
// Safe to execute
return p.Eval(code)
}Complete Example
package main
import (
"fmt"
"log"
"os"
"github.com/paularlott/scriptling"
"github.com/paularlott/scriptling/lint"
"github.com/paularlott/scriptling/stdlib"
)
func main() {
// Script to validate and execute
script := `
def calculate(a, b):
return a + b
result = calculate(10, 20)
`
// Step 1: Lint the script
result := lint.Lint(script, &lint.Options{Filename: "calculator.py"})
if result.HasErrors {
fmt.Println("Script has syntax errors:")
for _, err := range result.Errors {
fmt.Printf(" %s:%d: %s\n", err.File, err.Line, err.Message)
}
os.Exit(1)
}
fmt.Printf("Lint passed: %d file(s) checked, %d issue(s) found\n",
result.FilesChecked, len(result.Errors))
// Step 2: Execute the script
p := scriptling.New()
stdlib.RegisterAll(p)
_, err := p.Eval(script)
if err != nil {
log.Fatal(err)
}
// Get the result
if val, err := p.GetVarAsInt("result"); err == nil {
fmt.Printf("Result: %d\n", val)
}
}