Skip to content

Custom Configuration

Adam Venturella edited this page Oct 1, 2011 · 11 revisions

Custom Configuration

In the configuration section we touched briefly on the concept of adding your own custom configuration section. In this section we will expand on that idea and provide a sample custom configuration object.

We learned in the configuration section that in order to declare a custom configuration section, at a minimum, we need to add the following into our crazyhorse.config:

	{
		"custom_sections":
		[
			{"name":"social", "type":"myapp.configuration.social.SocialSection", "notes":"Will be available as CUSTOM_SOCIAL"}
		],
	}

When CreazyHorse reads the crazyhorse.config file at startup, it will initialize our SocialSection object and assign it to:

	crazyhorse.configuration.manager.Configuration.CUSTOM_SOCIAL

Remember, it will always be assigned with the prefix CUSTOM_. Additionaly, if we give it a name with hyphens, social-configuration those hyphens will be converted to underscores. Thus, our assignment would be:

crazyhorse.configuration.manager.Configuration.CUSTOM_SOCIAL_CONFIGURATION

Now that we know where it goes after it's initialized, what are the rules for making one?

Creating a Custom Configuration Handler

There are 3 rules when creating a custom configuration handler:

  1. It MUST be a class
  2. It MUST be callable
  3. It MUST extend crazyhorse.configuration.sections.ConfigurationSection

With those 3 rules in mind, and assuming we are building our handler for the section we defined above, lets see what a basic configuration handler looks like.

	from crazyhorse.configuration.sections import ConfigurationSection
	class SocialSection(ConfigurationSection):

    	def __init__(self):
        	self.dog = "Lucy"
        	self.cat = "Ollie"

    	def __call__(self):
        	return self

Note that __call__ returns self. You can return anything you like here, it does not have to be self. Whatever is returned from __call__ is what will be set in:

	crazyhorse.configuration.manager.Configuration.CUSTOM_SOCIAL

Within our application then I would be able to do this:

	from crazyhorse.configuration.manager import Configuration
	
	Configuration.CUSTOM_SOCIAL.dog # Lucy
	Configuration.CUSTOM_SOCIAL.cat # Ollie

That's all well and good, but what if we would like our values for dog and cat to be defined in our crazyhorse.config. No problem, lets see how that might look.

First, we need to amend our crazyhorse.config and include a section with the name we defined in our custom_sections collection.

	{
		"custom_sections":
		[
			{"name":"social", "type":"myapp.configuration.social.SocialSection", "notes":"Will be available as CUSTOM_SOCIAL"}
		],

		"social":
		{
			"dog":"Lucy the Dog",
			"cat":"Ollie the Cat"
		}
	}

Note that in our custom_section collection we defined the name attribute to "social". Consequently, we also placed a corresponding JSON section named "social".

CrazyHorse will now attempt to hand off that "social" section via a call to an instance of myapp.configuration.social.SocialSection. Since our custom handler is going to be called with an additional argument, the decoded JSON section, we need to update our class a bit:

	from crazyhorse.configuration.sections import ConfigurationSection
	class SocialSection(ConfigurationSection):

		def __init__(self):
        	self.dog = None
        	self.cat = None

		def __call__(self, section):
        	self.dog = section["dog"]
			self.cat = section["cat"]
			return self

Take a look at what changed.

First, we set our 2 instance variables to None, as we will be receiving those values from from the crazyhorse.config

Next, we altered the signature of __call__(self) to __call__(self, section). Section represents the decoded JSON section from the crazyhorse.config

Finally, we set our two instance variables to the values from the section that was passed in. We still return self here.

Thus, calling the values from this configuration section would look like this:

	from crazyhorse.configuration.manager import Configuration
	
	Configuration.CUSTOM_SOCIAL.dog # Lucy the Dog
	Configuration.CUSTOM_SOCIAL.cat # Ollie the Cat

Why rely on __call__?

We use the result of __call__ instead of just blindly setting your handler class as it gives you the opportunity to store any kind of object you like.

In our second example, we could have simply returned the decoded JSON section we were handed. That would have turned our access sample above into this:

	from crazyhorse.configuration.manager import Configuration
	
	Configuration.CUSTOM_SOCIAL["dog"] # Lucy the Dog
	Configuration.CUSTOM_SOCIAL["cat"] # Ollie the Cat

We could also decide to return a string, or another kind of object entirely. The point is, the handler gets to decide what the result should be.

Clone this wiki locally