[MAYA] Global variables best practice

I know of two ways to set global variables, from within a module, in maya:

import main
main.myVar = 5

or

import maya.mel as mm
mm.eval(‘global int $myVar; $myVar=5’)

Which do you recommend using? I’m starting the long process of building my own pipeline, and I don’t want to make a stupid mistake that nags at me in the future. main seems more pythonic, but the mm.eval method seems more in tune with how maya works globally. Thanks!

first, you WILL make stupid mistakes that will haunt you, its unavoidable :slight_smile:
Second, why are you creating these globals? Surely there is a better way

Thanks for the response Maxx! There are somethings that I will want to keep around that are persistent throughout new scenes, and don’t have a ui. I don’t plan on going nuts with global variables, but I have ended up in situations where I want to keep data accessible and mutable. In those situations, creating a global var is either unavoidable, or the alternative is even more hacky than the global route.

Do optionVars not work for you? It is kind of the proper way to persist a value between scenes and/or sessions.

In almost all cases using a global variable is a bad idea. So there are probably better ways.

Also in Python main isn’t global, it is just another module (albeit the default one for the interpreter) , otherwise you wouldn’t need to import it into you’re scripts namespace.

Actually do you have an example of where you think you’re needing to use this kind of functionality? Might be able to offer some alternatives with a more concrete examples.

The reason that I try to avoid option vars is that they are stored in maya’s prefs, which seem to always get corrupted by artists, and need deleting. This is probably fine as long as they aren’t needed between maya sessions. I’ll post a specific example when it comes up.

I concur with TheMaxx, mistakes are completely unavoidable. Mistakes do have an upside though, they often lead you toward a better solution.

I’m not quite sure what you’re attempting to do with main here. If you set a variable normally via python within Maya it will be available through main anyway.


x = 4
print x
print __main__.x

Additionally references to objects will persist across scene changes. So this name x referencing the integer 4 will be available after creating or opening another scene. My gut tells me that you probably want a pipeline application class to hold things like current context and configuration.


'''
mypipeline.py
'''


class MyPipeline(object):
    '''Main Pipeline Application Class, holds context and configuration'''
    
    _instance = None

    def __init__(self):
        # do some configuration things
        # maybe load config from a file
        # or maybe load config from environment variables
        self.context = {}
        self.config = {}


def get_pipeline():
    if not MyPipeline._instance:
        MyPipeline._instance = MyPipeline()
    return MyPipeline._instance


Here the get_pipeline function makes sure that we always get the same instance of the MyPipeline class. We use the MyPipeline._instance class attribute to keep track of that instance but never access MyPipeline._instance directly. Now you can use the MyPipeline instance returned by get_pipeline to store context info like the current project and asset, and configuration like path templates and logging level.


import mypipeline

pipeline = get_pipeline()
pipeline.context['project'] = '/path/to/current/project'

This pattern of having an Application class that composes context and config is really common in Python projects. It is just one possible route you can take. I think it’s probably important to point out that 99 out of 100 times when you think you need global variables in Python there is another simpler solution.

Not sure exactly what you’re looking to do, but here are some options:

If you need variables global to a script in use where multiple classes need access to the data, you could always create a new class to store them in. Similar in practice to what you’re doing with “main” (or “builtins”), but you wouldn’t be running the risk of conflicts with any existing variables in that module. Even if it there doesn’t appear to be a problem, it’s still possible that another script in the future could create one.

If you need a variable stored independent of the Maya session, Maya’s option variables are one possibility. You could also save the data to a JSON file. If the variable is not intended to be modified by the user there is also the option of simply storing it in a py file so other TAs can easily access it too.

If you want a variable global to the Maya session that multiple scripts/tools use but isn’t stored permanently somewhere, it’s probably time to rethink what you are doing. Mel’s global variables do work this way, which is probably what lead you to ask if you should use maya.mel, but it’s really not a good thing. Forgetting a global variable name already exists is all it takes to cause a problem. It might not even be your mistake, someone could add a mel script that has a conflict with your variables or even Autodesk may be using it for something else.

FYI: maya’s “optionVars” are stored in userPrefs.mel. “fileInfo” can be used to store values into individual maya files to be retrieved later.

Also for per scene stuff you could just create your own node and add attributes to it to store stuff. I have one script where it needs to maintain a list of objects to do a certain operation, and i found a easy solution was to create a custom boolean attribute on the transforms of said objects.

Rather than global variables you should consider class - level variables. These are effectively global per class but can – and should! – be hidden behind a function. A a minimal example is like this:


class FakeGlobal (object):
     value = None

     @classmethod
     def set_global(cls, val):
          cls.value = val

     @classmethdo
     def get_global(cls):
          return cls.value

and elsewhere:


from fakeGlobalModule import FakeGlobal

current = FakeGlobal.get_value()
if current is None:
      print 'Fake Global not set'
else:
      print 'Fake Global is', current

FakeGlobal.set_value (999)


since a class definition is global you will always be accessing the same copy of the FakeGlobal class so you can share state as needed – even though, as everybody has already piled on to say, state sharing should be avoided wherever possible. By using methods instead of directly accessing the variable you lessen the chance that you’ll accidentally change the state in a bad way – you can if you really need to implement a simple locking mechanism to make sure that multiple scripts don’t fight for access to the variable or change it out from under one another in bad ways.

If you really need a global you can just get the globals dictionary wth globals(). But don’t.