diff options
Diffstat (limited to 'Lib/distutils/text_file.py')
-rw-r--r-- | Lib/distutils/text_file.py | 91 |
1 files changed, 36 insertions, 55 deletions
diff --git a/Lib/distutils/text_file.py b/Lib/distutils/text_file.py index 09a798b1902..40b8484a685 100644 --- a/Lib/distutils/text_file.py +++ b/Lib/distutils/text_file.py @@ -4,13 +4,10 @@ provides the TextFile class, which gives an interface to text files that (optionally) takes care of stripping comments, ignoring blank lines, and joining lines with backslashes.""" -__revision__ = "$Id$" - -import sys +import sys, os, io class TextFile: - """Provides a file-like object that takes care of all the things you commonly want to do when processing a text file that has some line-by-line syntax: strip comments (as long as "#" is your @@ -33,7 +30,7 @@ class TextFile: something that provides 'readline()' and 'close()' methods). It is recommended that you supply at least 'filename', so that TextFile can include it in warning messages. If 'file' is not supplied, - TextFile creates its own using the 'open()' builtin. + TextFile creates its own using 'io.open()'. The options are all boolean, and affect the value returned by 'readline()': @@ -59,6 +56,8 @@ class TextFile: collapse_join [default: false] strip leading whitespace from lines that are joined to their predecessor; only matters if (join_lines and not lstrip_ws) + errors [default: 'strict'] + error handler used to decode the file content Note that since 'rstrip_ws' can strip the trailing newline, the semantics of 'readline()' must differ from those of the builtin file @@ -73,34 +72,32 @@ class TextFile: 'rstrip_ws': 1, 'join_lines': 0, 'collapse_join': 0, + 'errors': 'strict', } - def __init__ (self, filename=None, file=None, **options): + def __init__(self, filename=None, file=None, **options): """Construct a new TextFile object. At least one of 'filename' (a string) and 'file' (a file-like object) must be supplied. They keyword argument options are described above and affect the values returned by 'readline()'.""" - if filename is None and file is None: - raise RuntimeError, \ - "you must supply either or both of 'filename' and 'file'" + raise RuntimeError("you must supply either or both of 'filename' and 'file'") # set values for all options -- either from client option hash # or fallback to default_options for opt in self.default_options.keys(): if opt in options: - setattr (self, opt, options[opt]) - + setattr(self, opt, options[opt]) else: - setattr (self, opt, self.default_options[opt]) + setattr(self, opt, self.default_options[opt]) # sanity check client option hash for opt in options.keys(): if opt not in self.default_options: - raise KeyError, "invalid TextFile option '%s'" % opt + raise KeyError("invalid TextFile option '%s'" % opt) if file is None: - self.open (filename) + self.open(filename) else: self.filename = filename self.file = file @@ -111,43 +108,37 @@ class TextFile: # 'unreadline()' operation self.linebuf = [] - - def open (self, filename): + def open(self, filename): """Open a new file named 'filename'. This overrides both the 'filename' and 'file' arguments to the constructor.""" - self.filename = filename - self.file = open (self.filename, 'r') + self.file = io.open(self.filename, 'r', errors=self.errors) self.current_line = 0 - - def close (self): + def close(self): """Close the current file and forget everything we know about it (filename, current line number).""" - - self.file.close () + self.file.close() self.file = None self.filename = None self.current_line = None - - def gen_error (self, msg, line=None): + def gen_error(self, msg, line=None): outmsg = [] if line is None: line = self.current_line outmsg.append(self.filename + ", ") if isinstance(line, (list, tuple)): - outmsg.append("lines %d-%d: " % tuple (line)) + outmsg.append("lines %d-%d: " % tuple(line)) else: outmsg.append("line %d: " % line) outmsg.append(str(msg)) - return ''.join(outmsg) - + return "".join(outmsg) - def error (self, msg, line=None): - raise ValueError, "error: " + self.gen_error(msg, line) + def error(self, msg, line=None): + raise ValueError("error: " + self.gen_error(msg, line)) - def warn (self, msg, line=None): + def warn(self, msg, line=None): """Print (to stderr) a warning message tied to the current logical line in the current file. If the current logical line in the file spans multiple physical lines, the warning refers to the @@ -157,8 +148,7 @@ class TextFile: line.""" sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n") - - def readline (self): + def readline(self): """Read and return a single logical line from the current file (or from an internal buffer if lines have previously been "unread" with 'unreadline()'). If the 'join_lines' option is true, this @@ -168,7 +158,6 @@ class TextFile: line(s) just read. Returns None on end-of-file, since the empty string can occur if 'rstrip_ws' is true but 'strip_blanks' is not.""" - # If any "unread" lines waiting in 'linebuf', return the top # one. (We don't actually buffer read-ahead data -- lines only # get put in 'linebuf' if the client explicitly does an @@ -180,10 +169,11 @@ class TextFile: buildup_line = '' - while 1: + while True: # read the line, make it None if EOF line = self.file.readline() - if line == '': line = None + if line == '': + line = None if self.strip_comments and line: @@ -196,7 +186,7 @@ class TextFile: # lurking in there) and otherwise leave the line alone. pos = line.find("#") - if pos == -1: # no "#" -- no comments + if pos == -1: # no "#" -- no comments pass # It's definitely a comment -- either "#" is the first @@ -220,17 +210,15 @@ class TextFile: # result in "hello there". if line.strip() == "": continue - - else: # it's an escaped "#" + else: # it's an escaped "#" line = line.replace("\\#", "#") - # did previous line end with a backslash? then accumulate if self.join_lines and buildup_line: # oops: end of file if line is None: - self.warn ("continuation line immediately precedes " - "end-of-file") + self.warn("continuation line immediately precedes " + "end-of-file") return buildup_line if self.collapse_join: @@ -242,10 +230,10 @@ class TextFile: self.current_line[1] = self.current_line[1] + 1 else: self.current_line = [self.current_line, - self.current_line+1] + self.current_line + 1] # just an ordinary line, read it as usual else: - if line is None: # eof + if line is None: # eof return None # still have to be careful about incrementing the line number! @@ -254,7 +242,6 @@ class TextFile: else: self.current_line = self.current_line + 1 - # strip whitespace however the client wants (leading and # trailing, or one or the other, or neither) if self.lstrip_ws and self.rstrip_ws: @@ -281,24 +268,18 @@ class TextFile: # well, I guess there's some actual content there: return it return line - # readline () - - - def readlines (self): + def readlines(self): """Read and return the list of all logical lines remaining in the current file.""" - lines = [] - while 1: + while True: line = self.readline() if line is None: return lines - lines.append (line) + lines.append(line) - - def unreadline (self, line): + def unreadline(self, line): """Push 'line' (a string) onto an internal buffer that will be checked by future 'readline()' calls. Handy for implementing a parser with line-at-a-time lookahead.""" - - self.linebuf.append (line) + self.linebuf.append(line) |