Guide
xlwings: Run a VBA Macro From Python (Example)
A complete xlwings example for running a VBA macro from Python: open the workbook, bind the Sub with app.macro() or book.macro(), pass arguments, save, and quit Excel.
To run a VBA macro from Python with xlwings, you open the workbook in a live Excel instance, get a callable handle to the macro by name, and invoke it. xlwings routes the call through Excel's COM bridge (Windows) or AppleScript (macOS), so the Sub executes exactly as it would from the VBA editor — including any sheet edits, pivot refreshes, or exports it performs.
There are two ways to get that handle: app.macro("Name") (resolved at the Excel-application level) and book.macro("Name") (resolved against a specific workbook). Both are current and supported; app.macro() was added in xlwings 0.24.0 for application-scoped resolution, while book.macro() has been around longer. This page shows a complete, reusable example and the details that trip people up.
xlwings requires a local Excel install
xlwings drives the desktop Excel application, so this example only runs where Excel is installed — Windows (Excel 2010+) or macOS (Excel 2016+) — and where there is an interactive desktop session. It will not run on headless Linux, bare Docker images, or CI services such as GitHub Actions or AWS Lambda.
If your target is a server with no Excel, you cannot run VBA there. Port the macro's logic to Python instead: use pandas for data work and openpyxl for file edits, both of which run headless.
Working example
This function opens a workbook, binds a macro by name, calls it with any positional arguments you pass, saves, and shuts Excel down in a finally block so no process is left running:
import xlwings as xw
from pathlib import Path
def run_macro(workbook_path: str, macro_name: str, *args):
"""Open a macro-enabled workbook, run a VBA Sub, save, and clean up."""
app = xw.App(visible=False, add_book=False) # visible=True to debug dialogs
book = None
try:
book = app.books.open(str(Path(workbook_path).resolve()))
# Qualify the name with the workbook so resolution is unambiguous
macro = app.macro(f"'{book.name}'!{macro_name}")
macro(*args) # arguments are passed positionally
book.save()
finally:
if book is not None:
book.close()
app.quit()
if __name__ == "__main__":
run_macro(r"C:\reports\monthly_summary.xlsm", "FormatAndExport", True)
The matching VBA lives in a standard module in the workbook:
Sub FormatAndExport(ByVal exportPdf As Boolean)
' ... formatting / export logic ...
End Sub
Using book.macro() instead
If you are on an older xlwings, or simply prefer scoping to the workbook you already have open, book.macro() is equivalent. It doesn't need the 'Workbook'! qualifier because the workbook is implied:
book.macro(macro_name)(*args)
Passing arguments
xlwings forwards arguments to VBA positionally — there are no keyword arguments. Order matters, and the types map naturally: Python bool to VBA Boolean, int/float to numeric types, str to String.
# VBA: Sub BuildReport(ByVal region As String, ByVal year As Long)
run_macro(r"C:\reports\summary.xlsm", "BuildReport", "North", 2024)
For VBA Optional parameters you want to skip, pass None (it maps to VBA's missing/Empty) or an explicit default like "" or 0 to be unambiguous. Macros that take no arguments are just called with ():
book.macro("RefreshAll")()
Requirements and compatibility
- xlwings: any current release.
app.macro()requires 0.24.0+;book.macro()works on older versions too. - Excel: 2016+ or Microsoft 365. Windows uses
pywin32; macOS uses AppleScript. - File format: the workbook must be macro-enabled —
.xlsm,.xlsb, or.xlam. A plain.xlsxcannot store VBA, so there is no macro to run. - Macro security: untrusted files prompt before enabling macros, and that prompt will hang an unattended run. Put the workbook in a Trusted Location, or enable macros under File > Options > Trust Center > Macro Settings.
Troubleshooting
AttributeError: 'App' object has no attribute 'macro' — your xlwings predates 0.24.0. Either upgrade with pip install --upgrade xlwings, or switch to the book.macro() form, which works on older versions.
Macro not found / 'WorkbookName'!Name errors — Excel resolves the name strictly. Wrap workbook names that contain spaces in single quotes: app.macro("'My Report.xlsm'!Build"). For a macro in your personal macro workbook, qualify it with that file: app.macro("'PERSONAL.XLSB'!MyRoutine").
com_error / pywintypes.com_error (Windows) or an AppleScript timeout (macOS) — Excel is blocked, often by a modal dialog or a macro-security prompt. Set app.display_alerts = False before opening, verify the file isn't already open elsewhere, and terminate any orphaned process (taskkill /F /IM EXCEL.EXE on Windows) before retrying.
Excel process lingers after the script ends — an exception escaped before app.quit(). Keep the try/finally so book.close() and app.quit() always run, even on failure.
Headless / server run fails outright — expected: there is no Excel to drive. Move the logic to pandas/openpyxl as noted above, or run on a Windows workstation with Excel installed.
Where to go next
Once the macro call works, fold it into the full open–write–run–save lifecycle in Automating Excel with xlwings Basics, which covers sheet and range mapping in depth. To prepare the data your macro formats, stage it first with Writing DataFrames to Excel with Pandas, then hand the workbook to xlwings for the VBA step.