For Developers: How PyMEL Works

An Overview of PyMEL’s Wrapping Mechanisms

Parsed Caches and Maintained Constants

In order to fuse the many disparate parts of maya’s python package into a cohesive whole PyMEL requires a great deal of data describing how each MEL command and API class works: the arguments they require, the results they return, and their relationships to each other. Some of this data is parsed and cached, while other bits are manually maintained.

Below is a description of how each major cache is created.

MEL Commands

  1. gather MEL function data
    • parse data from MEL documentation
    • merge this with info from MEL help command
  2. create a dictionary of MEL-commands to the maya nodes they create, query, and edit

  3. create a dictionary of MEL-commands to the ui elements they create, query, and edit

  4. run tests on node commands to gather additional information required to ensure values returned by queries are compatible with values required for edits

  5. pickle this info into two separate caches: one used for auto-wraps and the other for doc strings. the latter data can be lazily loaded on request since it is not required for the wrap itself.

Nodes and API Classes

  1. gather API class data
    • parse API docs
    • correct erroneous docs based on documentation
  2. gather node hierarchy data
    • parse maya node hierarchy docs
    • determine MFn-to-maya-node mappings
    • determine apiType-to-maya-node mappings
  3. pickle this info into two separate caches: one for auto-wraps and the other for doc strings

Bridge

A special control-panel GUI is used to manage a dictionary controlling how MEL and API interact to produce PyNode classes. It is known as the bridge. The bridge allows us to:

  1. create manual overrides to correct input and output types
  2. create mappings between MEL commands and API commands that perform the same task
  3. specify whether MEL or API should be used to generate each PyMEL method
  4. specify the name of the method that will be produced

Run Time Function and Class Factories

When PyMEL is imported it uses the cached data to generate the wrapped functions and classes you’ve come to know and love. Here’s how in a nutshell.

Functions

Every command is wrapped in up to 3 stages.

1. Data Compatibility Wrap

pymel.internal.pmcmds wraps every function in maya.cmds such that any arguments that are passed to it are first converted into datatypes that maya.cmds will accept (string, int, float, or list/tuple thereof). The way we do this is simple yet powerful: if the argument has a __melobject__ method, we evaluate it and use the result in place of the original value. It is the responsibility of this method to return a “MEL-friendly” representation of the object’s data. For example, core.PyNode.__melobject__ returns its object’s name as a string, and datatypes.Matrix.__melobject__ returns itself converted into a flat 16-item list.

2. Manual Wrap

Optional manual wraps are created for cases that cannot be handled automatically or semi-automatically (below). They can use the auto-wrapped function in pmcmds as a starting point

3. Automatic Wrap

Certain wraps are applied automatically based on information attained during parsing. If a manual wrap of the function is found, it is used as the starting point, otherwise the lower-level pmcmds wrap is used.

For each function:

  1. add open-ended time ranges to appropriate flags: (1,None), (1,), slice(1,None), “1:”, etc
  2. cast results to PyNode or PyUI if it is a node or UI command, correcting where determined necessary in tests
  3. fix UI callbacks to return proper python objects instead of ‘true’, ‘false’, ‘1’, ‘0’, etc
  4. perform simple wraps: these are manually maintained, semi-automatic wraps of commands that are altered in standard and straightforward ways. For example, a command that should return a PyNode on a particular query is a wrap that can be handled easily through this mechanism.
  5. add docstrings based on cached data

Classes

  1. manually create classes that need custom methods
  2. add the appropriate __metaclass__ attribute: internal.factories.MetaMayaNodeWrapper or internal.factories.MetaMayaUIWrapper

for each node and UI type:

  1. choose the appropriate metaclass

  2. add methods:
    • use bridge to determine whether to use MEL or API
    • skip if it has already been manually added