Lua script won't execute python script - python

I have a lua script configured to trigger once a subject's metadata is sent to a specific online Orthanc server. I want it to scrape the subject ID and then call a python script with the ID as an argument. When I put the command manually into my terminal, it works, but the lua script doesn't seem to be executing it.
There is a built-in Orthanc function that scrapes the ID from the subject once it is sent to the server.
The initial lua script had the following:
path = "/path/to/python_script.py"
os.execute("python " .. path .. " " .. subjectId)
But the script wasn't getting called.
I first wanted to see if it was even getting triggered, so I added in:
file = io.open("/path/to/textfile.txt", "a")
file:write("\nI am alive, subjectId is " .. subjectId)
file:close()
And that worked!
So then I wanted to see if there was something wrong with os.execute, so I did:
os.execute("touch /same/path/deleteme.txt")
which worked as well.
So it doesn't seem like os.execute isn't working.
Does anyone have any idea why the script isn't getting called?
EDIT: Does anyone know how to check the status of the os.execute command?
EDIT: I am using Python 3.5.6, Lua 5.1.4, and Linux.

Firstly, to address you question about checking the status from os.execute: this function returns a a status code, which is system dependent (https://www.lua.org/manual/5.1/manual.html#pdf-os.execute). I tried to handle an invalid command by recording this status code, but found it to be somewhat unhelpful; additionally, the shell itself printed an error message.
os.execute("hello") -- 'hello' is not a shell command.
> sh: 1: hello: not found
This error message from the shell was not being caught and read by my Lua script, but was instead being sent directly to stderr. (Good reference about that: https://www.jstorimer.com/blogs/workingwithcode/7766119-when-to-use-stderr-instead-of-stdout.)
I found an interesting solution for catching any error output using temp files.
tmpname = os.tmpname()
os.execute(string.format("hello 2> %s", tmpname))
for line in io.lines(tmpname) do
print("line = " .. line)
end
This prints: line = sh: 1: hello: not found, which is the error described earlier. os.execute should also return the status of the command like this:
a, b, c = os.execute("echo hello")
> hello
print(a, b, c)
> true exit 0
d, e, f = os.execute("hello") -- An invalid command
> sh: 1: hello: not found
print(d, e, f)
> nil exit 127
In this example, c and f are the exit statuses of their respective commands. If the previous command, i.e. executing your Python script, failed, then the exit status should be non-zero.
To address your primary question regarding Python, I would double-check the path to the script--always a good idea to start with a simple sanity check. Consider using string.format to assemble the command like this:
command = string.format("python %s %i", tostring(path), tonumber(subjectId))
os.execute(command)
Also, it would be helpful to know which version of Lua/Python you are using, and perhaps your system as well.
EDIT: depending on whether or not you need them to remain for a bit, you should remove any temp files generated by os.tmpname with os.remove. I also attempted to replicate your situation with a simple test, and I had no trouble executing the Python script with os.execute in a Lua script located in a different directory.
For reference, this is the Lua script, called test.lua, I created in a temp directory called /tmp/throwaway:
#!/usr/bin/lua
local PATH_TO_PY_FILE = "/tmp/py/foo.py"
local function fprintf(fil, formal_arg, ...)
fil:write(string.format(formal_arg, ...))
return
end
local function printf(formal_arg, ...)
io.stdout:write(string.format(formal_arg, ...))
return
end
local function foo(...)
local t = {...}
local cmd = string.format("python3 %s %s", PATH_TO_PY_FILE, table.concat(t, " "))
local filename = os.tmpname()
local a, b, status = os.execute(string.format("%s 2> %s", cmd, filename))
printf("status = %i\n", status)
local num = 1
for line in io.lines(filename) do
printf("line %i = %s\n", num line)
num = num + 1
end
os.remove(filename)
return
end
local function main(argc, argv)
foo()
foo("hello", "there,", "friend")
return 0
end
main(#arg, arg)
(Please forgive my C-style main function, haha.)
In a separate temp directory, called /tmp/py, I created a Python file that looks like this:
import sys
def main():
for arg in sys.argv:
print(arg)
if __name__ == '__main__':
main()
The Lua script's function foo takes a variable number of arguments and supplies them as command-line arguments to the Python script; the Python script then simply prints those arguments one-by-one. Again, this was just a simple test for proof of concept.
The temp file created by os.tmpname should be in /tmp; as for your files, that is, your Lua and Python scripts, you would make sure you know exactly where those files are located. Hopefully that can resolve your problem.
Also, you could supply the path to the Python script--or any other necessary files--to the Lua script as command-line arguments and then slightly modify the existing code.
$> ./test.lua path-to-python-file
Then simply modify foo in test.lua to accept the Python file's path as an argument:
local function foo(py_path, ...)
local t = {...}
local cmd = string.format("python3 %s %s", py_path, table.concat(t, " "))
-- Everything else should remain the same.
end

Related

Running python script from perl, with argument to stdin and saving stdout output

My perl script is at path:
a/perl/perlScript.pl
my python script is at path:
a/python/pythonScript.py
pythonScript.py gets an argument from stdin, and returns result to stdout. From perlScript.pl , I want to run pythonScript.py with the argument hi to stdin, and save the results in some variable. That's what I tried:
my $ret = `../python/pythonScript.py < hi`;
but I got the following error:
The system cannot find the path specified.
Can you explain the path can't be found?
The qx operator (backticks) starts a shell (sh), in which prog < input syntax expects a file named input from which it will read lines and feed them to the program prog. But you want the python script to receive on its STDIN the string hi instead, not lines of a file named hi.
One way is to directly do that, my $ret = qx(echo "hi" | python_script).
But I'd suggest to consider using modules for this. Here is a simple example with IPC::Run3
use warnings;
use strict;
use feature 'say';
use IPC::Run3;
my #cmd = ('program', 'arg1', 'arg2');
my $in = "hi";
run3 \#cmd, \$in, \my $out;
say "script's stdout: $out";
The program is the path to your script if it is executable, or perhaps python script.py. This will be run by system so the output is obtained once that completes, what is consistent with the attempt in the question. See documentation for module's operation.
This module is intended to be simple while "satisfy 99% of the need for using system, qx, and open3 [...]. For far more power and control see IPC::Run.
You're getting this error because you're using shell redirection instead of just passing an argument
../python/pythonScript.py < hi
tells your shell to read input from a file called hi in the current directory, rather than using it as an argument. What you mean to do is
my $ret = `../python/pythonScript.py hi`;
Which correctly executes your python script with the hi argument, and returns the result to the variable $ret.
The Some of the other answers assume that hi must be passed as a command line parameter to the Python script but the asker says it comes from stdin.
Thus:
my $ret = `echo "hi" | ../python/pythonScript.py`;
To launch your external script you can do
system "python ../python/pythonScript.py hi";
and then in your python script
import sys
def yourFct(a, b):
...
if __name__== "__main__":
yourFct(sys.argv[1])
you can have more informations on the python part here

Executing complex python scripts from within python

I have a python script that has a method that takes in a string that contains another python script. I'd like to execute that script, call a function in it, and then use that function's results in my main script. It currently looks something like this:
def executePythonScript(self, script, param):
print 'Executing script'
try:
code = compile(script, '<string>', 'exec')
exec code
response = process(param)
except Exception as ex:
print ex
print 'Response: ' + response
return response
The "process" function is assumed to exist in the script that gets compiled and executed run-time.
This solution works well for VERY simple scripts, like:
def process():
return "Hello"
...but I'm having no luck getting more complex scripts to execute. For instance, if my script uses the json package, I get:
global name 'json' is not defined
Additionally, if my process function refers to another function in that same script, I'll get an error there as well:
def process():
return generateResponse()
def generateResponse():
return "Hello"
...gives me an error:
global name 'generateResponse' is not defined
Is this an issue of namespacing? How come json (which is a standard python package) does not get recognized? Any advice would be appreciated!
import subprocess
subprocess.call(["python","C:/path/to/my/script.py"])
I would recommend against this and just use import.
This is also under the assumption that your PYTHONPATH is set in your environment variables.

Starting a temporary MySQL server with Python

I would like to execute a specific version of mysqld through Python for unit testing. The idea is to execute the server on a thread, test, and kill the server when it's done. (Similar to testing.mysqld, which sadly doesn't work on Windows.). This is the current code:
#Create a temporary folder.
base_path = tempfile.mkdtemp()
#Extract the default files
zipfile.ZipFile(r"O:\Tools\mysql\min_mysql.zip").extractall(base_path)
#Setup my_ini file
my_ini_path = os.path.join(base_path, "my.ini").replace("\\", "/")
unix_base_path = posixpath.normpath(base_path).replace("\\", "/")
with open(my_ini_path, 'r') as my_ini:
filedata = my_ini.read()
filedata = filedata.replace("{{basedir}}", unix_base_path)
with open(my_ini_path, 'w', 0) as my_ini:
my_ini.write(filedata)
#Open mysqld
args = r"O:/Tools/mysql/bin/mysqld.exe --defaults-file=\"%s\"" % (my_ini_path)
args = shlex.split(args)
mysqld_process = subprocess.Popen(args, shell=True)
mysqld_process.wait()
But if I execute it through Python, I get this error:
Could not open required defaults file:
"c:\users\pelfeli1\appdata\local\temp\tmp2vct38\my.ini"
Fatal error in defaults handling. Program aborted
So far I have verified that the file exists before starting the process. If I print the command verbatim and execute it, the server runs fine.
There seems to be a difference between Popen and just executing in shell. What am I missing?
I'll copy my comment here, if you want to accept it as an answer:
I don't think this is the problem, but the args string shouldn't be
defined as raw (with the r). Instead, do this:
'O:/Tools/mysql/bin/mysqld.exe --defaults-file="%s"' (ie. use single
quotes). Unless you intend to pass the backslashes to the command line
Now, take into account that the following two strings
"foo\"bar\""
r"foo\"bar\""
Are not the same. The first one renders foo"bar", while the second gives you foo\"bar\".
So, what was happening is that the shell sees this as the file name: "c:\users\pelfeli1\appdata\local\temp\tmp2vct38\my.ini", including the quotes, because of the backquotes (\). You could have just written this:
args = 'O:/Tools/mysql/bin/mysqld.exe --defaults-file="%s"' % (my_ini_path)
just in case of spaces in my_ini_path, without problems.
Well, the problem was in the quotes. Just changed this line:
args = r"O:/Tools/mysql/bin/mysqld.exe --defaults-file=\"%s\"" % (my_ini_path)
to this line
args = "O:/Tools/mysql/bin/mysqld.exe --defaults-file=%s" % (my_ini_path)
I still have no idea why this changes anything, because printing args gives a valid (and working) command in both cases.

Error when calling a file from python

I want to call an R program from a python script.
I wrote the following:
os.system("cat " + variableName + " | code.R")
It returns the error: sh: 1 : code.R: not found
cat: write error: Broken pipe
Yet, I am sure of the name of the R file.
Why is it not working?
So, if code.R is a script that has to be interpreted you must build the pipe to the interpreter not to the script. You receive a Broken PIPE error because code.R by it self don't know how to handle command line arguments.
On the other hand if what you want is store the variable value inside code.R you have to change | by >>.
os.system("cat " + variablename + ">> code.R")
EDIT: Since it's working from terminal, try this:
import subprocess
input = open(variableName, "r")
result = suprocess.call(["code.R"], stdin=input) # result is the return code for the command being called.
see subprocess.call for more details.
Is code.R in the current working directory? Is it executable? Can you run cat xxx | code.R from the shell and have it work properly, instead of running your python program?

boto throwing an Content-MD5 error sometimes

So I basically wrote a function to upload a file to S3 using the key.set_contents_from_file() function but I'm finding it sometimes throws this error
<Error>
<Code>BadDigest</Code>
<Message>The Content-MD5 you specified did not match what we received.</Message>
<ExpectedDigest>TPCms2v7Hu43d+yoJHbBIw==</ExpectedDigest>
<CalculatedDigest>QSdeCsURt0oOlL3NxxGwbA==</CalculatedDigest>
<RequestId>2F0D40F29AA6DC94</RequestId><HostId>k0AC6vaV+Ip8K6kD0F4fkbdS13UdxoJ3X1M76zFUR/ZQgnIxlGJrAJ8BeQlKQ4m6</HostId></Error>
The function:
def uploadToS3(filepath, keyPath, version):
bucket = connectToS3() # Simple gets my bucket and returns it
key = Key(bucket)
f = open(filepath,'r')
key.name = keyPath
key.set_metadata('version', version)
key.set_contents_from_file(f) # offending line
key.make_public()
key.close()
If I open a python shell and manually call it, it works without a hitch, however the way I have to handle it (in which it doesn't work) involves calling it from a subprocess. This is because the caller is a python3 script, 2to3 didn't work and I didn't want to deal with the various years old branches for python3 versions.
Anyway, that seems to actually run it correctly as it gets in the function, the inputs are what's expected (I had them print out), but the # offending line keeps throwing this error. I have no idea what the cause is.
Is it possible bucket isn't being set properly? I feel like if that were the case calling Key(bucket) would have thrown an error
So I essentially run the below script, once as a subprocess called from a python3 script, the other from the console
sudo -u www-data python botoUtilities.py uploadToS3 /path/to/file /key/path
I have this logic inside to pass it to the correct function
func=None
args=[]
for arg in sys.argv[1:]:
if not func:
g = globals()
func = g[arg]
else:
if arg=='True':
args.append(True)
elif arg=='False':
args.append(False)
else:
args.append(arg)
if func:
wrapper(func, args)
It runs in both cases (I write to a file to print out the args) but only in the console case does it not get the error. This is incredibly frustrating. I can't figure out what is done differently. All I know is that it's not possible to send data to S3 using boto run from a subprocess

Categories

Resources