Friday, November 11, 2011

Python, make ConfigParser aware of spaces

There is a wonderful Python's module called ConfigParser which allows to process .ini-style configuration files easily. I prefer to use it everywhere rather than spend the time to implement my own solution. Recently there was a bug received that values with leading and trailing spaces are read incorrectly: spaces are lost. This might be important for cases when an application is sensitive for such values; e.g.: for passwords.

It was discovered that current Python's ConfigParser implementation cannot be tuned up not to strip values while reading a configuration. Also there was a corresponding issue found with an attached patch. Unfortunately the patch has not been applied to public available Python builds yet. Definitely it is absolute not convenient to patch Python everywhere where yours application is run.

The solution is not to lose leading and trailing spaces by wrapping them for quotes. Here is a helping code snippet to solve this issue:

class SpaceAwareConfigParser(ConfigParser.ConfigParser):
    def __init__(self, **args):
        KEEP_SPACES_KEYWORD = "keep_spaces"
        
        self.__keep_spaces = args.get(KEEP_SPACES_KEYWORD, True)
        args.pop(KEEP_SPACES_KEYWORD)

        ConfigParser.ConfigParser.__init__(self, **args)

    def get(self, section, option):
        value = ConfigParser.ConfigParser.get(self, section, option)
        if self.__keep_spaces:
            value = self._unwrap_quotes(value)

        return value

    def set(self, section, option, value):
        if self.__keep_spaces:
            value = self._wrap_to_quotes(value)

        ConfigParser.ConfigParser.set(self, section, option, value)        

    @staticmethod
    def _unwrap_quotes(src):
        QUOTE_SYMBOLS = ('"', "'")
        for quote in QUOTE_SYMBOLS:
            if src.startswith(quote) and src.endswith(quote):
                return src.strip(quote)

        return src

    @staticmethod
    def _wrap_to_quotes(src):
        if src and src[0].isspace():
            return '"%s"' % src

        return src

Overridden get() method removes quotes if any. So for ConfigParser's client that is transparent if the option has a value with quoted spaces or not; double and single quotes are supported. set() does vice versa.

So it is enough to replace instantination of ConfigParser in your Python's code just with SpaceAwareConfigParser one and it should work like expected.

3 comments:

  1. How do you call it?

    SpaceAwareConfigParser()

    I am getting errors when I try to do SpaceAwareConfigParser.RawConfigParser()

    ReplyDelete
    Replies
    1. Hey,

      Which errors do you receive?
      This construction should not work since 'RawConfigParser' is not a static property of 'SpaceAwareConfigParser' but a base class.

      Delete
    2. I ended up just using string replace but I am still curious on how to use this class. What exactly do you call?

      Delete