Skip to main content

Read and write VBA macros inside Excel, Word, and PowerPoint files in pure Python, no dependencies.

Project description

pyOpenVBA

PyPI version Python versions CI License: MIT Downloads

Read and write VBA macros inside Excel, Word, and PowerPoint files, in pure Python.

No external dependencies. No Office install required. Works on Windows, macOS, and Linux. Python 3.10 or newer.

Supports:

  • Excel (.xlsm, .xlsb, .xlam, .xls)
  • PowerPoint (.pptm, .potm, .ppt)
  • Word (.docm, .dotm, .doc)

Sponsor WilliamSmithEdward


Why use this?

Several excellent Python tools already exist for reading VBA out of Office files (oletools, olefile, and friends), and they remain a strong choice for forensics, malware analysis, and audit use-cases. pyOpenVBA focuses on the next step: safely writing changes back so the file still opens cleanly in the host application.

The write path is the whole point of the library:

  • Modify a module's source in place.
  • Add a new standard module, class module, or document/UserForm code-behind.
  • Rename any module (the CFB stream, dir record, PROJECT declaration, PROJECTwm name map, and Attribute VB_Name are all updated in lockstep).
  • Delete a module cleanly.
  • Save the file and have it reopen in the host application with no repair dialog. Every supported format is verified against live Office.
  • Create new .xlsm, .xlsb, .docm, or .pptm files on the fly, and inject VBA code into them.

That makes it a good fit for:

  • Version-controlling your VBA in git like normal source code, then pushing edits back without ever opening Office.
  • Diffing two workbooks or documents to see what changed in a module.
  • Generating or updating macros from a script without scripting Office through COM automation.
  • Reading and writing macros on a server (Linux / CI) where Office is not installed.
  • Agentic AI Integration - allow your AI agent easy access to both push and pull VBA code in your Office files.

pyOpenVBA is a complete read-and-write library, so it covers the full lifecycle of a VBA project in one place: extract, edit, version, write back, and verify.

Installation

From PyPI:

pip install pyOpenVBA

Requires Python 3.10 or newer. There are no other dependencies.

After install, the CLI is available either as a module or as a script:

python -m pyopenvba --help
pyopenvba --help

From source (for development):

git clone https://github.com/WilliamSmithEdward/pyOpenVBA
cd pyOpenVBA
pip install -e ".[dev]"

30-second tour

Excel

from pyopenvba import ExcelFile

with ExcelFile("workbook.xlsm") as wb:
    # 1. List all VBA modules in the workbook.
    print(wb.module_names())
    # ['ThisWorkbook', 'Sheet1', 'Module1']

    # 2. Read a module's source as a string.
    source = wb.get_module("Module1")
    print(source)

    # 3. Edit a module and save the workbook.
    wb.set_module("Module1", 'Sub Hello()\r\n    MsgBox "hi"\r\nEnd Sub\r\n')
    wb.save()                       # overwrites the original file
    # wb.save("edited.xlsm")        # ...or save to a new file

Word

from pyopenvba import WordFile

with WordFile("document.docm") as doc:
    print(doc.module_names())
    # ['ThisDocument', 'Module1']

    doc.set_module("Module1", 'Sub Hello()\r\n    MsgBox "hi"\r\nEnd Sub\r\n')
    doc.save()

PowerPoint

from pyopenvba import PowerPointFile

with PowerPointFile("presentation.pptm") as prs:
    print(prs.module_names())
    # ['Module1']

    prs.set_module("Module1", 'Sub Hello()\r\n    MsgBox "hi"\r\nEnd Sub\r\n')
    prs.save()

The API is identical across all three hosts: module_names(), get_module(), set_module(), save().


Create a brand-new file from scratch

Need a fresh macro-enabled file without launching Office? Use create_new() on any of the three file classes. The extension in the path controls the format:

from pyopenvba import ExcelFile, WordFile, PowerPointFile

# Excel - macro-enabled workbook (.xlsm) or binary workbook (.xlsb)
with ExcelFile.create_new("new_book.xlsm") as wb:
    wb.set_module("Module1", 'Sub Hello()\r\n    MsgBox "xlsm"\r\nEnd Sub\r\n')
    wb.save()

