Python's sh module - Running Command wrapper in background - python

When using python's sh module (not a part of stdlib), I can call a program in my path as a function and run it in the background:
from sh import sleep
# doesn't block
p = sleep(3, _bg=True)
print("prints immediately!")
p.wait()
print("...and 3 seconds later")
And I can use sh's Command wrapper and pass in the absolute path of an executable (helpful if the executable isn't in my path or has characters such as .):
import sh
run = sh.Command("/home/amoffat/run.sh")
run()
But trying to run the wrapped executable in the background, as follows:
import sh
run = sh.Command("/home/amoffat/run.sh", _bg=True)
run()
Fails with a traceback error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument '_bg'
How can I run an executable wrapped by sh.Command in the background? Looking for an elegant solution.
EDIT:
I used the python interpreter for testing passing _bg to the command (not the wrapper), which I now realize is a bad way to test for blocking and non-blocking processes:
>>> import sh
>>> hello = sh.Command("./hello.py")
>>> hello(_bg=True) # 5 second delay before the following prints and prompt is returned
HI
HI
HI
HI
HI
With hello.py being as follows:
#!/usr/bin/python
import time
for i in xrange(5):
time.sleep(1)
print "HI"

import sh
run = sh.Command("/home/amoffat/run.sh", _bg=True) # this isn't your command,
# so _bg does not apply
run()
Instead, do
import sh
run = sh.Command("/home/amoffat/run.sh")
run(_bg=True)
(BTW, the subprocess module provides a much less magical way to do such things.)

Related

Index error while working with sys argv in PyCharm

I'm using 'sys' module to get the filename as an argument in the command line while I'm running the script in cmd it is working as I want, but if I run this in PyCharm it raises an error Index Error: list index out of range. How to get rid of this error in PyCharm?
Here is the Code I'm trying to run:
import sys
def read_lines(file):
list_of_numbers = []
with open(file, mode='r') as read_file:
for number in read_file:
number = number.strip()
list_of_numbers.append(number)
return list_of_numbers
if __name__ == '__main__':
fun = read_lines(sys.argv[1])
print(fun)
While running the script directly from pycharm it raises following error:
Traceback (most recent call last):
File "D:\pythonProjects\test.py", line 10, in <module>
fun = read_lines(sys.argv[1])
IndexError: list index out of range
Presumably that's because you don't provide any arguments when running the script in PyCharm.
You can run the following script to print all the arguments:
import sys
if __name__ == '__main__':
print(sys.argv)
If you run it in the command line something like this
python3 test.py filename.txt
You should the output
['test.py', 'filename.txt']
(the first argument is the name of your script).
In PyCharm, you also have to specify filename.txt as a parameter.
Otherwise, you only get
['test.py']
which means that there is no element 1, hence the IndexError.
You can fix it by adding the filename.txt parameter to your run configuration in PyCharm.

Attaching Python debugger

I'd like to attach a Python debugger to a running process. Following this comment, I tried pdb-clone but have gotten confused. Here's the script I'm attaching to:
import os
import time
from pdb_clone import pdbhandler
pdbhandler.register()
def loop(my_pid):
print("Entering loop")
while True:
x = 'frog'
time.sleep(0.5)
print("Out of Loop")
if __name__ == '__main__':
my_pid = os.getpid()
print("pid = ", my_pid)
loop(my_pid)
If I run python3 target_code_1.py in one terminal and see PID = 95439, then in a second terminal try
sudo pdb-attach --kill --pid 95439
I get an error message (which I include below).
However, suppose I simultaneously run python3 target_code_1.py in a third terminal. I can now run sudo pdb-attach --kill --pid 95439 without error, but when I print my_pid, the value is 95440. On the other hand, if I run sudo pdb-attach --kill --pid 95440 and print my_pid, the value is 95439. (In other words, it looks like pdb-attach has swapped which thread it is attaching to.) This behavior appears to be repeatable. What is going on?
For the record, the initial error message is as follows:
sudo pdb-attach --kill --pid 95440
Traceback (most recent call last):
File "/usr/local/bin/pdb-attach", line 4, in <module>
attach.main()
File "/usr/local/lib/python3.7/site-packages/pdb_clone/attach.py", line 646, in main
attach(address)
File "/usr/local/lib/python3.7/site-packages/pdb_clone/attach.py", line 596, in attach
for count in asock.connect_retry(address, verbose):
File "/usr/local/lib/python3.7/site-packages/pdb_clone/attach.py", line 115, in connect_retry
self.connect(address)
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncore.py", line 342, in connect
raise OSError(err, errorcode[err])
OSError: [Errno 22] EINVAL
FWIW, I'm running on macOS Mojave 10.14.2, Python 3.7.0, Clang 9.1.0.
(If I am solving this problem the wrong way, e.g., if there is a better Python module to use that can attach to live process, I'd be happy to use it instead.)

ValueError: need more than 1 value to unpack psychopy: haven't touched the script since it was working yesterday and am suddenly getting this error

I recognize there are a decent amount of ValueError questions on here, but it seems none are specifically related to psychopy or my issue. I am coding an experiment from scratch on psychopy (no builder involved). Yesterday, my script was running totally fine. Today I tried running it without adding anything new or taking anything away and it's suddenly giving me this error:
File "/Users/vpam/Documents/fMRI_binding/VSTMbindingpaige.py", line 53, in <module>
script, filename = argv
ValueError: need more than 1 value to unpack
These are lines 52 and 53, apparently something in 53 (the last one) is making this happen, but I can't imagine what since it was working just fine yesterday. Anyone know why it's doing that? (I am running the oldest version of python in order to be able to include corrective audio feedback, but I have been running it on that with success):
from sys import argv
script, filename = argv
This is what I'm calling the filename (in the script it is above those other lines)
from sys import argv
script, filename = argv
from psychopy import gui
myDlg = gui.Dlg(title="Dr. S's experiment")
myDlg.addField('Subject ID','PJP')
ok_data = myDlg.show()
if myDlg.OK:
print(ok_data)
else:
print('user cancelled')
[sID]=myDlg.data
# Data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
data_file = sID + '_VSTMbinding.txt'
f = open(data_file,'a') #name file here
f.write(sID)
print myDlg.data
It looks like you're using Python2. Python3 gives a more detailed information in it's error message. The problem is that argv only contains a single value and you're trying to unpack it into two variables. argv contains the command line variables -- if this was running yesterday "without any changes" as you suggest, it's because you were providing a filename as a command-line argument.
py2.py
#!/usr/bin/env python
from sys import argv
script, filename = argv
print("Script: {0}\nFilename: {1}".format(script, filename))
py3.py
#!/usr/bin/env python3
from sys import argv
script, filename = argv
print("Script: {0}\nFilename: {1}".format(script, filename))
Running py2.py:
$ charlie on laptop in ~
❯❯ ./py2.py
Traceback (most recent call last):
File "./py2.py", line 4, in <module>
script, filename = argv
ValueError: need more than 1 value to unpack
$ charlie on laptop in ~
❯❯ ./py2.py filename
Script: ./py2.py
Filename: filename
Running py3.py:
$ charlie on laptop in ~
❯❯ ./py3.py
Traceback (most recent call last):
File "./py3.py", line 4, in <module>
script, filename = argv
ValueError: not enough values to unpack (expected 2, got 1)
$ charlie on laptop in ~
❯❯ ./py3.py filename
Script: ./py3.py
Filename: filename

Running a python method/function directly from a file

I would like to know if there is a way to directly run a python function directly from a file by just mentioning the filename followed by the function in a single line.
For example, lets say I have a file 'test.py' with a function 'newfunction()'.
----------test.py-----------
def newfunction():
print 'welcome'
Can I run the newfunction() doing something similar to this.
python test.py newfunction
I know how to import and to call functions etc.Having seen similar commands in django etc (python manage.py runserver), I felt there is a way to directly call a function like this. Let me know if something similar is possible.
I want to be able to use it with django. But an answer that is applicable everywhere would be great.
Try with globals() and arguments (sys.argv):
#coding:utf-8
import sys
def moo():
print 'yewww! printing from "moo" function'
def foo():
print 'yeeey! printing from "foo" function'
try:
function = sys.argv[1]
globals()[function]()
except IndexError:
raise Exception("Please provide function name")
except KeyError:
raise Exception("Function {} hasn't been found".format(function))
Results:
➜ python calling.py foo
yeeey! printing from "foo" function
➜ python calling.py moo
yewww! printing from "moo" function
➜ python calling.py something_else
Traceback (most recent call last):
File "calling.py", line 18, in <module>
raise Exception("Function {} hasn't been found".format(function))
Exception: Function something_else hasn't been found
➜ python calling.py
Traceback (most recent call last):
File "calling.py", line 16, in <module>
raise Exception("Please provide function name")
Exception: Please provide function name
I think you should take a look at:
https://docs.djangoproject.com/en/1.9/howto/custom-management-commands/
All those commands like migrate, runserver or dbshell etc. are implemented like how it was described in that link:
Applications can register their own actions with manage.py. To do this, just add a management/commands directory to the application.
Django will register a manage.py command for each Python module in that directory whose name doesn’t begin with an underscore.

How does the main function of the argparse template that ships with eclipse work in the interactive python shell?

If you create a new python module in eclipse, you have the option to use a argparse template for your new module. Below are code fragments from this template.
#!/usr/local/bin/python2.7
# encoding: utf-8
'''
eclipse_argparse -- shortdesc
eclipse_argparse is a description
It defines classes_and_methods
#author: user_name
#copyright: 2015 organization_name. All rights reserved.
#license: license
#contact: user_email
#deffield updated: Updated
'''
def main(argv=None): # IGNORE:C0111
'''Command line options.'''
if argv is None:
argv = sys.argv
else:
sys.argv.extend(argv)
...
program_shortdesc = __import__('__main__').__doc__.split("\n")[1]
...
if __name__ == "__main__":
sys.exit(main())
'...' means that I omitted the parts of the code that seem not important to my question.
I saved the file under eclipse_argparse.py.
As far as I understand, the main function is given the argument argv so that one can call the main function interactively from the python shell and give it a parameter:
>>> import eclipse_argparse
>>> eclipse_argparse.main('-bla')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "eclipse_argparse.py", line 57, in main
program_shortdesc = __import__('__main__').__doc__.split("\n")[1]
AttributeError: 'NoneType' object has no attribute 'split'
But this is not possible because __import__('__main__') evaluates to __main__, and not to eclipse_argparse as I would expect.
program_shortdesc should then evaluated to eclipse_argparse -- shortdesc
Why does this happen? Is my assumption, that you should be able to call the main method with an argument in an interactive session wrong?
Within a module, its own doc is available as __doc__. You don't need to import anything, or reference __main__ (which in the interactive shell points to shell environment, not any imported module).
This works when main is called from if __name__ and from a shell:
def main(argv=None): # IGNORE:C0111
# ...
print __doc__
In a shell I can do:
>>> import stack27864002
>>> stack27864002.__doc__
'\neclipse_argparse -- ...'
>>> stack27864002.main()
# same
I wonder why this is called a argparse template, since it has nothing to do with argparse.py module.
The default value for __doc__ is None, so it is a good idea to be prepared for that case - i.e. don't try to split it without first checking.
To make the code work in an interactive shell session, the code
program_shortdesc = __import__('__main__').__doc__.split("\n")[1]
should be changed to:
if __name__ == '__main__':
program_shortdesc = __import__('__main__').__doc__.split("\n")[1]
else:
program_shortdesc = __doc__.split("\n")[1]
What confused me was that they provide a main method with the possibility to supply an argument (which implies that you could import the module in a python shell and call the main function with some parameter, like import eclipse_argparse; eclipse_argparse.main('-some_parameter')) and then define a variable program_shortdesc that only works when you call the program through the '__main__' 'entry point', like python eclipse_argparse.py

Categories

Resources