pyOpenVBA 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ *.so
7
+ *.egg
8
+ *.egg-info/
9
+ dist/
10
+ build/
11
+ .eggs/
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+
18
+ # Distribution / packaging
19
+ *.whl
20
+ *.tar.gz
21
+ MANIFEST
22
+
23
+ # Pytest
24
+ .pytest_cache/
25
+ .cache/
26
+
27
+ # Type checking
28
+ .pyright/
29
+
30
+ # Editor / OS
31
+ .vscode/settings.json
32
+ .DS_Store
33
+ Thumbs.db
34
+
35
+ # Test artifacts
36
+ tests/fixtures/*.xlsm
37
+ tests/fixtures/*.xls
38
+
39
+ # Manually-generated Excel verification set (rebuilt by
40
+ # scripts/build_excel_verification_set.py)
41
+ tests/live_excel_testing/_excel_verify/
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2026 William Smith
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,285 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyOpenVBA
3
+ Version: 1.0.0
4
+ Summary: Read and write VBA macros inside Excel workbooks (.xlsm, .xlsb, .xlam, .xls) in pure Python, no dependencies.
5
+ Project-URL: Homepage, https://github.com/WilliamSmithEdward/pyOpenVBA
6
+ Project-URL: Repository, https://github.com/WilliamSmithEdward/pyOpenVBA
7
+ Project-URL: Issues, https://github.com/WilliamSmithEdward/pyOpenVBA/issues
8
+ Project-URL: Documentation, https://github.com/WilliamSmithEdward/pyOpenVBA#readme
9
+ Author-email: William Smith <williamsmithe@icloud.com>
10
+ License: MIT
11
+ License-File: LICENSE.md
12
+ Keywords: excel,macros,ms-cfb,ms-ovba,office,vba,xlam,xls,xlsb,xlsm
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: Office/Business
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Topic :: System :: Archiving
28
+ Classifier: Typing :: Typed
29
+ Requires-Python: >=3.10
30
+ Description-Content-Type: text/markdown
31
+
32
+ # pyOpenVBA
33
+
34
+ **Read and write the VBA macros inside Excel workbooks, in pure Python.**
35
+
36
+ No external dependencies. No Excel install required. Works on Windows,
37
+ macOS, and Linux. Python 3.10 or newer.
38
+
39
+ Supports `.xlsm`, `.xlsb`, `.xlam`, and legacy `.xls`.
40
+
41
+ ---
42
+
43
+ ## Why use this?
44
+
45
+ Several excellent Python tools already exist for **reading** VBA out of
46
+ Excel files (oletools, olefile, and friends), and they remain the right
47
+ choice for forensics, malware analysis, and audit use-cases. pyOpenVBA
48
+ focuses on the next step: safely **writing** changes back so the
49
+ workbook still opens cleanly in Excel.
50
+
51
+ The write path is the whole point of the library:
52
+
53
+ - **Replace** a module's source in place.
54
+ - **Add** a new standard module, class module, or document/UserForm
55
+ code-behind.
56
+ - **Rename** any module (the CFB stream, `dir` record, `PROJECT`
57
+ declaration, `PROJECTwm` name map, and `Attribute VB_Name` are all
58
+ updated in lockstep).
59
+ - **Delete** a module cleanly.
60
+ - **Save** the workbook and have it reopen in Excel with no "we found
61
+ a problem with some content" repair dialog. Every supported format
62
+ (`.xlsm`, `.xlsb`, `.xlam`, `.xls`) is verified against live Excel.
63
+ - **Safely refuse** to corrupt password-protected or digitally signed
64
+ projects unless you explicitly opt in.
65
+
66
+ That makes it a good fit for:
67
+
68
+ - **Version-controlling your VBA** in git like normal source code, then
69
+ pushing edits back without ever opening Excel.
70
+ - **Diffing** two workbooks to see what changed in `Module1`.
71
+ - **Generating or updating macros from a script** without scripting
72
+ Excel through COM automation.
73
+ - **Reading and writing macros on a server** (Linux / CI) where Excel
74
+ is not installed.
75
+
76
+ pyOpenVBA is a complete read-and-write library, so it covers the full
77
+ lifecycle of a VBA project in one place: extract, edit, version, write
78
+ back, and verify. If you only ever need to read, the existing tools
79
+ remain a solid choice; if you might ever need to write, start here and
80
+ you will not have to switch later.
81
+
82
+ ## Installation
83
+
84
+ ```bash
85
+ pip install pyOpenVBA
86
+ ```
87
+
88
+ Or from source:
89
+
90
+ ```bash
91
+ git clone https://github.com/WilliamSmithEdward/pyOpenVBA
92
+ cd pyOpenVBA
93
+ pip install -e .
94
+ ```
95
+
96
+ That's it. There are no other dependencies.
97
+
98
+ ---
99
+
100
+ ## 30-second tour
101
+
102
+ ```python
103
+ from pyopenvba import ExcelFile
104
+
105
+ with ExcelFile("workbook.xlsm") as wb:
106
+ # 1. List all VBA modules in the workbook.
107
+ print(wb.module_names())
108
+ # ['ThisWorkbook', 'Sheet1', 'Module1']
109
+
110
+ # 2. Read a module's source as a string.
111
+ source = wb.get_module("Module1")
112
+ print(source)
113
+
114
+ # 3. Edit a module and save the workbook.
115
+ wb.set_module("Module1", 'Sub Hello()\r\n MsgBox "hi"\r\nEnd Sub\r\n')
116
+ wb.save() # overwrites the original file
117
+ # wb.save("edited.xlsm") # ...or save to a new file
118
+ ```
119
+
120
+ That is the entire core API. Three methods.
121
+
122
+ ---
123
+
124
+ ## Add, rename, or delete a module
125
+
126
+ ```python
127
+ from pyopenvba import ExcelFile, VBAModuleKind
128
+
129
+ with ExcelFile("workbook.xlsm") as wb:
130
+ project = wb.vba_project()
131
+
132
+ project.add_module(
133
+ "NewModule",
134
+ "Sub Hi()\r\n MsgBox \"hi\"\r\nEnd Sub\r\n",
135
+ kind=VBAModuleKind.standard,
136
+ )
137
+ project.rename_module("OldName", "NewName")
138
+ project.delete_module("Obsolete")
139
+
140
+ wb.save("out.xlsm")
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Edit your macros as files on disk (recommended workflow)
146
+
147
+ This is the easiest way to manage VBA in a git repo. Export every
148
+ module to a folder, edit the files in any text editor, then push the
149
+ changes back into the workbook.
150
+
151
+ From the command line:
152
+
153
+ ```bash
154
+ # 1. Pull every module out of the workbook into ./vba/
155
+ python -m pyopenvba pull workbook.xlsm ./vba
156
+
157
+ # 2. ...edit ./vba/Module1.bas in your editor of choice...
158
+
159
+ # 3. Push your edits back into the workbook
160
+ python -m pyopenvba push ./vba workbook.xlsm
161
+
162
+ # Bonus: list modules without extracting anything
163
+ python -m pyopenvba ls workbook.xlsm
164
+ ```
165
+
166
+ From Python:
167
+
168
+ ```python
169
+ from pyopenvba import pull, push
170
+
171
+ pull("workbook.xlsm", "./vba")
172
+ # ...edit files...
173
+ push("./vba", "workbook.xlsm") # in place
174
+ push("./vba", "workbook.xlsm", out="edited.xlsm") # to a new file
175
+ ```
176
+
177
+ Module files use the extensions VBA already uses: `.bas` for standard
178
+ modules, `.cls` for class modules, `.frm` for UserForm code-behind.
179
+
180
+ ---
181
+
182
+ ## Supported formats
183
+
184
+ | Extension | What it is | Read | Write |
185
+ |-----------|------------------------------|:----:|:-----:|
186
+ | `.xlsm` | Macro-enabled workbook | yes | yes |
187
+ | `.xlsb` | Binary workbook | yes | yes |
188
+ | `.xlam` | Macro-enabled add-in | yes | yes |
189
+ | `.xls` | Legacy (Excel 97-2003) | yes | yes |
190
+
191
+ Every save is verified to reopen in Excel **without** the "we found a
192
+ problem with some content" repair dialog.
193
+
194
+ ---
195
+
196
+ ## Safety guards
197
+
198
+ `save()` refuses to silently produce a broken workbook.
199
+
200
+ ### Password-protected projects
201
+
202
+ If the VBA project is password-protected, any mutation will raise
203
+ `VBAProjectError` unless you explicitly opt in:
204
+
205
+ ```python
206
+ wb.save(allow_protected=True)
207
+ ```
208
+
209
+ The library never tries to decrypt or change the password - it just
210
+ preserves the existing protection bytes verbatim. The resulting
211
+ workbook still requires the original password to open the VBE.
212
+
213
+ ### Digitally-signed projects
214
+
215
+ A digital signature is invalidated by *any* change to the macros. On
216
+ mutation, the library drops the stale signature streams and emits a
217
+ `UserWarning` so you know trust has been removed:
218
+
219
+ ```python
220
+ import warnings
221
+ warnings.filterwarnings("error", category=UserWarning) # treat as fatal
222
+
223
+ # ...or silence the warning if you accept the consequence:
224
+ wb.save(allow_invalidate_signature=True)
225
+ ```
226
+
227
+ ---
228
+
229
+ ## What's out of scope
230
+
231
+ This library is intentionally focused on **module source code**. The
232
+ following are preserved byte-for-byte but not interpreted:
233
+
234
+ - UserForm **layout** (controls, properties, positions). Editing the
235
+ **code-behind** of a UserForm works fine; editing the design surface
236
+ does not.
237
+ - VBA project password decryption / re-encryption.
238
+ - Re-signing digitally signed projects.
239
+ - ActiveX license editing.
240
+
241
+ See [docs/roadmap.md](docs/roadmap.md) for the full feature matrix.
242
+
243
+ ---
244
+
245
+ ## Architecture
246
+
247
+ ```
248
+ src/pyopenvba/
249
+ __init__.py public API (ExcelFile, pull, push, exceptions)
250
+ excel.py ExcelFile facade (ZIP / CFB dispatch, pull/push helpers)
251
+ vba.py VBA project parser + MS-OVBA codec
252
+ cfb.py MS-CFB (Compound File Binary) parser/writer
253
+ exceptions.py custom exception hierarchy
254
+ __main__.py `python -m pyopenvba {pull,push,ls}` CLI
255
+ ```
256
+
257
+ For deeper documentation:
258
+
259
+ - [docs/architecture.md](docs/architecture.md) - internal module layout.
260
+ - [docs/ms-ovba-implementation-guide_v2.md](docs/ms-ovba-implementation-guide_v2.md) -
261
+ language-agnostic guide for re-implementing MS-OVBA in another
262
+ language.
263
+ - [docs/roadmap.md](docs/roadmap.md) - per-feature implementation status.
264
+
265
+ ---
266
+
267
+ ## Contributing
268
+
269
+ Bug reports, weird workbooks that break the library, and PRs are all
270
+ welcome. Please include the workbook (or a minimal redacted version)
271
+ when filing a parsing bug.
272
+
273
+ Run the test suite:
274
+
275
+ ```bash
276
+ pip install pytest pyright
277
+ pytest
278
+ pyright src tests
279
+ ```
280
+
281
+ ---
282
+
283
+ ## License
284
+
285
+ [MIT](LICENSE.md).
@@ -0,0 +1,254 @@
1
+ # pyOpenVBA
2
+
3
+ **Read and write the VBA macros inside Excel workbooks, in pure Python.**
4
+
5
+ No external dependencies. No Excel install required. Works on Windows,
6
+ macOS, and Linux. Python 3.10 or newer.
7
+
8
+ Supports `.xlsm`, `.xlsb`, `.xlam`, and legacy `.xls`.
9
+
10
+ ---
11
+
12
+ ## Why use this?
13
+
14
+ Several excellent Python tools already exist for **reading** VBA out of
15
+ Excel files (oletools, olefile, and friends), and they remain the right
16
+ choice for forensics, malware analysis, and audit use-cases. pyOpenVBA
17
+ focuses on the next step: safely **writing** changes back so the
18
+ workbook still opens cleanly in Excel.
19
+
20
+ The write path is the whole point of the library:
21
+
22
+ - **Replace** a module's source in place.
23
+ - **Add** a new standard module, class module, or document/UserForm
24
+ code-behind.
25
+ - **Rename** any module (the CFB stream, `dir` record, `PROJECT`
26
+ declaration, `PROJECTwm` name map, and `Attribute VB_Name` are all
27
+ updated in lockstep).
28
+ - **Delete** a module cleanly.
29
+ - **Save** the workbook and have it reopen in Excel with no "we found
30
+ a problem with some content" repair dialog. Every supported format
31
+ (`.xlsm`, `.xlsb`, `.xlam`, `.xls`) is verified against live Excel.
32
+ - **Safely refuse** to corrupt password-protected or digitally signed
33
+ projects unless you explicitly opt in.
34
+
35
+ That makes it a good fit for:
36
+
37
+ - **Version-controlling your VBA** in git like normal source code, then
38
+ pushing edits back without ever opening Excel.
39
+ - **Diffing** two workbooks to see what changed in `Module1`.
40
+ - **Generating or updating macros from a script** without scripting
41
+ Excel through COM automation.
42
+ - **Reading and writing macros on a server** (Linux / CI) where Excel
43
+ is not installed.
44
+
45
+ pyOpenVBA is a complete read-and-write library, so it covers the full
46
+ lifecycle of a VBA project in one place: extract, edit, version, write
47
+ back, and verify. If you only ever need to read, the existing tools
48
+ remain a solid choice; if you might ever need to write, start here and
49
+ you will not have to switch later.
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ pip install pyOpenVBA
55
+ ```
56
+
57
+ Or from source:
58
+
59
+ ```bash
60
+ git clone https://github.com/WilliamSmithEdward/pyOpenVBA
61
+ cd pyOpenVBA
62
+ pip install -e .
63
+ ```
64
+
65
+ That's it. There are no other dependencies.
66
+
67
+ ---
68
+
69
+ ## 30-second tour
70
+
71
+ ```python
72
+ from pyopenvba import ExcelFile
73
+
74
+ with ExcelFile("workbook.xlsm") as wb:
75
+ # 1. List all VBA modules in the workbook.
76
+ print(wb.module_names())
77
+ # ['ThisWorkbook', 'Sheet1', 'Module1']
78
+
79
+ # 2. Read a module's source as a string.
80
+ source = wb.get_module("Module1")
81
+ print(source)
82
+
83
+ # 3. Edit a module and save the workbook.
84
+ wb.set_module("Module1", 'Sub Hello()\r\n MsgBox "hi"\r\nEnd Sub\r\n')
85
+ wb.save() # overwrites the original file
86
+ # wb.save("edited.xlsm") # ...or save to a new file
87
+ ```
88
+
89
+ That is the entire core API. Three methods.
90
+
91
+ ---
92
+
93
+ ## Add, rename, or delete a module
94
+
95
+ ```python
96
+ from pyopenvba import ExcelFile, VBAModuleKind
97
+
98
+ with ExcelFile("workbook.xlsm") as wb:
99
+ project = wb.vba_project()
100
+
101
+ project.add_module(
102
+ "NewModule",
103
+ "Sub Hi()\r\n MsgBox \"hi\"\r\nEnd Sub\r\n",
104
+ kind=VBAModuleKind.standard,
105
+ )
106
+ project.rename_module("OldName", "NewName")
107
+ project.delete_module("Obsolete")
108
+
109
+ wb.save("out.xlsm")
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Edit your macros as files on disk (recommended workflow)
115
+
116
+ This is the easiest way to manage VBA in a git repo. Export every
117
+ module to a folder, edit the files in any text editor, then push the
118
+ changes back into the workbook.
119
+
120
+ From the command line:
121
+
122
+ ```bash
123
+ # 1. Pull every module out of the workbook into ./vba/
124
+ python -m pyopenvba pull workbook.xlsm ./vba
125
+
126
+ # 2. ...edit ./vba/Module1.bas in your editor of choice...
127
+
128
+ # 3. Push your edits back into the workbook
129
+ python -m pyopenvba push ./vba workbook.xlsm
130
+
131
+ # Bonus: list modules without extracting anything
132
+ python -m pyopenvba ls workbook.xlsm
133
+ ```
134
+
135
+ From Python:
136
+
137
+ ```python
138
+ from pyopenvba import pull, push
139
+
140
+ pull("workbook.xlsm", "./vba")
141
+ # ...edit files...
142
+ push("./vba", "workbook.xlsm") # in place
143
+ push("./vba", "workbook.xlsm", out="edited.xlsm") # to a new file
144
+ ```
145
+
146
+ Module files use the extensions VBA already uses: `.bas` for standard
147
+ modules, `.cls` for class modules, `.frm` for UserForm code-behind.
148
+
149
+ ---
150
+
151
+ ## Supported formats
152
+
153
+ | Extension | What it is | Read | Write |
154
+ |-----------|------------------------------|:----:|:-----:|
155
+ | `.xlsm` | Macro-enabled workbook | yes | yes |
156
+ | `.xlsb` | Binary workbook | yes | yes |
157
+ | `.xlam` | Macro-enabled add-in | yes | yes |
158
+ | `.xls` | Legacy (Excel 97-2003) | yes | yes |
159
+
160
+ Every save is verified to reopen in Excel **without** the "we found a
161
+ problem with some content" repair dialog.
162
+
163
+ ---
164
+
165
+ ## Safety guards
166
+
167
+ `save()` refuses to silently produce a broken workbook.
168
+
169
+ ### Password-protected projects
170
+
171
+ If the VBA project is password-protected, any mutation will raise
172
+ `VBAProjectError` unless you explicitly opt in:
173
+
174
+ ```python
175
+ wb.save(allow_protected=True)
176
+ ```
177
+
178
+ The library never tries to decrypt or change the password - it just
179
+ preserves the existing protection bytes verbatim. The resulting
180
+ workbook still requires the original password to open the VBE.
181
+
182
+ ### Digitally-signed projects
183
+
184
+ A digital signature is invalidated by *any* change to the macros. On
185
+ mutation, the library drops the stale signature streams and emits a
186
+ `UserWarning` so you know trust has been removed:
187
+
188
+ ```python
189
+ import warnings
190
+ warnings.filterwarnings("error", category=UserWarning) # treat as fatal
191
+
192
+ # ...or silence the warning if you accept the consequence:
193
+ wb.save(allow_invalidate_signature=True)
194
+ ```
195
+
196
+ ---
197
+
198
+ ## What's out of scope
199
+
200
+ This library is intentionally focused on **module source code**. The
201
+ following are preserved byte-for-byte but not interpreted:
202
+
203
+ - UserForm **layout** (controls, properties, positions). Editing the
204
+ **code-behind** of a UserForm works fine; editing the design surface
205
+ does not.
206
+ - VBA project password decryption / re-encryption.
207
+ - Re-signing digitally signed projects.
208
+ - ActiveX license editing.
209
+
210
+ See [docs/roadmap.md](docs/roadmap.md) for the full feature matrix.
211
+
212
+ ---
213
+
214
+ ## Architecture
215
+
216
+ ```
217
+ src/pyopenvba/
218
+ __init__.py public API (ExcelFile, pull, push, exceptions)
219
+ excel.py ExcelFile facade (ZIP / CFB dispatch, pull/push helpers)
220
+ vba.py VBA project parser + MS-OVBA codec
221
+ cfb.py MS-CFB (Compound File Binary) parser/writer
222
+ exceptions.py custom exception hierarchy
223
+ __main__.py `python -m pyopenvba {pull,push,ls}` CLI
224
+ ```
225
+
226
+ For deeper documentation:
227
+
228
+ - [docs/architecture.md](docs/architecture.md) - internal module layout.
229
+ - [docs/ms-ovba-implementation-guide_v2.md](docs/ms-ovba-implementation-guide_v2.md) -
230
+ language-agnostic guide for re-implementing MS-OVBA in another
231
+ language.
232
+ - [docs/roadmap.md](docs/roadmap.md) - per-feature implementation status.
233
+
234
+ ---
235
+
236
+ ## Contributing
237
+
238
+ Bug reports, weird workbooks that break the library, and PRs are all
239
+ welcome. Please include the workbook (or a minimal redacted version)
240
+ when filing a parsing bug.
241
+
242
+ Run the test suite:
243
+
244
+ ```bash
245
+ pip install pytest pyright
246
+ pytest
247
+ pyright src tests
248
+ ```
249
+
250
+ ---
251
+
252
+ ## License
253
+
254
+ [MIT](LICENSE.md).