111

I have tried to use Python's ConfigParser module to save settings. For my app it's important that I preserve the case of each name in my sections. The docs mention that passing str() to ConfigParser.optionxform() would accomplish this, but it doesn't work for me. The names are all lowercase. Am I missing something?

<~/.myrc contents>
[rules]
Monkey = foo
Ferret = baz

Python pseudocode of what I get:

import ConfigParser,os

def get_config():
   config = ConfigParser.ConfigParser()
   config.optionxform(str())
    try:
        config.read(os.path.expanduser('~/.myrc'))
        return config
    except Exception, e:
        log.error(e)

c = get_config()  
print c.options('rules')
[('monkey', 'foo'), ('ferret', 'baz')]
crennie
  • 674
  • 7
  • 18
pojo
  • 5,892
  • 9
  • 35
  • 47

5 Answers5

134

The documentation is confusing. What they mean is this:

import ConfigParser, os
def get_config():
    config = ConfigParser.ConfigParser()
    config.optionxform=str
    try:
        config.read(os.path.expanduser('~/.myrc'))
        return config
    except Exception, e:
        log.error(e)

c = get_config()  
print c.options('rules')

I.e. override optionxform, instead of calling it; overriding can be done in a subclass or in the instance. When overriding, set it to a function (rather than the result of calling a function).

I have now reported this as a bug, and it has since been fixed.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Martin v. Löwis
  • 124,830
  • 17
  • 198
  • 235
59

For me worked to set optionxform immediately after creating the object

config = ConfigParser.RawConfigParser()
config.optionxform = str
phoenix
  • 7,988
  • 6
  • 39
  • 45
ulitosCoder
  • 1,919
  • 1
  • 17
  • 22
16

Add to your code:

config.optionxform = lambda option: option  # preserve case for letters
FooBar167
  • 2,721
  • 1
  • 26
  • 37
  • 1
    This appears to work for me at least in python 2.7 and is much cleaner than the accepted answer. Thanks foo! – hrbdg Oct 02 '18 at 19:52
  • 2
    this is the same as top scored answer - see line `config.optionxform=str` :) just instead of your lamdba @Martin v. Löwis uses embedded `str` function – xuthus Jan 13 '20 at 12:07
  • @xuthus - Indeed, you can use 11 lines of code instead of 1 line. As you like. – FooBar167 Jan 14 '20 at 16:17
  • 1
    This (rather than using `str`) is technically [the method documented in the stdlib docs](https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.BOOLEAN_STATES). – phoenix Apr 14 '21 at 12:15
  • 1
    Apparently now `str` is the documented way in the docs. – Tamás Szelei Nov 09 '22 at 10:16
  • 1
    In fact, BOTH the methods are still documented there. – Dave Aug 27 '23 at 07:30
6

I know this question is answered, but I thought some people might find this solution useful. This is a class that can easily replace the existing ConfigParser class.

Edited to incorporate @OozeMeister's suggestion:

class CaseConfigParser(ConfigParser):
    def optionxform(self, optionstr):
        return optionstr

Usage is the same as normal ConfigParser.

parser = CaseConfigParser()
parser.read(something)

This is so you avoid having to set optionxform every time you make a new ConfigParser, which is kind of tedious.

phoenix
  • 7,988
  • 6
  • 39
  • 45
icedtrees
  • 6,134
  • 5
  • 25
  • 35
  • Since `optionxform` is just a method on the `RawConfigParser`, if you're going to go as far as creating your own subclass, you should instead just override the method on the subclass rather than redefining it per instantiation: `class CaseConfigParser(ConfigParser): def optionxform(self, optionstr): return optionstr` – OozeMeister Jun 08 '15 at 14:40
  • @OozeMeister great idea! – icedtrees Jun 10 '15 at 01:52
4

Caveat:

If you use defaults with ConfigParser, i.e.:

config = ConfigParser.SafeConfigParser({'FOO_BAZ': 'bar'})

and then try to make the parser case-sensitive by using this:

config.optionxform = str

all your options from config file(s) will keep their case, but FOO_BAZ will be converted to lowercase.

To have defaults also keep their case, use subclassing like in @icedtrees answer:

class CaseConfigParser(ConfigParser.SafeConfigParser):
    def optionxform(self, optionstr):
        return optionstr

config = CaseConfigParser({'FOO_BAZ': 'bar'})

Now FOO_BAZ will keep it's case and you won't have InterpolationMissingOptionError.

nidalpres
  • 3,778
  • 1
  • 18
  • 12