0

I'm writing a version-up script (an auto-version-number-incrementing script) in Python, but I'm feeling like I'm having a weird thing going on with my renaming code in Maya.

I'm not sure how Maya stored the file path but whenever I tried to rename it, it told me that "An invalid path was specified." along with my file path.

And to make things odder I guess, it only said this error in a couple of cases. Once I tried to rename a file from Maya's asset folder (yes I already set project for Maya to look for), it gave me that error. But everything worked fine when I opened files from Maya's scene folder. And then, in another project folder, everything worked just perfectly no matter if I opened a asset file or a scene file.

Anybody have any idea what's going on? Thank you very much!!!

a = cmds.file (query = True, expandName = True)  #result: D:/DATA/MAYA/myProject/assets/testFolder/test_01.ma
#print a[:-3]
x,y = a[-5:-3]  #result: x = 0, y = 1
x = int(x)      
y = int(y)
if y < 9:
    y += 1      #result: x = 0, y = 2
    #print (y)
elif y == 9:
    y = 0 
    x += 1      
x = str(x)
y = str(y)
b = a.replace(a[-5:-3], x+y)  #replace 01 with 02
#print b
cmds.file (rename = b)  #this is where I got the error. the result should be D:/DATA/MAYA/myProject/assets/testFolder/test_02.ma
slovik
  • 67
  • 7
  • Updating: I've just tried to versioned up manually to fileName_02 (the first one was fileName_01) and the script ran. Anybody have any idea? – slovik Oct 18 '19 at 09:44

3 Answers3

3
# Error: RuntimeError: file <maya console> line 1: An invalid path was specified.

This error triggers when running cmds.file(rename=your_path) but the directory of the path that's supplied doesn't exist, which makes sense because it isn't valid!

So all you have to do is create the folder before calling it. You can use os.makedirs for that. You don't want to include the file name in the full path, so you can also use os.path.dirname to strip it out. So instead of passing it "/my/full/path/file_name.mb", using os.path.dirname will strip it to "/my/full/path".

So expanding on itypewithmyhands's answer it will look like this:

import os

newVersion = getNextVersion()
versionFolder = os.path.dirname(newVersion)
if not os.path.exists(versionFolder):
    os.makedirs(versionFolder)
Green Cell
  • 4,677
  • 2
  • 18
  • 49
  • Thanks a lot Green Cell. The script ran now, but I'm still wondering how could this happen. I set a project from Maya project setup and for sure it did create a folder called assets where I can store every assets I got in there, and still, it told me the path isnt exist? – slovik Oct 21 '19 at 03:24
  • 1
    If you go to FIle>Set project it looks like it doesn't create the sub-directories, so I'm guessing that maybe it just didn't get created? Or you might want to double-check and print out the final path you're trying to save the new version to. Maybe it's accidentally pointing elsewhere. – Green Cell Oct 21 '19 at 03:47
  • Isnt it the `D:/DATA/MAYA/myProject/assets/testFolder/` the final path I'm trying to save to? Because when I queried the expand name of the current file, which is var `a` in my codes, it returned `D:/DATA/MAYA/myProject/assets/testFolder/test_01.ma`. And I dont think I changed anything to the path, Maya just like suddenly didnt realize the path anymore :)) – slovik Oct 21 '19 at 04:30
  • My guess is that `testFolder` didn't exist so it throws you that error. – Green Cell Oct 21 '19 at 04:36
  • It did exist in my drive actually. And the `test_01.ma` was in that as well. I just dont understand if Maya uses the path directly or understands it as something else and causes error for that? – slovik Oct 21 '19 at 05:00
  • It'll understand if you supply it a full path. You can always make sure Maya resolves the path properly by passing it to `os.path.exists` and it will return `True` if it finds it. It's a good habit to get into anyways instead of assuming it exists. – Green Cell Oct 21 '19 at 05:05
  • Hmm I figured it out. You're right, the path didnt exist at all (for the sake of the project I'm working on, I cant publish the real path here, the path above is a replacement). It was my mistake to use the string replace on `b = a.replace(a[-5:-3], x+y)`, it didnt only replace my version number in the end as I assumed but everything that matches that number order, including my path in it. so the file was technically saved into a non-exist folder and it gave me an error. – slovik Oct 21 '19 at 06:53
1

The below method is pretty verbose, because I'm using string operations instead of a regex. This hopefully makes it a bit more readable for you, but I'd suggest moving to a regex whenever you feel comfortable enough.

Please note, the function that deals with the string format to rename your scene will use a minimum of 2 digits (01, 02, etc) and a maximum of 3 digits (100, 101, etc). If you go above 999 iterations, you'll need to modify this slightly.

Note: This methodology relies on an underscore before your file version (eg. _01.mb) instead of your current string index method.

import maya.cmds as cmds

def getNextVersion():
    curName = cmds.file(query=True, expandName=True) # eg. D:/yourScene_01.mb
    curVersionFull = curName.split('_')[-1]          # eg. 01.mb
    versionExtension = curVersionFull.split('.')     # eg. ['01', 'mb']
    curVersionNum = int(versionExtension[0])         # eg. 1
    extension = versionExtension[1]                  # eg. 'mb'

    nextVersionNum = curVersionNum + 1               # increment your version
    if nextVersionNum < 100:
        nextVersionFull = '{:02d}.{}'.format(nextVersionNum, extension) # eg. 02.mb
    else:
        nextVersionFull = '{:03d}.{}'.format(nextVersionNum, extension) # eg. 100.mb

    newName = curName.replace(curVersionFull, nextVersionFull)
    return newName


