Repository: urfave/cli
Pull request #1378 (2022-04-26 23:15:27)

Feature:(Issue 1334) Add support for uint64slices
<!--
  This template provides some ideas of things to include in your PR description.
  To start, try providing a short summary of your changes in the Title above.
  If a section of the PR template does not apply to this PR, then delete that section.
 -->

## What type of PR is this?

<!--
  Delete any of the following that do not apply:
 -->


- feature

## What this PR does / why we need it:

<!--
  What goal is this change working towards?
  Provide a bullet pointed summary of how each file was changed.
  Briefly explain any decisions you made with respect to the changes.
  Include anything here that you didn't include in *Release Notes*
  above, such as changes to CI or changes to internal methods.
-->

Add support for uint64 slices

## Which issue(s) this PR fixes:

<!--
If this PR fixes one of more issues, list them here.
One line each, like so:

Fixes #1334
-->

## Special notes for your reviewer:

<!--
   Is there any particular feedback you would / wouldn't like?
   Which parts of the code should reviewers focus on?
-->

Please focus on altsrc changes as well, especially the type switch

## Testing

<!--
  Describe how you tested this change.
-->

Ran unit tests

## Release Notes

<!--
  If this PR makes user facing changes, please describe them here. This
  description will be copied into the release notes/changelog, whenever the
  next version is released. Keep this section short, and focus on high level
  changes.

  Put your text between the block. To omit notes, use NONE within the block.
-->

```
Add support for uint64slice flag 

```


meatballhat: @dearchap Are you up for addressing the conflicts and getting the test coverage above the threshold? No worries if not. I'm happy to take this work to completion.
dearchap: @meatballhat Let me take a look
dearchap: @meatballhat Done. Can you take a look ? 
dearchap: @meatballhat Can you take a look at why tests are failing ? I dont quite understand why. 
meatballhat: @dearchap looks like tests are happy now -- coverage thresholds are still grumpy, but I don't want to be draconian about those checks. Maybe add some coverage threshold wiggle room as part of this PR? e.g.:
Dokiys: > @meatballhat Can you take a look at why tests are failing ? I dont quite understand why.

codecov will use the coverage from the base commit. Checks show target coverage need 83.22. You can try [this](https://docs.codecov.com/docs/common-recipe-list#set-status-checks-to-block-coverage) specify a target

Patch:
diff --git a/.github/.codecov.yml b/.github/.codecov.yml
index 69cb760..5395ce7 100644
--- a/.github/.codecov.yml
+++ b/.github/.codecov.yml
@@ -1,1 +1,3 @@
 comment: false
+coverage:
+  threshold: 5%
diff --git a/flag-spec.yaml b/flag-spec.yaml
index 280f909..ffd5cc2 100644
--- a/flag-spec.yaml
+++ b/flag-spec.yaml
@@ -55,7 +55,12 @@ flag_types:
       - { name: Layout, type: string }
       - { name: Timezone, type: "*time.Location" }
 
-  # TODO: enable UintSlice
-  # UintSlice: {}
-  # TODO: enable Uint64Slice once #1334 lands
-  # Uint64Slice: {}
+  UintSlice: 
+    value_pointer: true
+    skip_interfaces:
+      - fmt.Stringer
+  Uint64Slice: 
+    value_pointer: true
+    skip_interfaces:
+      - fmt.Stringer
+
diff --git a/flag_uint64_slice.go b/flag_uint64_slice.go
new file mode 100644
index 0000000..e60c3ea
--- /dev/null
+++ b/flag_uint64_slice.go
@@ -0,0 +1,203 @@
+package cli
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// Uint64Slice wraps []int64 to satisfy flag.Value
+type Uint64Slice struct {
+	slice      []uint64
+	hasBeenSet bool
+}
+
+// NewUint64Slice makes an *Uint64Slice with default values
+func NewUint64Slice(defaults ...uint64) *Uint64Slice {
+	return &Uint64Slice{slice: append([]uint64{}, defaults...)}
+}
+
+// clone allocate a copy of self object
+func (i *Uint64Slice) clone() *Uint64Slice {
+	n := &Uint64Slice{
+		slice:      make([]uint64, len(i.slice)),
+		hasBeenSet: i.hasBeenSet,
+	}
+	copy(n.slice, i.slice)
+	return n
+}
+
+// Set parses the value into an integer and appends it to the list of values
+func (i *Uint64Slice) Set(value string) error {
+	if !i.hasBeenSet {
+		i.slice = []uint64{}
+		i.hasBeenSet = true
+	}
+
+	if strings.HasPrefix(value, slPfx) {
+		// Deserializing assumes overwrite
+		_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice)
+		i.hasBeenSet = true
+		return nil
+	}
+
+	for _, s := range flagSplitMultiValues(value) {
+		tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 64)
+		if err != nil {
+			return err
+		}
+
+		i.slice = append(i.slice, tmp)
+	}
+
+	return nil
+}
+
+// String returns a readable representation of this value (for usage defaults)
+func (i *Uint64Slice) String() string {
+	v := i.slice
+	if v == nil {
+		// treat nil the same as zero length non-nil
+		v = make([]uint64, 0)
+	}
+	str := fmt.Sprintf("%d", v)
+	str = strings.Replace(str, " ", ", ", -1)
+	str = strings.Replace(str, "[", "{", -1)
+	str = strings.Replace(str, "]", "}", -1)
+	return fmt.Sprintf("[]uint64%s", str)
+}
+
+// Serialize allows Uint64Slice to fulfill Serializer
+func (i *Uint64Slice) Serialize() string {
+	jsonBytes, _ := json.Marshal(i.slice)
+	return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
+}
+
+// Value returns the slice of ints set by this flag
+func (i *Uint64Slice) Value() []uint64 {
+	return i.slice
+}
+
+// Get returns the slice of ints set by this flag
+func (i *Uint64Slice) Get() interface{} {
+	return *i
+}
+
+// String returns a readable representation of this value
+// (for usage defaults)
+func (f *Uint64SliceFlag) String() string {
+	return withEnvHint(f.GetEnvVars(), f.stringify())
+}
+
+// TakesValue returns true of the flag takes a value, otherwise false
+func (f *Uint64SliceFlag) TakesValue() bool {
+	return true
+}
+
+// GetUsage returns the usage string for the flag
+func (f *Uint64SliceFlag) GetUsage() string {
+	return f.Usage
+}
+
+// GetCategory returns the category for the flag
+func (f *Uint64SliceFlag) GetCategory() string {
+	return f.Category
+}
+
+// GetValue returns the flags value as string representation and an empty
+// string if the flag takes no value at all.
+func (f *Uint64SliceFlag) GetValue() string {
+	if f.Value != nil {
+		return f.Value.String()
+	}
+	return ""
+}
+
+// GetDefaultText returns the default text for this flag
+func (f *Uint64SliceFlag) GetDefaultText() string {
+	if f.DefaultText != "" {
+		return f.DefaultText
+	}
+	return f.GetValue()
+}
+
+// GetEnvVars returns the env vars for this flag
+func (f *Uint64SliceFlag) GetEnvVars() []string {
+	return f.EnvVars
+}
+
+// Apply populates the flag given the flag set and environment
+func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error {
+	// apply any default
+	if f.Destination != nil && f.Value != nil {
+		f.Destination.slice = make([]uint64, len(f.Value.slice))
+		copy(f.Destination.slice, f.Value.slice)
+	}
+
+	// resolve setValue (what we will assign to the set)
+	var setValue *Uint64Slice
+	switch {
+	case f.Destination != nil:
+		setValue = f.Destination
+	case f.Value != nil:
+		setValue = f.Value.clone()
+	default:
+		setValue = new(Uint64Slice)
+	}
+
+	if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
+		for _, s := range flagSplitMultiValues(val) {
+			if err := setValue.Set(strings.TrimSpace(s)); err != nil {
+				return fmt.Errorf("could not parse %q as uint64 slice value from %s for flag %s: %s", val, source, f.Name, err)
+			}
+		}
+
+		// Set this to false so that we reset the slice if we then set values from
+		// flags that have already been set by the environment.
+		setValue.hasBeenSet = false
+		f.HasBeenSet = true
+	}
+
+	for _, name := range f.Names() {
+		set.Var(setValue, name, f.Usage)
+	}
+
+	return nil
+}
+
+// Get returns the flag’s value in the given Context.
+func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 {
+	return ctx.Uint64Slice(f.Name)
+}
+
+func (f *Uint64SliceFlag) stringify() string {
+	var defaultVals []string
+	if f.Value != nil && len(f.Value.Value()) > 0 {
+		for _, i := range f.Value.Value() {
+			defaultVals = append(defaultVals, strconv.FormatUint(i, 10))
+		}
+	}
+
+	return stringifySliceFlag(f.Usage, f.Names(), defaultVals)
+}
+
+// Uint64Slice looks up the value of a local Uint64SliceFlag, returns
+// nil if not found
+func (cCtx *Context) Uint64Slice(name string) []uint64 {
+	if fs := cCtx.lookupFlagSet(name); fs != nil {
+		return lookupUint64Slice(name, fs)
+	}
+	return nil
+}
+
+func lookupUint64Slice(name string, set *flag.FlagSet) []uint64 {
+	f := set.Lookup(name)
+	if f != nil {
+		if slice, ok := unwrapFlagValue(f.Value).(*Uint64Slice); ok {
+			return slice.Value()
+		}
+	}
+	return nil
+}
diff --git a/flag_uint_slice.go b/flag_uint_slice.go
new file mode 100644
index 0000000..350b29c
--- /dev/null
+++ b/flag_uint_slice.go
@@ -0,0 +1,214 @@
+package cli
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// UintSlice wraps []int to satisfy flag.Value
+type UintSlice struct {
+	slice      []uint
+	hasBeenSet bool
+}
+
+// NewUintSlice makes an *UintSlice with default values
+func NewUintSlice(defaults ...uint) *UintSlice {
+	return &UintSlice{slice: append([]uint{}, defaults...)}
+}
+
+// clone allocate a copy of self object
+func (i *UintSlice) clone() *UintSlice {
+	n := &UintSlice{
+		slice:      make([]uint, len(i.slice)),
+		hasBeenSet: i.hasBeenSet,
+	}
+	copy(n.slice, i.slice)
+	return n
+}
+
+// TODO: Consistently have specific Set function for Int64 and Float64 ?
+// SetInt directly adds an integer to the list of values
+func (i *UintSlice) SetUint(value uint) {
+	if !i.hasBeenSet {
+		i.slice = []uint{}
+		i.hasBeenSet = true
+	}
+
+	i.slice = append(i.slice, value)
+}
+
+// Set parses the value into an integer and appends it to the list of values
+func (i *UintSlice) Set(value string) error {
+	if !i.hasBeenSet {
+		i.slice = []uint{}
+		i.hasBeenSet = true
+	}
+
+	if strings.HasPrefix(value, slPfx) {
+		// Deserializing assumes overwrite
+		_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice)
+		i.hasBeenSet = true
+		return nil
+	}
+
+	for _, s := range flagSplitMultiValues(value) {
+		tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 32)
+		if err != nil {
+			return err
+		}
+
+		i.slice = append(i.slice, uint(tmp))
+	}
+
+	return nil
+}
+
+// String returns a readable representation of this value (for usage defaults)
+func (i *UintSlice) String() string {
+	v := i.slice
+	if v == nil {
+		// treat nil the same as zero length non-nil
+		v = make([]uint, 0)
+	}
+	str := fmt.Sprintf("%d", v)
+	str = strings.Replace(str, " ", ", ", -1)
+	str = strings.Replace(str, "[", "{", -1)
+	str = strings.Replace(str, "]", "}", -1)
+	return fmt.Sprintf("[]uint%s", str)
+}
+
+// Serialize allows UintSlice to fulfill Serializer
+func (i *UintSlice) Serialize() string {
+	jsonBytes, _ := json.Marshal(i.slice)
+	return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
+}
+
+// Value returns the slice of ints set by this flag
+func (i *UintSlice) Value() []uint {
+	return i.slice
+}
+
+// Get returns the slice of ints set by this flag
+func (i *UintSlice) Get() interface{} {
+	return *i
+}
+
+// String returns a readable representation of this value
+// (for usage defaults)
+func (f *UintSliceFlag) String() string {
+	return withEnvHint(f.GetEnvVars(), f.stringify())
+}
+
+// TakesValue returns true of the flag takes a value, otherwise false
+func (f *UintSliceFlag) TakesValue() bool {
+	return true
+}
+
+// GetUsage returns the usage string for the flag
+func (f *UintSliceFlag) GetUsage() string {
+	return f.Usage
+}
+
+// GetCategory returns the category for the flag
+func (f *UintSliceFlag) GetCategory() string {
+	return f.Category
+}
+
+// GetValue returns the flags value as string representation and an empty
+// string if the flag takes no value at all.
+func (f *UintSliceFlag) GetValue() string {
+	if f.Value != nil {
+		return f.Value.String()
+	}
+	return ""
+}
+
+// GetDefaultText returns the default text for this flag
+func (f *UintSliceFlag) GetDefaultText() string {
+	if f.DefaultText != "" {
+		return f.DefaultText
+	}
+	return f.GetValue()
+}
+
+// GetEnvVars returns the env vars for this flag
+func (f *UintSliceFlag) GetEnvVars() []string {
+	return f.EnvVars
+}
+
+// Apply populates the flag given the flag set and environment
+func (f *UintSliceFlag) Apply(set *flag.FlagSet) error {
+	// apply any default
+	if f.Destination != nil && f.Value != nil {
+		f.Destination.slice = make([]uint, len(f.Value.slice))
+		copy(f.Destination.slice, f.Value.slice)
+	}
+
+	// resolve setValue (what we will assign to the set)
+	var setValue *UintSlice
+	switch {
+	case f.Destination != nil:
+		setValue = f.Destination
+	case f.Value != nil:
+		setValue = f.Value.clone()
+	default:
+		setValue = new(UintSlice)
+	}
+
+	if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
+		for _, s := range flagSplitMultiValues(val) {
+			if err := setValue.Set(strings.TrimSpace(s)); err != nil {
+				return fmt.Errorf("could not parse %q as uint slice value from %s for flag %s: %s", val, source, f.Name, err)
+			}
+		}
+
+		// Set this to false so that we reset the slice if we then set values from
+		// flags that have already been set by the environment.
+		setValue.hasBeenSet = false
+		f.HasBeenSet = true
+	}
+
+	for _, name := range f.Names() {
+		set.Var(setValue, name, f.Usage)
+	}
+
+	return nil
+}
+
+// Get returns the flag’s value in the given Context.
+func (f *UintSliceFlag) Get(ctx *Context) []uint {
+	return ctx.UintSlice(f.Name)
+}
+
+func (f *UintSliceFlag) stringify() string {
+	var defaultVals []string
+	if f.Value != nil && len(f.Value.Value()) > 0 {
+		for _, i := range f.Value.Value() {
+			defaultVals = append(defaultVals, strconv.FormatUint(uint64(i), 10))
+		}
+	}
+
+	return stringifySliceFlag(f.Usage, f.Names(), defaultVals)
+}
+
+// UintSlice looks up the value of a local UintSliceFlag, returns
+// nil if not found
+func (cCtx *Context) UintSlice(name string) []uint {
+	if fs := cCtx.lookupFlagSet(name); fs != nil {
+		return lookupUintSlice(name, fs)
+	}
+	return nil
+}
+
+func lookupUintSlice(name string, set *flag.FlagSet) []uint {
+	f := set.Lookup(name)
+	if f != nil {
+		if slice, ok := unwrapFlagValue(f.Value).(*UintSlice); ok {
+			return slice.Value()
+		}
+	}
+	return nil
+}
diff --git a/godoc-current.txt b/godoc-current.txt
index 12db90e..7102c25 100644
--- a/godoc-current.txt
+++ b/godoc-current.txt
@@ -705,6 +705,14 @@ func (cCtx *Context) Uint(name string) uint
 func (cCtx *Context) Uint64(name string) uint64
     Uint64 looks up the value of a local Uint64Flag, returns 0 if not found
 
+func (cCtx *Context) Uint64Slice(name string) []uint64
+    Uint64Slice looks up the value of a local Uint64SliceFlag, returns nil if
+    not found
+
+func (cCtx *Context) UintSlice(name string) []uint
+    UintSlice looks up the value of a local UintSliceFlag, returns nil if not
+    found
+
 func (cCtx *Context) Value(name string) interface{}
     Value returns the value of the flag corresponding to `name`
 
@@ -1916,6 +1924,89 @@ func (f *Uint64Flag) String() string
 func (f *Uint64Flag) TakesValue() bool
     TakesValue returns true of the flag takes a value, otherwise false
 
+type Uint64Slice struct {
+	// Has unexported fields.
+}
+    Uint64Slice wraps []int64 to satisfy flag.Value
+
+func NewUint64Slice(defaults ...uint64) *Uint64Slice
+    NewUint64Slice makes an *Uint64Slice with default values
+
+func (i *Uint64Slice) Get() interface{}
+    Get returns the slice of ints set by this flag
+
+func (i *Uint64Slice) Serialize() string
+    Serialize allows Uint64Slice to fulfill Serializer
+
+func (i *Uint64Slice) Set(value string) error
+    Set parses the value into an integer and appends it to the list of values
+
+func (i *Uint64Slice) String() string
+    String returns a readable representation of this value (for usage defaults)
+
+func (i *Uint64Slice) Value() []uint64
+    Value returns the slice of ints set by this flag
+
+type Uint64SliceFlag struct {
+	Name string
+
+	Category    string
+	DefaultText string
+	FilePath    string
+	Usage       string
+
+	Required   bool
+	Hidden     bool
+	HasBeenSet bool
+
+	Value       *Uint64Slice
+	Destination *Uint64Slice
+
+	Aliases []string
+	EnvVars []string
+}
+    Uint64SliceFlag is a flag with type *Uint64Slice
+
+func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error
+    Apply populates the flag given the flag set and environment
+
+func (f *Uint64SliceFlag) Get(ctx *Context) []uint64
+    Get returns the flag’s value in the given Context.
+
+func (f *Uint64SliceFlag) GetCategory() string
+    GetCategory returns the category for the flag
+
+func (f *Uint64SliceFlag) GetDefaultText() string
+    GetDefaultText returns the default text for this flag
+
+func (f *Uint64SliceFlag) GetEnvVars() []string
+    GetEnvVars returns the env vars for this flag
+
+func (f *Uint64SliceFlag) GetUsage() string
+    GetUsage returns the usage string for the flag
+
+func (f *Uint64SliceFlag) GetValue() string
+    GetValue returns the flags value as string representation and an empty
+    string if the flag takes no value at all.
+
+func (f *Uint64SliceFlag) IsRequired() bool
+    IsRequired returns whether or not the flag is required
+
+func (f *Uint64SliceFlag) IsSet() bool
+    IsSet returns whether or not the flag has been set through env or file
+
+func (f *Uint64SliceFlag) IsVisible() bool
+    IsVisible returns true if the flag is not hidden, otherwise false
+
+func (f *Uint64SliceFlag) Names() []string
+    Names returns the names of the flag
+
+func (f *Uint64SliceFlag) String() string
+    String returns a readable representation of this value (for usage defaults)
+
+func (f *Uint64SliceFlag) TakesValue() bool
+    TakesValue returns true of the flag takes a value, otherwise false
+
 type UintFlag struct {
 	Name string
 
@@ -1978,6 +2069,93 @@ func (f *UintFlag) String() string
 func (f *UintFlag) TakesValue() bool
     TakesValue returns true of the flag takes a value, otherwise false
 
+type UintSlice struct {
+	// Has unexported fields.
+}
+    UintSlice wraps []int to satisfy flag.Value
+
+func NewUintSlice(defaults ...uint) *UintSlice
+    NewUintSlice makes an *UintSlice with default values
+
+func (i *UintSlice) Get() interface{}
+    Get returns the slice of ints set by this flag
+
+func (i *UintSlice) Serialize() string
+    Serialize allows UintSlice to fulfill Serializer
+
+func (i *UintSlice) Set(value string) error
+    Set parses the value into an integer and appends it to the list of values
+
+func (i *UintSlice) SetUint(value uint)
+    TODO: Consistently have specific Set function for Int64 and Float64 ? SetInt
+    directly adds an integer to the list of values
+
+func (i *UintSlice) String() string
+    String returns a readable representation of this value (for usage defaults)
+
+func (i *UintSlice) Value() []uint
+    Value returns the slice of ints set by this flag
+
+type UintSliceFlag struct {
+	Name string
+
+	Category    string
+	DefaultText string
+	FilePath    string
+	Usage       string
+
+	Required   bool
+	Hidden     bool
+	HasBeenSet bool
+
+	Value       *UintSlice
+	Destination *UintSlice
+
+	Aliases []string
+	EnvVars []string
+}
+    UintSliceFlag is a flag with type *UintSlice
+
+func (f *UintSliceFlag) Apply(set *flag.FlagSet) error
+    Apply populates the flag given the flag set and environment
+
+func (f *UintSliceFlag) Get(ctx *Context) []uint
+    Get returns the flag’s value in the given Context.
+
+func (f *UintSliceFlag) GetCategory() string
+    GetCategory returns the category for the flag
+
+func (f *UintSliceFlag) GetDefaultText() string
+    GetDefaultText returns the default text for this flag
+
+func (f *UintSliceFlag) GetEnvVars() []string
+    GetEnvVars returns the env vars for this flag
+
+func (f *UintSliceFlag) GetUsage() string
+    GetUsage returns the usage string for the flag
+
+func (f *UintSliceFlag) GetValue() string
+    GetValue returns the flags value as string representation and an empty
+    string if the flag takes no value at all.
+
+func (f *UintSliceFlag) IsRequired() bool
+    IsRequired returns whether or not the flag is required
+
+func (f *UintSliceFlag) IsSet() bool
+    IsSet returns whether or not the flag has been set through env or file
+
+func (f *UintSliceFlag) IsVisible() bool
+    IsVisible returns true if the flag is not hidden, otherwise false
+
+func (f *UintSliceFlag) Names() []string
+    Names returns the names of the flag
+
+func (f *UintSliceFlag) String() string
+    String returns a readable representation of this value (for usage defaults)
+
+func (f *UintSliceFlag) TakesValue() bool
+    TakesValue returns true of the flag takes a value, otherwise false
+
 type VisibleFlag interface {
 	Flag
 
diff --git a/zz_generated.flags.go b/zz_generated.flags.go
index bd2fc54..4799c92 100644
--- a/zz_generated.flags.go
+++ b/zz_generated.flags.go
@@ -309,6 +309,86 @@ func (f *TimestampFlag) IsVisible() bool {
 	return !f.Hidden
 }
 
+// Uint64SliceFlag is a flag with type *Uint64Slice
+type Uint64SliceFlag struct {
+	Name string
+
+	Category    string
+	DefaultText string
+	FilePath    string
+	Usage       string
+
+	Required   bool
+	Hidden     bool
+	HasBeenSet bool
+
+	Value       *Uint64Slice
+	Destination *Uint64Slice
+
+	Aliases []string
+	EnvVars []string
+}
+
+// IsSet returns whether or not the flag has been set through env or file
+func (f *Uint64SliceFlag) IsSet() bool {
+	return f.HasBeenSet
+}
+
+// Names returns the names of the flag
+func (f *Uint64SliceFlag) Names() []string {
+	return FlagNames(f.Name, f.Aliases)
+}
+
+// IsRequired returns whether or not the flag is required
+func (f *Uint64SliceFlag) IsRequired() bool {
+	return f.Required
+}
+
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *Uint64SliceFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
+// UintSliceFlag is a flag with type *UintSlice
+type UintSliceFlag struct {
+	Name string
+
+	Category    string
+	DefaultText string
+	FilePath    string
+	Usage       string
+
+	Required   bool
+	Hidden     bool
+	HasBeenSet bool
+
+	Value       *UintSlice
+	Destination *UintSlice
+
+	Aliases []string
+	EnvVars []string
+}
+
+// IsSet returns whether or not the flag has been set through env or file
+func (f *UintSliceFlag) IsSet() bool {
+	return f.HasBeenSet
+}
+
+// Names returns the names of the flag
+func (f *UintSliceFlag) Names() []string {
+	return FlagNames(f.Name, f.Aliases)
+}
+
+// IsRequired returns whether or not the flag is required
+func (f *UintSliceFlag) IsRequired() bool {
+	return f.Required
+}
+
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *UintSliceFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // BoolFlag is a flag with type bool
 type BoolFlag struct {
 	Name string


Test patch:
diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go
index bece6aa..b2cbb52 100644
--- a/altsrc/flag_test.go
+++ b/altsrc/flag_test.go
@@ -40,7 +40,7 @@ func (ris *racyInputSource) isSet(name string) bool {
 func TestGenericApplyInputSourceValue_Alias(t *testing.T) {
 	v := &Parser{"abc", "def"}
 	tis := testApplyInputSource{
-		Flag:     NewGenericFlag(&cli.GenericFlag{Name: "test",  Aliases: []string{"test_alias"}, Value: &Parser{}}),
+		Flag:     NewGenericFlag(&cli.GenericFlag{Name: "test", Aliases: []string{"test_alias"}, Value: &Parser{}}),
 		FlagName: "test_alias",
 		MapValue: v,
 	}
diff --git a/flag_test.go b/flag_test.go
index aee9b83..ddd3e67 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -122,12 +122,24 @@ func TestFlagsFromEnv(t *testing.T) {
 		return *s
 	}
 
+	newSetUintSlice := func(defaults ...uint) UintSlice {
+		s := NewUintSlice(defaults...)
+		s.hasBeenSet = false
+		return *s
+	}
+
 	newSetInt64Slice := func(defaults ...int64) Int64Slice {
 		s := NewInt64Slice(defaults...)
 		s.hasBeenSet = false
 		return *s
 	}
 
+	newSetUint64Slice := func(defaults ...uint64) Uint64Slice {
+		s := NewUint64Slice(defaults...)
+		s.hasBeenSet = false
+		return *s
+	}
+
 	newSetStringSlice := func(defaults ...string) StringSlice {
 		s := NewStringSlice(defaults...)
 		s.hasBeenSet = false
@@ -171,10 +183,18 @@ func TestFlagsFromEnv(t *testing.T) {
 		{"1.2,2", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int slice value from environment variable "SECONDS" for flag seconds: .*`},
 		{"foobar", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int slice value from environment variable "SECONDS" for flag seconds: .*`},
 
+		{"1,2", newSetUintSlice(1, 2), &UintSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
+		{"1.2,2", newSetUintSlice(), &UintSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as uint slice value from environment variable "SECONDS" for flag seconds: .*`},
+		{"foobar", newSetUintSlice(), &UintSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint slice value from environment variable "SECONDS" for flag seconds: .*`},
+
 		{"1,2", newSetInt64Slice(1, 2), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
 		{"1.2,2", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int64 slice value from environment variable "SECONDS" for flag seconds: .*`},
 		{"foobar", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int64 slice value from environment variable "SECONDS" for flag seconds: .*`},
 
+		{"1,2", newSetUint64Slice(1, 2), &Uint64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
+		{"1.2,2", newSetUint64Slice(), &Uint64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as uint64 slice value from environment variable "SECONDS" for flag seconds: .*`},
+		{"foobar", newSetUint64Slice(), &Uint64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint64 slice value from environment variable "SECONDS" for flag seconds: .*`},
+
 		{"foo", "foo", &StringFlag{Name: "name", EnvVars: []string{"NAME"}}, ""},
 		{"path", "path", &PathFlag{Name: "path", EnvVars: []string{"PATH"}}, ""},
 
@@ -324,6 +344,16 @@ func TestFlagStringifying(t *testing.T) {
 			fl:       &IntFlag{Name: "pens", DefaultText: "-19"},
 			expected: "--pens value\t(default: -19)",
 		},
+		{
+			name:     "uint-slice-flag",
+			fl:       &UintSliceFlag{Name: "pencils"},
+			expected: "--pencils value\t",
+		},
+		{
+			name:     "uint-slice-flag-with-default-text",
+			fl:       &UintFlag{Name: "pens", DefaultText: "29"},
+			expected: "--pens value\t(default: 29)",
+		},
 		{
 			name:     "int64-flag",
 			fl:       &Int64Flag{Name: "flume"},
@@ -344,6 +374,16 @@ func TestFlagStringifying(t *testing.T) {
 			fl:       &Int64SliceFlag{Name: "handles", DefaultText: "-2"},
 			expected: "--handles value\t(default: -2)",
 		},
+		{
+			name:     "uint64-slice-flag",
+			fl:       &Uint64SliceFlag{Name: "drawers"},
+			expected: "--drawers value\t",
+		},
+		{
+			name:     "uint64-slice-flag-with-default-text",
+			fl:       &Uint64SliceFlag{Name: "handles", DefaultText: "-2"},
+			expected: "--handles value\t(default: -2)",
+		},
 		{
 			name:     "path-flag",
 			fl:       &PathFlag{Name: "soup"},
@@ -1018,6 +1058,47 @@ func TestIntSliceFlagApply_SetsAllNames(t *testing.T) {
 	expect(t, err, nil)
 }
 
+func TestIntSliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1 , 2")
+	var val IntSlice
+	fl := IntSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []int(nil))
+	expect(t, set.Lookup("goat").Value.(*IntSlice).Value(), []int{1, 2})
+}
+
+func TestIntSliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1 , 2")
+	val := NewIntSlice(3, 4)
+	fl := IntSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []int{3, 4})
+	expect(t, set.Lookup("goat").Value.(*IntSlice).Value(), []int{1, 2})
+}
+
+func TestIntSliceFlagApply_DefaultValueWithDestination(t *testing.T) {
+	defValue := []int{1, 2}
+
+	fl := IntSliceFlag{Name: "country", Value: NewIntSlice(defValue...), Destination: NewIntSlice(3)}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{})
+	expect(t, err, nil)
+	expect(t, defValue, fl.Destination.Value())
+}
+
 func TestIntSliceFlagApply_ParentContext(t *testing.T) {
 	_ = (&App{
 		Flags: []Flag{
@@ -1107,6 +1188,56 @@ func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
 	}
 }
 
+func TestInt64SliceFlagApply_SetsAllNames(t *testing.T) {
+	fl := Int64SliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
+	expect(t, err, nil)
+}
+
+func TestInt64SliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1 , 2")
+	var val Int64Slice
+	fl := Int64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []int64(nil))
+	expect(t, set.Lookup("goat").Value.(*Int64Slice).Value(), []int64{1, 2})
+}
+
+func TestInt64SliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1 , 2")
+	val := NewInt64Slice(3, 4)
+	fl := Int64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []int64{3, 4})
+	expect(t, set.Lookup("goat").Value.(*Int64Slice).Value(), []int64{1, 2})
+}
+
+func TestInt64SliceFlagApply_DefaultValueWithDestination(t *testing.T) {
+	defValue := []int64{1, 2}
+
+	fl := Int64SliceFlag{Name: "country", Value: NewInt64Slice(defValue...), Destination: NewInt64Slice(3)}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{})
+	expect(t, err, nil)
+	expect(t, defValue, fl.Destination.Value())
+}
+
 func TestInt64SliceFlagApply_ParentContext(t *testing.T) {
 	_ = (&App{
 		Flags: []Flag{
@@ -1169,6 +1300,298 @@ func TestInt64SliceFlagValueFromContext(t *testing.T) {
 	expect(t, f.Get(ctx), []int64{1, 2, 3})
 }
 
+var uintSliceFlagTests = []struct {
+	name     string
+	aliases  []string
+	value    *UintSlice
+	expected string
+}{
+	{"heads", nil, NewUintSlice(), "--heads value [ --heads value ]\t"},
+	{"H", nil, NewUintSlice(), "-H value [ -H value ]\t"},
+	{"heads", []string{"H"}, NewUintSlice(uint(2), uint(17179869184)),
+		"--heads value, -H value [ --heads value, -H value ]\t(default: 2, 17179869184)"},
+}
+
+func TestUintSliceFlagHelpOutput(t *testing.T) {
+	for _, test := range uintSliceFlagTests {
+		fl := UintSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
+		output := fl.String()
+
+		if output != test.expected {
+			t.Errorf("%q does not match %q", output, test.expected)
+		}
+	}
+}
+
+func TestUintSliceFlagWithEnvVarHelpOutput(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("APP_SMURF", "42,17179869184")
+
+	for _, test := range uintSliceFlagTests {
+		fl := UintSliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
+		output := fl.String()
+
+		expectedSuffix := " [$APP_SMURF]"
+		if runtime.GOOS == "windows" {
+			expectedSuffix = " [%APP_SMURF%]"
+		}
+		if !strings.HasSuffix(output, expectedSuffix) {
+			t.Errorf("%q does not end with"+expectedSuffix, output)
+		}
+	}
+}
+
+func TestUintSliceFlagApply_SetsAllNames(t *testing.T) {
+	fl := UintSliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
+	expect(t, err, nil)
+}
+
+func TestUintSliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1 , 2")
+	var val UintSlice
+	fl := UintSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []uint(nil))
+	expect(t, set.Lookup("goat").Value.(*UintSlice).Value(), []uint{1, 2})
+}
+
+func TestUintSliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1 , 2")
+	val := NewUintSlice(3, 4)
+	fl := UintSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []uint{3, 4})
+	expect(t, set.Lookup("goat").Value.(*UintSlice).Value(), []uint{1, 2})
+}
+
+func TestUintSliceFlagApply_DefaultValueWithDestination(t *testing.T) {
+	defValue := []uint{1, 2}
+
+	fl := UintSliceFlag{Name: "country", Value: NewUintSlice(defValue...), Destination: NewUintSlice(3)}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{})
+	expect(t, err, nil)
+	expect(t, defValue, fl.Destination.Value())
+}
+
+func TestUintSliceFlagApply_ParentContext(t *testing.T) {
+	_ = (&App{
+		Flags: []Flag{
+			&UintSliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewUintSlice(1, 2, 3)},
+		},
+		Commands: []*Command{
+			{
+				Name: "child",
+				Action: func(ctx *Context) error {
+					expected := []uint{1, 2, 3}
+					if !reflect.DeepEqual(ctx.UintSlice("numbers"), expected) {
+						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.UintSlice("numbers"))
+					}
+					if !reflect.DeepEqual(ctx.UintSlice("n"), expected) {
+						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.UintSlice("n"))
+					}
+					return nil
+				},
+			},
+		},
+	}).Run([]string{"run", "child"})
+}
+
+func TestUintSliceFlag_SetFromParentContext(t *testing.T) {
+	fl := &UintSliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewUintSlice(1, 2, 3, 4)}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+	ctx := &Context{
+		parentContext: &Context{
+			flagSet: set,
+		},
+		flagSet: flag.NewFlagSet("empty", 0),
+	}
+	expected := []uint{1, 2, 3, 4}
+	if !reflect.DeepEqual(ctx.UintSlice("numbers"), expected) {
+		t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.UintSlice("numbers"))
+	}
+}
+func TestUintSliceFlag_ReturnNil(t *testing.T) {
+	fl := &UintSliceFlag{}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+	ctx := &Context{
+		parentContext: &Context{
+			flagSet: set,
+		},
+		flagSet: flag.NewFlagSet("empty", 0),
+	}
+	expected := []uint(nil)
+	if !reflect.DeepEqual(ctx.UintSlice("numbers"), expected) {
+		t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.UintSlice("numbers"))
+	}
+}
+
+var uint64SliceFlagTests = []struct {
+	name     string
+	aliases  []string
+	value    *Uint64Slice
+	expected string
+}{
+	{"heads", nil, NewUint64Slice(), "--heads value [ --heads value ]\t"},
+	{"H", nil, NewUint64Slice(), "-H value [ -H value ]\t"},
+	{"heads", []string{"H"}, NewUint64Slice(uint64(2), uint64(17179869184)),
+		"--heads value, -H value [ --heads value, -H value ]\t(default: 2, 17179869184)"},
+}
+
+func TestUint64SliceFlagHelpOutput(t *testing.T) {
+	for _, test := range uint64SliceFlagTests {
+		fl := Uint64SliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
+		output := fl.String()
+
+		if output != test.expected {
+			t.Errorf("%q does not match %q", output, test.expected)
+		}
+	}
+}
+
+func TestUint64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("APP_SMURF", "42,17179869184")
+
+	for _, test := range uint64SliceFlagTests {
+		fl := Uint64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
+		output := fl.String()
+
+		expectedSuffix := " [$APP_SMURF]"
+		if runtime.GOOS == "windows" {
+			expectedSuffix = " [%APP_SMURF%]"
+		}
+		if !strings.HasSuffix(output, expectedSuffix) {
+			t.Errorf("%q does not end with"+expectedSuffix, output)
+		}
+	}
+}
+
+func TestUint64SliceFlagApply_SetsAllNames(t *testing.T) {
+	fl := Uint64SliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
+	expect(t, err, nil)
+}
+
+func TestUint64SliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1 , 2")
+	var val Uint64Slice
+	fl := Uint64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []uint64(nil))
+	expect(t, set.Lookup("goat").Value.(*Uint64Slice).Value(), []uint64{1, 2})
+}
+
+func TestUint64SliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1 , 2")
+	val := NewUint64Slice(3, 4)
+	fl := Uint64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []uint64{3, 4})
+	expect(t, set.Lookup("goat").Value.(*Uint64Slice).Value(), []uint64{1, 2})
+}
+
+func TestUint64SliceFlagApply_DefaultValueWithDestination(t *testing.T) {
+	defValue := []uint64{1, 2}
+
+	fl := Uint64SliceFlag{Name: "country", Value: NewUint64Slice(defValue...), Destination: NewUint64Slice(3)}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{})
+	expect(t, err, nil)
+	expect(t, defValue, fl.Destination.Value())
+}
+
+func TestUint64SliceFlagApply_ParentContext(t *testing.T) {
+	_ = (&App{
+		Flags: []Flag{
+			&Uint64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewUint64Slice(1, 2, 3)},
+		},
+		Commands: []*Command{
+			{
+				Name: "child",
+				Action: func(ctx *Context) error {
+					expected := []uint64{1, 2, 3}
+					if !reflect.DeepEqual(ctx.Uint64Slice("numbers"), expected) {
+						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Uint64Slice("numbers"))
+					}
+					if !reflect.DeepEqual(ctx.Uint64Slice("n"), expected) {
+						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Uint64Slice("n"))
+					}
+					return nil
+				},
+			},
+		},
+	}).Run([]string{"run", "child"})
+}
+
+func TestUint64SliceFlag_SetFromParentContext(t *testing.T) {
+	fl := &Uint64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewUint64Slice(1, 2, 3, 4)}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+	ctx := &Context{
+		parentContext: &Context{
+			flagSet: set,
+		},
+		flagSet: flag.NewFlagSet("empty", 0),
+	}
+	expected := []uint64{1, 2, 3, 4}
+	if !reflect.DeepEqual(ctx.Uint64Slice("numbers"), expected) {
+		t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Uint64Slice("numbers"))
+	}
+}
+func TestUint64SliceFlag_ReturnNil(t *testing.T) {
+	fl := &Uint64SliceFlag{}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+	ctx := &Context{
+		parentContext: &Context{
+			flagSet: set,
+		},
+		flagSet: flag.NewFlagSet("empty", 0),
+	}
+	expected := []uint64(nil)
+	if !reflect.DeepEqual(ctx.Uint64Slice("numbers"), expected) {
+		t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Uint64Slice("numbers"))
+	}
+}
+
 var float64FlagTests = []struct {
 	name     string
 	expected string
@@ -1267,6 +1690,56 @@ func TestFloat64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
 	}
 }
 
+func TestFloat64SliceFlagApply_SetsAllNames(t *testing.T) {
+	fl := Float64SliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
+	expect(t, err, nil)
+}
+
+func TestFloat64SliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1.0 , 2.0")
+	var val Float64Slice
+	fl := Float64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []float64(nil))
+	expect(t, set.Lookup("goat").Value.(*Float64Slice).Value(), []float64{1, 2})
+}
+
+func TestFloat64SliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
+	defer resetEnv(os.Environ())
+	os.Clearenv()
+	_ = os.Setenv("MY_GOAT", "1.0 , 2.0")
+	val := NewFloat64Slice(3.0, 4.0)
+	fl := Float64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+	err := set.Parse(nil)
+	expect(t, err, nil)
+	expect(t, val.Value(), []float64{3, 4})
+	expect(t, set.Lookup("goat").Value.(*Float64Slice).Value(), []float64{1, 2})
+}
+
+func TestFloat64SliceFlagApply_DefaultValueWithDestination(t *testing.T) {
+	defValue := []float64{1.0, 2.0}
+
+	fl := Float64SliceFlag{Name: "country", Value: NewFloat64Slice(defValue...), Destination: NewFloat64Slice(3)}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{})
+	expect(t, err, nil)
+	expect(t, defValue, fl.Destination.Value())
+}
+
 func TestFloat64SliceFlagValueFromContext(t *testing.T) {
 	set := flag.NewFlagSet("test", 0)
 	set.Var(NewFloat64Slice(1.23, 4.56), "myflag", "doc")
@@ -1275,6 +1748,29 @@ func TestFloat64SliceFlagValueFromContext(t *testing.T) {
 	expect(t, f.Get(ctx), []float64{1.23, 4.56})
 }
 
+func TestFloat64SliceFlagApply_ParentContext(t *testing.T) {
+	_ = (&App{
+		Flags: []Flag{
+			&Float64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewFloat64Slice(1.0, 2.0, 3.0)},
+		},
+		Commands: []*Command{
+			{
+				Name: "child",
+				Action: func(ctx *Context) error {
+					expected := []float64{1.0, 2.0, 3.0}
+					if !reflect.DeepEqual(ctx.Float64Slice("numbers"), expected) {
+						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Float64Slice("numbers"))
+					}
+					if !reflect.DeepEqual(ctx.Float64Slice("n"), expected) {
+						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Float64Slice("n"))
+					}
+					return nil
+				},
+			},
+		},
+	}).Run([]string{"run", "child"})
+}
+
 var genericFlagTests = []struct {
 	name     string
 	value    Generic
@@ -2359,6 +2855,38 @@ func TestInt64Slice_Serialized_Set(t *testing.T) {
 	}
 }
 
+func TestUintSlice_Serialized_Set(t *testing.T) {
+	sl0 := NewUintSlice(1, 2)
+	ser0 := sl0.Serialize()
+
+	if len(ser0) < len(slPfx) {
+		t.Fatalf("serialized shorter than expected: %q", ser0)
+	}
+
+	sl1 := NewUintSlice(3, 4)
+	_ = sl1.Set(ser0)
+
+	if sl0.String() != sl1.String() {
+		t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
+	}
+}
+
+func TestUint64Slice_Serialized_Set(t *testing.T) {
+	sl0 := NewUint64Slice(1, 2)
+	ser0 := sl0.Serialize()
+
+	if len(ser0) < len(slPfx) {
+		t.Fatalf("serialized shorter than expected: %q", ser0)
+	}
+
+	sl1 := NewUint64Slice(3, 4)
+	_ = sl1.Set(ser0)
+
+	if sl0.String() != sl1.String() {
+		t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
+	}
+}
+
 func TestTimestamp_set(t *testing.T) {
 	ts := Timestamp{
 		timestamp:  nil,
@@ -2458,29 +2986,41 @@ type flagDefaultTestCase struct {
 func TestFlagDefaultValue(t *testing.T) {
 	cases := []*flagDefaultTestCase{
 		{
-			name:    "stringSclice",
+			name:    "stringSlice",
 			flag:    &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")},
 			toParse: []string{"--flag", "parsed"},
 			expect:  `--flag value [ --flag value ]	(default: "default1", "default2")`,
 		},
 		{
-			name:    "float64Sclice",
+			name:    "float64Slice",
 			flag:    &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)},
 			toParse: []string{"--flag", "13.3"},
 			expect:  `--flag value [ --flag value ]	(default: 1.1, 2.2)`,
 		},
 		{
-			name:    "int64Sclice",
+			name:    "int64Slice",
 			flag:    &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)},
 			toParse: []string{"--flag", "13"},
 			expect:  `--flag value [ --flag value ]	(default: 1, 2)`,
 		},
 		{
-			name:    "intSclice",
+			name:    "intSlice",
 			flag:    &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)},
 			toParse: []string{"--flag", "13"},
 			expect:  `--flag value [ --flag value ]	(default: 1, 2)`,
 		},
+		{
+			name:    "uint64Slice",
+			flag:    &Uint64SliceFlag{Name: "flag", Value: NewUint64Slice(1, 2)},
+			toParse: []string{"--flag", "13"},
+			expect:  `--flag value [ --flag value ]	(default: 1, 2)`,
+		},
+		{
+			name:    "uintSlice",
+			flag:    &UintSliceFlag{Name: "flag", Value: NewUintSlice(1, 2)},
+			toParse: []string{"--flag", "13"},
+			expect:  `--flag value [ --flag value ]	(default: 1, 2)`,
+		},
 		{
 			name:    "string",
 			flag:    &StringFlag{Name: "flag", Value: "default"},
@@ -2523,29 +3063,41 @@ type flagValueTestCase struct {
 func TestFlagValue(t *testing.T) {
 	cases := []*flagValueTestCase{
 		&flagValueTestCase{
-			name:    "stringSclice",
+			name:    "stringSlice",
 			flag:    &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")},
 			toParse: []string{"--flag", "parsed,parsed2", "--flag", "parsed3,parsed4"},
 			expect:  `[parsed parsed2 parsed3 parsed4]`,
 		},
 		&flagValueTestCase{
-			name:    "float64Sclice",
+			name:    "float64Slice",
 			flag:    &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)},
 			toParse: []string{"--flag", "13.3,14.4", "--flag", "15.5,16.6"},
 			expect:  `[]float64{13.3, 14.4, 15.5, 16.6}`,
 		},
 		&flagValueTestCase{
-			name:    "int64Sclice",
+			name:    "int64Slice",
 			flag:    &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)},
 			toParse: []string{"--flag", "13,14", "--flag", "15,16"},
 			expect:  `[]int64{13, 14, 15, 16}`,
 		},
 		&flagValueTestCase{
-			name:    "intSclice",
+			name:    "intSlice",
 			flag:    &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)},
 			toParse: []string{"--flag", "13,14", "--flag", "15,16"},
 			expect:  `[]int{13, 14, 15, 16}`,
 		},
