MSyntax Bug in api 2?

I’ve been working on a module for cleaning up all the stupid boilerplate you have to write when making python Maya plugins. However I’m going nuts with the argument parsing: I think I’m seeing a bug where the first argument added to an MSyntax() does not actually take: I have to add an unused flag before I can get the Syntax to correctly parse them.

Am I going crazy? Or have other people seen this? Maya2015 SP5

Checked this with 2014-2016, all seem to work just fine. (Though I am on 2015 SP6, so maybe it was fixed in the newer SP?)

The code came from Maya’s documentation.
I added an extra flag of otherFlag to go along with myFlag that was in their by default. Command seemed to behave as expected.


# pySampleCommandFlag.py

import sys

import maya.api.OpenMaya as OpenMaya
# ... additional imports here ...

kPluginCmdName = 'myCommandWithFlag'

kShortFlagName = '-mf'
kLongFlagName = '-myFlag'
kOtherShortFlagName = '-of'
kOtherLongFlagName = '-otherFlag'

def maya_useNewAPI():
    """
    The presence of this function tells Maya that the plugin produces, and
    expects to be passed, objects created using the Maya Python API 2.0.
    """
    pass
    
##########################################################
# Plug-in 
##########################################################
class MyCommandWithFlagClass( OpenMaya.MPxCommand ):
    
    def __init__(self):
        ''' Constructor. '''
        OpenMaya.MPxCommand.__init__(self)
    
    def doIt(self, args):
        ''' Command execution. '''
        
        # We recommend parsing your arguments first.
        self.parseArguments( args )

        # Remove the following 'pass' keyword and replace it with the code you want to run. 
        pass
    
    def parseArguments(self, args):
        ''' 
        The presence of this function is not enforced,
        but helps separate argument parsing code from other
        command code. 
        '''
        
        # The following MArgParser object allows you to check if specific flags are set.
        argData = OpenMaya.MArgParser( self.syntax(), args )
        
        if argData.isFlagSet( kShortFlagName ):
                
            # In this case, we print the passed flag's value as an integer.
            # We use the '0' to index the flag's first and only parameter.
            flagValue = argData.flagArgumentInt( kShortFlagName, 0 )
            print kLongFlagName + ': ' + str( flagValue )
        elif argData.isFlagSet( kOtherShortFlagName ):
            # In this case, we print the passed flag's value as an string.
            # We use the '0' to index the flag's first and only parameter.

            flagValue = argData.flagArgumentString( kOtherShortFlagName, 0 )
            print kOtherLongFlagName + ': ' + str( flagValue )
            
        
        # ... If there are more flags, process them here ...

##########################################################
# Plug-in initialization.
##########################################################
def cmdCreator():
    ''' Create an instance of our command. '''
    return MyCommandWithFlagClass() 

def syntaxCreator():
    ''' Defines the argument and flag syntax for this command. '''
    syntax = OpenMaya.MSyntax()
    
    # In this example, our flag will be expecting a numeric value, denoted by OpenMaya.MSyntax.kDouble. 
    syntax.addFlag( kShortFlagName, kLongFlagName, OpenMaya.MSyntax.kDouble )
    syntax.addFlag( kOtherShortFlagName, kOtherLongFlagName, OpenMaya.MSyntax.kString )
    
    # ... Add more flags here ...
        
    return syntax
    
def initializePlugin( mobject ):
    ''' Initialize the plug-in when Maya loads it. '''
    mplugin = OpenMaya.MFnPlugin( mobject )
    try:
        mplugin.registerCommand( kPluginCmdName, cmdCreator, syntaxCreator )
    except:
        sys.stderr.write( 'Failed to register command: ' + kPluginCmdName )

def uninitializePlugin( mobject ):
    ''' Uninitialize the plug-in when Maya un-loads it. '''
    mplugin = OpenMaya.MFnPlugin( mobject )
    try:
        mplugin.deregisterCommand( kPluginCmdName )
    except:
        sys.stderr.write( 'Failed to unregister command: ' + kPluginCmdName )

