A Design Pattern for Configuration Management in Python
Design Patterns

Configuration Management is a requisite for medium or large projects, since you want to externalize all the config so that changes do not ripple when you modify any value. What does that mean? Let me break it down with an example of using Python with MongoDB.

The Example

Let’s take an example of connecting to MongoDB and getting 2 collections, let’s call it the Student collection and Teacher Collection. We shall have three types of config. One for the Mongo collection itself, and one each for the field names and collection names of the collection.

So what happens if we don’t/do have configuration Management? Well, a few things:

1) Changes will Ripple:

Suppose you decide to change the host name – since you are moving from testing to production onto a real server. You have to make the change of this host name in multiple locations. Suppose now you move it back into local for testing. Changes again.

2) Flexibility:

If you externalize your config to a single location, you have to make changes only in a single place. It’s quick, and easy.

3) Less Worry:

You won’t have to worry where you have to make the changes to make your app work in local. If you don’t have it centralized, your life will be hell if you want to make changes.

4) Externalization:

This means other naive people can use it, just by changing the config. So a non-programmer can set up your system by just changing the values relevant to his requirements. It also helps testing teams (if you have one).

I can list more, but you’ve got the idea (I hope).

So let’s get to the actual config values. (If you don’t know Mongo, don’t worry, these are just configurable values.)

  • Mongo Connection Properties: Host, Port, Password, Username, Database
  • Name Student Collection Properties: Collection Name, All Field Names
  • Teacher Collection Properties: Collection Name, All Field Names

Existing Solutions

Let’s take a brief look at existing solutions to store config files (from 1): 1) YAML: YAML is a human friendly data serialization standard for all programming languages. PyYAML is a YAML parser, that can load and read YAML files. You can see 1 for a concrete example 2) ConfigParser: This is python’s built in module for, well, parsing config files in .ini format. All good 3) Python dict: This is the simplest format, with just a python dict specifying key-value pairs like so:

 config = { 'MONGO_DB_NAME' : 'Database' , 'MONGO_HOST' : localhost ,.. }

These approaches handle config well, and at the end transform the config file into a python dictionary. I argue that this isn’t sufficient for Config Management, and go a step further : Creation of Classes that serves these properties in a clean way.


Step 1: Create a wrapper around the config dict.

Let’s assume you chose one of the 3 ways to store the configuration, and you’ve put it into a dict named ‘conf’. Create a class called Config (or Configuration), with a method get_property, which takes in a arg that is a key into the conf dictionary. Like so:

# loaded config 
conf = { 
         key1: val1,
         key2: val2,
         keyn : valn

class Config(object):
    def __init__(self):
        self._config = conf # set it to conf

    def get_property(self, property_name):
        if property_name not in self._config.keys(): # we don't want KeyError
            return None  # just return None if not found
        return self._config[property_name]

We can now do something like this:

 configuration = Config()
 mongo_client = pymongo.MongoClient(host=configuration.get_property('MONGO_HOST'), port=configuration.get_property('MONGO_PORT'))

This just doesn’t cut it, since we have to remember MONGO_HOST, blah blah. So we go to step 2

Step 2: Make Classes for each type of config by extending the Config class.

Self Explanatory!

class MongoConfig(Config):

class StudentConfig(Config):

class TeacherConfig(Config):

Step 3: Make properties for each, well, property of that config.

If you don’t know what python properties are, just google them around a bit. I’m gonna explain one class only here, the other classes can be similarly built.

class MongoConfig(Config):

    def host(self):
        return self.get_property('MONGO_HOST')

    def port(self):
        return self.get_property('MONGO_PORT')

    def username(self):
        return self.get_property('MONGO_USERNAME')

    def db_name(self):
        return self.get_property('MONGO_DB_NAME')

    def password(self):
        return self.get_property('MONGO_PASSWORD')

Since MongoConfig is extended from Config, it has full access to the Config class methods, so it can use get_property to get the relevant properties.

To illustrate how it can be used, here’s a method to get a collection using pymongo:

def get_collection(collection_name):
    configuration = config.MongoConfig()
    mongo_client = pymongo.MongoClient(host=configuration.host, port=configuration.port)
    db = mongo_client[configuration.db_name]
    if configuration.username is not None:
        db.authenticate(configuration.username, password=configuration.password)
    return db[collection_name]

You can see this code in the following gist: https://gist.github.com/samarthbhargav/e491a6ab5a83d05eb833

Feel free to comment, etc. Refrences: