Skip to content

Plugins

MrBooks edited this page Mar 11, 2026 · 1 revision

PyCompyle plugin documentation

Table of Contents

Overview

Plugins can be imported by passing the file path or the name (if it is a built-in plugin) to the argument --plugin or -pl.

Example:

python -m PyCompyle myfile.py --plugin=myplugin.py -pl=built_in_plugin

Exec hooks

What is it

An exec hook is a function with a specific name that the plugin system extracts and runs via exec(). This allows the plugin code to run within the main program's context and access its local/global variables.

How to create one

Define a function in your plugin file using one of the reserved names. The system will strip the function header and execute the body logic:

  • init(): Runs after the build folder had been created.
  • midway(): Runs before the wrapping phase.
  • end(): Runs at the end.

Example plugin logic:

def init():
    print("Plugin started.")
    # This logic runs as if it were written directly in the main script

Special Case Hooks

Special case hooks allow you to inject code conditionally based on module imports. Any function starting with special_case is treated as a hook. The system uses the function's signature to determine where and when to inject the code:

  • import_name: The module that triggers this hook.
  • top: If True, the code is injected before the folder is copied. if False it will run after the folder has been copied.
  • continue_after: If True, the program continues its standard logic after the hook runs (To mainly be used when top is True to prevent the folder being copied if you have custom logic).

Example:

def special_case_numpy(import_name="numpy", top=True):
    print("Numpy being copied! Doing stuff.")

Monkey patching

The patches dictionary in your plugin allows you to modify or replace functions at runtime. .

Replacement Patch

By default, the system replaces the target function with your new function. Provide the full dot-path to the target as the key.

def my_custom_exit(code=0):
    print(f"Intercepted exit attempt with code: {code}")

patches = {
    "sys.exit": my_custom_exit
}

Wrapper Patch

If you want to extend a function's behavior rather than replace it, use the wrap flag. Your function will receive the original function as its argument and should return a new wrapped function.

def wrap_shutil_rmtree(func):
	    def wrapper(*args, **kwargs):
        print(f"Before calling {original_func.__name__}")
        
        result = original_func(*args, **kwargs)
        
        print(f"After calling {original_func.__name__}")
        return result
        
    return wrapper

patches = {
    "shutil.rmtree": {
        "func": wrap_shutil_rmtree,
        "wrap": True
    }
}