Hacker News new | ask | show | jobs
by dashwav 2241 days ago
I recently ran into a few roadblocks as well when trying to get my python applications set up in a true 12factor way, and I was surprised by how much I had begun to take for granted the ease of configuration that the Go library Viper provided. I went ahead and wrote a library [Gila](https://gitlab.com/dashwav/gila) that implements a similar set of features that I think does a very good job of allowing for 12factor app building in python.

Specific to the article, my way of handling the issue of "turn everything into an environment variable" is to rely on the cascading feature of Gila/Viper and store sane defaults in code that only are overridden when need be. This allows for default values to be set, overridden by config files provided, and further overridden either by code or by ENV variables.

This would allow us to instead of having config written in python (something I usually try to stay away from) we could have it written in something like YAML or TOML instead and loaded in on the fly in each environment, while still allowing for overrides in either the code or by ENV variables.

I recently (last few months) have switched all of my python projects over to using Gila and I have been super impressed by how well the language itself lends to building dockerized 12factor apps when using tools made for that purpose - which is a sentiment I would not have had last year at this time.

https://gitlab.com/dashwav/gila

https://gila.readthedocs.io/en/latest/index.html

I appreciate any feedback either the author or HN in general have as well.

4 comments

Just lightly skimmed the docs, so I am probably missing something. Do all the consumers of gila config use the singleton class instance and just call gila.get() everywhere?

If I'm just focusing on how to make my code easier to read by my fellow developers, I would want to see something like this be based off of a dictionary-style API, that could be imported and used like so:

    import gila as config
    bucket_name = config["bucket_name"]
Since .get() is already so close to this API, I wonder if you considered this and rejected it for a specific reason?
First off, thank you for looking at the project and giving feedback - I really appreciate it!

- Do all the consumers of gila config use the singleton class instance and just call gila.get() everywhere?

You can either use the singleton pattern (which is the recommended way of utilizing the library) or you can assign the Gila object to a local variable and ship it around your code (this would allow you to have two separate configurations at the same time). You could see that here: https://gitlab.com/dashwav/gila/-/blob/develop/examples/mult...

I also think striving for readability is the way to go when building code in general, and that is actually one of the reasons I opted to keep the `.get()` syntax! This may come down to personal opinions but I think that when the behavior of a library is fundamentally different than that of a base type like dict it should be explicit that a library is being used.

While dictionaries also have a `.get()` syntax I think that seeing `config['value']` would lead a developer to make the assumption that config is simply a dictionary, and this might lead to erroneous code whereas `gila.get("value")` (or even `config.get('value')`) will make it more obvious that this is not a dictionary but actually a library that is doing more than a dictionary under the hood.

As a final point this syntax keeps it closer to the Viper go library that I was inspired by and therefore eases the transition between the two should anyone have familiarity with either library.

You could make it behave like a dictionary by add the __getitem__ function:

  config.__getitem__ = lambda x: config.get(x)
Lately, I've switched from doing mostly Python to mostly Go, and I've fallen in love with the Flags-First https://github.com/peterbourgon/ff approach. Basically, you have three levels of config: a conf file, ENV vars, and CLI flags. As you do local dev, you switch stuff on and off with CLI flags, and then when it's time to promote to prod, you switch to having everything as an ENV var or a secrets file or whatever is easy to do in your hosting system. It's very convenient, and Go's standard flags package gives you a simple way to get strongly typed values out of strings.
This looks great, I'm going to consider this for future projects!

I do a lot of Django, and have found the `django-configurations` package to be pretty good. The thing it's missing that Gila provides is the config file layer, but that's also not _typically_ how Django projects are configured, config is generally done in Python rather than config files.

Yeah I had used django in the past and had run-ins with `django-configurations` and while it is an extremely powerful configuration library, I set out to make Gila a much more portable and simpler API for more generalized use - with the goal that this library can be nearly drop in with 0 configuration in most projects.

I would love to hear any feedback should you decide to use Gila in one of your projects!

I'm curious what the argument is for overriding config files with ENV vars, rather than vice versa. I'm not saying it's wrong, it's just not something I've considered before in terms of prioritisation if you have competing settings in both.
It's a common pattern with container and cloud platforms. Configurations are passed declaratively through whatever yaml interface they expose as environment variables. This way you can have a higher level config that glues all the components together.