2

as an example here, i want to make a function to temporarily direct the stdout to a log file.

the tricky thing is that the codes have to keep the file handler and std sav for restoration after the redirect, i wrote it in class type to keep these two variables.

here below the full code:

class STDOUT2file:
    def __init__(self,prefix='report@'):

        now=dt.date.today()
        repname=repnameprefix=prefix+now.strftime("%Y%m%d")+'.txt'
        count=0
        while os.path.isfile(repname):
            count+=1
            repname=repnameprefix+(".%02d" %(count))
        self.sav=sys.stdout
        f=open(repname,'w')
        sys.stdout=f
        self.fname=repname
        self.fhr=f


    def off(self,msg=False):

        sys.stdout=self.sav
        self.fhr.close()
        if msg: 
            print('output to:'+self.fname)
        return

here is the code to apply it:

outbuf=STDOUT2file()

#codes to print out to stdout

outbuf.off(msg=True)

i want to make it more clean, read about 'closure' but it returns a function at the first call, kind of assigment type as similar as class.

i want it to be like:

STDOUT2file('on')

STDout2file('off',msg=True)

note: redirecting to stdout is an example i encountered just now.. what i am wondering is, any way other than class type to make simple functionality like those on/off type, which involve store/retrieval of state variables that should be better made invisible to outside.

2 Answers 2

3

Try using a context manager instead. This idiom is common enough that it was included in the PEP that introduced context managers (slightly modified here):

from contextlib import contextmanager

@contextmanager
def redirect_stdout(new_stdout):
    import sys
    save_stdout = sys.stdout
    sys.stdout = new_stdout
    try:
        yield
    finally:
        sys.stdout = save_stdout

Or, if you like, the class-based version with __enter__ and __exit__:

class redirect_stdout:
    """Context manager for temporarily redirecting stdout to another file

    docstring truncated
    """

    def __init__(self, new_target):
        self.new_target = new_target

    def __enter__(self):
        self.old_target = sys.stdout
        sys.stdout = self.new_target
        return self.new_target

    def __exit__(self, exctype, excinst, exctb):
        sys.stdout = self.old_target

Raymond Hettinger actually committed this to contextlib, it will be included in python 3.4 as contextlib.redirect_stdout().

Basic usage:

with open('somelogfile','a') as f:
    with stdout_redirected(f):
        print(something)
Sign up to request clarification or add additional context in comments.

1 Comment

tks. but the redirect to stdout is just an example.. what i am asking is, if i want to do some functionalities that involve storing of certain states variable that would be retrieved later, and i want to encapsulate (or make it invisible) these variable, could i do it other than class type, which for some simple functions, like those on/off type, is an overkill.
1

Yes, you can save state information in a function. Just name the variable functionname.something and it will be saved. For example:

def stdout2file(status, prefix='pre', msg=False):
    import datetime as dt
    import os
    import sys
    if not hasattr(stdout2file, 'sav'):
        stdout2file.sav = None
    if status == 'on':
        if stdout2file.sav:
            print('You have already triggered this once  Ignoring this request.')
        else:
            now = dt.date.today()
            repname = repnameprefix = prefix + now.strftime("%Y%m%d") + '.txt'
            count = 0
            while os.path.isfile(repname):
                count += 1
                repname = repnameprefix + (".%02d" %(count))
            stdout2file.sav = sys.stdout
            f = open(repname,'w')
            sys.stdout = f
            stdout2file.fhr = f
    elif status == 'off':
        if not stdout2file.sav:
            print('Redirect is "off" already.  Ignoring this request')
        else:
            sys.stdout = stdout2file.sav
            stdout2file.fhr.close()
            if msg:
                print('output to:' + stdout2file.fhr.name)
            stdout2file.sav = None
    else:
        print('Unrecognized request')

It is also possible to keep status information in mutable keyword parameters like so:

def stdout_toggle(prefix='pre', msg=False, _s=[None, None]):
    import datetime as dt
    import os
    import sys
    if _s[0] is None:
        now = dt.date.today()
        repname = repnameprefix = prefix + now.strftime("%Y%m%d") + '.txt'
        count = 0
        while os.path.isfile(repname):
            count += 1
            repname = repnameprefix + (".%02d" %(count))
        f = open(repname,'w')
        _s[:] = [sys.stdout, f]
        sys.stdout = f
    else:
        sys.stdout = _s[0]
        _s[1].close()
        if msg:
            print('output to:' + _s[1].name)
        _s[:] = [None, None]

The user can call the above without any arguments and it will toggle between the redirect between on and off. The function remembers the current status through the keyword parameter _s which is a mutable list.

Although some consider the fact that mutable keyword parameters are preserved between function calls to be a language flaw, it is consistent with python philosophy. It works because the default values for keyword parameters are assigned when the function is first defined, that is when the def statement is executed, and not when the function is called. Consequently, _s=[None, None] is assigned once at definition and is free to vary thereafter.

8 Comments

your code works! btw, how to avoid the redirection was triggered twice? checking stdout2file.sav is none didn't work, as it is 'reference before it is defined', tkyou!
@timeislove OK. I added checking so that, if there are two "on" or two "off" requests in a row, only the first will be processed. If also will refuse to turn "off" if not first turned "on".
though the conversion works, it looks like more lines of codes than i expected, more than that of using class type... hmm...
What are you talking about? It should be the same number of lines as with the class version, since the only change is self.save into stdout2file.sav.
@timeislove There were some superfluous lines and I removed them. For example, there was no need to keep stdout2file.fname around since the same information is in stdout2file.fhr.name. Note also that I included three import statements in the function. The class needs the same imports but they are not shown in the class definition.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.