How can I execute a python scripts after I launch Maya with subprocess
subprocess.Popen('C:/maya/maya.exe')
I need Maya with GUI. And the Python script I need to run is not static (and I don`t need to execute something all the time I run Maya), so I don’t think I can utilize userSetup.py.
I need to create/open asset/shot scenes from an external app, like Shotgun.
So gather all your data and pickle it in a temporary file. Then build the command for maya to unpickle that file, and pass that data to whatever function you want.
[Edit]
The biggest caveat is you have to make sure that whatever classes you have created are available using the same import statement both in and out of maya.
There’s a couple of ways around the nasty encoding issues that come with trying to run a commandline maya or mayapy.
Python support was bolted on after the fact so you have to indirect through mel. Rather than packing too much encoding into a command line (where it’s really easy to fatfinger something like a nested string encoding) one good trick is just to generate a mel file (temporary or permanent ) and pass that to the maya.
Keystone packs any arbitrary python you want into a mel file which can be run from the commandline. It’s designed for distributing an entire toolkit with multiple files, but it’d work for a single script if you wanted to do that.
If you have a simpler need to just pass some Python to maya without all the hassles, you can just steal the operative part of that like so:
import base64
import subprocess
def launch_maya(maya_path, python_script, *additional_args):
encoded_python = base64.b64encode(python_script.encode('utf-8'))
script_text = '''python("import base64; exec (base64.urlsafe_b64decode({}))")'''
args = [maya_path, '-c', script_text.format(encoded_python)]
args.extend(str(i) for i in additional_args)
subprocess.check_call(args)
launch_maya('c:/program files/autodesk/maya2018/bin/maya.exe', 'import sys; print("hello world " * int(sys.argv[-1]))', 7)
In this version python_script is the text of whatever python you want to run; you could just read it from a file if you wanted to.
If you pass additional arguments to the command line they show up in sys.argv so you can get at them (as in the example above) – they will all be strings, but you can pass any string you want.
If you plan on reusing the same script, you could convert the above function to generate a mel script that included the encoded python – that would make it double-clickable. That’s how Keystone works.
This problem seems to have different solutions depending on how the command is launched.
Is there a reliable way to do this?
This will work in cmd.exe but not with subprocess.Popen(command, shell=True):
maya.exe -hideConsole -command python(\"print('TEST')\")
RESULT using cmd.exe:
python("print('TEST')")
RESULT using subprocess:
python(print('TEST'))
This work subprocess but not in cmd.exe:
maya.exe -hideConsole -command python(\\\"print('TEST')\\\")
RESULT using cmd.exe:
python(\"print('TEST')\")
RESULT using subprocess:
python("print('TEST')")
Throwing my solution into the ring - I’ve found it easier to generate a new local command script and direct maya to it at startup, than to pass multi-line commands directly to the -command flag at startup:
scriptPath = r'C:my/local/scratch/script/path.py'
# source, compile, and execute script file
pythonCmd = fr"""with open(r'{scriptPath}', 'r') as f:exec(compile(f.read(), 'startupScript', 'exec'), {"{}"}, {"{}"})""" # horrible but it works
# begin maya process
newProcess = subprocess.Popen(
(mayaExePath,
"-command",
fr"""python("{pythonCmd}")""", # horrible but it works
"-noAutoloadPlugins"
),
shell=True,
)
In true software development style, this works and I’m never touching it again.
The separate script file also has the advantage of being easier to debug than a command that is only ever put together at the point of use.
It also lets us focus on the issue of formatting any arbitrary commands to a python script, rather than the additional constraint of that script passing gracefully through the MEL layer - if there are any kind of string or dict literals involved, I’ve found this to be a proper nightmare.