Mute printing of an imported Python script - python

I want to import a python script in to another script.
$ cat py1.py
test=("hi", "hello")
print test[0]
$ cat py2.py
from py1 import test
print test
If I execute py2.py:
$ python py2.py
hi
('hi', 'hello')
Can I anyway mute the first print which is coming from the from py1 import test?
I can't comment the print in py1, since it is being used somewhere else.

py1.py use an if __name__=="__main__":
So like your py1.py would look like:
def main():
test=("hi", "hello")
print test[0]
if __name__=="__main__":
main()
This will allow you to still use py1.py normally, but when you import it, it won't run the main() function unless you call it.
This explains what's going on

Simply open the /dev/null device and overwrite the sys.stdout variable to that value when you need it to be quiet.
import os
import sys
old_stdout = sys.stdout
sys.stdout = open(os.devnull, "w")
from py1 import test
sys.stdout = old_stdout
print test

You might want to consider changing the other script to still print when its run 'in the other place' - if you're running py1 as a shell command, try to make sure all "executable statements" in a file are inside a block.
if __name__ == "__main__":
print test
(see What does if __name__ == "__main__": do?)
This would fix the underlying issue without having you do weird things (which would be redirecting the standard out, and then putting it back etc), or opening the file and executing line by line on an if block.

You could implement this functionality with methods:
py1.py
test=("hi", "hello")
def print_test():
print(test)
def print_first_index():
print(test[0])
py2.py
import py1
py1.print_test()
As MooingRawr pointed out, this would require you to change whichever classes use py1.py to import it and call the py1.print_first_index() function which may not be to your liking.

Related

How to capture python subprocess stdout in unittest

I am trying to write a unit test that executes a function that writes to stdout, capture that output, and check the result. The function in question is a black box: we can't change how it is writing it's output. For purposes of this example I've simplified it quite a bit, but essentially the function generates its output using subprocess.call().
No matter what I try I can't capture the output. It is always written to the screen, and the test fails because it captures nothing. I experimented with both print() and os.system(). With print() I can capture stdout, but not with os.system() either.
It's also not specific to unittesting. I've written my test example without that with the same results.
Questions similar to this have been asked a lot, and the answers all seem to boil down to use subprocess.Popen() and communicate(), but that would require changing the black box. I'm sure there's an answer I just haven't come across, but I'm stumped.
We are using Python-2.7.
Anyway my example code is this:
#!/usr/bin/env python
from __future__ import print_function
import sys
sys.dont_write_bytecode = True
import os
import unittest
import subprocess
from contextlib import contextmanager
from cStringIO import StringIO
# from somwhere import my_function
def my_function(arg):
#print('my_function:', arg)
subprocess.call(['/bin/echo', 'my_function: ', arg], shell=False)
#os.system('echo my_function: ' + arg)
#contextmanager
def redirect_cm(new_stdout):
old_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield
finally:
sys.stdout = old_stdout
class Test_something(unittest.TestCase):
def test(self):
fptr = StringIO()
with redirect_cm(fptr):
my_function("some_value")
self.assertEqual("my_function: some_value\n", fptr.getvalue())
if __name__ == '__main__':
unittest.main()
There are two issues in the above code
StringIO fptr does not shared by the current and the spawned process, we could not get the result in current process even if the spawned process has written result to StringIO object
Changing sys.stdout doesn’t affect the standard I/O streams of processes executed by os.popen(), os.system() or the exec*() family of functions in the os module
A simple solution is
use os.pipe to share result between the two processes
use os.dup2 instead of changing sys.stdout
A demo example as following shown
import sys
import os
import subprocess
from contextlib import contextmanager
#contextmanager
def redirect_stdout(new_out):
old_stdout = os.dup(1)
try:
os.dup2(new_out, sys.stdout.fileno())
yield
finally:
os.dup2(old_stdout, 1)
def test():
reader, writer = os.pipe()
with redirect_stdout(writer):
subprocess.call(['/bin/echo', 'something happened what'], shell=False)
print os.read(reader, 1024)
test()

if __name__ == '__main__' function call

I am trying to work around a problem I have encountered in a piece of code I need to build on. I have a python module that I need to be able to import and pass arguments that will then be parsed by the main module. What I have been given looks like this:
#main.py
if __name__ == '__main__'
sys.argv[] #pass arguments if given and whatnot
Do stuff...
What I need is to add a main() function that can take argument(s) and parse them and then pass them on like so:
#main.py with def main()
def main(args):
#parse args
return args
if __name__ == '__main__':
sys.argv[] #pass arguments if given and whatnot
main(sys.argv)
Do stuff...
To sum up: I need to import main.py and pass in arguments that are parsed by the main() function and then give the returned information to the if __name_ == '__main_' part.
EDIT
To clarify what I am doing
#hello_main.py
import main.py
print(main.main("Hello, main"))
ALSO I want to still be able to call main.py from shell via
$: python main.py "Hello, main"
Thus preserving the name == main
Is what I am asking even possible? I have been spending the better part of today researching this issue because I would like to, if at all possible, preserve the main.py module that I have been given.
Thanks,
dmg
Within a module file you can write if __name__ == "__main__" to get specific behaviour when calling that file directly, e.g. via shell:
#mymodule.py
import sys
def func(args):
return 2*args
#This only happens when mymodule.py is called directly:
if __name__ == "__main__":
double_args = func(sys.argv)
print("In mymodule:",double_args)
One can then still use the function when importing to another file:
#test.py
import mymodule
print("In test:",mymodule.func("test "))
Thus, calling python test.py will result in "In test: test test ", while calling python mymodule.py hello will result in "In mymodule: hello hello ".

How to prevent running the __main__ guard when using execfile?

The BDFL posted in 2003 an article about how to write a Python main function. His example is this:
import sys
import getopt
class Usage(Exception):
def __init__(self, msg):
self.msg = msg
def main(argv=None):
if argv is None:
argv = sys.argv
try:
try:
opts, args = getopt.getopt(argv[1:], "h", ["help"])
except getopt.error, msg:
raise Usage(msg)
# more code, unchanged
except Usage, err:
print >>sys.stderr, err.msg
print >>sys.stderr, "for help use --help"
return 2
if __name__ == "__main__":
sys.exit(main())
The reason for the optional argument argv to main() is, "We change main() to take an optional argv argument, which allows us to call it from the interactive Python prompt."
He explains the last line of his code like this:
Now the sys.exit() calls are annoying: when main() calls sys.exit(),
your interactive Python interpreter will exit! The remedy is to let
main()'s return value specify the exit status. Thus, the code at the
very end becomes
if __name__ == "__main__":
sys.exit(main())
and the calls to sys.exit(n) inside main() all become return n.
However, when I run Guido's code in a Spyder console, it kills the interpreter. What am I missing here? Is the intention that I only import modules that have this type of main(), never just executing them with execfile or runfile? That's not how I tend to do interactive development, especially given that it would require me to remember to switch back and forth between import foo and reload(foo).
I know I can catch the SystemExit from getopt or try to use some black magic to detect whether Python is running interactively, but I assume neither of those is the BDFL's intent.
Your options are to not use execfile or to pass in a different __name__ value as a global:
execfile('test.py', {'__name__': 'test'})
The default is to run the file as a script, which means that __name__ is set to __main__.
The article you cite only applies to import.
Another way to handle it, which I alluded to briefly in the question, is to try to detect if you're in an interactive context. I don't believe this can be done portably, but here it is in case it's helpful to someone:
if __name__ == "__main__":
if 'PYTHONSTARTUP' in os.environ:
try:
main() # Or whatever you want to do here
except SystemExit as se:
logging.exception("")
else:
sys.exit(main())

Why doesn't vim print anything in console when running Python code?

I am trying to write some python code using tornado. Here is my code.
import sys
import tornado.ioloop
import tornado.web
import constants
class student():
name = ""
class MainHandler(tornado.web.RequestHandler):
def get(self):
loader = tornado.template.Loader(".")
print "MainiiiHandler"
self.write(loader.load("base.html").generate(pics=constants.pics))
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
So when i visit 127.0.0.1:8888, it should print MainiiiHandler in terminal. When i run python code with 'python test.py', it turns out actually like this. But when i run with :make in vim, it won't print MainiiiHandler. Because i really like the make function in vim, so can you help me solve this problem.
Check how
makeprg is python %
is written.
:set makeprg="python %"
does NOT work for me (echoes an empty string)
while
:set makeprg=python\ %
actually DOES work.
(if it doesn't help) This is what :h make shows:
The program given with the 'makeprg' option is started (default "make") with the optional [arguments] and the output is saved in the errorfile (for Unix it is also echoed on the screen).
If your system is not Unix, I suppose you have to supply the code that will print the contents of errorfile for you (don't know for sure as I tested it only under Linux).

Only print when run as script?

Is there a better way to only print when run as a script, when __name__ == '__main__' ?
I have some scripts that I also import and use parts of.
Something like the below will work but is ugly, and would have to be defined in each script separately:
def printif(s):
if globals()['__name__'] == '__main__':
print (s)
return
I looked briefly at some of python's logging libraries but would prefer a two lighter solution...
edit:
I ended up doing something like this:
# mylog.py
import sys
import logging
log = logging.getLogger()
#default logging level
log.setLevel(logging.WARNING)
log.addHandler(logging.StreamHandler(sys.stdout))
And from the script:
import log from mylog
...
log.info(...)
log.warning(...)
...
if __name__ == '__main__':
#override when script is run..
log.setLevel(logger.INFO)
This scheme has minimal code duplication, per script log levels, and a project-wide default level...which is exactly what I wanted.
run_as_script = False
def printif(s):
if run_as_script:
print (s)
return
if __name__ == '__main__':
run_as_script = True
In light of user318904's comment on my other answer, I'll provide an alternative (although this may not work in all cases, it might just be "good enough").
For a separate module:
import sys
def printif(s):
if sys.argv[0] != '':
print (s)
Using a logging library is really not that heavyweight:
import logging
log = logging.getLogger('myscript')
def dostuff(...):
....
log.info('message!')
...
if __name__ == '__main__':
import sys
log.setLevel(logging.INFO)
log.addHandler(logging.StreamHandler(sys.stdout))
...
One wart is the "WARNING: no handlers found for myscript" message that logging prints by default if you import this module (rather than run it as a script), and call your function without setting up logging. It'll be gone in Python 3.2. For Python 2.7, you can shut it off by adding
log.addHandler(logging.NullHandler())
at the top, and for older versions you'd have to define a NullHandler class like this:
class NullHandler(logging.Handler):
def emit(self, record):
pass
Looking back at all this, I say: go with Gerrat's suggestion. I'll leave mine here, for completeness.

Categories

Resources