or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-features.mdflag-access.mdflag-definition.mdflag-types.mdflagset-methods.mdgo-integration.mdindex.mdusage-help.md
tile.json

go-integration.mddocs/

Go Flag Integration

This document covers integration between pflag and Go's standard library flag package, allowing applications to use both packages together and support third-party libraries that use standard Go flags.

Import

import (
	goflag "flag"
	"github.com/spf13/pflag"
)

Adding Go Flags to pflag

AddGoFlag

func (f *FlagSet) AddGoFlag(goflag *goflag.Flag)

Add a single flag from Go's flag package to a pflag FlagSet.

Usage:

import (
	goflag "flag"
	"github.com/spf13/pflag"
)

// Define a Go flag
var goVerbose = goflag.Bool("goverbose", false, "go verbose mode")

// Add it to pflag
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
goFlag := goflag.CommandLine.Lookup("goverbose")
fs.AddGoFlag(goFlag)

fs.Parse(os.Args[1:])

AddGoFlagSet

func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet)

Add all flags from a Go flag.FlagSet to a pflag FlagSet.

Usage:

import (
	goflag "flag"
	"github.com/spf13/pflag"
)

// Define Go flags
var (
	goHost = goflag.String("gohost", "localhost", "host address")
	goPort = goflag.Int("goport", 8080, "port number")
)

// Create pflag FlagSet and add all Go flags
fs := pflag.NewFlagSet("app", pflag.ExitOnError)
fs.AddGoFlagSet(goflag.CommandLine)

// Now can use both pflag and Go flags together
fs.StringP("output", "o", "out.txt", "output file")
fs.Parse(os.Args[1:])

Common Pattern: Supporting Go Flags in Your Application

package main

import (
	"fmt"
	goflag "flag"
	"os"
	"github.com/spf13/pflag"
)

func main() {
	// pflag flags
	var ip = pflag.IntP("port", "p", 8080, "server port")

	// Import flags from Go's flag package
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)

	// Parse using pflag
	pflag.Parse()

	fmt.Printf("Server will run on port %d\n", *ip)
}

Converting Between Flag Types

PFlagFromGoFlag

func PFlagFromGoFlag(goflag *goflag.Flag) *Flag

Create a pflag Flag from a Go flag.Flag. This allows manual conversion and inspection of Go flags.

Usage:

import (
	goflag "flag"
	"github.com/spf13/pflag"
)

// Define a Go flag
goTimeout := goflag.Duration("timeout", 30*time.Second, "request timeout")

// Convert to pflag Flag
goFlag := goflag.CommandLine.Lookup("timeout")
pflags := pflag.PFlagFromGoFlag(goFlag)

fmt.Printf("pflag Name: %s\n", pflags.Name)
fmt.Printf("pflag Usage: %s\n", pflags.Usage)
fmt.Printf("pflag Value: %s\n", pflags.Value.String())

CopyToGoFlagSet

func (f *FlagSet) CopyToGoFlagSet(newSet *goflag.FlagSet)

Copy all flags from a pflag FlagSet to a Go flag.FlagSet. This is useful when interfacing with APIs that require Go's flag.FlagSet.

Usage:

import (
	goflag "flag"
	"github.com/spf13/pflag"
)

// Define pflag flags
pfs := pflag.NewFlagSet("myapp", pflag.ExitOnError)
pfs.Int("port", 8080, "server port")
pfs.String("host", "localhost", "server host")

pfs.Parse(os.Args[1:])

// Copy to Go flag set (e.g., for a library that needs goflag.FlagSet)
goFs := goflag.NewFlagSet("goflags", goflag.ExitOnError)
pfs.CopyToGoFlagSet(goFs)

// Now goFs contains equivalent Go flags

Working with Third-Party Libraries

Many Go libraries use the standard flag package. Here's how to integrate them with pflag.

Example: Using glog with pflag

package main

import (
	goflag "flag"
	"fmt"
	"os"
	"github.com/golang/glog"
	"github.com/spf13/pflag"
)

func main() {
	// Define your pflag flags
	port := pflag.IntP("port", "p", 8080, "server port")
	config := pflag.String("config", "default.conf", "config file")

	// Add Go flags (glog uses these) to pflag
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)

	// Parse with pflag
	pflag.Parse()

	// Initialize glog (it uses Go flags internally)
	defer glog.Flush()

	glog.Info("Starting server")
	fmt.Printf("Server running on port %d with config %s\n", *port, *config)
}

// Now can use both pflag and glog flags:
// myapp -p 9090 --config=prod.conf -logtostderr=true -v=2

Example: Integrating Multiple Flag Sources

package main

import (
	goflag "flag"
	"os"
	"github.com/spf13/pflag"
)

// Simulate third-party package flags
var thirdPartyDebug = goflag.Bool("tpdebug", false, "third party debug mode")

func main() {
	// Your application flags
	fs := pflag.NewFlagSet("app", pflag.ExitOnError)
	appPort := fs.IntP("port", "p", 8080, "application port")
	appVerbose := fs.Bool("verbose", false, "verbose output")

	// Add third-party library flags
	fs.AddGoFlagSet(goflag.CommandLine)

	// Parse everything together
	fs.Parse(os.Args[1:])

	// All flags are now available
	// *appPort, *appVerbose, *thirdPartyDebug
}

