|
|
||||||||||||||||||
|
|
||||||||||||||||||
![]() |
![]() |
Issue 2 - Revision 1 / Published in 2002
|
|||
Configuration Files Made Easy - - - - - - - - - - - - Sean Reifschneider | Originally published in Py Issue 2
Configuration files are something that many programs use. The complexity of configuration languages can vary greatly. On one end of the spectrum are simple “name” = “value” assignments (often found in shell scripts; for example, the files under Configuration files are commonly used, but there is no single standard that they conform to. The result is that there are very few tools for managing them. However, the Python programming language, because of its dynamic nature, can be of help. In this article, I’ll discuss a way of creating a flexible configuration system that allows for complex Python-based configuration files. Because Python itself does most of the heavy lifting, the amount of code necessary to pull this off is small. How small? For a basic “name”=”value,” with all the normal Python syntax available, my example requires 2 lines of code. BACKGROUNDSome of the most powerful configuration systems are domain-specific languages. These are languages that are designed specifically for doing one thing good. One example, which comes to mind, is fetchmail. Fetchmail is a program for retrieving e-mail from remote servers (for example, your ISP), and making it available on your local machine. A sample fetchmail configuration: poll mail.example.com proto pop2: user “jsmith”, with password “My^Hat”, is “John.Smith” here;
This is pretty straightforward—download mail via POP2 from
Another example of a domain-specific language would be the
In a more general-purpose configuration language, for example using Python’s [File “cfgparser-example.conf”] # section describing mail retrieval from # “mail.example.com”. [mail.example.com] username: jsmith password: My^Hat protocol: pop2 localuser: John.Smith spoolfile: /var/spool/mail/%(localuser)s NOTE: Python version 2.2 and higher allows “.” in the section name. Previous versions only allowed alphanumeric, hyphen, and dash.
This is an example of the syntax recognized by the
While there isn’t a lot of flexibility in this configuration language, it’s fairly easy to use. You first need to set up the import ConfigParser cfg = ConfigParser.ConfigParser() cfg.read(‘cfgparser-example.conf’)
Now, to find out what protocol to use when talking to the >>> print cfg.get(‘mail.example.com’, ‘protocol’) pop2 A slightly more complicated example: import ConfigParser cfg = ConfigParser.ConfigParser() cfg.read(‘cfgparser.conf’) for server in cfg.sections(): print ‘Settings for server “%s”:’ % server for option in cfg.options(server): print ‘ %s is: “%s”’ % (option, cfg.get(server, option))
The outer Settings for server “mail.example.com”: username is: “jsmith” localuser is: “John.Smith” spoolfile is: “/var/spool/mail/John.Smith” protocol is: “pop2” password is: “My^Hat”
While simple to deal with, this is lacking some expressiveness. The above demonstrates about the most complexity you can achive with the
At the beginning of the article I promised you a configuration file with a rich set of Python expressions, while also being very easy to use.
The basic idea being to get the Python interpreter to deal with the processing of the configuration file. This gives you access to the full set of syntax allowed by the Python language. For example, one thing I find I am frequently doing is writing code in my development environment, which I’d like to be able to directly deploy out to test and production environments. However, keeping track of multiple configuration files can be a pain. It would be nice to be able have the configuration tailor itself depending on the machine it’s running on:
Using the
The value of the
The
You can get a bit more fancy than just “name”=”value” mappings, with little additional code. Returning to our mail-retrieving example, you may want to set some global values, and allow the user to poll various mail servers. This can be done by using a class that controls the configuration data:
While the above looks more complicated than our original example, at its heart it’s the same
This one sets verbose and interactive flags if the program is running on the development machine. It can be run by passing the name of the configuration file to the
This produces the following output on the development machine:
As shown, this configuration mechanism allows for the execution of arbitrary code in the configuration file. Care must be taken in the event that the configuration file is writable by a user with privileges other than the user running the system. For example, a program running periodically from cron with root privileges should not read a configuration file that’s writable by a non-privileged user. This is not a concern exclusively with this configuration mechanism. You rarely want a root-level process directed by a user-writable configuration file. However, the power and flexibility of this mechanism makes it particularly easy to exploit. There are things that can be done if writing an application that runs with one user’s privileges, but need to read configuration files written by another user. For example, before reading the configuration file, the program can assume a lower-privileged user’s identity. With appropriate consideration of the security issues, this configuration mechanism can safely become a useful part of your programming toolbox.
I have used this mechanism successfully in a number of projects. It’s amazing how quickly you can make rather complex configuration systems. In a number of cases I’ve found that what would normally be special cases in the code could be moved off to the configuration file, and allow the ultimate flexibility.
One particular benefit is that users who are unfamiliar with Python can stick with fairly simple “name”=”value” and
|
||||||||||
|
Py is committed to bringing you great Python Articles. | ||||||||||
![]() |
Reproduction of material from any of PyZine's pages without prior written permission is strictly prohibited. Copyright 2003 - 2005 PyZine |
|