Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

""" brain-dead simple parser for ini-style files. 

(C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed 

""" 

__version__ = "0.2.dev2" 

 

__all__ = ['IniConfig', 'ParseError'] 

 

COMMENTCHARS = "#;" 

 

class ParseError(Exception): 

    def __init__(self, path, lineno, msg): 

        Exception.__init__(self, path, lineno, msg) 

        self.path = path 

        self.lineno = lineno 

        self.msg = msg 

 

    def __str__(self): 

        return "%s:%s: %s" %(self.path, self.lineno+1, self.msg) 

 

class SectionWrapper(object): 

    def __init__(self, config, name): 

        self.config = config 

        self.name = name 

 

    def lineof(self, name): 

        return self.config.lineof(self.name, name) 

 

    def get(self, key, default=None, convert=str): 

        return self.config.get(self.name, key, convert=convert, default=default) 

 

    def __getitem__(self, key): 

        return self.config.sections[self.name][key] 

 

    def __iter__(self): 

        section = self.config.sections.get(self.name, []) 

        def lineof(key): 

            return self.config.lineof(self.name, key) 

        for name in sorted(section, key=lineof): 

            yield name 

 

    def items(self): 

        for name in self: 

            yield name, self[name] 

 

 

class IniConfig(object): 

    def __init__(self, path, data=None): 

        self.path = str(path) # convenience 

56        if data is None: 

            f = open(self.path) 

            try: 

                tokens = self._parse(iter(f)) 

            finally: 

                f.close() 

        else: 

            tokens = self._parse(data.splitlines(True)) 

 

        self._sources = {} 

        self.sections = {} 

 

        for lineno, section, name, value in tokens: 

63            if section is None: 

                self._raise(lineno, 'no section header defined') 

            self._sources[section, name] = lineno 

            if name is None: 

67                if section in self.sections: 

                    self._raise(lineno, 'duplicate section %r'%(section, )) 

                self.sections[section] = {} 

            else: 

71                if name in self.sections[section]: 

                    self._raise(lineno, 'duplicate name %r'%(name, )) 

                self.sections[section][name] = value 

 

    def _raise(self, lineno, msg): 

        raise ParseError(self.path, lineno, msg) 

 

    def _parse(self, line_iter): 

        result = [] 

        section = None 

        for lineno, line in enumerate(line_iter): 

            name, data = self._parseline(line, lineno) 

            # new value 

            if name is not None and data is not None: 

                result.append((lineno, section, name, data)) 

            # new section 

            elif name is not None and data is None: 

88                if not name: 

                    self._raise(lineno, 'empty section name') 

                section = name 

                result.append((lineno, section, None, None)) 

            # continuation 

            elif name is None and data is not None: 

94                if not result: 

                    self._raise(lineno, 'unexpected value continuation') 

                last = result.pop() 

                last_name, last_data = last[-2:] 

98                if last_name is None: 

                    self._raise(lineno, 'unexpected value continuation') 

 

                if last_data: 

                    data = '%s\n%s' % (last_data, data) 

                result.append(last[:-1] + (data,)) 

        return result 

 

    def _parseline(self, line, lineno): 

        # blank lines 

        if iscommentline(line): 

            line = "" 

        else: 

            line = line.rstrip() 

        if not line: 

            return None, None 

        # section 

        if line[0] == '[': 

            realline = line 

            for c in COMMENTCHARS: 

                line = line.split(c)[0].rstrip() 

120            if line[-1] == "]": 

                return line[1:-1], None 

            return None, realline.strip() 

        # value 

        elif not line[0].isspace(): 

            try: 

                name, value = line.split('=', 1) 

126                if ":" in name: 

                    raise ValueError() 

            except ValueError: 

                try: 

                    name, value = line.split(":", 1) 

                except ValueError: 

                    self._raise(lineno, 'unexpected line: %r' % line) 

            return name.strip(), value.strip() 

        # continuation 

        else: 

            return None, line.strip() 

 

    def lineof(self, section, name=None): 

        lineno = self._sources.get((section, name)) 

        if lineno is not None: 

            return lineno + 1 

 

    def get(self, section, name, default=None, convert=str): 

        try: 

            return convert(self.sections[section][name]) 

        except KeyError: 

            return default 

 

    def __getitem__(self, name): 

150        if name not in self.sections: 

            raise KeyError(name) 

        return SectionWrapper(self, name) 

 

    def __iter__(self): 

        for name in sorted(self.sections, key=self.lineof): 

            yield SectionWrapper(self, name) 

 

    def __contains__(self, arg): 

        return arg in self.sections 

 

def iscommentline(line): 

    c = line.lstrip()[:1] 

    return c in COMMENTCHARS