This guide explains how to create plugins for PyInstaller Advanced Builder.
Plugins run automatically after a successful build. Use them for tasks like:
- Creating zip archives
- Uploading builds to a server
- Running post-build scripts
- Cleaning up temporary files
- Sending notifications
Create a file in the plugins/ folder:
# plugins/my_plugin.py
from app.core.plugin_loader import PostBuildPlugin
class MyPlugin(PostBuildPlugin):
NAME = "My Plugin"
DESCRIPTION = "Does something after build"
VERSION = "1.0.0"
AUTHOR = "Your Name"
def execute(self, context):
output_path = context.get('output_path')
self.logger.info(f"Build output: {output_path}")
# your logic here
return True # return False if something failedDrop the file in plugins/ and restart the app. It shows up in the Plugins tab.
Runs after build completes. Most common type.
from app.core.plugin_loader import PostBuildPlugin
class MyPostPlugin(PostBuildPlugin):
NAME = "My Post Plugin"
DESCRIPTION = "Runs after build"
VERSION = "1.0.0"
AUTHOR = "You"
def execute(self, context):
# context contains build info
return TrueHas hooks for both pre-build and post-build. Use when you need to modify build settings.
from app.core.plugin_loader import BuildProcessorPlugin
class MyProcessor(BuildProcessorPlugin):
NAME = "My Processor"
DESCRIPTION = "Modifies build process"
VERSION = "1.0.0"
AUTHOR = "You"
def pre_build(self, context):
# modify context before build starts
return context
def post_build(self, context):
# runs after build
return True
def execute(self, context):
return self.post_build(context)The context dictionary passed to plugins contains:
| Key | Type | Description |
|---|---|---|
build_config |
BuildConfig |
Script path, output dir, options |
output_path |
Path |
Where the build was saved |
build_result |
BuildResult |
Status, build time, errors |
app_config |
AppConfig |
App settings, theme, etc. |
def execute(self, context):
# Build configuration
config = context.get('build_config')
script = config.script_path
output = config.output_dir
app_name = config.app_name
# Build result
result = context.get('build_result')
if result.status.value == 'success':
self.logger.info("Build succeeded!")
# Output path
output_path = context.get('output_path')
return TrueUse self.logger for output. Messages appear in the build console.
def execute(self, context):
self.logger.debug("Debug message") # gray
self.logger.info("Info message") # white
self.logger.warning("Warning message") # orange
self.logger.error("Error message") # red
self.logger.success("Success message") # green
return TrueEvery plugin needs these class attributes:
NAME = "Plugin Name" # shown in UI
DESCRIPTION = "What it does" # shown in UI
VERSION = "1.0.0" # version string
AUTHOR = "Your Name" # credit
PLUGIN_TYPE = "post_build" # or "build_processor"Creates a timestamped zip archive of the build output.
my_app_20250118_143052.zip
Removes PyInstaller's temporary files:
*.specfilebuild/folder
Keeps your project directory clean.
plugins/
├── __init__.py # empty, required
├── README.md # this file
├── zip_output.py # example: zip + clean plugins
└── your_plugin.py # your custom plugins
-
Return value matters — Return
Truefor success,Falsefor failure. Failed plugins are logged. -
Don't block — Plugins run on the main thread. Keep them fast or use threading for heavy tasks.
-
Handle errors — Wrap risky code in try/except. Log errors with
self.logger.error(). -
Test separately — Test your plugin logic in a standalone script first.
-
Check paths exist — Always verify
output_pathexists before using it.
import requests
from pathlib import Path
from app.core.plugin_loader import PostBuildPlugin
class UploadPlugin(PostBuildPlugin):
NAME = "Upload Build"
DESCRIPTION = "Uploads build to server"
VERSION = "1.0.0"
AUTHOR = "You"
def execute(self, context):
output_path = context.get('output_path')
if not output_path or not Path(output_path).exists():
self.logger.error("No output to upload")
return False
try:
# find the exe file
exe_files = list(Path(output_path).glob("*.exe"))
if not exe_files:
self.logger.error("No exe found")
return False
exe_path = exe_files[0]
self.logger.info(f"Uploading {exe_path.name}...")
with open(exe_path, 'rb') as f:
response = requests.post(
"https://your-server.com/upload",
files={"file": f}
)
if response.ok:
self.logger.success("Upload complete!")
return True
else:
self.logger.error(f"Upload failed: {response.status_code}")
return False
except Exception as e:
self.logger.error(f"Upload error: {e}")
return Falsefrom app.core.plugin_loader import PostBuildPlugin
class NotifyPlugin(PostBuildPlugin):
NAME = "Desktop Notify"
DESCRIPTION = "Shows notification when build completes"
VERSION = "1.0.0"
AUTHOR = "You"
def execute(self, context):
try:
from plyer import notification
result = context.get('build_result')
config = context.get('build_config')
title = "Build Complete"
message = f"{config.app_name or 'App'} built in {result.build_time:.1f}s"
notification.notify(
title=title,
message=message,
timeout=5
)
return True
except ImportError:
self.logger.warning("plyer not installed, skipping notification")
return TrueCheck the included zip_output.py for a working reference.
Open an issue on GitHub if you're stuck.