Search code examples
pythonstdoutsys

Is there a way to get python to output to the shell and simultaneously output to str in a variable?


The only ways I can find in python that redirects the console output to string in a variable also seem to turn off the functionality of displaying the output to the console in a live manner similar to how python would output the code normally.

I currently am changing the sys.stdout but again, this is either one or the other it seems.

If I redefine it, I get the perfect performance for distant error checking, as I am able to save the output variable to a cloud based spreadsheet in the event of exception handling, which sends me notifications anywhere that I am.

However, redefining it means I don't get to come and locally check on the output of the program while it is "running smoothly"

EDIT: some of your answers have helped me refine my question. Here is a fresh re wording:

What is the best way to concisely store and record outputs with a single variable as the elements are printed to the console without overwriting sys.stdout?

old_stdout = sys.stdout
new_stdout = io.StringIO()
sys.stdout = new_stdout

def update_error_log_ss(traceback_, summary, output = ""):
    print("Connecting to Smart Sheet to Update Error Log")
    token = 'token'
    error_log_sheetid = 00000000
    ss_client = ss.Smartsheet(token)
    now = datetime.datetime.now()
    sheet = ss_client.Sheets.get_sheet(error_log_sheetid)
    colid = []
    for col in sheet.columns:
        colid.append(col.id)
        print(str(col.id) + " - " + col.title)
    totalcols = len(colid)
    row_add = ss.models.Row()
    row_add.to_top = True
    row_add.cells.append({
        'column_id': colid[0],
        'value': str(now)
    })
    row_add.cells.append({
        'column_id': colid[1],
        'value': summary
    })
    row_add.cells.append({
        'column_id': colid[2],
        'value': traceback_
    })
    row_add.cells.append({
        'column_id': colid[3],
        'value': output
    })
    response = ss_client.Sheets.add_rows(
        error_log_sheetid,
        [row_add]
    )

    return

except Exception:
        if debug_ == False:
synch.update_error_log_ss(traceback.format_exc(),'initialization failure',new_stdout.getvalue())
main_()
        else:
            synch.update_error_log_ss(traceback.format_exc(),'initialization failure')
        main_()

The only issue I have with the above solution is that in order for new_stdout.getvalue() to get defined, sys.stdout has to be overwritten


Solution

  • According to the print docs:

    The file argument must be an object with a write(string) method

    So you can just create one for your needs and assign it to sys.stdout which is the default file argument for print:

    import io
    import sys
    
    class my_stdout:
        def __init__(self):
            self.real_stdout = sys.stdout
            self.new_stdout = io.StringIO()
    
        def write(self, s):
            self.real_stdout.write(s)
            self.new_stdout.write(s)
    
        def getvalue(self):
            return self.new_stdout.getvalue()
    
    new_stdout = my_stdout()
    sys.stdout = new_stdout
    print("Original: Hello world!")
    # for debug:
    sys.stdout = new_stdout.real_stdout
    print("From stringio:", new_stdout.getvalue())
    

    Output:

    Original: Hello world!
    From stringio: Original: Hello world!