Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   Python (http://www.programmingforums.org/forum43.html)
-   -   Storing preferences (http://www.programmingforums.org/showthread.php?t=10687)

titaniumdecoy Jul 11th, 2006 7:33 PM

Storing preferences
 
I want to store my script's preferences in a file that is human-readable and -editable. Is there a standard Python class for storing and parsing a file in the format VARIABLE=VALUE on each line? I know I can write my own function to do this very easily but since Python seems to have built-in classes for just about everything I thought I'd ask. Thanks.

Sane Jul 11th, 2006 7:35 PM

Well, it's a little dangerous. But you can actually just straight out execute the configuration file.

:

exec open('options.conf', 'r').read()

But, it's dangerous. And slow. :D

Cerulean Jul 11th, 2006 7:47 PM

Quote:

Originally Posted by Sane
But, it's dangerous. And slow.

I don't know if i'd call it dangerous. I'd say that it's nice having the ability to tap into a fully blown programming language when doing your config. I use Python for some of my configurations and it works just fine.
As for slow, I'd imagine that loading Python is way faster than reading in XML via Python or parsing stuff yourself.
Also, if your config file is just named config.py, you can just import it without having to manually open and read and exec it.
:

import config
print config.foo

Now what would be harder would be to write Python code back out into the file (say you changed the config programatically from your application).
When I want simple key=value, newline seperated config options, I just do something like this:
:

def readConfig(fname):
    data = file(fname).read()
    return dict([x.split("=") for x in data.split("\n")])

def writeConfig(config, fname):
    f = file(fname, "w")
    f.write("\n".join(["=".join(item) for item in config.items()]))
    f.close()

Obviously you could abstract this way into a Configuration class or some such that behaves like a dictionary but handles syncing for you automagically.

Arevos Jul 11th, 2006 7:49 PM

Take a look at ConfigParser.

If you had a configuration file called test.cfg:
:

[defaults]
host = localhost
port = 80


Then you could access it thus:
:

>>> from ConfigParser import ConfigParser
>>> config = ConfigParser()
>>> config.read("test.cfg")
["test.cfg"]
>>> config.sections()
['defaults']
>>> config.options("defaults")
['host', 'port']
>>> config.get("defaults", "host")
'localhost'
>>> config.get("defaults", "port")
'80'


titaniumdecoy Jul 11th, 2006 7:57 PM

Does ConfigParser support comments, eg lines beginning with #?

What I am trying to do is read and set each variable AND value from the config file. At the moment I don't know the names of the variables, which I was asking about in this thread. This probably isn't possible with ConfigParser.

Sane Jul 11th, 2006 8:11 PM

I was bored so I made this silly little class.

It gets the job done, and it's safe from any stupid stunts the user tries to pull.

:



class conf:
    def __init__(self, filename):
        self.CONF_FILE = filename

    def save(self, opts):

        data = '\n'.join(['%s = %s'%(key, opts[key]) for key in opts])

        stream = open(self.CONF_FILE, 'w')
        stream.write(data)
        stream.close()

    def load(self):

        stream = open(self.CONF_FILE, 'r')
        data = stream.read().split('\n') # readlines keeps the \n there, I don't like that
        stream.close()

        opts = dict()

        for line in data:
            newline = line.split('=', 1)

            if len(newline) < 2:
                raise Exception, 'A line without an equal sign was found'

            lennewline0 = len(newline[0])

            for char in range(lennewline0):
                if newline[0][lennewline0-char-1] != ' ':
                    key = newline[0][:lennewline0-char]
                    break

            for char in range(len(newline[1])):
                if newline[1][char] != ' ':
                    value = newline[1][char:]
                    break

            try:
                newvalue = int(value)

            except ValueError:
                newvalue = value

            opts[key] = newvalue

        return opts
               

if __name__ == '__main__':
           
    data = conf('options.conf')

    data.save( {'level' : 4,
                'volume' : 9,
                'graphics' : 'high',
                'event' : 4112 } )

    options = data.load()

    print options['event']


With some simple modifications, you could make it so it sets actual variables in an object using "setattr". That's if you would rather not have a dictionary of values.

Arevos Jul 11th, 2006 8:14 PM

Quote:

Originally Posted by titaniumdecoy
Does ConfigParser support comments, eg lines beginning with #?

From the ConfigParser documentation:
Quote:

Lines beginning with "#" or ";" are ignored and may be used to provide comments.
Quote:

Originally Posted by titaniumdecoy
This probably isn't possible with ConfigParser.

Again, from the ConfigParser documentation:
Quote:

write(fileobject)
Write a representation of the configuration to the specified file object. This representation can be parsed by a future read() call. New in version 1.6.

titaniumdecoy Jul 11th, 2006 8:21 PM

Thanks Arevos and Sane, but ConfigParser seems a little too complicated, and I wanted to be able to represent tuples of values by items separated with commas; previously read variables as values prepended with $; comments; and more. So I wrote my own little class:

:

import gimmie

prefsfile = r'C:\Documents and Settings\jboyle\Desktop\python_scripts\OLD\nightcrawler_prefs.txt'

prefs = {}

def readprefs(prefsfile, cls):
    """Read constants from prefsfile into new variables in cls"""
    for line in prefsfile:
        line = line.strip()
        # Process line if it is not empty or a comment
        if len(line) > 0 and not line.startswith('#'):
            var, values = line.split('=')
            values = values.split(',')
           
            # Replace $VALUE with corresponding value
            values = fillvalues(values, cls)
           
            # Return value or tuple of values
            if len(values) == 1: setattr(cls, var, values[0])
            else: setattr(cls, var, tuple(values))

def fillvalues(vals, cls):
    """Replace each $VALUE in vals with corresponding value in cls"""
    temp_vals = list(vals)
    for i, v in enumerate(temp_vals):
        if v.startswith('$') and hasattr(cls, v.strip('$')):
            w = getattr(cls, v.strip('$'))
            # If w is a single-element tuple, extract the element
            if type(w) is tuple and len(w) == 1: w = w[0]
            vals[i] = w
    return vals

def readfile(prefsfile, method, cls):
    """Read each line in file and pass it to method along with cls"""
    try:
        file = open(prefsfile)
        try:
            method(file, cls)
        finally:
            file.close()
    except IOError:
        print 'There was an error reading the NightCrawler prefs file',
        print '[%s]' % prefsfile

readfile(prefsfile, readprefs, gimmie)


Sane Jul 11th, 2006 8:26 PM

By the way, for clarifaction, that is not a class. >_>;;

A class is... basically what my code demonstrates. You can create an instance of a class with an object, and call functions of the class as methods. Containing all the individual variables within each individual instance of the class.

titaniumdecoy Jul 11th, 2006 8:29 PM

I know what a class is. I get lazy sometimes. :mad:

Although, having thought further on the matter, the script I wrote doesn't address saving new values. That is a problem. A very big one.

I may have to go back and look at ConfigParser.

In fact, having a human-readable file is more problematic than I thought. I may go back to just using the shelve module.


All times are GMT -5. The time now is 12:52 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC