-
-
Save theodox/2c712a91155c7e1c4c15 to your computer and use it in GitHub Desktop.
''' | |
Exposes the MayaPyManager class, which is used to run instances of MayaPy with explict control over paths and environment variables. A Manager can run scripts, modules, or command strings in a separate MayaPy environment; results and errors are captured and returned. | |
Typical uses might be: | |
- running unit tests | |
- running a copy of Maya.standalone as a headless RPC server with StandaloneRPC https://github.com/theodox/standaloneRPC | |
- spawning multipe copies of maya to batch process files in parallel on a multi-core machine | |
- do any of the above on multiple maya versions concurrently | |
Basic usage is simply to create a MayaPyManager and then call run_script, run_module or run_command: | |
example = MayaPyManager( '/path/to/Maya2014/bin/mayapy.exe', None) | |
output, errors = example.run_command("print 'hello world'") | |
print output | |
> hello world | |
To control the PYTHONPATH of the created instance, pass the paths you want as a string array to the MayaPyManager. These paths will override the default system paths inside the interpreter. | |
example = MayaPyManager( '/path/to/Maya2014/bin/mayapy.exe', None, 'path/to/modules', 'another/path/to/modules', 'etc/etc') | |
You can also control the environment variables in the interpreter by providing a dictionary: | |
custom_env = os.environ.copy() | |
custom_env['MAYA_DEBUG'] = 'False' | |
custom_env['P4_CLIENT'] = 'test_client' | |
example = MayaPyManager( '/path/to/Maya2014/bin/mayapy.exe', custom_env) | |
PYTHONHOME and PYTHONPATH will be set automatically, so don't bother changing them yourself. | |
Copyright (c) 2014 Steve Theodore | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
THE SOFTWARE. | |
''' | |
import os | |
import subprocess | |
class MayaPyManager(object): | |
''' | |
For running a maya python interpreter* under controlled conditions: | |
- Override default paths | |
- Overrides environment variables | |
- Disable userSetup.py | |
* Technically this _should_ work for any Python installation, although non-maya | |
versions may not be able to find their standard libraries if these are | |
installed in non-standard locations. | |
See https://docs.python.org/2/tutorial/interpreter.html for more details on interpeter flags | |
Here are the supported flags | |
-B : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x | |
-d : debug output from parser; also PYTHONDEBUG=x | |
-E : ignore PYTHON* environment variables (such as PYTHONPATH) | |
-O : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x | |
-OO : remove doc-strings in addition to the -O optimizations | |
-Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew | |
-s : don't add user site directory to sys.path; also PYTHONNOUSERSITE | |
-S : don't imply 'import site' on initialization | |
-t : issue warnings about inconsistent tab usage (-tt: issue errors) | |
-v : verbose (trace import statements); also PYTHONVERBOSE=x | |
can be supplied multiple times to increase verbosity | |
-W arg : warning control; arg is action:message:category:module:lineno | |
''' | |
DEFAULT_FLAGS = [] | |
def __init__(self, interpreter, environ, *paths, **flags): | |
''' | |
Create a MayaPyManager for ths supplied interpreter and paths | |
Arguments: | |
- intepreter is a disk path to a maya python interpreter | |
- environ is a dictionary of environment variables. | |
If no dictionary is provided, intepreter will use os.environ. | |
- paths is an array of strings. It will completely replace | |
existing PYTHONPATH variables | |
Any flag set to True will be used by the intepreter, eg | |
MayaPyManager('path/to/mayapy.exe', None, 'path', v=True) | |
will produce a command line like: | |
path/to/mayapy.exe -v <script.py> | |
when run. | |
''' | |
self.interpreter = interpreter | |
assert os.path.isfile(interpreter) and os.path.exists(interpreter) , "'%s' is not a valid interpreter path" % interpreter | |
self.paths = paths | |
self.flags = flags | |
self.environ = environ | |
def run_script(self, pyFile, *args): | |
''' | |
Run the supplied script file in the interpreter. Returns a tuple (results, errors) which contain, | |
respectively, the output and error printouts produced by the script. Note that if errors is not | |
None, the script did not complete successfully | |
Arguments will be passed to the script (NOT to the python interpreter). Thus | |
someMayaPyMgr.run_script('test/script.py', "hello", "world") | |
will be produce a command line like: | |
c:/path/to/maya2014/mayapy.exe test/script.py hello world | |
If the script expects flag -type argument, its up to the user to provide them in the right form: | |
someMayaPyMgr.run_script('test/script.py', "-g", "greeting") | |
will be produce a command line like: | |
c:/path/to/maya2014/mayapy.exe test/script.py -g greeting | |
''' | |
rt_env = self._runtime_environment(self.paths) | |
arg_string = "" | |
if len(args): | |
arg_string = " ".join(map(str, *args)) | |
flagstring = self._flag_string() | |
cmdstring = '''"%s" %s "%s" %s''' % (self.interpreter, flagstring, pyFile, arg_string) | |
runner = subprocess.Popen(cmdstring, env = rt_env, | |
stdout = subprocess.PIPE, | |
stderr = subprocess.PIPE) | |
return runner.communicate() | |
def run_module(self, module): | |
''' | |
Run the supplied moudle file in the interpreter ('running' here is 'importing'). | |
Returns a tuple (results, errors) which contain, respectively, the output and | |
error printouts produced by the module. Note that if errors is not None, the | |
module did not import correctly | |
The module must in the PYTHONPATH used by the intepreter. | |
''' | |
rt_env = self._runtime_environment(self.paths) | |
flagstring = self._flag_string() | |
cmdstring = '''"%s" %s -m %s''' % (self.interpreter, flagstring, module) | |
runner = subprocess.Popen(cmdstring, env = rt_env, | |
stdout = subprocess.PIPE, | |
stderr = subprocess.PIPE) | |
return runner.communicate() | |
def run_command (self, cmd,): | |
''' | |
Run the supplied command string in the intepreter. | |
Returns a tuple (results, errors) which contain, respectively, the output and | |
error printouts produced by the command string. Note that if errors is not None, | |
the commands did not execute correctly. | |
''' | |
rt_env = self._runtime_environment(self.paths) | |
flagstring = self._flag_string() | |
cmdstring = '''"%s" %s -c "%s"''' % (self.interpreter, flagstring, cmd) | |
runner = subprocess.Popen(cmdstring, env = rt_env, | |
stdout = subprocess.PIPE, | |
stderr = subprocess.PIPE) | |
return runner.communicate() | |
def _flag_string(self): | |
''' | |
generate correctly formatted flag strings | |
''' | |
flagged = lambda x, y : "-" + x | |
flaggedOpt = lambda x, y: "-" + x + " " + str(y) | |
default_flags = [flagged(f, v) for f, v in self.flags.items() if v and not f in ('W', 'Q')] or [''] | |
special_flags = [flaggedOpt(f, v) for f, v in self.flags.items() if v and f in ('W', 'Q')] or [''] | |
return " ".join(default_flags + special_flags) | |
def _runtime_environment(self, *new_paths): | |
''' | |
Returns a new environment dictionary for this intepreter, with only the supplied paths | |
(and the required maya paths). Dictionary is independent of machine level settings; | |
non maya/python related values are preserved. | |
''' | |
runtime_env = os.environ.copy() | |
if self.environ: | |
runtime_env = self.environ.copy() | |
new_paths = list(self.paths) | |
quoted = lambda x : '%s' % os.path.normpath(x) | |
# set both of these to make sure maya auto-configures | |
# it's own libs correctly | |
runtime_env['MAYA_LOCATION'] = os.path.dirname(self.interpreter) | |
runtime_env['PYTHONHOME'] = os.path.dirname(self.interpreter) | |
# use PYTHONPATH in preference to PATH | |
runtime_env['PYTHONPATH'] = ";".join(map(quoted, new_paths)) | |
runtime_env['PATH'] = '' | |
return runtime_env |
fixed stupid cut-and-paste bug in paths
Take a look at line 114, I think you forgot to add the interpreter variable to the string substitution.
- assert os.path.isfile(interpreter) and os.path.exists(interpreter) , "'%s' is not a valid interpreter path"
+ assert os.path.isfile(interpreter) and os.path.exists(interpreter) , "'%s' is not a valid interpreter path" % interpreter
I always getting this error.
File "C:\Python27\lib\subprocess.py", line 672, in init
errread, errwrite)
File "C:\Python27\lib\subprocess.py", line 882, in _execute_child
startupinfo)
TypeError: environment can only contain strings
when trying:
self.le_textOut.setText('...initializing Maya scene, please wait')
custom_env = os.environ.copy()
custom_env['MAYA_LOCATION'.encode("utf-8")] = "C:\Program Files\Autodesk\Maya2015".encode("utf-8")
pmng = MayaPyManager( r'C:/Program Files/Autodesk/Maya2015/bin/mayapy.exe',
custom_env
)
self.le_textOut.append (str(pmng))
pmng.run_module( "maya.standalone" )
self.le_textOut.append ( str(pmng.run_command( 'maya.standalone.initialize(name="python")' ) ))
self.le_textOut.append ( str(pmng.run_command( 'cmds.file(%s, force=True,open=True)'%path ) ))
self.le_textOut.append ( str(pmng.run_command( 'cams=cmds.ls(type="camera")' ) ))
no idea if that's correct, maybe someone can point me out. I tried literally everything to get the subprocess to spawn a mayapy and then to try to load maya standalone in there and do various commands.
basically everything failed.
It looks like you're trying to use the manager interactively; that's not really what it's for. It's designed to start mayapy with pre-selected set of arguments and environment variables:, To run Maya interactively from outside you'd need to use the commandport or something like standaloneRPC
Unless you're trying to explcitly override MAYA_LOCATION in this standalone you should not need to do anything explicity, since the default behaviour is just to inherit the current environment.,
Here's the simplest example:
make a python file like this:
import maya.standalone
maya.standalone.initialize()
print "started"
import maya.cmds as cmds
print "LS", cmds.ls()
and save it as test.py.
To call it you'd do
test_mp = mm.MayaPyManager("C:/program files/Autodesk/maya2016/bin/mayapy.exe", None, '')
results, errors = test_mp.run_script('test.py')
results
should be something like
started
LS [u'time1', u'sequenceManager1', u'hardwareRenderingGlobals', u'renderPartition', u'renderGlobalsList1', u'defaultLightList1', u'defaultShaderList1', u'postProcessList1', u'defaultRenderUtilityList1', u'defaultRenderingList1', u'lightList1', u'defaultTextureList1', u'lambert1', u'particleCloud1', u'initialShadingGroup', u'initialParticleSE', u'initialMaterialInfo', u'shaderGlow1', u'dof1', u'defaultRenderGlobals', u'defaultRenderQuality', u'defaultResolution', u'defaultLightSet', u'defaultObjectSet', u'defaultViewColorManager', u'defaultColorMgtGlobals', u'hardwareRenderGlobals', u'characterPartition', u'defaultHardwareRenderGlobals', u'lightLinker1', u'persp', u'perspShape', u'top', u'topShape', u'front', u'frontShape', u'side', u'sideShape', u'hyperGraphInfo', u'hyperGraphLayout', u'globalCacheControl', u'brush1', u'strokeGlobals', u'ikSystem', u'layerManager', u'defaultLayer', u'renderLayerManager', u'defaultRenderLayer']
Remember, though, that the manager runs a script once -- it's not interactive
added quotes to run_command - for some reason this behavior varies for maya 2014 and maya 2011