Releases: WilliamSmithEdward/pyOpenVBA
Releases · WilliamSmithEdward/pyOpenVBA
Release list
v3.0.1
v3.0.0
Added
AccessReader(EXPERIMENTAL) -- pure-Python read-only support for
Microsoft Access.accdb/.mdb(ACE / Jet 4) databases:AccessReader(path)parses the 4 KiB page-layout file header and
validates the ACE / Jet signature.iter_vba_modules()yields every embedded VBA module (VBAModule
dataclass withname,start_offset,attributes_text,source).
Modules are discovered by scanning for MS-OVBA stream signatures and
walking the LVAL page chains they live on -- no Access COM, no
MSysObjects parser required.vba_module_names()deduplicates shadow / undo copies and returns
the live module name list.read_vba_module(name)returns the user-visible source string with
\r\nline endings preserved; matches Access COM
CodeModule.Lines()output byte-for-byte (verified on a 1000-line
Module + 1000-line Class + 500-line Module live fixture against an
Access COM oracle).- Re-exported from
pyopenvbaasAccessReader. - Write path (re-compress + re-allocate LVAL pages) is not implemented;
Access support is read-only by design.
Changed
- BREAKING: Renamed
pyopenvba.accessmodule topyopenvba.access_read
and renamed theAccessFileclass toAccessReaderto make the
read-only access posture explicit. - Adopted strict static analysis: pyright
typeCheckingMode = "strict"
and a curated ruff lint configuration (E, F, W, B, UP, SIM, I, RUF, PIE, C4, PERF, N, TC, RET, TRY) now run clean acrosssrc/and
tests/with 0 errors.
Removed
- Pruned ~1800 lines of dead Access write-path / probe code and the
associated tests that exercised never-public APIs.
v2.0.1
Added
synthesize_class_header(name)-- new public helper (importable from
pyopenvba) that returns the standard eight-line attribute header for a
plain VBA class module, including the universalVB_BaseCLSID. It is
now also emitted automatically byadd_module(kind=VBAModuleKind.other)
when a bare body is supplied, matching the existing behaviour for standard
modules. Callers no longer need to construct or hard-code the CLSID
constant themselves.
Fixed
- README relative links were broken on PyPI. The links to
LICENSE.md,
docs/roadmap.md,docs/architecture.md, and
docs/ms-ovba-implementation-guide_v2.mdwere relative paths that
resolved correctly on GitHub but 404'd on the PyPI project page. All
five occurrences are now absolutegithub.com/blob/main/...URLs.
Changed
- Demo scripts (
create_new_excel_with_class_demo.py,
create_new_with_class_demo.py,create_new_word_with_class_demo.py,
inject_xlsb_with_class_demo.py) updated to use the body-only
add_modulecall, removing the manual_CLASS_VB_BASEconstant and
DATAMODEL_HEADERblock. - README Architecture section updated to include
synthesize_class_header
in the__init__.pypublic API listing.
v2.0.0
Added
WordFile-- full read/write support for Word macro-enabled files:
.docm,.dotm(OOXML/ZIP), and legacy.doc(raw CFB/BIFF8).
Exposes the same API asExcelFile:module_names(),get_module(),
set_module(),vba_project(),save(),pull_modules(),
push_modules().PowerPointFile-- full read/write support for PowerPoint
macro-enabled files:.pptm,.potm(OOXML/ZIP), and legacy.ppt
(raw CFB). Same API surface asExcelFileandWordFile.WordFile.create_new(path)-- create a brand-new.docmfrom
scratch without launching Word. Ships withThisDocumentand an empty
Module1; opens cleanly with no repair prompt.PowerPointFile.create_new(path)-- create a brand-new.pptm
from scratch without launching PowerPoint. Ships with an empty
Module1; opens cleanly with no repair prompt.ExcelFile.create_new()now supports.xlsbin addition to
.xlsm. The extension in the path controls which baked-in template is
used.pull_word(document, dest_dir)/push_word(src_dir, document)
-- disk-based pull/push helpers for Word, mirroring the Excelpull()
/push()API.pull_ppt(presentation, dest_dir)/push_ppt(src_dir, presentation)
-- disk-based pull/push helpers for PowerPoint.scripts/bake_xlsb_template.py-- bakes the empty.xlsbtemplate
blob into_templates/__init__.pyusing the same splice pattern as the
docm/pptm bake scripts.- Class module creation is now fully supported across all three hosts.
When adding a class module viaadd_module(kind=other), callers must
supply the full attribute header including
Attribute VB_Base = "0{FCFB3D2A-A0FA-1068-A738-08002B3371B5}"(the
universal VBA class CLSID); without it Office raises "Invalid procedure
call or argument" on instantiation.
Changed
pyproject.tomldescription updated to reflect all three supported
Office hosts;word,powerpoint,docm, andpptmadded to
keywords.- README fully updated: tagline, supported formats tables, 30-second tour,
create_newsection, and pull/push workflow section now cover Excel,
Word, and PowerPoint.
v1.1.1
Fixed
- Editing a document module's source via
set_module()(e.g.ThisWorkbook,
Sheet1) silently broke the workbook in Excel. The leading
Attribute VB_Name = "ThisWorkbook"/Attribute VB_Base = "..."/
Attribute VB_PredeclaredId = Trueheader lines that bind a document
module to its host object were being stripped on a source replacement.
Excel then re-compiled the module without those bindings and either
silently dropped the code or showed an empty module in the VBE.
Added
- VBE-style body-only source edits.
ExcelFile.set_module(name, text)
now accepts either a full source replacement (text beginning with
Attribute VB_*orVERSION ... CLASS) or a bare body. When a bare
body is supplied, the module's existing attribute header is
automatically re-prepended, matching the VBE UX where the user only
types the executable code. VBAModule.bodyproperty: read or write a module's executable body
without touching its attribute header.VBAModule.attribute_headerfield: the contiguous leading
VERSION ... CLASSblock +Attribute VB_*lines + separator,
captured at parse time.split_attribute_header(source) -> (header, body)public helper.add_module(name, body, kind=standard)now synthesizes a minimal
Attribute VB_Name = "<name>"header when the caller doesn't supply
one. Caller-supplied headers are passed through unchanged.add_module(kind=other)requires an explicit attribute header.
pyOpenVBA refuses to invent class or document module headers since
their host-binding metadata can't be safely guessed.rename_module()re-keys the in-sourceAttribute VB_Name = "..."
line to the new logical name so the source matches the dir-stream
binding.- New
TestAttributeHeaderPreservationtest class covering:
header splitting (standard, document, class, headerless),
set_modulebody-only preservation on a document module,
set_modulefull-source replacement,
add_moduleheader synthesis vs. caller-supplied,
add_module(kind=other)rejection without a header,
and theVBAModule.bodyproperty round-trip.
v1.1.0
Added
ExcelFile.create_new(path)-- create a brand-new macro-enabled
workbook from scratch in pure Python, without ever launching Excel.
The new file ships with a fresh VBA project containingThisWorkbook,
Sheet1, and an emptyModule1, opens cleanly in Excel with no
"found a problem with some content" repair prompt, and is ready for
immediate edits via the normalvba_project()/save()flow.- New
TestExcelFileCreateNewtest class covering write-out, expected
modules, emptyModule1, round-trip with user code, overwrite of an
existing file, and creation of missing parent directories.
Internal
- New
src/pyopenvba/_templates/__init__.pymodule embedding a
byte-for-byte clone of a freshly Excel-authored empty.xlsmas a
zlib-compressed base85 constant. No binary fixtures are shipped in the
wheel; the template is regenerated byscripts/bake_empty_template.py
fromtests/live_excel_testing/freshly_touched.xlsm.
v1.0.1 - MS-OVBA compression and PROJECT-stream fixes
Fixed
- Excel rejected modules whose source spanned more than one 4 KB chunk with "An error occurred while loading ". The MS-OVBA compressor was emitting raw (CompressedChunkFlag = 0) chunks for full 4096-byte blocks. Although spec-legal, Office itself never writes raw chunks for module source streams -- empirically confirmed against an Excel-authored workbook containing a 16,881-byte module (all five of its chunks were token-compressed). The compressor now always emits token-compressed (flag = 1) chunks for module source; raw chunks remain only as a fallback for adversarial 4096-byte high-entropy input that overflows LZ encoding.
- Re-running an add-module workflow after a delete produced duplicate
PROJECTentries, which Excel treats as corruption. Callingadd_module(name, ...)afterdelete_module(name)in the same save now cancels the pending delete and treats the operation as a source rewrite, matching Excel's own behaviour.serialize_project_streamadditionally scrubs duplicateModule=and workspace declarations on every structural save, healing files that were corrupted by earlier versions.
Added
demo/folder containing a runnable end-to-end demo (push_demo_module.py+test_macro_workbook.xlsm+demo.md).- New regression tests:
TestCompress.test_full_chunk_emitted_as_token_compressed_not_rawandTestCompress.test_long_module_round_trip_through_excel_saveverify that no raw chunks are produced for realistic VBA source.TestLargeModuleFixtureuses an Excel-authored 16 KB module as an empirical anchor and round-trips it through pyOpenVBA's saver.test_delete_then_readd_same_name_does_not_duplicate_project_declandtest_save_heals_preexisting_duplicate_project_declarationscover the PROJECT-stream fix.
tests/live_excel_testing/large_vba_module.xlsmfixture (Excel-authored reference for multi-chunk module compression).
Full changelog: https://github.com/WilliamSmithEdward/pyOpenVBA/blob/main/docs/changelog.md
Compare: v1.0.0...v1.0.1
v1.0.0: Feature-complete VBA round-trip for .xlsm / .xlsb / .xlam / .xls
First stable release of pyOpenVBA — a pure-Python, zero-dependency
reader/writer for VBA macros embedded in Microsoft Office files.
Highlights
- Full round-trip of VBA project source across
.xlsm,.xlsb,
.xlam, and legacy.xls, verified to reopen in Excel without any
repair dialog. - Topology operations: add, rename, and delete modules with
consistent updates acrossdir,PROJECT,PROJECTwm, and the
underlying CFB streams. - Safety gates:
- Refuses to mutate password-protected projects unless
allow_protected=Trueis passed. - Drops invalidated digital signatures on mutation with a warning,
silenceable viaallow_invalidate_signature=True.
- Refuses to mutate password-protected projects unless
_VBA_PROJECTcache invalidation per MS-OVBA 2.3.4.1 on every
mutating save (header preserved, performance cache zeroed).__SRP_*stripping on save so Office regenerates a clean
performance cache.- Disk workflow:
pull/pushhelpers for round-tripping
module sources to and from a directory of.bas/.cls/.frm
files. - Zero runtime dependencies, supports Python 3.10+.
- Strict Pyright clean (0 errors) on both
src/andtests/.
Quality
- 244 passing tests, 2 documented skips, 0 xfails.
- Persistent fuzz corpus (~50 seeds across CFB, OVBA decompression,
dir,PROJECT, andPROJECTwmparsers). - Manual Excel verification matrix (9/9) covering no-op, source edit,
add, rename, delete, xlsm + xlsb, and protected mutation.
Documentation
- docs/ms-ovba-implementation-guide_v2.md
— language-agnostic, distilled guide for implementing MS-OVBA in any
language; supersedes v1. - docs/architecture.md — internal layering
and module map. - docs/roadmap.md — all 26 implementation gates
closed (24 PASS, 2 explicitly out of scope).
Out of scope (by design)
- UserForm layout editing (designer sub-storage preserved
verbatim; code-behind edits are fully supported). - VBA project password decryption / re-encryption.
- Re-signing digitally signed projects.
Install
Clone and install in editable mode:
git clone https://github.com/WilliamSmithEdward/pyOpenVBA
pip install -e ./pyOpenVBA
PyPI wheel coming in a follow-up release.
Acknowledgements
Built against [MS-OVBA] v20260519 and [MS-CFB] v3, with live
verification on Excel for Microsoft 365 (May 2026).