Python subprocess module causing crash + reopening

Python subprocess module causing crash + reopening

Anonymous
Not applicable
3,309 Views
10 Replies
Message 1 of 11

Python subprocess module causing crash + reopening

Anonymous
Not applicable

I'm making a call to subprocess in my addin script that reads: 

 

s2_out = subprocess.check_output([sys.executable, "fullscriptpath.py", "34"])

 

fullscriptpath.py is a basic script that reads:

 

import sys
def main(arg):
    print("Hello World"+arg)

if __name__ == "__main__":
    main(sys.argv[1])

Upon encountering the call to subprocess, however, Fusion freezes (I'm pretty sure as a crash, since it doesn't unfreeze if I wait) and spontaneously opens a second instance of Fusion. Is this a quirk of the subprocess module, or maybe sys.executable? 

0 Likes
3,310 Views
10 Replies
Replies (10)
Message 2 of 11

OceanHydroAU
Collaborator
Collaborator

Do you want it to run in the background (return immediately?)

 

If yes...

 

First, I discovered I need to have this so that it works on Mac and Windows:-

 

# cross-platform 'find our python'
def get_exec():
    try:
        for ppath in (sys.exec_prefix, sys.executable, *sys.path, sys.argv[0]):
            for subpath in (['bin'],['Python'],['']):
                for pyname in ('python.exe','python'): # pythonw prevents the unwanted black box popup - but fails to run due to env stuff
                    mypython=os.path.join(ppath,*subpath,pyname)
                    if os.path.isfile( mypython  return mypython
    except: pass

    try:
        if sys.platform.startswith('darwin'):
            return os.path.join(sys.exec_prefix,'bin','python')
        else:
            return os.path.join(sys.path[0],'Python','python')

    except: return 'python' # give up searching - hope that the OS knows how to fix it itself...

 

 

Then, you call like this:-

 

prog_folder= os.path.dirname(os.path.realpath(__file__)) 

try:
   r=subprocess.Popen([get_exec(),os.path.join(prog_folder,"script_to_run.py"), str(PARMS_FOR_SCRIPT) ])
except:
    eprint('subprocess failed {}'.format(traceback.format_exc()))

 

 

Note that this will pop up an ugly black box in Windows, and python "print" goes to this screen, but Mac users see nothing.

 

What are you trying to do?  If it's something to do with update checks, or asynchronous data transfers, I spent *weeks* on this problem, and ended up solving it with non-blocking sockets.

 

p.s. don't try threads - they fail, kinda obvious they would, but I gave it a go to be sure...

0 Likes
Message 3 of 11

Anonymous
Not applicable

This works to a degree, although the returned item is a NoneType instead of a string. I've tried using the full path and the path from the script folder, but neither work. Is there a specific home directory I should be using when specifying the path? 

0 Likes
Message 4 of 11

OceanHydroAU
Collaborator
Collaborator

Paths must be full, not relative.

 

Also, assorted things do not work if there is a space in the path or file name (if you are on mac, you can overcome that by putting what you need into /tmp/ )

 

I don't know what you mean by "works to a degree" - it work perfectly for me, and I've tested more than half a dozen different computers and operating system combinations to make certain.

 

If you give us every possible detail of what you're doing, we can easily help you.  If it is not confidential, pasting your code is usually the fastest, so we don't have to guess.

0 Likes
Message 5 of 11

Anonymous
Not applicable

I'm currently using the hardcoded full path (run by right click -> copy full path), in the function provided above. It returns bytes now since I've added to the "stdin" and "stdout" fields, but the bytes decode as blanks. Before it would only return NoneTypes. My code looks like this: 

 

 

 

 

#!/usr/bin/env python

import adsk.core, adsk.fusion, adsk.cam, traceback
import threading, random, json, os, sys, subprocess

app = None
ui = adsk.core.UserInterface.cast(None)

# cross-platform 'find our python'
def get_exec():
    try:
        for ppath in (sys.exec_prefix, sys.executable, *sys.path, sys.argv[0]):
            for subpath in (['bin'],['Python'],['']):
                for pyname in ('python.exe','python'): # pythonw prevents the unwanted black box popup - but fails to run due to env stuff
                    mypython=os.path.join(ppath,*subpath,pyname)
                    if os.path.isfile( mypython ):
                          return mypython
    except: pass

    try:
        if sys.platform.startswith('darwin'):
            return os.path.join(sys.exec_prefix,'bin','python')
        else:
            return os.path.join(sys.path[0],'Python','python')

    except: return 'python' # give up searching - hope that the OS knows how to fix it itself...

def run_file(fname,params):
    prog_folder= os.path.dirname(os.path.realpath(fname))

    # fullpath = os.path.realpath(fname)

    if ui:
        ui.messageBox("Path: " + os.path.join(prog_folder,fname))

    try:
       p = subprocess.Popen([get_exec(),"fullpath", str(params) ]
       , shell = True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)


       pr, err = p.communicate()
       r = pr.decode('ascii')

       if ui:
           ui.messageBox('Output: ' + r)
           ui.messageBox('Potential Error: ' + err)
       return r
    except:
        if ui:
            ui.messageBox('subprocess failed {}'.format(traceback.format_exc()))
        return "failure"

def run(context):
    global ui
    global app
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        if ui:
            ui.messageBox('Before Script Call')
        s2_out = run_file('samplereturn.py',"")
        if ui:
            ui.messageBox('After Script Call ' + s2_out)

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


def stop(context):
    try:
        # os.system("./Dependencies/justdosomething.sh")
        ui.messageBox('Stop addin')
        # s2_out = subprocess.check_output([sys.executable, "fullpath", "34"])

        # run_file('Dependencies/samplereturn.py')
        # if ui:
        #     ui.messageBox('After Script Call ' + s2_out)
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

 

0 Likes
Message 6 of 11

OceanHydroAU
Collaborator
Collaborator

My example was for a background process

 

Do you want it to run in the background (return immediately?).  If yes... "

 

You will need to change the Popen bit to something else if you want to wait for the output, otherwise, there is no output, because the function returns immediately before the subprocess has made any yet...

 

I cannot think why you would want to do that - you are already running in python, why do you want to shell out to something in python and wait for it?  You should get whatever that is, and either make it a module so you can call it, or just bring in into you main code ?

0 Likes
Message 7 of 11

Anonymous
Not applicable

This was more of a proof-of-concept to prove that an addin could do this for other existing scripts, that do more than just returning "hello world". I'm now seeing that it would probably be more convenient to just turn the external script into a module and have it run as part of the add-in, though. So thank you. 

0 Likes
Message 8 of 11

Anonymous
Not applicable

The other reason why I wanted to run the second script outside of the Fusion addin was so that I could activate a python virtual environment that has the necessary imports outside of the Fusion script, since I was having trouble getting modules to work within it. How does one work with external imports/custom modules in Fusion python scripting? Right now I'm running a simple "import" at the top of my Fusion script that imports the sample script given above as a module, but this results in the addin not running at all. 

Message 9 of 11

ryansmith_
Contributor
Contributor

I see this is an old post but have been trying the same. I couldn't figure out how to get the pyserial module to work properly locally in my Add-In, so I use "subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'pyserial'])". This only works in a Script and I can't seem to get it to work in any part of an Add-In

0 Likes
Message 10 of 11

Jorge_Jaramillo
Collaborator
Collaborator

@Jorge_Jaramillo - this post is being edited to remove PII.

 

Hi,

In the message #5 on this topic https://forums.autodesk.com/t5/fusion-360-api-and-scripts/import-data-from-excel-type-xlsx/td-p/1124... I posted a step-by-step instruction on how to import external python modules when working inside Fusion 360.
Since future September release this will change since Pyhton ver3.10 will be deployed with Fusion 360.

Regards,
Jorge Jaramillo

Message 11 of 11

ryansmith_
Contributor
Contributor

Hi @Jorge_Jaramillo thanks for the quick view and reply. I did see that thread and post and that method is on my list to try. I'm hoping to execute everything from within the Add-In, including any package installs. I also posted here in #13 about my current method that only works in a Script. I'm hoping to reduce the number of things a user (or me) has to do to keep the Add-In working.

0 Likes