Using Abseil vs. Directly calling main()?

2.3k views Asked by At

I've been using the vanilla

def main():
    # Do stuff


if __name__ == '__main__':
    main()

but recently saw people doing

from absl import app

def main(_):
    # Do things

if __name__ == '__main__':
    app.run(main)

Abseil provides flags.FLAGS, but I've been using ArgumentParser, which works perfectly fine, so there is no win for Abseil in this aspect.

Then, why bother go the Abseil route?

PS: Related discussion on Reddit (which doesn't really answer this question): https://www.reddit.com/r/Python/comments/euhl81/is_using_googles_abseil_library_worth_the/

1

There are 1 answers

0
Jcnars On

Consider a design pattern, where you are passing a json file (that contains say site-specific constants) at the cmd line as input to your Python script. Say,the json file contains immutable constants and you want to maintain it that way.

You want the constants from json file contents to be made available to all the modules within your project.

One way to do this is by implementing a central module that deserializes the json into a Python object. ABSL helps you to solve this by accessing (via the FLAGS) the input file in the central module and then storing it into a class variable so all modules across your project can use this.

Without ABSL, you would need to first argparse the input file in main module, then send it to the central module.

A code example of this can be something like: main.py:

from centralmod import DeserializeClass
import centralmod
from absl import flags
from absl import app

_JSON_FILE = flags.DEFINE_string("json_file", None, "Constants", short_name='j', required=True)


def scenario():
    import someothermodule
    someothermodule.do_something()


def populate_globalvar():
    centralmod.populate_somevar()
    deserialized_data = DeserializeClass.somevar


def main(argv):
    populate_globalvar()
    scenario()


if __name__ == '__main__':
    app.run(main)

centralmod.py:

from absl import flags
import json

FLAGS = flags.FLAGS


class DeserializeClass:
    @classmethod
    def get_value(cls, key):
        return DeserializeClass.somevar[key]


def populate_somevar():
    with open(FLAGS.json_file) as json_constants_fh:
        deserialized_data = json.load(json_constants_fh)
    setattr(DeserializeClass, 'somevar', deserialized_data)

and someothermod.py:

from centralmod import DeserializeClass
site_specific_consts = DeserializeClass.somevar

def do_something():
    print(f"\nScenario: Testing. The site-specific constants are:\n{site_specific_consts}")
    print(f"the value of key ssh_key_file is {DeserializeClass.get_value('ssh_key_file')}")
    print(f"the value of key nodes is {DeserializeClass.get_value('nodes')}")