Detect if I am running from maya?

maya

#1

Is there preferred method, or method at all, for detecting if I am running (python) from Maya?

Basically, I am working on a template for a generic PyQT window class, that will:

  1. run and load in vanilla python (2.6) create a QApplication, load a .ui file, make a toolWindow object attached to the app
    *I know how to setup and load a generic PyQT object, etc.

or

  1. If I am running from Maya, will get the mainWindow, and create my toolWindow and parent it to mainWindow
    *I know how to do all this PyQT setup and stuff working with maya

I know what code I basically need to portion off into say a maya based class method, stuff like:

#Create/Reload window instance in maya
if cmds.window(self.mainWindowName, exists=True ):
[INDENT]cmds.deleteUI(self.mainWindowName, window=True )[/INDENT]
pass

I also need to control weather or not I am doing maya imports i.e. ‘import maya.cmds as cmds’.

I just can’t find a great solution, my fallback is maybe something like:
global gMaya
gMaya = False

… and then carry that scope through the classes, but override with =True in some sort of wrapper that maya calls.

Anyone have thoughts?


#2

cheap way to find out if you’re in maya:


import sys

def is_interactive_maya():
    return type (sys.stdout) != file

I’d strongly recommend against if testing all over your code!! Break it up into maya-specific, non-maya-specific, and common sections. If the Maya and non-Maya versions have identical signatures you can swap them in at load time and the rest of the code doesn’t care. ‘If’ statements are always evil.


#3

BTW that test can be confused if you’ve taken over sys.stdout yourself :slight_smile: You can also look to see of the word “Autodesk” shows up in sys.path…


#4

Wouldn’t something like this do the trick for a one-time check?

try:
    import maya.cmds as cmds
except ImportError:
    # no maya
else:
    # If for some reason you had the maya module in your sys.path
    try:
        if cmds.about(batch=True):
            # in batch mode
    except AttributeError:
        # cmds module isn't fully loaded/populated (which only happens in batch, maya.standalone, or maya GUI)
        # no maya

#5

[QUOTE=Theodox;21685]cheap way to find out if you’re in maya:


import sys

def is_interactive_maya():
    return type (sys.stdout) != file

I’d strongly recommend against if testing all over your code!! Break it up into maya-specific, non-maya-specific, and common sections. If the Maya and non-Maya versions have identical signatures you can swap them in at load time and the rest of the code doesn’t care. ‘If’ statements are always evil.[/QUOTE]

That’s a good idea too, I’ll try it out. I was thinking I would just have a maya-only setup Class.method(), which would only get called in the init if I detected that I was running in maya.


#6

[QUOTE=capper;21687]Wouldn’t something like this do the trick for a one-time check?

try:
    import maya.cmds as cmds
except ImportError:
    # no maya
else:
    # If for some reason you had the maya module in your sys.path
    try:
        if cmds.about(batch=True):
            # in batch mode
    except AttributeError:
        # cmds module isn't fully loaded/populated (which only happens in batch, maya.standalone, or maya GUI)
        # no maya

Or even using imp.find_module? I can’t think of an instance where you’d be able to import maya.cmds and not be in a maya session (even with mayapy[/QUOTE]

Thank you, I’ll experiment with these methods also!

BTW - I was looking at tests like ‘sys.version’ but it returns the python version, which isn’t useful in detecting maya.
Then I was on the hunt again, and I stumbled across this in the documentation:


TipTo find whether you are in Maya's internal Python interpreter, you can execute the following code block:


```
try:
import maya.standalone
maya.standalone.initialize() 	
except:
pass
```


If you are in Maya's internal Python interpreter, the initialize() call will throw an exception, which will be safely caught.

LINK


#7

how would you recommend handling a case like this?


def foo(node=None):
    if not isinstance(node, basestring):
        print 'not a string!'
        return
    if not node:
        print 'nothing to work on!'
        return
    if cmds.nodeType(node) != 'mesh':
        print 'expected a shape node!'
        return
    # node is a valid input, do something to it.


#8

well, part of it is I’d just let the command fail on bad inputs - that’s how you find the bugs :). flat If-tests , like “if not node: return” are fine too: they let you bail out as needed and don’t complicate program flow. What’s bad is multiple tests of the same value, or deeply nested tests, or worst of all both at once – it can quickly become impossible to be sure what code is running. If you’ve never read it, The Art Of Readable Code has a great discussion of if-ing

