I'm trying to run a very simple python script that clears and writes to a CSV file, from inside of java but I'm having a lot of trouble doing it.
The scripts don't require any input and the output is all written into a CSV file so all I need to do is get the python scripts to run through my java code.
Below is a bit of code that I've seen all over the internet but doesn't seem to be working for me. It seems like for both of the scripts, using this command does nothing to the csv. No errors are thrown and the java program simply exits presumably without executing the python scripts.
public static void main(String[] args) throws IOException
{
Process p = Runtime.getRuntime().exec("python Refresh.py");
}
here are the scripts I'm trying to run.
Script1:
file = open("products.csv","r+")
file.truncate(0)
file.close()
Script2:
from bs4 import BeautifulSoup as soup
from urllib.request import Request, urlopen
import time
filename = "products.csv"
f = open(filename, "a")
#connects to the page and reads and saves raw HTML
for i in (0,25,50,75):
my_url = 'https://www.adorama.com/l/Computers/Computer-Components/Video-and-Graphics-Cards?startAt='+ str(i) +'&sel=Expansion-Ports_HDMI'
hdr = {'User-Agent': 'Mozilla/5.0'}
client = Request(my_url,headers=hdr)
page = urlopen(client).read()
#parsing the HTML
page_soup = soup(page, "html.parser")
#print (page_soup.h1)
containers = page_soup.findAll("div",{"class":"item"})
#print (len(containers))
containers.pop()
for container in containers:
title_container = container.findAll("div",{"class":"item-details"})
title = title_container[0].h2.a.text.strip()
status_container = container.findAll("div",{"class":"item-actions"})
status = status_container[0].form.button.text.strip()
if (status == "Temporarily not available"):
status = "Out of stock"
else:
status = "In stock"
price = container.find("div","prices").input["value"]
link = container.a["href"]
f.write(title.replace(",", "|") + "," + price.replace(",", "") + "," + status + "," + link + "\n")
time.sleep(0.01)
f.close()
The java file, Python script, and the csv file are all in the same folder.
Use the newer ProcessBuilder class:
ProcessBuilder pb = new ProcessBuilder("python","Refresh.py");
Process p = pb.start();
Hope that works for you!
You are not checking for errors from the python script. You can achieve this simply by merging STDERR to STDOUT and reporting the content of STDOUT to console:
Process p = new ProcessBuilder("python", "Refresh.py")
.redirectErrorStream(true)
.start();
p.getInputStream().transferTo(System.out);
int rc = p.waitFor();
This should print out the error message from python and give you error code back. You may have problems with path to files, so you might need to adjust your arguments to explicit pathnames to "python" and/or "Refresh.py".
I managed to fix the issue by constantly reading the "print" and error outputs of the Python file. Whilst I still don't completely understand how this fixed the issue, my best guess is that with this, the Java code keeps the python script "running" until the script itself is finished doing its thing, instead of just opening the script and instantly moving on.
Here's the code:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Test {
public static void main(String... args) throws Exception {
String[] callAndArgs = {"python3", "YourScript.py"};
Process p = Runtime.getRuntime().exec(callAndArgs);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String s;
while ((s = stdInput.readLine()) != null) {
//System.out.println(s);
}
while ((s = stdError.readLine()) != null) {
//System.out.println(s);
}
}
}
Another Notable Detail is that this code only seems to work when Compiled and run through the Terminal/Geany. If I run the same thing with IntelliJ it does not work. Once again, I'm not sure why this is but I'm suspecting that IntelliJ compiles and runs in a VM of some sorts.
Related
I have to open a python script from c++. For this I decided to use ShellExecuteEx like below:
SHELLEXECUTEINFO ShExecInfo = { 0 };
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
ShExecInfo.lpFile = "python";
ShExecInfo.lpParameters = strParams.c_str();
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_NORMAL;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
CloseHandle(ShExecInfo.hProcess);
size_t exeResult = (size_t)ShExecInfo.hInstApp;
// check if the executable ran
if (exeResult <= 32)
{
However the python script ends without getting a chance to see the output error / traceback:
if __name__ == "__main__":
f = open("python_log.txt", "w")
f.write("hello")
try:
main()
except Exception as e:
print("An exception occurred", str(e))
f.write(str(e))
var = traceback.format_exc()
print(var)
f.write(var)
f.close()
wait = input("Press Enter to exit.")
I don't know what else to add or how to see the output of the python script that is called by the c++ code. I've thought about running the cmd and start the py script from there, so that I have the output, but I didn't find a way to implement it in c++, it just starts the cmd and no calling the script.
Any help is greatly appreciated.
Thank you!
If you set the SEE_MASK_NO_CONSOLE flag in the fMask member of the SHELLEXECUTEINFO structure before calling ShellExecuteEx, then the Python interpreter will inherit your C++ program's console (assuming it is attached to one), instead of a new console being created for the Python interpreter. That way, your C++ program will have full control on when the console is closed. The console won't automatically be closed when the Python interpreter exits, as long as your C++ program is still running and attached to the console.
If your C++ program is not already attached to a console, then you can attach it to one by calling the function AllocConsole. You should do this before calling ShellExecuteEx.
I am using a Python script that receives variables from a shell script to transmit data via an API call, the third variable being the commit message. When I try to use it however, it only prints the first word of the string and stops at the next space. Example:
"Testing this out" becomes "Testing".
I want the entire thing to transmit. It is transmitted from a shell script that is the third argument and comes over as "3=Testing this out" I have the code currently removing the 3= part, but the problem described above happens.
This is what I have so far:
The shell script $1, 2, and 3 are prompts taken by a program called appworx as user entered prompts:
python3 /RPS_files/script_deployment/Official_Gitlab.py $1 $2 $3
The python code:
import requests
import string
import json
import sys
from pathlib import Path
url = "hidden for privacy purposes"
headers = {
"PRIVATE-TOKEN": "hidden for privacy purposes",
"Content-Type": "application/json"
}
author = sys.argv[1]
author = author.replace("1=","")
filename = "/RPS_files/script_deployment_nz/" + sys.argv[2]
filename = filename.replace("2=","")
commit = sys.argv[3]
commit = commit.replace("3=","")
content = Path(filename).read_text()
sql_substring = ".sql"
loader_substring = ".ctl"
shell_substring = ".sh"
if sql_substring in filename:
file_path = "New Testing Folder/NZ SQL Folder" + filename
elif loader_substring in filename:
file_path = "New Testing Folder/NZ Loader Scripts Folder" + filename
elif shell_substring in filename:
file_path = "New Testing Folder/NZ Shell Scripts" + filename
payload = {
"id": "9081",
"branch" : "master",
"author_name": author,
"author_email" : "N/A",
"committer_name": author,
"committer_email" : "N/A",
"commit_message": commit,
"actions": [
{
"action":"create",
"file_path": file_path,
"content": content
}
]
}
verify='/RPS_files/script_deployment/cacert.pem'
response = requests.post(url, headers=headers, data=json.dumps(payload), verify=verify)
pretty_json = json.loads(response.text)
print(json.dumps(pretty_json, indent=2))
Hmm..
I did an easy script to receive data a while ago.
I used the popen function from the os module.
Did it like this:
stream = popen("command to run")
data = stream.read()
print(data)
For me it did the trick to receive the complete shell output
You can concatenate all the arguments from number 3 to the end with space like this
commit = " ".join(sys.argv[3:])
Or simply you can pass the commit message in quotes
python pgm.py arg1 arg2 'commit message'
The issue here is that space is used to separate arguments on the prompt - and that INCLUDES spaces in variables.
The reason is that variables get replaced before the command gets executed, so it's effectively running:
python3 /RPS_files/script_deployment/Official_Gitlab.py arg1 arg2 Testing this out
As you can see, $3 is just the word "Testing" - the rest is in $4 and $5.
If you quote your variables, the shell knows to treat it as a single argument, which is what you want. A good habit to get into is to ALWAYS quote your variables unless you explicitly want it treated otherwise - which is almost always never (whenever I do, I leave a comment explaining why, as future is me very likely to quote it).
In this case I would be running:
python3 /RPS_files/script_deployment/Official_Gitlab.py "$1" "$2" "$3"
Another reason to quote them all - if $1 is empty for any reason, $2 suddenly becomes the first argument, and everything gets shifted forward a place. If it's in quotes, however, even if it's empty it still gets run as "", so at least the emptiness of gets preserved.
As a bonus, you can use bashes prefix matching feature to remove the leading "3=" if it exists at the same time:
python3 /RPS_files/script_deployment/Official_Gitlab.py "${1#1=}" "${2#2=}" "${3#3=}"
I have a web interface built with Spring and I want to execute the command "python file.py" from it.
The main problem is that inside the file.py there is a pyomo model that is supposed to give some output. I can execute a python script if it's a simple print or something, but the pyomo model is completely ignored.
What could be the reason?
Here is the code I wrote in the controller to execute the call:
#PostMapping("/execute")
public void execute(#ModelAttribute("component") #Valid Component component, BindingResult result, Model model) {
Process process = null;
//System.out.println("starting!");
try {
process = Runtime.getRuntime().exec("python /home/chiara/Documents/GitHub/Pyomo/Solver/test/sample.py");
//System.out.println("here!");
} catch (Exception e) {
System.out.println("Exception Raised" + e.toString());
}
InputStream stdout = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout, StandardCharsets.UTF_8));
String line;
try {
while ((line = reader.readLine()) != null) {
System.out.println("stdout: " + line);
}
} catch (IOException e) {
System.out.println("Exception in reading output" + e.toString());
}
}
Update: I found that what I was missing was that I didn't check where the code run. So be sure to do so and eventually move the input files (if you have any) in the directory where python is executing, otherwise the script can't find them and elaborate them.
You can use
cwd = os.getcwd()
to check the current working directory of a process.
Another possibility is to redirect the stderr on the terminal or in a log file, because from the Server terminal you won't see anything even if there are errors.
The code posted in the question is the correct way to invoke a bash command from java.
I've got an After Effects Scripting question, but I'm not sure it will be resolved with AE knowledge, maybe more with standalone development.
I want to launch an external process from After Effects, actually I want to launch a render of the openned AEP file with the aerender.exe provided with After Effects while keeping it usable.
var projectFile = app.project.file;
var aeRender = "C:\\Program Files\\Adobe\\Adobe After Effects CC 2018\\Support Files\\aerender.exe";
var myCommand = "-project" + " " + projectFile.fsName;
system.callSystem("cmd /c \""+aeRender+"\"" + " " + myCommand);
So I wrote this simple JSX code and it works, it renders the scene render queue properly.
But After Effects is freezing, it waits for the end of the process.
I want it to stay usable.
So I tried to write a .cmd file and launch it with AE system.callSystem and I got the same problem,
I tried to go through an .exe file (compiled from a simple python with pyInstaller), same problem :
import sys
import subprocess
arg = sys.argv
pythonadress = arg[0]
aeRender = arg[1]
projectFileFSname = arg[2]
myCommand = "-project" + " " +projectFileFSname
callSystem = "cmd /c \""+aeRender +"\"" + " " + myCommand
subprocess.run(callSystem)
I even tried with "cmd /c start ", and it seems to be worse as After Effects continue freezing after the process is completed.
Is there a way to make AE believe the process is complete while it's actually not ?
Any help would be very apreciated !
system.callSystem() will freeze the script's execution so instead, you can dynamically create a .bat file and run it with .execute().
Here's a sample .js:
var path = {
"join": function ()
{
if (arguments.length === 0) return null;
var args = [];
for (var i = 0, iLen = arguments.length; i < iLen; i++)
{
args.push(arguments[i]);
}
return args.join(String($.os.toLowerCase().indexOf('win') > -1 ? '\\' : '/'));
}
};
if (app.project.file !== null && app.project.renderQueue.numItems > 0)
{
var
// aeRenderPath = path.join(new File($._ADBE_LIBS_CORE.getHostAppPathViaBridgeTalk()).parent.fsName, 'aerender.exe'), // works only in CC 2018 and earlier
aeRenderPath = path.join(new File(BridgeTalk.getAppPath(BridgeTalk.appName)).parent.fsName, 'aerender.exe'),
batFile = new File(path.join(new File($.fileName).parent.fsName, 'render.bat')),
batFileContent = [
'"' + aeRenderPath + '"',
"-project",
'"' + app.project.file.fsName + '"'
];
batFile.open('w', undefined, undefined);
batFile.encoding = 'UTF-8';
batFile.lineFeed = 'Unix';
batFile.write(batFileContent.join(' '));
batFile.close();
// system.callSystem('explorer ' + batFile.fsName);
batFile.execute();
$.sleep(1000); // Delay the script so that the .bat file can be executed before it's being deleted
batFile.remove();
}
You can, of course, develop it further and make it OSX compatible, add more features to it .etc, but this is the main idea.
Here's a list with all the aerender options (if you don't already know them): https://helpx.adobe.com/after-effects/using/automated-rendering-network-rendering.html
Btw, $._ADBE_LIBS_CORE.getHostAppPathViaBridgeTalk() will get you the "AfterFX.exe" file path so you can get the "aerender.exe" path easier this way.
EDIT: $._ADBE_LIBS_CORE was removed in CC2019 so you can use BridgeTalk directly instead for CC 2019 and above.
I have a node script :
//start.js
var spawn = require('child_process').spawn,
py = spawn('python', ['compute_input.py']),
data = [1,2,3,4,5,6,7,8,9],
dataString = '';
py.stdout.on('data', function(data){
dataString += data.toString();
});
py.stdout.on('end', function(){
console.log('Sum of numbers=',dataString);
});
py.stdin.write(JSON.stringify(data));
py.stdin.end();
and a python script :
## compute_input.py
import sys, json, numpy as np
#Read data from stdin
def read_in():
lines = sys.stdin.readlines()
#Since our input would only be having one line, parse our JSON data from that
return json.loads(lines[0])
def main():
#get our data as an array from read_in()
lines = read_in()
#create a numpy array
np_lines = np.array(lines)
#use numpys sum method to find sum of all elements in the array
lines_sum = np.sum(np_lines)
#return the sum to the output stream
print lines_sum
#start process
if __name__ == '__main__':
main()
These two scripts are in a folder my_folder/
If I'm inside my_folder and run the command node start.js, I get Sum of number=45, the scripts is working.
If I'm outside the folder and run the command node my_folder/start.js, I get Sum of number=, the script is not working.
Why ??
Most obvious reason: you are using a relative path for your python script so it's looked up in the current working directory. If you execute your node.js script from the same directory the python script is found, if you execute it from anywhere else (that doesn't happen to contain a compute_input.py file xD) then the python script is not found and the python call fails.
Use the absolute path instead and you should be fine (how you get the absolute path from your node.js script is left as an exercice)