Parsing Go Test Flags

pflag skips over Go test's built-in flags (starting with -test.) by default. Use ParseSkippedFlags to parse them separately.

ParseSkippedFlags

func ParseSkippedFlags(osArgs []string, goFlagSet *goflag.FlagSet) error

Explicitly parse go test flags (starting with -test.) using goflag.Parse(), since pflag.Parse() skips them by default.

Usage:

import (
	goflag "flag"
	"os"
	"testing"
	"github.com/spf13/pflag"
)

func TestMain(m *testing.M) {
	// Define custom test flags
	var customFlag = pflag.String("custom", "", "custom test flag")

	// Add Go flags to pflag
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)

	// Parse pflag flags
	pflag.Parse()

	// Parse skipped Go test flags
	pflag.ParseSkippedFlags(os.Args[1:], goflag.CommandLine)

	// Run tests
	os.Exit(m.Run())
}

// Now you can use both custom flags and go test flags:
// go test ./your/tests -run ^YourTest$ -v --custom=value

Why ParseSkippedFlags is Needed

By default, pflag doesn't parse shorthand versions of go test flags. This causes issues with commands like:

go test -v --custom-flag=value

The -v flag is skipped by pflag. Use ParseSkippedFlags to handle it:

func TestMain(m *testing.M) {
	pflag.String("custom", "", "custom flag")
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)

	// Parse pflag flags first
	pflag.Parse()

	// Then parse skipped go test flags
	pflag.ParseSkippedFlags(os.Args[1:], goflag.CommandLine)

	os.Exit(m.Run())
}

Drop-In Replacement Pattern

The simplest way to migrate from Go's flag package to pflag is the drop-in replacement pattern:

// Before (using standard flag package)
import "flag"

var port = flag.Int("port", 8080, "server port")

func main() {
	flag.Parse()
	// ...
}
// After (using pflag as drop-in replacement)
import flag "github.com/spf13/pflag"

var port = flag.Int("port", 8080, "server port")

func main() {
	flag.Parse()
	// ...
}

Exception: Flag Struct Direct Instantiation

If you directly instantiate the Flag struct, you need to add the Shorthand field:

// Standard flag package
flag := &flag.Flag{
	Name:  "myflag",
	Usage: "my flag",
	Value: myValue,
}

// pflag (note additional Shorthand field)
flag := &pflag.Flag{
	Name:      "myflag",
	Shorthand: "",  // Add this field
	Usage:     "my flag",
	Value:     myValue,
}

Complete Integration Example

Here's a complete example showing various integration patterns:

package main

import (
	"fmt"
	goflag "flag"
	"os"
	"time"
	"github.com/spf13/pflag"
)

// Third-party library flags (simulated)
var (
	libTimeout = goflag.Duration("lib-timeout", 10*time.Second, "library timeout")
	libRetries = goflag.Int("lib-retries", 3, "library retry count")
)

func main() {
	// Application flags using pflag
	port := pflag.IntP("port", "p", 8080, "server port")
	host := pflag.StringP("host", "h", "localhost", "server host")
	verbose := pflag.BoolP("verbose", "v", false, "verbose output")
	config := pflag.String("config", "default.conf", "config file path")

	// Add Go flags from third-party libraries
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)

	// Parse all flags together
	pflag.Parse()

	// Check for required values
	if *config == "" {
		fmt.Fprintln(os.Stderr, "Error: --config is required")
		pflag.Usage()
		os.Exit(1)
	}

	// Use flags from both sources
	if *verbose {
		fmt.Printf("Starting server on %s:%d\n", *host, *port)
		fmt.Printf("Config file: %s\n", *config)
		fmt.Printf("Library timeout: %v\n", *libTimeout)
		fmt.Printf("Library retries: %d\n", *libRetries)
	}

	// Process remaining arguments
	files := pflag.Args()
	if len(files) > 0 {
		fmt.Printf("Processing %d files: %v\n", len(files), files)
	}
}

// Usage:
// myapp -p 9090 -h 0.0.0.0 -v --config=prod.conf --lib-timeout=30s --lib-retries=5 file1.txt file2.txt

Compatibility Notes

Differences Between pflag and Go flag

  1. Long Form: Go flags use single dash (-flag), pflag uses double dash (--flag)
  2. Shorthand: pflag adds shorthand support with single dash (-f)
  3. Combined Shorthands: pflag allows combining boolean shorthands (-abc)
  4. Interspersing: pflag allows flags anywhere before --, Go flag stops at first non-flag
  5. Boolean Values: Both support various boolean representations
  6. Flag Struct: pflag adds Shorthand field to Flag struct

What Works the Same

  • Value types and parsing rules
  • Default values and help messages
  • Parse(), Args(), Arg(), NArg() functions
  • Flag lookup and iteration
  • Custom Value interface implementation

Migration Tips

  1. Incremental Migration: Start by aliasing import flag "github.com/spf13/pflag"
  2. Add Shorthands Gradually: Take advantage of shorthand support where it makes sense
  3. Test Thoroughly: Ensure command-line parsing behavior matches expectations
  4. Update Documentation: Clarify the new double-dash convention in user docs
  5. Keep Go Flag Support: Use AddGoFlagSet to maintain compatibility with dependencies