0

I have written code that works for my cause and displays the results in the terminal:

print 'The following results are found:'
# some code iterations here
...
...
print 'User - {0}, Title - {1}'.format(...)

Currently, I am trying to implement a new optional argument such that it allows me to choose whether I would like the above results to be written to a text file.

While I can get it to work, it is not in the most elegant method:

# output_to_path is a boolean argument here.

if output_to_file:
    # file_path, I use `open(file_dir, "w")`
    print >> file_path, 'The following results are found:'
print 'The following results are found:'
# some code iterations here
...
...
if output_to_file:
    print 'User - {0}, Title - {1}'.format(...)
print 'User - {0}, Title - {1}'.format(...)

Is it possible to only write the above print statements once, whether output_to_file is true or false? I ask as I do have a ton of print statements to begin with.

user2722968
  • 13,636
  • 2
  • 46
  • 67
dissidia
  • 1,531
  • 3
  • 23
  • 53
  • You can adapt the answer to [Use a context manager for python script output to a file?](https://stackoverflow.com/questions/8495422/use-a-context-manager-for-python-script-output-to-a-file) to output to two different destinations (the original `sys.stdout` _and_ a specified file) depending on a flag it maintains internally. The context manager can check the flag to detemine whether to write output to one or both of them. – martineau May 16 '18 at 20:01
  • Are you asking how to output to a file or how to avoid the second print statement if the `if` statement is executed? To avoid the duplicate print you just need to use an `else` statement – BigGerman May 16 '18 at 20:01
  • 1
    What about creating a function `log` that writes into a file if the optional argument is true, and you just have to call it like `log("write this")` – Rodolfo May 16 '18 at 20:03
  • It's a good idea to use Python 3 for new code, Python 2 reaches its official End of Life some time in 2020. But if you're forced to use Python 2 you can use the more powerful Python 3 `print` _function_ rather than the old `print` statement in Python 2.6+ by putting `from __future__ import print_function` at the top of your imports. – PM 2Ring May 16 '18 at 20:18
  • Also see https://stackoverflow.com/questions/14906764/how-to-redirect-stdout-to-both-file-and-console-with-scripting – PM 2Ring May 16 '18 at 20:19
  • @Rodolfo By `log` that you have mentioned, do you meant the `import logging`, the one that is within Python? – dissidia May 16 '18 at 21:19
  • @dissidia I've added an answer to explain what I mean – Rodolfo May 17 '18 at 13:37

2 Answers2

2

Here's a way to do it with a context manager, which is similar to what's being done in the answer to the question I referred you to in a comment below your question.

The twist is that in order to be able to selectively turn output to the files on and off as desired, the simplest route seemed to be implement it as a class (instead of applying the contextlib's @contextmanager decorator to a function as was done there).

Hope this isn't too much code...

import sys

class OutputManager(object):
    """ Context manager that controls whether sysout goes only to the interpreter's
        current stdout stream or to both it and a given file.
    """
    def __init__(self, filename, mode='wt'):
        self.output_to_file = True
        self.saved_stdout = sys.stdout
        self.file = open(filename, mode)
        sys.stdout = self

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        sys.stdout = self.saved_stdout  # Restore.
        self.file.close()

    def write(self, message):
        self.saved_stdout.write(message)
        if self.output_to_file:
            self.file.write(message)

    def enable(self):
        self.output_to_file = True

    def disable(self):
        self.output_to_file = False


if __name__ == '__main__':
    # Sample usage.
    with OutputManager('cmtest.txt') as output_manager:
        print 'This line goes to both destinations.'
        output_manager.disable()
        print 'This line goes only to the display/console/terminal.'
        output_manager.enable()
        print 'Once again, to both destinations.'
martineau
  • 119,623
  • 25
  • 170
  • 301
1

You could write a function that does what you want:

def custom_print(message):
    print(message) # always prints to stdout
    if output_to_file:
        print >> file_path, message

Then you call it like this:

custom_print('The following results are found:')
...
custom_print('User - {0}, Title - {1}'.format(...))
Rodolfo
  • 573
  • 2
  • 8
  • 18