Source code for asterisk.config

#!/usr/bin/env python
# vim: set expandtab:
"""
.. module:: config
   :synopsis: Parse Asterisk configuration files.

This module provides parsing functionality for asterisk config files.

Example
----------

.. code-block:: python

   import asterisk.config
   import sys
   
   # load and parse the config file
   try:
      config = asterisk.config.Config('/etc/asterisk/extensions.conf')
   except asterisk.config.ParseError as e:
      print "Parse Error line: %s: %s" % (e.line, e.strerror)
      sys.exit(1)
   except IOError as e:
      print "Error opening file: %s" % e.strerror
      sys.exit(1)
   
   # print our parsed output
   for category in config.categories:
      print '[%s]' % category.name   # print the current category

      for item in category.items:
         print '   %s = %s' % (item.name, item.value)


Specification
-------------
"""

import sys


[docs]class ParseError(Exception): pass
class Line(object): def __init__(self, line, number): self.line = '' self.comment = '' line = line.strip() # I guess we don't preserve indentation self.number = number parts = line.split(';') if len(parts) >= 2: self.line = parts[0].strip() self.comment = ';'.join( parts[1:]) # Just in case the comment contained ';' else: self.line = line def __str__(self): return self.get_line() def get_line(self): if self.comment and self.line: return '%s\t;%s' % (self.line, self.comment) elif self.comment and not self.line: return ';%s' % self.comment return self.line class Category(Line): def __init__(self, line='', num=-1, name=None): Line.__init__(self, line, num) if self.line: if (self.line[0] != '[' or self.line[-1] != ']'): raise ParseError( self.number, "Missing '[' or ']' in category definition") self.name = self.line[1:-1] elif name: self.name = name else: raise Exception( "Must provide name or line representing a category") self.items = [] self.comments = [] def get_line(self): if self.comment: return '[%s]\t;%s' % (self.name, self.comment) return '[%s]' % self.name def append(self, item): self.items.append(item) def insert(self, index, item): self.items.insert(index, item) def pop(self, index=-1): self.items.pop(index) def remove(self, item): self.items.remove(item) class Item(Line): def __init__(self, line='', num=-1, name=None, value=None): Line.__init__(self, line, num) self.style = '' if self.line: self.parse() elif (name and value): self.name = name self.value = value else: raise Exception("Must provide name or value representing an item") def parse(self): try: name, value = self.line.split('=', 1) except ValueError: if self.line.strip()[-1] == ']': raise ParseError(self.number, "Category name missing '['") else: raise ParseError( self.number, "Item must be in name = value pairs") if value and value[0] == '>': self.style = '>' # preserve the style of the original value = value[1:].strip() self.name = name.strip() self.value = value def get_line(self): if self.comment: return '%s =%s %s\t;%s' % (self.name, self.style, self.value, self.comment) return '%s =%s %s' % (self.name, self.style, self.value) class Config(object): def __init__(self, filename): self.filename = filename self.raw_lines = [] # Holds the raw strings self.lines = [] # Holds things in order self.categories = [] # load and parse the file self.load() self.parse() def load(self): self.raw_lines = open(self.filename).readlines() #try: #self.raw_lines = open(self.filename).readlines() #except IOError: #sys.stderr.write('WARNING: error opening filename: %s No data read. Starting new file?' % self.filename) #self.raw_lines = [] def parse(self): cat = None num = 0 for line in self.raw_lines: num += 1 line = line.strip() if not line or line[0] == ';': item = Line(line or '', num) self.lines.append(item) if cat: cat.comments.append(item) continue elif line[0] == '[': cat = Category(line, num) self.lines.append(cat) self.categories.append(cat) continue else: item = Item(line, num) self.lines.append(item) if cat: cat.append(item) continue