I'm writing my first VSCode extension. In short, the extension opens a terminal (PowerShell) and executes a command:
term = vscode.window.activeTerminal;
term.sendText("ufs put C:\\\Users\\<userID>\\AppData\\Local\\Programs\\Python\\Python38-32\\Lib\\site-packages\\mymodule.py");
After selecting a Python environment, VSCode should know where Python is located (python.pythonPath). But the path to Python will obviously vary depending on the Python installation, version and so on. So I was hoping that I could do something like:
term.sendText("ufs put python.pythonPath\\Lib\\site-packages\\mymodule.py");
But how can I do this in my extension (TypeScript)? How do I refer to python.pythonPath?
My configuration:
Windows 10
Python 3.8.2
VSCode 1.43.2
Microsofts Python extension 2020.3.69010
Node.js 12.16.1
UPDATE:
Nathan, thank you for your comment. I used a child process as suggested. It executes a command to look for pip. Relying on the location of pip is not bullet proof, but it works for now:
var pwd = 'python.exe -m pip --version';
const cp = require('child_process');
cp.exec(pwd, (err, stdout, stderr) => {
console.log('Stdout: ' + stdout);
console.log('Stderr: ' + stderr);
if (err) {
console.log('error: ' + err);
}
});
Not sure where to go from here to process stdout, but I tried child_process.spawn using this accepted answer:
function getPath(cmd, callback) {
var spawn = require('child_process').spawn;
var command = spawn(cmd);
var result = '';
command.stdout.on('data', function (data) {
result += data.toString();
});
command.on('close', function (code) {
return callback(result);
});
}
let runCommand = vscode.commands.registerCommand('extension.getPath', function () {
var resultString = getPath("python.exe -m pip --version", function (result) { console.log(result) });
console.log(resultString);
});
Hopefully, this would give me stdout as a string. But the only thing I got was undefined. I'm way beyond my comfort zone now. Please advise me how to proceed.
Related
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'd like to run the following shell command 10 times
./file.py 1111x
with the 'x' ranging from 0 to 9
i.e. a different port for each .file.py file. I need each instance running in its own shell. I already tried creating a batch file and a python script that calls the windows shell but both without success.
What about this...
import os
import subprocess
for x in range(0,10):
command = './file.py 1111' + str(x)
os.system(command)
#or
subprocess.call('cmd ' + command, shell=True)
What you're looking for is a powershell job. You may need to tweak this a bit to accommodate your specific requirements, but this should do what you need.
[ScriptBlock]$PyBlock = {
param (
[int]$x,
[string]$pyfile
)
try {
[int]$Port = (11110 + $x)
python $pyfile $Port
}
catch {
Write-Error $_
}
}
try {
0..9 | ForEach-Object {
Start-Job -Name "PyJob $_" -ScriptBlock $PyBlock -ArgumentList #($_, 'path/to/file.py')
}
Get-Job | Wait-Job -Timeout <int>
#If you do not specify a timeout then it will wait indefinitely.
#If you use -Timeout then make sure it's long enough to accommodate the runtime of your script.
Get-Job | Receive-Job
}
catch {
throw $_
}
I cannot figure out how I can execute a python script from my C code. I read that I can embed the python code inside C, but I want simply to launch a python script as if I execute it from command line. I tried with the following code:
char * paramsList[] = {"/bin/bash", "-c", "/usr/bin/python", "/home/mypython.py",NULL};
pid_t pid1, pid2;
int status;
pid1 = fork();
if(pid1 == -1)
{
char err[]="First fork failed";
die(err,strerror(errno));
}
else if(pid1 == 0)
{
pid2 = fork();
if(pid2 == -1)
{
char err[]="Second fork failed";
die(err,strerror(errno));
}
else if(pid2 == 0)
{
int id = setsid();
if(id < 0)
{
char err[]="Failed to become a session leader while daemonising";
die(err,strerror(errno));
}
if (chdir("/") == -1)
{
char err[]="Failed to change working directory while daemonising";
die(err,strerror(errno));
}
umask(0);
execv("/bin/bash",paramsList); // python system call
}
else
{
exit(EXIT_SUCCESS);
}
}
else
{
waitpid(pid1, &status, 0);
}
I don't know where the error is since if I replace the call to python script with the call to another executable, it works well.
I have added at the beginning of my python script the line:
#!/usr/bin/python
What can I do?
Thank you in advance
From the Bash man page:
-c string If the -c option is present, then commands are read
from string. If there are arguments after the string,
they are assigned to the positional parameters,
starting with $0.
E.g.
$ bash -c 'echo x $0 $1 $2' foo bar baz
x foo bar baz
You, however don’t want to assign to the positional parameters, so change your paramList to
char * paramsList[] = { "/bin/bash", "-c",
"/usr/bin/python /home/mypython.py", NULL };
Using char * paramsList[] = {"/usr/bin/python", "/tmp/bla.py",NULL}; and execv("/usr/bin/python",paramsList); // python system call caused a successful invocation of the python script named bla.py
I have a simple Python script that asks for your name, then spits it back out:
def main():
print('Enter your name: ')
for line in sys.stdin:
print 'You entered: ' + line
Pretty simple stuff! When running this in the OS X Terminal, it works great:
$ python nameTest.py
Enter your name:
Craig^D
You entered: Craig
But, when attempting to run this process via an NSTask, the stdout only appears if additional flush() calls are added to the Python script.
This is how I have my NSTask and piping configured:
NSTask *_currentTask = [[NSTask alloc] init];
_currentTask.launchPath = #"/usr/bin/python";
_currentTask.arguments = [NSArray arrayWithObject:#"nameTest.py"];
NSPipe *pipe = [[NSPipe alloc] init];
_currentTask.standardOutput = pipe;
_currentTask.standardError = pipe;
dispatch_queue_t stdout_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
__block dispatch_block_t checkBlock;
checkBlock = ^{
NSData *readData = [[pipe fileHandleForReading] availableData];
NSString *consoleOutput = [[NSString alloc] initWithData:readData encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^{
[self.consoleView appendString:consoleOutput];
});
if ([_currentTask isRunning]) {
[NSThread sleepForTimeInterval:0.1];
checkBlock();
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
NSData *readData = [[pipe fileHandleForReading] readDataToEndOfFile];
NSString *consoleOutput = [[NSString alloc] initWithData:readData encoding:NSUTF8StringEncoding];
[self.consoleView appendString:consoleOutput];
});
}
};
dispatch_async(stdout_queue, checkBlock);
[_currentTask launch];
But when running the NSTask, this is how it appears (it is initially blank, but after entering my name and pressing CTRL+D, it finishes all at once):
Craig^DEnter your name:
You entered: Craig
So, my question is: How can I read the stdout from my NSTask without requiring the additional flush() statements in my Python script? Why does the Enter your name: prompt not appear immediately when run as an NSTask?
When Python sees that its standard output is a terminal, it arranges to automatically flush sys.stdout when the script reads from sys.stdin. When you run the script using NSTask, the script's standard output is a pipe, not a terminal.
UPDATE
There is a Python-specific solution to this. You can pass the -u flag to the Python interpreter (e.g. _currentTask.arguments = #[ #"-u", #"nameTest.py"];), which tells Python not to buffer standard input, standard output, or standard error at all. You can also set PYTHONUNBUFFERED=1 in the process's environment to achieve the same effect.
ORIGINAL
A more general solution that applies to any program uses what's called a “pseudo-terminal” (or, historically, a “pseudo-teletype”), which we shorten to just “pty”. (In fact, this is what the Terminal app itself does. It is a rare Mac that has a physical terminal or teletype connected to a serial port!)
Each pty is actually a pair of virtual devices: a slave device and a master device. The bytes you write to the master, you can read from the slave, and vice versa. So these devices are more like sockets (which are bidirectional) than like pipes (which are one-directional). In addition, a pty also let you set terminal I/O flags (or “termios”) that control whether the slave echoes its input, whether it passes on its input a line at a time or a character at a time, and more.
Anyway, you can open a master/slave pair easily with the openpty function. Here's a little category that you can use to make an NSTask object use the slave side for the task's standard input and output.
NSTask+PTY.h
#interface NSTask (PTY)
- (NSFileHandle *)masterSideOfPTYOrError:(NSError **)error;
#end
NSTask+PTY.m
#import "NSTask+PTY.h"
#import <util.h>
#implementation NSTask (PTY)
- (NSFileHandle *)masterSideOfPTYOrError:(NSError *__autoreleasing *)error {
int fdMaster, fdSlave;
int rc = openpty(&fdMaster, &fdSlave, NULL, NULL, NULL);
if (rc != 0) {
if (error) {
*error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
}
return NULL;
}
fcntl(fdMaster, F_SETFD, FD_CLOEXEC);
fcntl(fdSlave, F_SETFD, FD_CLOEXEC);
NSFileHandle *masterHandle = [[NSFileHandle alloc] initWithFileDescriptor:fdMaster closeOnDealloc:YES];
NSFileHandle *slaveHandle = [[NSFileHandle alloc] initWithFileDescriptor:fdSlave closeOnDealloc:YES];
self.standardInput = slaveHandle;
self.standardOutput = slaveHandle;
return masterHandle;
}
#end
You can use it like this:
NSTask *_currentTask = [[NSTask alloc] init];
_currentTask.launchPath = #"/usr/bin/python";
_currentTask.arguments = #[[[NSBundle mainBundle] pathForResource:#"nameTest" ofType:#"py"]];
NSError *error;
NSFileHandle *masterHandle = [_currentTask masterSideOfPTYOrError:&error];
if (!masterHandle) {
NSLog(#"error: could not set up PTY for task: %#", error);
return;
}
Then you can read from the task and write to the task using masterHandle.