2

I am working on web game which uses python and javascript. I wanted to write a python script that automatically builds my js files using browserify. In order to run browserify, I need to use the local installation of it in node_modules/.bin. I read that the command npm bin will give me the location of npm binaries so I wanted to use it in my script.

I tried to do

import subprocess

c = subprocess.run(("npm", "bin"), capture_output=True)

but that gave me an error:

FileNotFoundError: [WinError 2] The system cannot find the file specified

After searching a bit, I found that using shell=True fixes this:

import subprocess

c = subprocess.run(("npm", "bin"), capture_output=True, shell=True)

However, after reading this question I realized that using shell=True isn't a good practice and it can lead to security issues.

So my question is, how can I run an external command using python without the risk of shell=True?

I am probably going to face the same problem when I try to run browserify through python (haven't tried it yet), so I should ask this question now.

Thanks in advance.

Shai Avr
  • 942
  • 8
  • 19
  • To my mind, the error message implies it can't find `npm`, so I would try using the full path to `npm` in your call. – Mark Setchell Jul 24 '19 at 10:44
  • But how can it be portable? I don't want to hardcode the path to `npm`, especially because it would break between different platforms. – Shai Avr Jul 24 '19 at 10:51
  • Firstly, does it work? – Mark Setchell Jul 24 '19 at 10:52
  • Yes. Just tried it. The weird thing is that it only works if I do `\npm.cmd` but not if I do `\npm` (I am on windows). – Shai Avr Jul 24 '19 at 10:59
  • So, if you use `shell=True`, a shell is started and the shell has a default PATH that knows where the `npm` binary is located. If you don't use `shell=True`, you will need to work out or know or check where `npm` is installed and prefix that to the command, or do something similar. – Mark Setchell Jul 24 '19 at 11:04
  • @MarkSetchell, ...so I assume Windows does something different from the UNIX model where the shell just inherits the PATH with no changes? – Charles Duffy Jul 24 '19 at 11:10
  • @ShaiAvr, since I don't know Windows this is a guess, but does `npm.cmd` behave any differently? – Charles Duffy Jul 24 '19 at 11:11
  • @CharlesDuffy Sorry Charles, I try to avoid Windows at all costs! I know in Windows **Control Panel** there is a **System** icon and if you click on that you can set the **PATH** variable and change it. If you run a Python subprocess with `shell=True` I assume it loads that **PATH** and can find `npm` from it - just like Unix would. But if you run a subprocess without `shell=True`, I presume it starts a shell with no **PATH** and therefore cannot find `npm`. I may be wrong - I often am! :-) – Mark Setchell Jul 24 '19 at 11:15
  • 1
    @ShaiAvr, also, note that Windows is different enough that most of the argument about why `shell=False` is safer is... more ambiguous there. In the UNIX world (the civilized world, I'd call it), the program invoking an `execve()` syscall can provide the called a precise argument vector; in Windows, the child process gets a string and is responsible for parsing it itself; that `main()` on Windows takes argv and argc arguments is a consequence of the libc having a default implementation of that parser, but programs aren't required to use it. – Charles Duffy Jul 24 '19 at 11:16
  • @MarkSetchell, on UNIX, noninteractive, non-login shells (like those invoked with `shell=True`) do not load dotfiles (unless `ENV` is set to a filename), so they keep the parent process's PATH. – Charles Duffy Jul 24 '19 at 11:17
  • @CharlesDuffy Agreed, but I don't know if Windows subprocesses inherit the parent's PATH, or they get started with an empty PATH or a default PATH. – Mark Setchell Jul 24 '19 at 11:20
  • Agreed, and I don't know that detail either, which is why I asked if you did -- makes a difference as to whether the suggestion above will work. I thought since you were making the assertion, you might have special knowledge. – Charles Duffy Jul 24 '19 at 11:21
  • Yes, they do inherit PATH. For example, if you start your cmd.exe from an open explorer.exe via "open cmd window here", it will always get the old PATH that the explorer.exe had obtained when it started. Whereas a fresh "cmd.exe" run from startmenu will see changes to PATH that you've done in the System menu in the meantime, without rebooting (or killing all explorer.exe instances). That might also be exactly the problem here. Did you try & modify PATH to make `npm` available? – Jeronimo Jul 24 '19 at 11:40
  • @MarkSetchell Is there a way to locate the path to the `npm` binary? As I said before, I don't want to hardcode it. – Shai Avr Jul 24 '19 at 13:09
  • I am not that good on Windows, but try `WHERE npm` in a Command Prompt and that may tell you where `npm` is, then you could call that via Python's `subprocess` ... though Charles' suggestion was that it is not so bad to use `shell=True` on Windows. – Mark Setchell Jul 24 '19 at 13:25
  • @MarkSetchell That might work, but I need something cross platform. I am currently developing the project on my Windows machine, but if I want to deploy it, I will probably deploy to a linux server or heroku, so I'd like to not rely on platform specific commands, which is why I am writing this script in python instead of cmd, powershell or bash. – Shai Avr Jul 24 '19 at 17:49

1 Answers1

0

According to this post https://stackoverflow.com/a/89243/11576550

You can use subprocess.run(["ls", "-l"])

So try subprocess.run(["npm", "bin"])

Haven't tried it yet

Nikko Bobier
  • 100
  • 11