Call extern software at python console in Pcbnew

I am trying to call a external software (in my Pcbnew script / testing at python console).
I tried subprocess.Popen, subprocess.call and other methods that work at the system terminal with Python loaded but I am always getting some errors like:

Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/usr/lib/python2.7/subprocess.py", line 523, in call
    return Popen(*popenargs, **kwargs).wait()
File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
    raise child_exception

How properly call it?

I think you didn’t include the last few lines of the stack trace that show the actual message.

Can you also post your code? Are you trying to launch a subprocess or execute external program/file without caring about it’s output/return value?

1 Like

Exactly. I am trying to create an easy way to the users call KiCost from the Pcbnew (passing the XML)

Basically, KiCost is installed at Python3 and the issues that made me execute as external software is:

  1. KiCost need few Python libraries and keeping it external made possible use to other EDAs;
  2. KiCad python is integrated with Linux but use Py2 in Ubuntu;
  3. KiCad use the own Python at Windows.

So, I want just create a icon that launch my application as other OS process.

The problem is the way you are passing parameters to python3

Each argument of the called process needs to be passed as separate argument to call() see example here
https://docs.python.org/2/library/subprocess.html#using-the-subprocess-module

Update: oh wait, you are passing shell=True as well so in theory it should parse spaces. But don’t do that, pass arguments separately instead and don’t use shell.

1 Like

subprocess.call(['kicost','--guide',''], shell=True) works at OS terminal but return 1 in console script. Same as the code subprocess.call(['kicost --guide ""'], shell=True) (single parameter input).

I can reduce the test to single subprocess.call('kicost') and have the same effect.

What is ‘kicost’ ? Is it an executable or symlink to one? If it works in OS terminal but not from kicad scripting console that is likely because of PATH differences. python launched standalone can find ‘kicost’ in it’s path directories but not from kicad. Inspect sys.path list for both cases.

1 Like

It is a python (Py3) module (with the __main.py__ file to direct access, I can call just using kicost at OS terminal).
I checked import sys \n sys.path. All path in the OS python2 are visible at KiCad python console (and few more).

Does this work? subprocess.call(['python3', '-m', 'kicost','--guide',''], shell=True)

subprocess.call(['python3', '-m', 'kicost','--guide',''], shell=True) in special is having problem to parse even in the OS terminal. It made the terminal just execute and still on Python3 terminal (executing on Py2, the terminal goes to Py3).

But

 subprocess.call(['python3 -m kicost --guide ""'], shell=True)

works at OS terminal and result in the same error at KiCad console (a exit 1 code).

I see, it’s the shell parameter that is messing arguments up. I just tested this in python2 and it worked
subprocess.call(['python3', '-V'])
It printed python3 version.
But it waits for process to complete anyway which not what you need.

To launch detached process try subprocess.Popen with creationflags=DETACHED_PROCESS

For a simpler solution use platform specific launcher process like start and xdg-open. See

1 Like

I am using Linux/Ubuntu here. xdg-open is a command to open a file with the associated software, not to run a command.

Ah, right, I thought it would open executables as well. Well, in that case Popen is your friend.

Same:

 >> os.system('kicost')
 256 # that is ERROR 1
>> subprocess.Popen(['kicost'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
<subprocess.Popen object at 0x7f8f02868c90> # But the KiCost software is not loaded

kicost is not an executable. You need to run python3 -m kicost <kicost_params_here>.

subprocess.Popen(['python3 -m kicost'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

subprocess.Popen(['python3', '-m', 'kicost'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
<subprocess.Popen object at 0x7f8f02868f50> # and nothing

I can specify the path to the kicost module, but I get the same error.

Popen(['python3 -m kicost']
this doesnt work because that will look for executable literally named ‘python3 -m kicost’, this is the correct form
subprocess.Popen(['python3', '-m', 'kicost'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

I just tried this and this works. Both from OS terminal and from pcbnew console.

1 Like

The strange is that, from KiCad console:

>>> import subprocess as subp
>>> proc = subp.Popen(["python3", "-m", "kicost", "--version"], stdout=subp.PIPE, stdin=subp.PIPE)
>>> proc.stdout.readlines()
[]

Return no error, but also no command output. From OS terminal I get, from same commands:

['KiCost v.1.0.5 on Linux-4.15.0-48-generic-x86_64-with-Ubuntu-16.04-xenial(64bit) Python 3.5.2\n']

That works in both cases for me. You need to wait a few milliseconds before proc.stdout.readlines(). If you pasted all commands at once the process may not have initialized yet or pipe buffer may not have cleared.

1 Like

Yes, I waited (and type one by time). Even, trying proc.comunicate() first reach, second time returns “ValueError: I/O operation on closed file”.

proc = subp.Popen(["python3", "--version"], stdout=subp.PIPE, stdin=subp.PIPE) made me read the Python 3 version by proc.stdout.readlines().

So, have to be something related with my module.

But you did resolve your initial issue, right?