I have a rather unusual request, I think... I'll explain the why after I explain the what.
What
I want to detect whenever my object is written to stdout, so that I can perform side effects at that time. So, for example, when I type:
sys.stdout.write(instance_of_my_class)
it should perform side effects. I've made my class be a subclass of str
and overrode __call__
, __unicode__
, __str__
, __repr__
, index
, decode
, encode
, format
, __format__
, __getattribute__
,__getitem__
, and __len__
so that each of them prints a statement indicating that they've been called, but it seems that sys.stdout.write
calls none of those in order to print an object.
Note that I'm specifically talking about sys.stdout.write
and not, for example, print
- I have found that print
calls __str__
on whatever it is given.
Why
This question continues from where the answer to Colored Python Prompt in Windows? left off.
I have found that each time python needs to display an interactive prompt, it calls __str__
on sys.ps1
and sys.ps2
, and then it saves the results to be displayed on the command line. This means any side effects in sys.ps2.__str__
are caused right after the ones in sys.ps1.__str__
, but I want to have those wait until it's time to display sys.ps2
.
So rather than return a str
in sys.ps2.__str__
, I've been returning my subclass of str
, which I'm hoping will somehow be capable of catching when sys.stdout.write
is called on it.
A built-in file object that is analogous to the interpreter’s standard output stream in Python. stdout is used to display output directly to the screen console. Output can be of any form, it can be output from a print statement, an expression statement, and even a prompt direct for input. By default, streams are in text mode.
Read from stdin in python Python stderr is known as a standard error stream. It is similar to stdout because it also directly prints to the console but the main difference is that it only prints error messages. After writing the above code (python print to stderr), you can observe that it print debug message using sys.stderr.
Writing to Standard Output ( stdout) using print is simple: print ( "Hello Standard Output!" ) print( "Hello Standard Output!" ) But if you want to write to Standard Error ( stderr ), you'll have to import the sys library: You can also write to stdout or stderr using the sys.stdout and sys.stderr file objects:
The easiest way to redirect stdout in Python is to just assign it an open file object. Let’s take a look at a simple example: print('This string goes to stdout, NOT the file!') redirect_to_file('Python rocks!') import sys def redirect_to_file (text): original = sys.stdout sys.stdout = open ('/path/to/redirect.txt', ...
Intriguing problem! My first guess is that sys.stdout.write
doesn't call the __str__
method because your object already is a str
(or at least a subclass of it, which is good enough for all intents and purposes)... so no casting methods are needed.
Further investigation suggests that sys.stdout.write
really doesn't ever want to call the __str__
method ...
With a little introspection, you can find out which methods of your str
subclass are called by sys.stdout.write
(the answer is, not many):
class superstring(str):
def __getattribute__(self, name):
print "*** lookup attribute %s of %s" % (name, repr(self))
return str.__getattribute__(self, name)
foo = superstring("UberL33tPrompt> ")
sys.stdout.write(foo)
Running in a Unicode environment (Python 2.7, iPython notebook), this prints:
*** lookup attribute __class__ of 'UberL33tPrompt> '
*** lookup attribute decode of 'UberL33tPrompt> '
UberL33tPrompt>
It seems rather kludge-y, but you could override the subclass's decode
method to perform the desired side effects.
However, in a non-Unicode environment there are no attribute lookups.
Rather than using a subclass of str
, maybe what you need is some kind of "wrapper" around str
. Here's an ugly exploratory hack which creates a class that delegates most of its attributes to str
, but which is not strictly a subclass thereof:
class definitely_not_a_string(object):
def __init__(self, s):
self.s = s
def __str__(self):
print "*** Someone wants to see my underlying string object!"
return self.s
def decode(self, encoding, whatever):
print "*** Someone wants to decode me!"
return self.s.decode(encoding, whatever)
def __getattribute__(self, name):
print "*** lookup attribute %s of %s" % (name, repr(self))
if name in ('s', '__init__', '__str__', 'decode', '__class__'):
return object.__getattribute__(self, name)
else:
return str.__getattribute__(self, name)
foo = definitely_not_a_string("UberL33tPrompt> ")
sys.stdout.write(foo)
In the Unicode environment, this gives basically the same results:
*** lookup attribute __class__ of <__main__.definitely_not_a_string object at 0x00000000072D79B0>
*** lookup attribute decode of <__main__.definitely_not_a_string object at 0x00000000072D79B0>
*** Someone wants to decode me!
*** lookup attribute s of <__main__.definitely_not_a_string object at 0x00000000072D79B0>
UberL33tPrompt>
However, when I run in a non-Unicode environment, definitely_not_a_string
gives an error message:
TypeError: expected a character buffer object
... this shows that the .write
method is going straight to the C-level buffer interface when it doesn't need to do any Unicode decoding.
It seems that overriding the decode
method is a possible kludge in Unicode environments, since sys.stdout.write
calls this method when it needs to decode a str
into Unicode.
However, in non-Unicode environments it appears that .write
doesn't do any attribute lookups whatsoever, but simply goes straight to the C-level character buffer protocol, so there's no way to intercept its access from Python code. Indeed, help(sys.stdout.write)
verifies that it's a built-in function (aka written in C, not Python).
Why not monkeypatch stdout.write?
stdoutRegistry = set()
class A(object):
def __init__(self):
self.stdoutRegistry.add(self)
def stdoutNotify(self):
pass
original_stdoutWrite = sys.stdout.write
def stdoutWrite(*a, **kw):
if a in stdoutRegistry:
a.stdoutNotify()
original_stdoutWrite(*a, **kw)
sys.stdout.write = stdoutWrite
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With