with ExcelFile.create_new("new_book.xlsb") as wb:
    wb.set_module("Module1", 'Sub Hello()\r\n    MsgBox "xlsb"\r\nEnd Sub\r\n')
    wb.save()

# Word - macro-enabled document (.docm)
with WordFile.create_new("new_doc.docm") as doc:
    doc.set_module("Module1", 'Sub Hello()\r\n    MsgBox "docm"\r\nEnd Sub\r\n')
    doc.save()

# PowerPoint - macro-enabled presentation (.pptm)
with PowerPointFile.create_new("new_prs.pptm") as prs:
    prs.set_module("Module1", 'Sub Hello()\r\n    MsgBox "pptm"\r\nEnd Sub\r\n')
    prs.save()

Each new file is built from a baked-in template captured from a freshly Office-authored file, so it opens cleanly with no repair prompt.


Add, rename, or delete a module

The same vba_project() API works for all three hosts:

from pyopenvba import ExcelFile, VBAModuleKind

with ExcelFile("workbook.xlsm") as wb:
    project = wb.vba_project()

    # Add a standard module
    project.add_module(
        "NewModule",
        'Sub Hi()\r\n    MsgBox "hi"\r\nEnd Sub\r\n',
        kind=VBAModuleKind.standard,
    )

    # Add a class module (VB_Base attribute is required)
    _CLASS_VB_BASE = "0{FCFB3D2A-A0FA-1068-A738-08002B3371B5}"
    project.add_module(
        "MyClass",
        f'Attribute VB_Name = "MyClass"\r\n'
        f'Attribute VB_Base = "{_CLASS_VB_BASE}"\r\n'
        "Attribute VB_GlobalNameSpace = False\r\n"
        "Attribute VB_Creatable = False\r\n"
        "Attribute VB_PredeclaredId = False\r\n"
        "Attribute VB_Exposed = False\r\n"
        "Attribute VB_TemplateDerived = False\r\n"
        "Attribute VB_Customizable = False\r\n"
        "Option Explicit\r\n",
        kind=VBAModuleKind.other,
    )

    project.rename_module("OldName", "NewName")
    project.delete_module("Obsolete")

    wb.save("out.xlsm")

Edit your macros as files on disk (recommended workflow)

This is the easiest way to manage VBA in a git repo. Export every module to a folder, edit the files in any text editor, then push the changes back.

Excel

From the command line:

# Pull every module out of the workbook into ./vba/
python -m pyopenvba pull workbook.xlsm ./vba

# ...edit ./vba/Module1.bas in your editor of choice...

# Push your edits back into the workbook
python -m pyopenvba push ./vba workbook.xlsm

# List modules without extracting
python -m pyopenvba ls workbook.xlsm

From Python:

from pyopenvba import pull, push

pull("workbook.xlsm", "./vba")
push("./vba", "workbook.xlsm")                    # in place
push("./vba", "workbook.xlsm", out="edited.xlsm") # to a new file

Word

from pyopenvba import pull_word, push_word

pull_word("document.docm", "./vba")
push_word("./vba", "document.docm")
push_word("./vba", "document.docm", out="edited.docm")

PowerPoint

from pyopenvba import pull_ppt, push_ppt

pull_ppt("presentation.pptm", "./vba")
push_ppt("./vba", "presentation.pptm")
push_ppt("./vba", "presentation.pptm", out="edited.pptm")

Module files use the extensions VBA already uses: .bas for standard modules, .cls for class modules and code-behind.


Supported formats

Excel

Extension What it is Read Write create_new
.xlsm Macro-enabled workbook yes yes yes
.xlsb Binary workbook yes yes yes
.xlam Macro-enabled add-in yes yes no
.xls Legacy (Excel 97-2003) yes yes no

Word

Extension What it is Read Write create_new
.docm Macro-enabled document yes yes yes
.dotm Macro-enabled template yes yes no
.doc Legacy (Word 97-2003) yes yes no

PowerPoint

Extension What it is Read Write create_new
.pptm Macro-enabled presentation yes yes yes
.potm Macro-enabled template yes yes no
.ppt Legacy (PowerPoint 97-2003) yes yes no

Every save is verified to reopen in the host application without the "we found a problem with some content" repair dialog.


Safety guards

save() refuses to silently produce a broken file.

Password-protected projects

If the VBA project is password-protected, any mutation will raise VBAProjectError unless you explicitly opt in:

wb.save(allow_protected=True)

The library never tries to decrypt or change the password - it just preserves the existing protection bytes verbatim. The resulting file still requires the original password to open the VBE.

Digitally-signed projects

A digital signature is invalidated by any change to the macros. On mutation, the library drops the stale signature streams and emits a UserWarning so you know trust has been removed:

import warnings
warnings.filterwarnings("error", category=UserWarning)   # treat as fatal

# ...or silence the warning if you accept the consequence:
wb.save(allow_invalidate_signature=True)

What's out of scope

This library is intentionally focused on module source code. The following are preserved byte-for-byte but not interpreted:

  • UserForm layout (controls, properties, positions). Editing the code-behind of a UserForm works fine; editing the design surface does not.
  • VBA project password decryption / re-encryption.
  • Re-signing digitally signed projects.
  • ActiveX license editing.

See docs/roadmap.md for the full feature matrix.


Architecture

src/pyopenvba/
  __init__.py     public API (ExcelFile, WordFile, PowerPointFile,
                              pull/push, pull_word/push_word, pull_ppt/push_ppt,
                              VBAModuleKind, exceptions)
  excel.py        ExcelFile facade (ZIP / CFB dispatch, pull/push helpers)
  word.py         WordFile facade
  powerpoint.py   PowerPointFile facade
  vba.py          VBA project parser + MS-OVBA codec
  cfb.py          MS-CFB (Compound File Binary) parser/writer
  exceptions.py   custom exception hierarchy
  _templates/     baked-in empty .xlsm/.xlsb/.docm/.pptm bytes for create_new()
  __main__.py     `python -m pyopenvba {pull,push,ls}` CLI

For deeper documentation:


Contributing

Bug reports, weird files that break the library, and PRs are all welcome. Please include the file (or a minimal redacted version) when filing a parsing bug.

Run the full local check (same as CI):

pip install -e ".[dev]"
pyright src tests
pytest -p no:randomly

CI runs the test matrix on Python 3.10 / 3.11 / 3.12 / 3.13 across Linux, plus 3.12 on Windows and macOS, on every push and pull request. Releases are published to PyPI automatically when a v*.*.* tag is pushed.


License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyopenvba-2.0.0.tar.gz (180.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyopenvba-2.0.0-py3-none-any.whl (167.5 kB view details)

Uploaded Python 3

File details

Details for the file pyopenvba-2.0.0.tar.gz.

File metadata

  • Download URL: pyopenvba-2.0.0.tar.gz
  • Upload date:
  • Size: 180.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyopenvba-2.0.0.tar.gz
Algorithm Hash digest
SHA256 13dffff806f70665707e9c6a484b2c82cefd4605a204138058eac9adf9b2f0e7
MD5 7950e9d915fc6505cbd4db5da2ddf639
BLAKE2b-256 cc00f69e20b9e6e95e3d798575e097791aae6db2aea0162533413320c20597e4

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyopenvba-2.0.0.tar.gz:

Publisher: publish.yml on WilliamSmithEdward/pyOpenVBA

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyopenvba-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: pyopenvba-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 167.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyopenvba-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 64fe0ca6042cef276144fb0c0a00dba5b6f45c4f17e2460c9e9be35c3301e9c7
MD5 8df2c37fd63bf8db389729f04a7237f5
BLAKE2b-256 b99e2cd1b38c820646434d20265342d6ded998a16978c88a75f189dc69c0e551

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyopenvba-2.0.0-py3-none-any.whl:

Publisher: publish.yml on WilliamSmithEdward/pyOpenVBA

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page