I’m reasonably certain it’s a bug: It also manifests as garbled characters in the names of flags when you call help on them in MEL. Irritating

https://groups.google.com/forum/#!topic/python_inside_maya/VFMnejYCfEM

So looks like someone else has run into something similar, back in 2009 even, so it might not even be part of the 2.0 api.

we have a scant few commands written in the API 1.0, and I haven’t encountered this before.
I’ll try upgrading one of our command plugins to API 2.0 and see what happens.

Thx Rob, that link is exactly the behavior I’m seeing. Well, it stinks that it’s broken but I’m reassured that I am not insane. And I chased down the garbled characters: C++, in its infinite wisdom and mercy, cares that the strings are declared as literals rather than passed by reference! so:


     syntax.addFlag("-c", "-cppStinks", MSyntax.kString)

behaves nicely but


     flag = 'cppStinks"
     short, long = "-" + flag[0], "-" + flag
     syntax.addFlag(short, long, MSyntax.kString)

looks wrong, although it seems to behave correctly.

FWIW the module I was working on when ran into this is up on GitHub at https://github.com/theodox/plugger. I’m still not sure how I feel about the Syntax helper in their (especially since the static-strings-only bug makes it less useful but I’d appreciate feedback

for reference here is how we define the syntax in API 1.0. It is working fine for me.


class QuantizeVertices(mpx.MPxCommand):
    '''Quantizes selected vertices to the nearest grid line.'''
    
    CMD_NAME = 'quantizeVertices'
    
    ARG_X = 'x'
    ARG_X_LONG = 'quantizeX'
    ARG_Y = 'y'
    ARG_Y_LONG = 'quantizeY'
    ARG_Z = 'z'
    ARG_Z_LONG = 'quantizeZ'
    ARG_PRECISION = 'pre'
    ARG_PRECISION_LONG = 'precision'

...

    @staticmethod
    def syntaxCreator():
        ''''''
        syntax = om.MSyntax()
        
        syntax.addFlag(QuantizeVertices.ARG_X, QuantizeVertices.ARG_X_LONG)
        syntax.addFlag(QuantizeVertices.ARG_Y, QuantizeVertices.ARG_Y_LONG)
        syntax.addFlag(QuantizeVertices.ARG_Z, QuantizeVertices.ARG_Z_LONG)
        syntax.addFlag(QuantizeVertices.ARG_PRECISION, QuantizeVertices.ARG_PRECISION_LONG, om.MSyntax.kDouble)
        
        syntax.useSelectionAsDefault(True)
        syntax.setObjectType(om.MSyntax.kSelectionList)
        return syntax

Looking at their samples it is clear that they declare all those flag names outside the main class bodies because of this. C++ programmer writing C++ in python syndrome.

are you referring to the most recent samples for api 2.0 included with 2016?

the pyMetaDataCmd.py file included in the devkit/plugins/scripts/ folder is setup like this:


class PyMetaDataCmd(om.MPxCommand):
    cmdName = 'pyMetaData'

    nameFlag = '-n'
    nameFlagLong = '-name'

    forceFlag = '-f'
    forceFlagLong = '-force'

    valueFlag = '-v'
    valueFlagLong= '-value'


    @staticmethod
    def createSyntax():
        syntax = om.MSyntax()
        syntax.setObjectType(om.MSyntax.kSelectionList)
        syntax.useSelectionAsDefault(True)
        syntax.addFlag(PyMetaDataCmd.forceFlag, PyMetaDataCmd.forceFlagLong)
        syntax.addFlag(PyMetaDataCmd.nameFlag, PyMetaDataCmd.nameFlagLong, om.MSyntax.kString)
        syntax.addFlag(PyMetaDataCmd.valueFlag, PyMetaDataCmd.valueFlagLong, om.MSyntax.kString)
        return syntax

I was looking at the one included in the APi2 doc, which define the strings at the module level. But I think the real differentiator is between variables that are declared (flag = ‘-f’) and those that are calculated at runtime (flag = ‘-’ + variable[0])