slovik
slovik

Reputation: 77

An invalid path was specified when tried to rename file

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

Upvotes: 0

Views: 1778

Answers (3)

Daniel Skovli
Daniel Skovli

Reputation: 495

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)

Upvotes: 1

Green Cell
Green Cell

Reputation: 4777

# 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)

Upvotes: 3

slovik
slovik

Reputation: 77

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.

Upvotes: 0

Related Questions