1

I'm building a desktop application using node.js,electron.js,Vue.js and Python. I'm im using python to do some printing. I'm using the python-shell npm library (https://www.npmjs.com/package/python-shell). From python I'm using the pywin32 and sys libraries to print and to get the arguments i pass from JavaScript code. In my JavaScript code I have done as below and my output has more than 4000 characters. This the string i want to get printed. but not all the lines are getting printed. Is there a character limit when passing the values? If so how can i know the limit and can someone suggest me a better path to do this

const PythonShell = require("python-shell");

var options = {          
        pythonOptions: ['-u'],
        scriptPath: __dirname,
        args: [printerName, output, imgPath]
};

PythonShell.run("../../../../extras/script/printer.py", options, function (err, results) {
    if (err) throw err;
    for(var k in results){
        console.log(results[k])
    }              
});
E.D
  • 78
  • 7
  • Command-line limits are operating system specific, since you are using Windows, see https://stackoverflow.com/questions/2966981/what-is-the-maximum-length-of-a-windows-command-line-cmd-exe-command. See also the accepted answer to https://superuser.com/questions/1070272/why-does-windows-have-a-limit-on-environment-variables-at-all – cdarke Jul 05 '18 at 18:13
  • Thank you for the support. I see there is a limit in windows command line arguments but is there another way to send such a long string to a python script using java script? – E.D Jul 05 '18 at 18:25
  • Use standard streams (`stdin`/`stdout`) and pass using a pipe, anonymous or named depending on the relationship between processes. – cdarke Jul 05 '18 at 22:01

2 Answers2

3

The command line has a maximum size, which depends on your OS (and possibly your settings).1 That command line has to be enough to fit the name of the Python interpreter, and your script path, and the -u, and all of your (properly quoted) args. It's probably 4096 on your system, so when you have around 4000 characters worth of arguments, things start failing.

There are two general solutions to this.


First, if the script uses arguments by looping over them and handing them independently, as many Unix tools do, you can call it repeatedly with, say, 50 arguments at a time. This is basically the same trick xargs uses for huge argument lists.


Second, you can change your script to read its input from a file, or from standard input, instead of from command-line arguments.

You haven't shown us your code, but let's say it looked like this:

import sys
for arg in sys.argv[1:]:
    print('arg:', arg)

You could change it to this:

import sys
for line in sys.stdin:
    print('arg:', line, end='')

But now you have to change the calling side. I don't know PythonShell, and my JS is pretty rusty, but from a quick look at the docs examples, it seems like it should look something like this:

var options = {          
        pythonOptions: ['-u'],
        scriptPath: 'myscript.py',
};

myscript = new PythonShell(options);
var results = [];
myscript.on('message', function(message)) {
    results += [message];
}
for (var i in args) {
    myscript.send(args[i] + '\n');
}
myscript.end(function(err, code, signal) {    
    if (err) throw err;
    for (var k in results){
        console.log(results[k])
    }              
});

Using a file instead of stdin is pretty similar. It would look something like this:

import sys
filename = sys.argv[1]
with open(filename) as f:
    for line in f:
        print('arg:', line, end='')

And then calling it is closer to your original code:

tempFile = new IDontRememberHowToSecurelyMakeATempFileInNode();
for (var i in args) {
    tempFile.write(args[i] + '\n');
}

var options = {          
    pythonOptions: ['-u'],
    scriptPath: __dirname,
    args: [tempFile.howeverYouGetTheFileName()]
};

PythonShell.run("../../../../extras/script/printer.py", options, function (err, results) {
    if (err) throw err;
    for(var k in results){
        console.log(results[k])
    }              
});

1. On Windows, it's more complicated than this. In some versions, the command-line arguments and set of environment variables together have to be under another limit, and the limits may be different for UTF-16 vs. 8-bit inputs, and there are additional limits if you're using the MSVCRT functions that emulate POSIX-style APIs instead of calling the Windows APIs directly, etc.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • I used the write to file method. but I still have the same issue. below are some parts of my python code. i checked the temporary file it has the complete string i need to print. but in the printer it stops at some point. `printer_name = sys.argv[1] filename = sys.argv[2] image_path = sys.argv[3] pointSize = int(sys.argv[4]) bill_format = "" with open(filename) as f: for line in f: bill_format += line` – E.D Jul 06 '18 at 09:51
  • `hDC = win32ui.CreateDC () hDC.CreatePrinterDC (printer_name) printable_area = hDC.GetDeviceCaps (HORZRES), hDC.GetDeviceCaps (VERTRES) printer_size = hDC.GetDeviceCaps (PHYSICALWIDTH), hDC.GetDeviceCaps (PHYSICALHEIGHT) printer_margins = hDC.GetDeviceCaps (PHYSICALOFFSETX), hDC.GetDeviceCaps (PHYSICALOFFSETY)` hDC.StartDoc (image_path) hDC.StartPage () Y = 20 if image_path != " " : x1 = int(((printer_size[0]-printer_margins[0]/2) - logo_width) / 2) x2 = int (x1 + logo_width) ` – E.D Jul 06 '18 at 09:53
  • ` y1 = int (printer_size[1] / 150) y2 = int (y1 + logo_height) dib.draw (hDC.GetHandleOutput (), (x1, y1, x2, y2)) Y = y2 + Y hDC.SetMapMode(win32con.MM_TEXT) for line in (bill_format).split("\n"): hDC.TextOut((printer_margins[0] + line_space),Y,line) Y += int (line_space * 1.25) hDC.EndPage () hDC.EndDoc () hDC.DeleteDC ()` – E.D Jul 06 '18 at 09:53
0

As you can see in python-shell documentation you can use pyshell.send() to send data to python stdin - that would not have the limitation.

nosklo
  • 217,122
  • 57
  • 293
  • 297