Implement support for unqiue config instances.

This commit adds a new type, SpewState, which can be used to create
instances with unique configuration options.  The methods of SpewState are
equivalent to the top-level functions.  Full documentation and examples
are included.
This commit is contained in:
Dave Collins 2013-01-12 12:06:59 -06:00
parent 40eb25a95d
commit 034a2a5a5e
6 changed files with 303 additions and 12 deletions

View File

@ -55,3 +55,5 @@ type ConfigState struct {
// Config is the active configuration in use by spew. The configuration
// can be changed by modifying the contents of spew.Config.
var Config ConfigState = ConfigState{Indent: " "}
var defaultConfig = ConfigState{Indent: " "}

View File

@ -58,20 +58,29 @@ printing style, use the convenience wrappers Printf, Fprintf, etc with either
Configuration Options
Configuration of spew is handled by fields in the ConfigState type. For
convenience, all of the top-level functions use a global state available
via the spew.Config global.
It is also possible to create a SpewState instance which provides a unique
ConfigState accessible via the Config method. The methods of SpewState are
equivalent to the top-level functions. This allows concurrent configuration
options. See the SpewState documentation for more details.
The following configuration options are available:
spew.Config.MaxDepth
* MaxDepth
Maximum number of levels to descend into nested data structures.
There is no limit by default.
spew.Config.Indent
* Indent
String to use for each indentation level for Dump functions.
It is a single space by default. A popular alternative is "\t".
spew.Config.DisableMethods
* DisableMethods
Disables invocation of error and Stringer interface methods.
Method invocation is enabled by default.
spew.Config.DisablePointerMethods
* DisablePointerMethods
Disables invocation of error and Stringer interface methods on types
which only accept pointer receivers from non-pointer variables.
Pointer method invocation is enabled by default.

View File

@ -281,9 +281,9 @@ func (d *dumpState) dump(v reflect.Value) {
}
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func Fdump(w io.Writer, a ...interface{}) {
// fdump is a helper function to consolidate the logic from the various public
// methods which take varying writers and config states.
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
for _, arg := range a {
if arg == nil {
w.Write(interfaceBytes)
@ -292,13 +292,19 @@ func Fdump(w io.Writer, a ...interface{}) {
continue
}
d := dumpState{w: w, cs: &Config}
d := dumpState{w: w, cs: cs}
d.pointers = make(map[uintptr]int)
d.dump(reflect.ValueOf(arg))
d.w.Write(newlineBytes)
}
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func Fdump(w io.Writer, a ...interface{}) {
fdump(&Config, w, a...)
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
@ -320,5 +326,5 @@ spew.Config. See ConfigState for options documentation.
See Fdump if you would prefer dump to an arbitrary io.Writer.
*/
func Dump(a ...interface{}) {
Fdump(os.Stdout, a...)
fdump(&Config, os.Stdout, a...)
}

View File

@ -130,3 +130,95 @@ func ExamplePrintf() {
// ppui8: <**>5
// circular: {1 <*>{1 <*><shown>}}
}
// This example demonstrates how to use a SpewState.
func ExampleSpewState() {
// A SpewState does not need initialization.
ss := new(spew.SpewState) // or var ss spew.SpewState
// Modify the indent level of the SpewState only. The global configuration
// is not modified.
ssc := ss.Config()
ssc.Indent = "\t"
// Output using the SpewState instance.
v := map[string]int{"one": 1}
ss.Printf("v: %v\n", v)
ss.Dump(v)
// Output:
// v: map[one:1]
// (map[string]int) {
// (string) "one": (int) 1
// }
}
// This example demonstrates how to use a SpewState.Dump to dump variables to
// stdout
func ExampleSpewState_Dump() {
// See the top-level Dump example for details on the types used in this
// example.
// A SpewState does not need initialization.
ss := new(spew.SpewState) // or var ss spew.SpewState
ss2 := new(spew.SpewState) // or var ss2 spew.SpewState
// Modify the indent level of the first SpewState only.
ssc := ss.Config()
ssc.Indent = "\t"
// Setup some sample data structures for the example.
bar := Bar{Flag(flagTwo), uintptr(0)}
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
// Dump using the SpewState instances.
ss.Dump(s1)
ss2.Dump(s1)
// Output:
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// flag: (spew_test.Flag) flagTwo,
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) {
// (string) "one": (bool) true
// }
// }
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// flag: (spew_test.Flag) flagTwo,
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) {
// (string) "one": (bool) true
// }
// }
//
}
// This example demonstrates how to use SpewState.Printf to display a variable
// with a format string and inline formatting.
func ExampleSpewState_Printf() {
// See the top-level Dump example for details on the types used in this
// example.
// A SpewState does not need initialization.
ss := new(spew.SpewState) // or var ss spew.SpewState
ss2 := new(spew.SpewState) // or var ss2 spew.SpewState
// Modify the method handling of the first SpewState only.
ssc := ss.Config()
ssc.DisableMethods = true
// This is of type Flag which implements a Stringer and has raw value 1.
f := flagTwo
// Dump using the SpewState instances.
ss.Printf("f: %v\n", f)
ss2.Printf("f: %v\n", f)
// Output:
// f: 1
// f: flagTwo
}

View File

@ -316,6 +316,14 @@ func (f *formatState) Format(fs fmt.State, verb rune) {
f.buffer.WriteTo(fs)
}
// newFormatter is a helper function to consolidate the logic from the various
// public methods which take varying config states.
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
fs := &formatState{value: v, cs: cs}
fs.pointers = make(map[uintptr]int)
return fs
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
@ -333,7 +341,5 @@ use of the custom formatter by calling one of the convenience functions such as
Printf, Println, or Printf.
*/
func NewFormatter(v interface{}) fmt.Formatter {
fs := &formatState{value: v, cs: &Config}
fs.pointers = make(map[uintptr]int)
return fs
return newFormatter(&Config, v)
}

View File

@ -19,6 +19,7 @@ package spew
import (
"fmt"
"io"
"os"
)
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
@ -113,3 +114,178 @@ func convertArgs(args []interface{}) (formatters []interface{}) {
}
return formatters
}
// SpewState provides a context which can have its own configuration options.
// The configuration options can be manipulated via the Config method. The
// methods of SpewState are equivalent to the top-level functions.
//
// A SpewState does not need any special initialization, so new(SpewState) or
// just declaring a SpewState variable, is sufficient to initialilize a
// SpewState using the default configuration options.
type SpewState struct {
cs *ConfigState
}
// Config returns a pointer to the active ConfigState for the SpewState
// instance. Set the fields of the returned structure to the desired
// configuration settings for the instance.
func (s *SpewState) Config() (cs *ConfigState) {
if s.cs == nil {
cs := defaultConfig
s.cs = &cs
}
return s.cs
}
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a Formatter interface returned by s.NewFormatter. It returns
// the formatted string as a value that satisfies error. See NewFormatter
// for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, s.NewFormatter(a), s.NewFormatter(b))
func (s *SpewState) Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, s.convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a Formatter interface returned by s.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, s.NewFormatter(a), s.NewFormatter(b))
func (s *SpewState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, s.convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a Formatter interface returned by s.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, s.NewFormatter(a), s.NewFormatter(b))
func (s *SpewState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, s.convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a Formatter interface returned by s.NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, s.NewFormatter(a), s.NewFormatter(b))
func (s *SpewState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, s.convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a Formatter interface returned by s.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(s.NewFormatter(a), s.NewFormatter(b))
func (s *SpewState) Print(a ...interface{}) (n int, err error) {
return fmt.Print(s.convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a Formatter interface returned by s.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, s.NewFormatter(a), s.NewFormatter(b))
func (s *SpewState) Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, s.convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a Formatter interface returned by s.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(s.NewFormatter(a), s.NewFormatter(b))
func (s *SpewState) Println(a ...interface{}) (n int, err error) {
return fmt.Println(s.convertArgs(a)...)
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
printing functions. The formatter is useful for inline printing of smaller data
types similar to the standard %v format specifier.
The custom formatter only responds to the %v and %+v verb combinations. Any
other variations such as %x, %q, and %#v will be sent to the the standard fmt
package for formatting. In addition, the custom formatter ignores the width and
precision arguments (however they will still work on the format specifiers not
handled by the custom formatter).
Typically this function shouldn't be called directly. It is much easier to make
use of the custom formatter by calling one of the convenience functions such as
s.Printf, s.Println, or s.Printf.
*/
func (s *SpewState) NewFormatter(v interface{}) fmt.Formatter {
// The Config method creates the config state if needed, so call it instead
// of using s.cs directly to ensure the zero value SpewState is sane.
return newFormatter(s.Config(), v)
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func (s *SpewState) Fdump(w io.Writer, a ...interface{}) {
// The Config method creates the config state if needed, so call it instead
// of using s.cs directly to ensure the zero value SpewState is sane.
fdump(s.Config(), w, a...)
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom error/Stringer interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the error/Stringer interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
The configuration options are controlled by accessing the ConfigState associated
with s via the Config method. See ConfigState for options documentation.
See Fdump if you would prefer dump to an arbitrary io.Writer.
*/
func (s *SpewState) Dump(a ...interface{}) {
// The Config method creates the config state if needed, so call it instead
// of using s.cs directly to ensure the zero value SpewState is sane.
fdump(s.Config(), os.Stdout, a...)
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a spew Formatter interface using
// the ConfigState associated with s.
func (s *SpewState) convertArgs(args []interface{}) (formatters []interface{}) {
// The Config method creates the config state if needed, so call it instead
// of using s.cs directly to ensure the zero value SpewState is sane.
cs := s.Config()
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = newFormatter(cs, arg)
}
return formatters
}