However the pythonic alternative for multiple choice situations is to use dictionary lookups



def foo (node):
    nt = cmds.nodeType(mesh)  # let it fail - if i call it empty, the bug needs to be identified

    def mesh_func(node):
       print "I'm a mesh"

    def dummy_func(node):
       print "i dont know what to do with %s" % node

    curve_func = lambda p: print p + "is a curve"
    
    opts = {'mesh': mesh_func, 'curve': curve_func}  
   # the callables are stuffed into the dictionary
    func_to_call = opts.get(nt , dummy)  
    # dummy func is the default value
    func_to_call(nt )

this looks more complex (for a short example like this it is!) but it has two big plusses:

  1. you can expand it easily: the sub-defs or lambdas are clearly self contained. If the same functionality were wrapped in ifs you’d have to watch your indents and exit points all the way down. Adding a new function doesn’t disturb existing code at all. And of course they can just be other functions - I only inlined them here to make the point.

  2. in practice, you would probably not want the sub defs in line. They could be class member functions (that’s the most common) but you could also do by configuring at runtime:



def mesh_func (mesh):
    #blah

def curve func(curve)(:
    #blah

def light_func(light): 
    # blah

def fail(node):
   print "unrecognized node"

def foo (node, **opts)
    nt = cmds.nodeType(node) 
    opts.get(nt, fail)(nt)

def handle_lights_and_curves(node):
    foo (node, curve=curve_func, light=light_func)

def handle_curves_and_meshes(node):
    foo (node, curve=curve_func, mesh=mesh_func)



#9

And btw: Maya.standalone.initialize() is SLOOOW if it does run :slight_smile:


#10

[QUOTE=Theodox;21691]well, part of it is I’d just let the command fail on bad inputs - that’s how you find the bugs :). flat If-tests , like “if not node: return” are fine too: they let you bail out as needed and don’t complicate program flow. What’s bad is multiple tests of the same value, or deeply nested tests, or worst of all both at once – it can quickly become impossible to be sure what code is running. If you’ve never read it, The Art Of Readable Code has a great discussion of if-ing

However the pythonic alternative for multiple choice situations is to use dictionary lookups



def foo (node):
    nt = cmds.nodeType(mesh)  # let it fail - if i call it empty, the bug needs to be identified

    def mesh_func(node):
       print "I'm a mesh"

    def dummy_func(node):
       print "i dont know what to do with %s" % node

    curve_func = lambda p: print p + "is a curve"
    
    opts = {'mesh': mesh_func, 'curve': curve_func}  
   # the callables are stuffed into the dictionary
    func_to_call = opts.get(nt , dummy)  
    # dummy func is the default value
    func_to_call(nt )

this looks more complex (for a short example like this it is!) but it has two big plusses:

  1. you can expand it easily: the sub-defs or lambdas are clearly self contained. If the same functionality were wrapped in ifs you’d have to watch your indents and exit points all the way down. Adding a new function doesn’t disturb existing code at all. And of course they can just be other functions - I only inlined them here to make the point.

  2. in practice, you would probably not want the sub defs in line. They could be class member functions (that’s the most common) but you could also do by configuring at runtime:



def mesh_func (mesh):
    #blah

def curve func(curve)(:
    #blah

def light_func(light): 
    # blah

def fail(node):
   print "unrecognized node"

def foo (node, **opts)
    nt = cmds.nodeType(node) 
    opts.get(nt, fail)(nt)

def handle_lights_and_curves(node):
    foo (node, curve=curve_func, light=light_func)

def handle_curves_and_meshes(node):
    foo (node, curve=curve_func, mesh=mesh_func)


[/QUOTE]

…it can quickly become impossible to be sure what code is running…

On that note, when I do have some trouble tracking code flow in loops or branches, even with a IDE and the stack, I fall back to logging.
I recently added this method to my simple logging class I setup, It’ll return nice information about where it was called from - it will log the current function, the file it is in and line number the function starts.


    #----------------------------------------------------------------------    
    def autolog(self, message):
        '''Automatically log the current function details.'''
        # Get the previous frame in the stack, otherwise it would
        # be this function!!!
        func = inspect.currentframe().f_back.f_code
        # Dump the message + the name of this function to the log.
        self.log.debug( '{0}: {1} in {2}:{3}'
                        ''.format( message,
                                   func.co_name,
                                   func.co_filename, 
                                   func.co_firstlineno)
                        ) #end

I use it like this:

>> LOG = SetupLogger():
>> logMessage = ‘Testing’
>> LOG.autolog(logMessage)

and it’ll return something like this>> 2013-06-21 09:33:01,193 - DEBUG - Testing: main in C:\depot\bpTools\maya\python\packages\bp_debugLogger\bp_debugLogger.py:281


#11

This is basically how we do it:

import sys, os, re

if re.match( "maya\\.exe", os.path.basename( sys.executable ), re.I ):
    print "Running in Maya."

#12

[QUOTE=capper;21687]Wouldn’t something like this do the trick for a one-time check?

try:
    import maya.cmds as cmds
except ImportError:
    # no maya
else:
    # If for some reason you had the maya module in your sys.path
    try:
        if cmds.about(batch=True):
            # in batch mode
    except AttributeError:
        # cmds module isn't fully loaded/populated (which only happens in batch, maya.standalone, or maya GUI)
        # no maya

[/QUOTE]

This method works well.
I have played around with using this code at the top of a module to control imports - something structured based on your suggestion:


# -------------------------------------------------------------------------
global gMaya
gMaya = None

# -------------------------------------------------------------------------
# run this, if we are in Maya
def setMaya(gMaya):
    gMaya = True
    # Other Maya setup
    #
    return gMaya

# -------------------------------------------------------------------------
# run this if we are NOT in Maya
def setNoMaya(gMaya):
    gMaya = False
    # other setup stuff
    #
    return gMaya   

# -------------------------------------------------------------------------
try:
    import maya.cmds as cmds
    setMaya(gMaya)
except ImportError:
    # NO Maya
    setNoMaya(gMaya)
else:
    # If for some reason you had the maya module in your sys.path
    try:
        if cmds.about(batch=True):
            # in batch mode
            setMaya(gMaya)
    except AttributeError:
        # cmds module isn't fully loaded/populated (which only happens in batch,
        # maya.standalone, or maya GUI)
        # NO Maya
        setNoMaya(gMaya)

# -------------------------------------------------------------------------
# Now, run the import IF we are running from Maya
if gMaya:
    import maya.OpenMayaUI as omui

I also setup a similar test using a derivative of this method for switching/controlling imports at the UI packages level… <package_ui>_init_.py

So as suggested, I can have one set of modules for non-Maya, and another set of counterparts for Maya. I like this later method the best, thanks again.


#13

[QUOTE=madyasiwi;21700]This is basically how we do it:

import sys, os, re

if re.match( "maya\\.exe", os.path.basename( sys.executable ), re.I ):
    print "Running in Maya."

[/QUOTE]

I like this method. Thanks madyasiwi.


#14

I don’ think you need to bother with the global vars and so on. With a folder setup like:

 /myModule
     __init__.py
    common.py
    mayaOnly.py
    nonMaya.py

You just do the initialization in __init__.py at import time:


import os, sys

if os.path.basename(sys.executable) == 'mayapy.exe':
   import mayaOnly as host
else:
   import  nonMaya as host

and in both mayaOnly.py and nonMaya you’d start with


from common import *

All code outside the module would just do


from myMod import host
host.createWindow()  # which does any host specific setup
something = host.im_a_common_function()  #this is the same func no mattter what environment

The hassle factor is primarily that public functions in mayaOnly.py and nonMaya.py need to have the same signatures. That’s a great use for automatic testing :slight_smile: