How to tell if a tool is up to date

I’m very new to being a technical artist, and I’m running into a (probably common) issue with some of my tools out in the wild.

I’ve got a suite of Maya tools distributed to artists around the studio, and I’d like to find some way of showing the user the tool is out of date when they open it. I have a version number specified on each tool’s UI, and it would be great if that number was highlighted or red when the user should get latest from our source control.

So my question: is there a way for Python to query an online database that I upkeep with the current version numbers? Or does anyone have another solution to this issue they wouldn’t mind sharing?

Hi!

Interesting question, I think there are many ways to do this.

You could take a look at the Requests library for Python. Requests: HTTP for Humans™ — Requests 2.32.3 documentation.

request = requests.get('http://database.com/api')
response = request.json()

Or use urllib (Probably better to use in Maya python, not sure.)
https://docs.python.org/2/library/urllib.html

Ultra simple example to get something from a url:

import urllib
opener = urllib.FancyURLopener({})
f = opener.open("http://www.python.org/")
test = f.read()

So you basicly need to store your latest version number/ID somewhere. Maybe you can also build a simple API on the server side to have more control or information about the specific tool.
I hope this helps!

PS: You could also check the latest version using your version control software ofcourse, just like any other asset?

None of my tools are stored locally on peoples machines. They are all hosted on a remote server and filed in as needed. This means I only have to update 1 file to fix everyone on the floor. I also dont have to deal with straglers and multiple versions on the floor. The only files stored locally are the launcher for the tool browser and some icons for the toolbars. I have a script that runs inside the tool browser to update everything for them if needed. It also writes an INI file to a folder with their username, terminal, all important filepaths and what tools were installed and when.

Im a Max user, so Im not exactly sure how you would do it in Maya, but what I would do is have a remote file that was a struct with a list of my tools and their current gen ie Toolgen.cooltoolone=1.9, Toolgen.cooltooltwo=2.1 etc. As each tools launches, it queries the value, and if it != then the text or highlight becomes red, or a popup occurs.

vonBerg: Thanks for the tips! It seems like one of those links will lead me where I need to go.

Dstinct: Your setup sounds quite streamlined, just having a launcher dependent on the user’s local files. Unfortunately, the only distribution I have access to is the company source control, in this case Perforce.

I’ll explore the options you two were kind enough to offer and post my findings here. Thanks again!

To check if files are up to date in a local perforce workspace, you can always run p4 sync -n //path/to/tools/…
The -n flag will basically turn sync into a query instead of a sync, so if you get back any results, the tools are out of date.

you should also read the various threads around here on tools distribution. at least inside a python environment, you probably want to make sure all the versions are up to date and not just one tool – otherwise you can get wierd ‘it works on my machine’ bugs that are extremely hard to understand. Try Distribution techniques for external Python tools - Pipelines - Tech-Artists.Org and a roundup of previous threads here: Deploy tools for your Maya team - Pipelines - Tech-Artists.Org

@theodox + @r.white: Great info, thanks.

Yes, seriously, thanks for the feedback. This is the kind of stuff I wouldn’t think of on my own.

Here is what I implemented to check if a tool is up to date (according to Perforce).

I end up using the P4Python library. It was easy to connect with P4, set the client (workspace) and user then query a file path to see if A) it’s in the workspace, and B) is up to date.

## Query P4 whether a script is up to date.    
def check_version(supplied_path = None):
    stack = inspect.stack()
    if supplied_path == None:
        script_path = inspect.getfile(stack[1][0])
    else:
        script_path = supplied_path
    
    from P4 import P4, P4Exception
    p4 = P4()
    with p4.connect():
        print 'Connected with P4'
        
        user_name = get_perforce_user()        
        client_name = get_perforce_client()
        
        print 'P4 username : ' + user_name
        print 'P4 workspace : ' + client_name
        
        if client_name is not None and user_name is not None:
            p4.user = user_name
            p4.client = client_name
            try:
                localRev = p4.run_have(script_path)[0]['haveRev']
                headRev = p4.run_files(script_path)[0]['rev']
                
                if localRev < headRev:
                    cmds.warning(script_path + ' is out of date.')
                    return False
                elif localRev == headRev:
                    print script_path + ' is up to date.'
                    return True
            except P4Exception:
                for e in p4.errors:
                    cmds.warning(e)
                return False

The section that has worked so far, but I’m not super confident about is this line:

script_path = inspect.getfile(stack[1][0])

Since I have multiple classes calling this function, I wanted to keep that call as simple as possible. So I looked for a way for that function to get the class that called it, and use that information to derive the script path.

So from there, I edited my tools to call check_version(), and the window’s version label is displayed in red if function returns False.

I am, however, still exploring better options discussed in this thread. Having read through the links Theodox posted, I now have a better understanding of preferred practices, such as using a single ‘launcher’ application for the artists to boot up which houses the utility tools.

The inspect method is OK. but it might be easier to pass in the code object or module you want to check and look at its file attribute instead.

It’s also a very common practice to embed version info into a module as version = (0,1) or whatever, then just check that against an expected store. Both of those do require importing the module, however – you can’t look at the module variables or file until a module is loaded which may mean side effects you don’t like. Your method looks like it’s also post-import since you’re getting the file name from the stack of running code.