From 90f1603499bed70c19dd4268a52677ad8174df9d Mon Sep 17 00:00:00 2001 From: wirawan Date: Wed, 11 May 2011 20:28:51 +0000 Subject: [PATCH] * Added text_output as a converse facility to print out text stream to a file. Useful for a shortened output statements in the code, and also for reroutable output (to stdout? to file? to both?) and flexible output kind. --- iofmt/text_output.py | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 iofmt/text_output.py diff --git a/iofmt/text_output.py b/iofmt/text_output.py new file mode 100644 index 0000000..172ce01 --- /dev/null +++ b/iofmt/text_output.py @@ -0,0 +1,183 @@ +# $Id: text_output.py,v 1.1 2011-05-11 20:28:51 wirawan Exp $ +# +# wpylib.iofmt.text_output module +# Quick-n-dirty text output utilities +# +# Wirawan Purwanto +# Created: 20110511 +# +# Routines put here are commonly used in my own scripts. +# They are not necessarily suitable for general-purpose uses; evaluate +# your needs and see if they can them as well. +# +# 20110426: Created as Logger class in my check-git-vs-cvs.py tool. +# 20110511: Moved to wpylib.iofmt.text_output . +# +""" +Simple text-based output writer. +This is supposedly the converse of text_input, but with different +capabilities. + +This module is part of wpylib project. +""" + +import sys + +class text_output(object): + """A simple text output. + For use as a convenience tool like this: + + def subroutine(..., out=sys.stdout): + from pyqmc.iofmt.text_output import text_output + o = text_output(out) + o("Line1\n") + o("Line2\n") + o("part of Line3: ") + for x in [2,3,4]: + o(" this is subpart %s" % x) + o("\n") + + The text_output object is file-like for write-only purposes, + and can be wrapped by yet another text_output object. + + The object's `_output' attribute can be set to whatever action + (object method) you like to use (e.g. via subclassing), or + by defining a stand-alone function that accepts "self" as the + first argument. + The set_write_func() function *must* be called for this purpose. + + To mute the output completely, set the `out' argument to None when + creating the object. + + Caveat: + * If the file-like object is closed, then we will do nothing + for the rest of the __call__ invocation. + """ + + """--------------------------------------------------------------------- + Private members: + * _autoopen -- boolean to indicate privately owned file object + * _output -- method to output the stuff + * out -- the output + + Internal notes: + * A carefully thought hack is required to allow interchangeable + "_output" method below. + Here is my collected wisdom: + - We can't define __call__ as a class attribute, like this: + + self.__call__ = self.write + + It won't work when the object is called: i.e. the following will cause + an exception with "__call__" method not found: + + self("blah\n") + + - We shouldn't use the following either: + + self._output = self.write + + because the _output has an im_self member, which is a strong reference + to self. + This circular dependence causes the object (self) to never vanish when + it is supposed to. + + - Some options are available out there to introduce a "weak" object + method (e.g. WeakMethod, http://code.activestate.com/recipes/81253/) + but it is too much for what we want to accomplish here. + + What I chose below is the *most* liberal choice which allow unbound + function (with "self" as the first argument) to become the + standard _output routine using slight . + This is the best and most flexible, in my opinion, rather than imposing + extra restrictions. + ---------------------------------------------------------------------""" + + def __init__(self, out=sys.stdout, flush=False): + """Initializes the text output. + Options: + - flush: if true, will flush every time the default action is invoked. + """ + print sys.getrefcount(self) + self.out = None + self.open(out) + print sys.getrefcount(self) + if flush: + self.set_write_func(self.write_flush) + else: + self.set_write_func(self.write) + print sys.getrefcount(self) + def __del__(self): + print "Deleting object %s, file %s" % (self, self.out) + self.close() + def set_write_func(self, method): + """Sets the default '_output' function to a python bound method. + Always use this method, instead of setting self._output directly! + + NOTE: + This is intentionally sloppy (no im_class or im_self checks), + so that it can be used to perform DIRTY hack,such as allowing an + arbitrary function (bound function from another unrelated class, or + unbound function) to be attached as the output function.""" + if hasattr(method, "im_func"): + self._output = method.im_func + else: + # assume that method is a stand-alone callable object + # that can accept "self" as the first argument + self._output = method + def open(self, out=sys.stdout): + self.close() + self._autoopen = False + if out == None: + self.out = None + elif self.is_file_like(out): + self.out = out + else: # assume a string (a filename) + self.out = open(out, "w") + self.outfilename = out + self._autoopen = True + def close(self): + """Closes the text_output's output object. + At least, flushes everything at the end of the output's association + with this text_output object. + """ + if self.out: + if self._autoopen: + print "Closing file " + self.out.name + self.out.close() # depends on existing close() method + else: + self.out.flush() + self.out = None + + @staticmethod + def is_file_like(obj): + return isinstance(obj, file) \ + or (hasattr(obj, "write") and hasattr(obj, "flush")) + + def __call__(self, *_list, **_args): + """Unfortunately __call__ cannot be a usual class attribute. + So we have to use a thin dispatcher here.""" + self._output(self, *_list, **_args) + + # Original I/O routine is preserved by _* method name + # FIXME: A better optimization can be introduced needed if + # self.out is yet another text_output instance. + # But beware of possible method polymorphism if you do this. (!!!) + def _write(self, s): + if self.out: self.out.write(s) + def _flush(self): + if self.out: self.out.flush() + def _write_flush(self, s): + if self.out: + self.out.write(s) + self.out.flush() + # The logger itself is a file-like object, too: + write = _write + flush = _flush + write_flush = _write_flush + + +def test1(): + O = text_output("/tmp/test1abc.txt", flush=1) + O("this is a test\n") + O("--------------\n")