#!/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 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