Skip to content

Script Mate - micro framework

ScriptMate is designed to solve a common problem for Maya users, especially those who are not technically inclined: organizing and using Python scripts effectively. Here’s why I created it and how it can benefit you:

Download the Latest Version from:

Motivation

Python can seem daunting to non-technical users. Even after writing simple scripts in Maya, creating interfaces with PyQt can be challenging. Many users resort to adding buttons to Maya’s shelf, but this method is unreliable. Updating Maya or dealing with corrupted preferences can lead to lost scripts and frustration.

I created ScriptMate because I needed a reliable, portable solution for managing my scripts. It’s designed for anyone who wants a clean, scalable way to organize scripts in Maya without relying on its fragile preferences system. Whether you’re a solo artist or managing a team, ScriptMate makes script management effortless.

How It Works

ScriptMate uses a directory routing logic inspired by web development to avoid the complexity of custom UIs for each script. Here’s a simple breakdown:

Folder-Based Menus:

  • Create new menus by prefixing folders with menu_. For example, menu_NewUserMenu becomes “New User Menu”.

  • Create submenus with folders prefixed with sub_, like sub_Development_ModelingTools for a “ModelingTools” submenu under “Development”.

folder-structure

  • Template-Wrapped Scripts:
    • Instead of running scripts directly, you wrap them in a simple template:
    OPERATOR = {
    "name": "User Script",
    "category": "Modeling"
    }
    def user_script():
    print("Hello World")
    def execute() -> None:
    user_script()
    return None
    This ensures only properly wrapped functions are executed.

Preferences: You can set a local script directory for your private scripts (not shared with others) and a global network directory for scripts visible to your team. This makes collaboration smooth, with everyone syncing to the same network path.

unzip

Hot-Reloading: No more restarting Maya just to update your scripts. Simply hit the “Update Scripts” button in the plugin settings, and you’re good to go.

Security Feature Implementation:

ScriptMate has built-in protection to stop scripts from running if they include risky system-level calls—like importing OS-related modules (os, shutil, http, etc.).

Let’s say you get a Python script from someone and add it to your collection. If it’s not wrapped in the required template, ScriptMate won’t let it run. Template-Wrapped Scripts

Scripts need to follow a simple format before they can be executed:

OPERATOR = {
"name": "User Script",
"category": "Modeling"
}
def user_script():
print("Hello World")
def execute() -> None:
user_script()
return None

Automatic Safety Checks

ScriptMate scans every script before execution, checking which Python modules it imports. If it finds any from your restricted list (like os, shutil, or http), it flags the script, logs the issue, and blocks execution. The log will show exactly where the flagged imports are in the script, so you can review them before running anything.

ScriptMate lets you control which Python modules are considered unsafe by adding them to the security section in the config file. If a script tries to import any of these, ScriptMate will block execution and log the issue.

Config Structure

The config file includes a security section where you can define restricted modules:

config.json
"security": {
"modules": [
"os",
"sys",
"subprocess",
"shutil",
"socket",
"http",
"urllib",
"ctypes",
"pickle",
"marshal",
"eval",
"exec",
"compile",
"xmlrpc"
]
},

Allowing Certain Imports

If a script gets flagged but you know the import is safe, you can manually allow it by adding an @unsafe decorator above the execute() function. You can even leave a note explaining why:

@unsafe(reason="Loading files from disk")
def execute() -> None:
user_script()
return None

This info gets logged, which is super useful when managing lots of scripts or sharing them with a team. If you’re working with others over a network, this helps keep things safe while still allowing flexibility.

Play It Safe

Python is powerful—too powerful, sometimes. If someone really wanted to, they could work around these protections. But ScriptMate at least gives you a chance to catch bad scripts before they run.

If you’re adding a new script from an untrusted source, always try running it without the @unsafe decorator first. That way, ScriptMate can check the script statically (without actually running the code) and flag anything suspicious.

And as a general rule, be careful running random scripts in Maya’s terminal. Maya has no built-in security limits, so a bad script could easily mess with your system.

Error Handling & Logging

An important detail: if a user script has a syntax error or any other issue, it won’t crash the entire plugin. Instead, ScriptMate will simply skip loading the faulty script and log a detailed error message.

  • If Maya is already running and you click the “Update Scripts” button, the error log will appear in the Maya console.
  • If Maya is not started yet, the log will be written to a log file while Maya is launching.

Maya Error Unsafe warning


Log File Location & Customization

This is how the log file looks when written to:

  • "~/crudo.dev/logs/scriptMate/logs/log.log"

If needed, you can change this path in the core config file (inside the plugin directory).

  • If ScriptMate is running from a network drive and you set the log file path to a shared network location, the log will be global for all users.
  • This can be useful for monitoring which scripts users are executing—especially in a studio environment.

Log File Contents

The log file includes important system details, such as:

  • Local User IP
  • Username
  • Hostname

Unsafe warning

Installation

  1. Download the Latest Version from GitHub

    • Grab the latest version of ScriptMate from the official repository or download page. downalods
  2. Unzip the Archive:

    • After downloading, unzip the archive to reveal the following contents:
      • plugin_folder (contains the plugin files)
      • .mod file (module descriptor for Maya) unzip
  3. Copy Files to Maya’s Module Directory

    • You need to copy both the plugin_folder and .mod file into Maya’s module directory. The exact path depends on your operating system:

    (If the modules folder doesn’t exist, you can create it manually.)

    Terminal window
    /Users/USERNAME/Library/Preferences/Asutodesk/maya/2025/modules/

    unzip

  4. Restart Maya

    • After copying the files, restart Maya. The plugin will automatically load, and you should see ScriptMate in your menus.

Folder Structure Requirements

To use ScriptMate effectively, adhere to the following folder structure conventions:

  1. Menus:

    • Any folder prefixed with menu_ creates a new context menu.
    • The menu name is derived from the folder name, replacing underscores with spaces.
    • Example: A folder named menu_NewUserMenu creates a menu called New User Menu.
  2. Submenus:

    • Subfolders prefixed with sub_ create submenus within their parent menu.
    • The submenu’s name is derived from the folder name, replacing underscores with spaces.
    • Example: A folder named sub_Development_ModelingTools creates a ModelingTools submenu under a Development menu.
  3. Script Files:

    • Python scripts must be wrapped in a specific template to ensure compatibility.
    • The template must include:
    OPERATOR = {
    "name": "Script Name",
    "category": "Category Name"
    }
    def user_script():
    # Script logic here
    pass
    def execute() -> None:
    user_script()
    return None
    • OPERATOR["name"] determines the script’s display name in the menu.
    • OPERATOR["category"] groups the script into a specific submenu if applicable.

Features

  1. Dynamic Menu and Script Organization:

    • Automatically scans directories and organizes scripts into menus and submenus based on folder structure.
  2. Default Main Menu:

    • If a folder or script is not within a menu_ directory, it will appear in the default top-level menu ( crudo | ScriptMate ) in Maya’s main menu bar.
  3. Script and Folder Ignoring:

    • Any file or folder prefixed with __ will be ignored by the plugin.
    • Example: __IgnoreThisMenu or __hidden_script.py will not be loaded.
  4. Preferences:

    • Supports defining separate local script directories (private scripts) and global network directories (shared scripts visible to the team).
  5. Hot-Reloading:

    • Scripts and menus can be updated without restarting Maya. Simply press the Update Scripts button in the plugin’s settings.
  6. Icon support:

    • You can add custom icons for your scripts! Just place them inside an icons folder within a menu_ directory or in the root of your userScripts path.
    • Example config:
    "userScripts": [
    {
    "local_path": "~/crudo.dev/userScripts/maya",
    "network_path": "~/crudo.dev/userScripts/maya"
    }
    ]
    • Then, specify the icon inside your script:
    OPERATOR = {
    "name": "My Script",
    "category": "Modeling",
    "icon": "my_icon.svg"
    }
    • If the icon file exists, it will be used in the menu next to the script name.

Examples

Folder Structure:

  • DirectoryscriptLibrary
    • icons
    • Directorymenu_Tools
      • icons
      • script_cleanScene.py
      • Directorysub_Modeling
        • script_optimizeMesh.py
    • __menu_IgnoreThisMenu
    • Directorysub_Testing_Category
      • __script_hiddenScript.py
      • script_test.py

Resulting Menu Layout in Maya:

  • icons
  • Tools:
    • icons
    • Clean Scene
    • Modeling:
      • Optimize Mesh
  • crudo | ScriptMate:
    • Testing:
      • Debug Tool

The menu IgnoreThisMenu and script hidden.py will be ignored because they are prefixed with __.

Default Main Menu Behavior Update

The default top-level menu (crudo | ScriptMate) only applies to local script paths.

For Local Scripts:

  • If a folder or script is not inside a menu_ directory, it will automatically appear in the default main menu.
  • This allows flexibility when managing personal scripts without strict organization.

For Network Scripts:

  • All scripts and packages in the network directory must be placed inside a menu_ folder.
  • If a script is not inside a menu_ directory, it will be ignored.
  • This keeps the shared environment tidy and prevents clutter.

Why?
This ensures a structured workflow, making it easier to manage shared scripts while allowing more flexibility for personal ones.

Managing Plugins & Scripts Without Conflicts

Since we need to manage all our plugins and scripts without conflicts as much as possible, user packages in ScriptMate load with a package namespace inside Maya’s Python environment.

By default, all Maya Python plugins defined by a .mod file add new paths to sys.path, which can cause problems if multiply plugins have a generic package name inside them.

For instance: lets say PluginA and PluginB both hae a package called utils. Now Here’s the problem:

  • Maya loads PluginA firs.
  • The utils module from PluginA gets cached by Python and won’t be reloaded.
  • Later, when PluginB tries to import its own utils, Python returns the cached version from PluginA insted.
  • This causes PluginB to fail because it’s loading the wrong module.

Finding a Solution

I’m not a Python expert. Everything I’ve done so far has been to solve my needs, and nome more. But to fix this issue, I had to learn how Python loads modules on low level and how its import system works, and how to prevent this kind of module clashing.

The solution? Python’s importlib module. importlib allows dynamic module loading, letting us manually handle Python files and register them the way we need.

After a lot of trial and error, I found a system that works: Dynamically loading all user packages with unique namespaces.

How ScriptMate Handles Packages

Whenever a new package (plugin) is created inside a menu_ directory, ScriptMate registers it as its own module in Maya’s Python environment.

For example:

  • If we create PluginA inside menu_, it will be registered as PluginA in Maya.
  • If another package PluginB is created, it will be registered as PluginB.
  • Now, when importing modules inside these plugins:
    import PluginA.utils # Loads PluginA's utils module
    import PluginB.utils # Loads PluginB's utils module
    No more clashing! Each package has its own fully isolated namespace.

Rules for Plugin Development with ScriptMate

Since every user package is treated as a unique namespace, we need to follow some simple rules when developing plugins for ScriptMate:

  1. Use Absolute Imports for Your Plugin Modules

    • Always start imports with [rootPackageName].[module]
    • Example:
      from PluginA.tools import rig # Correct
      from PluginB.utils import helper # Correct
  2. Use Relative Imports Normally

    • Relative imports inside the same plugin work as usual.
    • Example (inside PluginA):
      from . import helpers # Works fine
      from .tools import rigging_tool # No problem
  3. Namespace Protection in Maya

    • Since ScriptMate dynamically registers these packages, you can call any module inside Maya without conflicts.
    • Example:
      from PluginA.tools import rig
      • This could be a collection of functions that you assign to a hotkey in Maya’s Hotkey Manager.
      • Everything is namespaced and protected from conflicts.

What Happens if Two Packages Have the Same Name?

If two user packages have the same name inside different menus, only the first one will be loaded. The second one will be skipped to avoid conflicts.

But in reality, the chances of this happening in a properly organized environment are very low.

This approach gives us:

  • No name clashes between plugins
  • A modular way to manage user scripts
  • Safe, structured, and scalable plugin management in Maya

It took a lot of experiments and failures, but in the end, this system works exactly how I wanted—keeping everything clean, modular, and protected from collisions.