+		&flagValueTestCase{
+			name:    "uint64Slice",
+			flag:    &Uint64SliceFlag{Name: "flag", Value: NewUint64Slice(1, 2)},
+			toParse: []string{"--flag", "13,14", "--flag", "15,16"},
+			expect:  `[]uint64{13, 14, 15, 16}`,
+		},
+		&flagValueTestCase{
+			name:    "uintSlice",
+			flag:    &UintSliceFlag{Name: "flag", Value: NewUintSlice(1, 2)},
+			toParse: []string{"--flag", "13,14", "--flag", "15,16"},
+			expect:  `[]uint{13, 14, 15, 16}`,
+		},
 	}
 	for i, v := range cases {
 		set := flag.NewFlagSet("test", 0)
diff --git a/zz_generated.flags_test.go b/zz_generated.flags_test.go
index 1d9afda..06ab7a6 100644
--- a/zz_generated.flags_test.go
+++ b/zz_generated.flags_test.go
@@ -160,6 +160,44 @@ func TestTimestampFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
 	_ = f.IsVisible()
 }
 
+func TestUint64SliceFlag_SatisfiesFlagInterface(t *testing.T) {
+	var f cli.Flag = &cli.Uint64SliceFlag{}
+
+	_ = f.IsSet()
+	_ = f.Names()
+}
+
+func TestUint64SliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
+	var f cli.RequiredFlag = &cli.Uint64SliceFlag{}
+
+	_ = f.IsRequired()
+}
+
+func TestUint64SliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
+	var f cli.VisibleFlag = &cli.Uint64SliceFlag{}
+
+	_ = f.IsVisible()
+}
+
+func TestUintSliceFlag_SatisfiesFlagInterface(t *testing.T) {
+	var f cli.Flag = &cli.UintSliceFlag{}
+
+	_ = f.IsSet()
+	_ = f.Names()
+}
+
+func TestUintSliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
+	var f cli.RequiredFlag = &cli.UintSliceFlag{}
+
+	_ = f.IsRequired()
+}
+
+func TestUintSliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
+	var f cli.VisibleFlag = &cli.UintSliceFlag{}
+
+	_ = f.IsVisible()
+}
+
 func TestBoolFlag_SatisfiesFlagInterface(t *testing.T) {
 	var f cli.Flag = &cli.BoolFlag{}