Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load module-wide config in python

Let's assume, that there are following minimalistic python classes inside one module, e.g. Module:

module/
   __init__.py
   db.py
   document.py

db.py

import yaml

class DB(object):
    config = {}

    @classmethod
    def load_config(cls, config_path):
        cls.config = yaml.load(open(config_path, 'r').read())

and document.py

from .db import DB

class Document(object):
    db = None

    def __init__(self):
        self.db = DB()

End-user is going to use such Module as follows:

from Module import DB, Document

DB.load_config('/path/to/config.yml')

Document.do_some_stuff()

doc1 = Document()
doc2 = Document.find(...)
doc2.update_something(...)
doc2.save()

It is expected that Document class and every instance of it will have internally an access to class DB with a config specified by user. However, since Document performs an internal import of DB class (from .db import DB) it receives a 'fresh' DB class with default config.

I did a lot of searches, most of questions and answers are about module-wide configs, but not specified by the end user.

How can I achieve such functionality? I guess that there is some architectural problem here, but what is the most simple way to solve it?

like image 260
ololobus Avatar asked Feb 25 '26 11:02

ololobus


1 Answers

Perhaps this isn't the most appropriate answer, but a few months back I wrote a module called aconf for this exact purpose. It's a memory-based global configuration module for Python written in 8 lines. The idea is you can do the following:

You create a Config object to force the user to input the configuration your program requires (in this case it's inside config.py):

""" 'Config' class to hold our desired configuration parameters. 

Note:
    This is technically not needed. We do this so that the user knows what he/she should pass 
    as a config for the specific project. Note how we also take in a function object - this is
    to demonstrate that one can have absolutely any type in the global config and is not subjected
    to any limitations.
"""

from aconf import make_config

class Config:
    def __init__(self, arg, func):
        make_config(arg=arg, func=func)

You consume your configuration throughout your module (in this case, inside functionality.py):

""" Use of the global configuration through the `conf` function. """

from aconf import conf

class Example:
    def __init__(self):
        func = conf().func
        arg = conf().arg

        self.arg = func(arg)

And then use it (in this case inside main.py):

from project.config import Config
from project.functionality import Example

# Random function to demonstrate we can pass _anything_ to 'make_config' inside 'Config'.
def uppercase(words):
    return words.upper()

# We create our custom configuration without saving it.
Config(arg="hello world", func=uppercase)

# We initialize our Example object without passing the 'Config' object to it.
example = Example()
print(example.arg) 
# >>> "HELLO WORLD"

The entire aconf module is the following:

__version__ = "1.0.1"
import namedtupled

def make_config(**kwargs):
    globals()["aconf"] = kwargs

conf = lambda: namedtupled.map(globals()["aconf"])
config = lambda: globals()["aconf"]

... in essence, you just save your configuration to globals() during runtime.

It's so stupid it makes me wonder if you should even be allowed to do this. I wrote aconf for fun, but have never personally used it in a big project. The reality is, you might run into the problem of making your code weird for other developers.

like image 67
Felipe Avatar answered Feb 27 '26 02:02

Felipe