cmds.file(rename=getNextVersion())
cmds.file(save=True)

Update:

You've already accepted another answer, and I believe the core of your issue is what @green-cell describes. As for how that is even possible, I don't actually know. Maya would generally not let you save to a folder that doesn't exist.

Eg:

import maya.cmds as cmds
cmds.file(rename="D:/doesntexist/somefile.mb")
# Error: An invalid path was specified. : D:/doesntexist/
# Traceback (most recent call last):
#   File "<maya console>", line 2, in <module>
# RuntimeError: An invalid path was specified. : D:/doesntexist/ # 

Anyway, I've thrown together a more thorough function for you here. Please note that it's difficult to cater for all possible filename patterns, especially if users are allowed to specify their own file names in any part of the process. This time I'm using regular expressions, which makes your replacement much more robust (though, far from infallible).

import re
import os
import maya.cmds as cmds

def getNextVersion(createDir=True):
    '''Find next available filename iteration based on current scene name

    Args:
        createDir (bool, optional): True to create the output directory if required. False to leave as-is (may trigger a failure)

    Returns:
        str|None: Full file path with incremented version number. None on failure
    '''

    # Grab current filename. This always returns something. If unsaved, it will return something unsavory, but still a string
    curFile = cmds.file(query=True, expandName=True)
    print('Current filename is {}'.format(curFile))

    # This matches a digit between 1 and 4 numbers in length, followed immediately by a file extension between 2 and 3 characters long
    m = re.match(r'(.+?)(\d{1,4})\.(\w{2,3})', curFile)
    if m == None:
        print('Failed at regex execution. Filename does not match our desired pattern')
        return None

    # Extract regex matches
    basePath = m.group(1)
    version = m.group(2)
    extension = m.group(3)

    # Failsafe. Should not trigger
    if not basePath or not version or not extension:
        print('Failed at failsafe. Filename does not match our desired pattern')
        return None

    # Increment file version
    numDigits = len(version)
    newFile = None
    try:
        version = int(version) + 1

        # Deal with padding
        newLength = len(str(version))
        if newLength > numDigits:
            numDigits = newLength

        # Compile new path
        newFile = '{base}{ver:{numDig:02d}d}.{ext}'.format(base=basePath, ver=version, numDig=numDigits, ext=extension)
    except Exception as e:
        print('Error parsing new version for path {}: {}'.format(curFile, e))
        return None

    # Another failsafe
    if not newFile:
        print('Failed at failsafe. Filename calculations succeeded, but new path is not valid')
        return None

    # Create output dir if needed
    dirname = os.path.dirname(newFile)
    if createDir and not os.path.isdir(dirname):
        try:
            os.makedirs(dirname)
            print('Created all dirs required to build path {}'.format(dirname))
        except Exception as e:
            print('Error creating path {}: {}'.format(dirname, e))
            return None

    return newFile


nextVersion = getNextVersion()
if not nextVersion:
    cmds.error('Error parsing new filename increment. Please see console for more information')
else:
    cmds.file(rename=nextVersion)
    cmds.file(save=True)
Daniel Skovli
  • 455
  • 3
  • 8
  • 1
    Probably a good idea to throw `getNextVersion()` in a `try-except` clause, in case you're dealing with a scene name that does not conform to your template. – Daniel Skovli Oct 20 '19 at 05:52
  • Hmm, thanks you so much for your way of approaching. It worked fine in some cases but not all unfortunately :( I still got the "An invalid path was specified" error when i run the script with *_01 file type (I mean the error happened only with the first version which is 01. If I saved the file as *_02 the error disappeared. If the first version was *_00 as once i tried, then I re-saved it as *_01 then it worked fine as well). Do you have any idea with this? Thank you for helping me out!!! – slovik Oct 21 '19 at 01:46
  • And by the way, can you show me how to do this with regEx? I havent learnt it so I'm still kind of confuse how to use it. Thanks!!! – slovik Oct 21 '19 at 03:28
  • 1
    No worries. I've updated my answer now to illustrate use with regex, hopefully it makes sense. As for why you're failing occasionally on the first save, I really don't know, but I believe it has to do with the desired directory not existing like @green-cell mentions – Daniel Skovli Oct 21 '19 at 03:37
  • 1
    To create regular expressions it can be useful to use a tool like https://regexr.com/ – Daniel Skovli Oct 21 '19 at 03:37
  • Thank you so much @itypewithmyhands!!! The regEx confused me a bit but I'm looking for it on Google. Thanks so much for your help! – slovik Oct 21 '19 at 04:17
  • 1
    You're most welcome. Regex can be a bit confusing, and I'm far from a veteran at it. Use the site I linked to above, it will explain your expressions to you as you type them, plus provide you with a cheat sheet for common operators – Daniel Skovli Oct 21 '19 at 04:39
  • Note that there's no need to explicitly use `return None` in a function. Simply using `return` will also result as `None`. – Green Cell Oct 21 '19 at 07:04
0

Updating #02:

I realized that the error happened only with the first version. Say if I have the first version as sceneName_01.ma and try to version it up and save it, Maya will tell me that "An invalid path was specified". But if I rename it manually as sceneName_02.ma and re-run the code again, the code will work normally.

It's not because of the number of the version though. Once I tried to save my first version as sceneName_00.ma and try the script, it still gave me the same error. I had to rename it to sceneName_01.ma manually and re-run the script, until then it worked.

slovik
  • 67
  • 7