How to access flags outside of main package?
Asked Answered
L

4

9

We parse flags in main.go which is in main package, of course. Then we have another package where we want to read some flag's value.

flags.Args() work fine, it will return all non-flag values.

But I cannot figure out to how read already parsed value for a flag in a package other than main.

Is it possible?

Thanks

Amer

Latticed answered 23/10, 2014 at 21:29 Comment(0)
D
17

I had the same requirement recently and I wanted a solution that avoided calling flag.Parse repeatedly in init functions.

Perusing the flag package I found Lookup(name string) which returns a Flag which has a Value. Every built in Value implements the flag.Getter interface. The call chain looks like this:

flag.Lookup("httplog").Value.(flag.Getter).Get().(bool)

If you mistype the flag name or use the wrong type you get a runtime error. I wrapped the lookup in a function that I call directly where needed since the lookup and get methods are fast and the function is not called often. So the main package declares the flag.

// main.go
package main

import "flag"

var httplog = flag.Bool("httplog", false, "Log every HTTP request and response.")

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

And the utility package, which is decoupled from main except for the flag name, reads the flag value.

// httpdiag.go
package utility

import "flag"

func logging() bool {
    return flag.Lookup("httplog").Value.(flag.Getter).Get().(bool)
}
Dragone answered 13/11, 2016 at 14:7 Comment(0)
R
11

You can define the var storing the flag in the separate package, as an exported variable, then call the flag parsing in the main package to use that variable, like this:

mypackage/const.go

var (
    MyExportedVar string
)

mainpackage/main.go

func init() {
    flag.StringVar(&mypackage.MyExportedVar, "flagName", "defaultValue", "usage")

    flag.Parse()
}

This way, everybody can access that flag, including that package itself.

Note: this only works for exported variables.

Rema answered 16/5, 2017 at 9:0 Comment(1)
This! should be accepted answer. Good that it doesn't introduce flag name dependencies between modules and keeps flag definition and parsing to single module.Epicurus
P
6

You can define the flag in that package and call flag.Parse() in func init(), flag.Parse can be called multiple times.

However, if you want to access the flag's value from multiple packages you have to expose it or create an exposed function to check it.

for example:

// pkgA
var A  = flag.Bool("a", false, "why a?")

func init() {
    flag.Parse()
}

// main package
func main() {
    flag.Parse()
    if *pkgA.A {
        // stuff
    }
}

Also you can use FlagSet.Parse(os.Args) if you want to reparse the args.

Paint answered 23/10, 2014 at 21:37 Comment(0)
A
-2

When you parse your flags, parse them into global variables which start with an initial Capital so they are public, eg

package main

var Species = flag.String("species", "gopher", "the species we are studying")

func main() {
    flag.Parse()
}

Then in your other package you can refer to them as

package other

import "/path/to/package/main"

func Whatever() {
    fmt.Println(main.Species)
}
Asteria answered 23/10, 2014 at 21:38 Comment(5)
I tried this. The problem, is then I get import cycle not allowed That is because other package is under main folder.Latticed
@Latticed can't you just pass the args to the other package?Paint
@OneOfOne, yes we can use that. I really like flags, because it take care of default value and help text in one location. I was hoping the there is an easy way to access flags from outside of main package. But not a big deal if it is not possible. We might just use args and write a custom helper.Latticed
@Latticed if the flag is just used by your package you don't need put it in main, or just pass the flag's value from main to the pkg like pkg.New(flag-value).Paint
Thanks, I know that is a good solution. Probably better than reading global/env value in non-main package. But in our case, main instantiate another class in a different package, that class using factory pattern may instantiate the class where we need to access flags. So this means we will have to pass this value around a bit. This might a bit too much refactoring than worth right now. It looks like flag.Args() is the way to go. Thank you @Paint & Nick!Latticed

© 2022 - 2024 — McMap. All rights reserved.