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.master
parent
0531e4e511
commit
90f1603499
1 changed files with 183 additions and 0 deletions
@ -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") |
Loading…
Reference in new issue