Last active
July 6, 2026 14:13
-
-
Save tokejepsen/bb1e5249595e183cbcf2c706d5a3f051 to your computer and use it in GitHub Desktop.
Maya: Change Namespace
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # maya_change_namespace.py | |
| # | |
| # USAGE | |
| # ----- | |
| # Run this script from Maya's Script Editor (Python tab). | |
| # | |
| # 1. Set `namespace` (below) to the NEW namespace you want to assign. | |
| # !! Do this EVERY time before running — the value is not remembered !! | |
| # | |
| # 2. Select any node that belongs to the reference you want to rename | |
| # (a mesh, a joint, a shape — anything from that reference). | |
| # | |
| # 3. Run the script. | |
| # | |
| # WHAT IT DOES | |
| # ------------ | |
| # Maya's built-in namespace rename is unreliable on complex scenes, | |
| # and cannot handle moving a reference from the root namespace (no prefix) | |
| # to a named namespace. This script works around that by: | |
| # | |
| # a) Saving the current scene as Maya ASCII (.ma). | |
| # b) Editing the ASCII text directly — updating the reference file | |
| # command and every node reference in the body via regex. | |
| # c) Writing the result to a new auto-versioned file and reopening it. | |
| # | |
| # REQUIREMENTS | |
| # ------------ | |
| # - The current scene must already be saved as a Maya ASCII file (.ma). | |
| # Running on a Maya Binary (.mb) scene will raise an error to prevent | |
| # the original file being silently overwritten in a different format. | |
| # - The scene must be saved (not untitled) before running. | |
| # - Exactly one node from the target reference must be selected. | |
| # | |
| # SAFETY | |
| # ------ | |
| # - The original file is never modified; output is always a new version. | |
| # - The new file is written atomically (temp file → rename) to prevent | |
| # corrupt output if a write is interrupted. | |
| # | |
| # DISCLAIMER | |
| # ---------- | |
| # THIS SCRIPT IS PROVIDED AS-IS. Bumpybox accepts no responsibility for | |
| # data loss or scene corruption. Always back up your files before testing | |
| # or running this script on production assets. | |
| import re | |
| import os | |
| import tempfile | |
| from maya import cmds | |
| # !! SET THIS TO THE DESIRED NEW NAMESPACE BEFORE RUNNING !! | |
| namespace = "temp" | |
| selection = cmds.ls(selection=True) | |
| if not selection: | |
| raise RuntimeError("No objects selected.") | |
| ref_node = cmds.referenceQuery(selection[0], referenceNode=True) | |
| file_path = cmds.referenceQuery(selection[0], filename=True) | |
| # 1. Process ALL nodes in the reference (including Shading, Materials, Sets, etc.) | |
| ref_nodes = cmds.referenceQuery(ref_node, nodes=True) | |
| # Sort nodes by string length descending to prevent shorter names | |
| # accidentally replacing parts of longer names (e.g., 'pSphere1' vs 'pSphere10') | |
| ref_nodes.sort(key=len, reverse=True) | |
| # Normalize the existing namespace string | |
| raw_namespace = cmds.referenceQuery(ref_node, namespace=True) | |
| old_namespace = raw_namespace.strip(":") | |
| current_file = cmds.file(q=True, sn=True) | |
| if not current_file: | |
| raise RuntimeError("No file currently open in Maya.") | |
| if not current_file.lower().endswith(".ma"): | |
| raise RuntimeError( | |
| f"Current scene must be saved as Maya ASCII (.ma) before running this script.\n" | |
| f"Current file: {current_file}\n" | |
| f"Save the scene via File > Save As, choosing 'Maya ASCII', then try again." | |
| ) | |
| cmds.file(save=True, type="mayaAscii") | |
| with open(current_file, "r", encoding="utf-8") as f: | |
| contents = f.read() | |
| # Normalise line endings so the ;\n split works on files from any OS | |
| contents = contents.replace("\r\n", "\n").replace("\r", "\n") | |
| new_contents = "" | |
| for line in contents.split(";\n"): | |
| if file_path in line: | |
| # Strip -renamingPrefix / -rpr first — mutually exclusive with -ns in Maya ASCII. | |
| # Maya uses the short flag -rpr in practice; both forms must be removed to prevent | |
| # the conflict "The -renamingPrefix flag and -namespaceName flag are mutually exclusive". | |
| line = re.sub(r'\s*-(renamingPrefix|rpr)\s+"[^"]*"', '', line) | |
| if re.search(r'-(ns|namespace)\s+"[^"]*"', line): | |
| line_replacement = re.sub(r'-(ns|namespace)\s+"[^"]*"', f'-ns "{namespace}"', line) | |
| else: | |
| line_replacement = line.replace('file ', f'file -ns "{namespace}" ', 1) | |
| print(f"Replacing reference line: {line}\nwith: {line_replacement}") | |
| line = line_replacement | |
| new_contents += line + ";\n" | |
| # 2. Loop through all nodes with an upgraded, flexible regex pattern | |
| for node in ref_nodes: | |
| # Isolate the base node name (stripping any existing namespace paths) | |
| base_node = node.split(":")[-1] | |
| if old_namespace: | |
| # Targets: "old_ns:node", :old_ns:node, or |old_ns:node | |
| pattern = rf'(["|:])(:?){re.escape(old_namespace)}:{re.escape(base_node)}(\b)' | |
| replacement = rf'\1{namespace}:{base_node}\3' | |
| else: | |
| # Targets root namespace: "node", :node, or |node | |
| # This catches "rootGroup.visibility" and "mesh.instObjGroups" seamlessly | |
| pattern = rf'(["|:])(:?){re.escape(base_node)}(\b)' | |
| replacement = rf'\1{namespace}:{base_node}\3' | |
| new_contents = re.sub(pattern, replacement, new_contents) | |
| # 3. Handle versioning and save file | |
| base, ext = os.path.splitext(current_file) | |
| match = re.search(r'_v(\d{3,})', base) | |
| if match: | |
| version = int(match.group(1)) + 1 | |
| base = base[:match.start()] | |
| else: | |
| version = 1 | |
| new_file = f"{base}_v{version:03d}{ext}" | |
| while os.path.exists(new_file): | |
| version += 1 | |
| new_file = f"{base}_v{version:03d}{ext}" | |
| # Write atomically: write to a temp file alongside the target, then rename. | |
| # This prevents a corrupt partial file if the write is interrupted. | |
| target_dir = os.path.dirname(new_file) | |
| fd, tmp_path = tempfile.mkstemp(dir=target_dir, suffix=".tmp") | |
| try: | |
| with os.fdopen(fd, "w", encoding="utf-8") as f: | |
| f.write(new_contents) | |
| os.replace(tmp_path, new_file) | |
| except Exception: | |
| os.unlink(tmp_path) | |
| raise | |
| cmds.file(new_file, open=True, force=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment