Skip to content

Commit d1dc475

Browse files
committed
Added: mems.lib.text_file: provides TextFile class for parsing text
files with (optional) comment stripping, blank line skipping, whitespace removal, and line joining with trailing backslashes.
1 parent 894ee6f commit d1dc475

File tree

1 file changed

+206
-0
lines changed

1 file changed

+206
-0
lines changed

Lib/distutils/text_file.py

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
"""text_file
2+
3+
provides the TextFile class, which gives an interface to text files
4+
that (optionally) takes care of stripping comments, ignoring blank
5+
lines, and joining lines with backslashes."""
6+
7+
# created 1999/01/12, Greg Ward
8+
9+
__revision__ = "$Id$"
10+
11+
from types import *
12+
import os, string, re
13+
14+
15+
class TextFile:
16+
filename = None
17+
file = None
18+
current_line = None
19+
20+
default_options = { 'strip_comments': 1,
21+
'comment_re': re.compile (r'\s*#.*'),
22+
'skip_blanks': 1,
23+
'join_lines': 0,
24+
'lstrip_ws': 0,
25+
'rstrip_ws': 1,
26+
}
27+
28+
def __init__ (self, filename=None, **options):
29+
30+
# set values for all options -- either from client option hash
31+
# or fallback to default_options
32+
for opt in self.default_options.keys():
33+
if options.has_key (opt):
34+
if opt == 'comment_re' and type (options[opt]) is StringType:
35+
self.comment_re = re.compile (options[opt])
36+
else:
37+
setattr (self, opt, options[opt])
38+
39+
else:
40+
setattr (self, opt, self.default_options[opt])
41+
42+
# sanity check client option hash
43+
for opt in options.keys():
44+
if not self.default_options.has_key (opt):
45+
raise KeyError, "invalid TextFile option '%s'" % opt
46+
47+
self.filename = filename
48+
if self.filename:
49+
self.open ()
50+
51+
52+
def open (self, filename=None):
53+
if not self.filename:
54+
if not filename:
55+
raise RuntimeError, "must provide a filename somehow"
56+
57+
self.filename = filename
58+
59+
self.file = open (self.filename, 'r')
60+
self.current_line = 0
61+
62+
63+
def close (self):
64+
self.file.close ()
65+
self.file = None
66+
self.filename = None
67+
self.current_line = None
68+
69+
70+
def readline (self):
71+
72+
buildup_line = ''
73+
74+
while 1:
75+
# read the line, optionally strip comments
76+
line = self.file.readline()
77+
if self.strip_comments and line:
78+
line = self.comment_re.sub ('', line)
79+
80+
# did previous line end with a backslash? then accumulate
81+
if self.join_lines and buildup_line:
82+
# oops: end of file
83+
if not line:
84+
self.warn ("continuation line immediately precedes "
85+
"end-of-file")
86+
return buildup_line
87+
88+
line = buildup_line + line
89+
90+
# careful: pay attention to line number when incrementing it
91+
if type (self.current_line) is ListType:
92+
self.current_line[1] = self.current_line[1] + 1
93+
else:
94+
self.current_line = [self.current_line, self.current_line+1]
95+
# just an ordinary line, read it as usual
96+
else:
97+
if not line:
98+
return None
99+
100+
# still have to be careful about incrementing the line number!
101+
if type (self.current_line) is ListType:
102+
self.current_line = self.current_line[1] + 1
103+
else:
104+
self.current_line = self.current_line + 1
105+
106+
107+
# strip whitespace however the client wants (leading and
108+
# trailing, or one or the other, or neither)
109+
if self.lstrip_ws and self.rstrip_ws:
110+
line = string.strip (line)
111+
else:
112+
if self.lstrip_ws:
113+
line = string.lstrip (line)
114+
if self.rstrip_ws:
115+
line = string.rstrip (line)
116+
117+
# blank line (whether we rstrip'ed or not)? skip to next line
118+
# if appropriate
119+
if line == '' or line == '\n' and self.skip_blanks:
120+
continue
121+
122+
if self.join_lines:
123+
if line[-1] == '\\':
124+
buildup_line = line[:-1]
125+
continue
126+
127+
if line[-2:] == '\\\n':
128+
buildup_line = line[0:-2] + '\n'
129+
continue
130+
131+
# well, I guess there's some actual content there: return it
132+
return line
133+
134+
# end readline
135+
136+
137+
def readlines (self):
138+
lines = []
139+
while 1:
140+
line = self.readline()
141+
if line is None:
142+
return lines
143+
lines.append (line)
144+
145+
146+
if __name__ == "__main__":
147+
test_data = """# test file
148+
149+
line 3 \\
150+
continues on next line
151+
"""
152+
153+
# result 1: no fancy options
154+
result1 = map (lambda x: x + "\n", string.split (test_data, "\n")[0:-1])
155+
156+
# result 2: just strip comments
157+
result2 = ["\n", "\n", "line 3 \\\n", "continues on next line\n"]
158+
159+
# result 3: just strip blank lines
160+
result3 = ["# test file\n", "line 3 \\\n", "continues on next line\n"]
161+
162+
# result 4: default, strip comments, blank lines, and trailing whitespace
163+
result4 = ["line 3 \\", "continues on next line"]
164+
165+
# result 5: full processing, strip comments and blanks, plus join lines
166+
result5 = ["line 3 continues on next line"]
167+
168+
def test_input (count, description, file, expected_result):
169+
result = file.readlines ()
170+
# result = string.join (result, '')
171+
if result == expected_result:
172+
print "ok %d (%s)" % (count, description)
173+
else:
174+
print "not ok %d (%s):" % (count, description)
175+
print "** expected:"
176+
print expected_result
177+
print "** received:"
178+
print result
179+
180+
181+
filename = "test.txt"
182+
out_file = open (filename, "w")
183+
out_file.write (test_data)
184+
out_file.close ()
185+
186+
in_file = TextFile (filename, strip_comments=0, skip_blanks=0,
187+
lstrip_ws=0, rstrip_ws=0)
188+
test_input (1, "no processing", in_file, result1)
189+
190+
in_file = TextFile (filename, strip_comments=1, skip_blanks=0,
191+
lstrip_ws=0, rstrip_ws=0)
192+
test_input (2, "strip comments", in_file, result2)
193+
194+
in_file = TextFile (filename, strip_comments=0, skip_blanks=1,
195+
lstrip_ws=0, rstrip_ws=0)
196+
test_input (3, "strip blanks", in_file, result3)
197+
198+
in_file = TextFile (filename)
199+
test_input (4, "default processing", in_file, result4)
200+
201+
in_file = TextFile (filename, strip_comments=1, skip_blanks=1,
202+
join_lines=1, rstrip_ws=1)
203+
test_input (5, "full processing", in_file, result5)
204+
205+
os.remove (filename)
206+

0 commit comments

Comments
 (0)