You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
5.9 KiB
183 lines
5.9 KiB
# $Id: text_output.py,v 1.3 2011-09-02 15:07:47 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, mode="w"):
|
|
"""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, mode=mode)
|
|
#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, mode="w"):
|
|
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, mode)
|
|
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")
|
|
|