Skip to main content

pyPrettyVBA

A formatter for VBA, in exported modules (.bas, .cls, .frm) or right inside Office files (.xlsm, .docm, .pptm, .accdb and more). It is in the spirit of Prettier but configurable: every change it makes belongs to a named rule that can be switched off, tuned, or suppressed for one line, a region or a whole module. It spells code the way the VBE spells it, lays it out the way you configure, and refuses to return output that would run differently from the input.

Before:

public function RestockList(ws as worksheet) as collection
dim r as long,qty as long,result as new collection
on error goto fail
for r=2 to ws.cells(ws.rows.count,1).end(xlup).row
qty=ws.cells(r,3).value
if qty<low_stock then
result.add ws.cells(r,1).value
elseif qty=0 then result.add ws.cells(r,1).value & " (out)"
end if
next r

After pyprettyvba format:

Public Function RestockList(ws As Worksheet) As Collection
    Dim r As Long, qty As Long, result As New Collection
    On Error GoTo fail
    For r = 2 To ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
        qty = ws.Cells(r, 3).Value
        If qty < LOW_STOCK Then
            result.Add ws.Cells(r, 1).Value
        ElseIf qty = 0 Then result.Add ws.Cells(r, 1).Value & " (out)"
        End If
    Next r

The whole module, before and after, is in tests/fixtures/showcase.

Install

pyPrettyVBA needs Python 3.11 or later. Its one dependency is pyOpenVBA, which reads and writes the VBA inside Office files and is pure Python with no dependencies of its own.

pip install pyprettyvba

Use

pyprettyvba format src/            # format every module under src/ in place
pyprettyvba format --check src/    # exit 1 if anything would change
pyprettyvba format --diff src/     # show the changes, write nothing
pyprettyvba check src/             # list what each rule found, by line
pyprettyvba check --fix src/       # fix what can be fixed, list the rest
pyprettyvba rules                  # every rule, on or off, fixable or not
pyprettyvba rules indent           # one rule's options
pyprettyvba config src/Module1.bas # the settings that apply to a file
pyprettyvba init                   # write a starter pyprettyvba.toml

check prints one violation per line, file:line:column: rule message, with [*] on the ones formatting fixes. --output-format also offers grouped, json and github (workflow annotations), and --statistics counts them by rule. A path of - reads standard input. Exit codes: 0 clean, 1 findings or changes, 2 an error.

From Python:

from pyprettyvba import Config, format_source

result = format_source(source_text, Config({"preset": "vbe"}))
result.output       # the formatted text
result.violations   # what each rule found, with line and column in the input

format_file and format_paths read and write files; project_names gathers what a project's modules declare, so that a name declared in one module is spelled the same way in the others.

Office files

The VBA inside a workbook, document, presentation or database is checked and formatted in place, with no Office installed:

pyprettyvba check Book1.xlsm        # Book1.xlsm:Module1:12:5: spacing ...
pyprettyvba format --diff Book1.xlsm
pyprettyvba format Book1.xlsm

Excel (.xlsm, .xlsb, .xlam, .xls), Word (.docm, .dotm, .doc), PowerPoint (.pptm, .potm, .ppt) and Access (.accdb, .mdb) files are read and written through pyOpenVBA. The modules of a file are one project, as they are in the VBE, so a name one of them declares is spelled its way in the others. The file is saved beside the original and then moved over it, so an interrupted save changes nothing. A project with a digital signature is left unwritten, since any edit would invalidate the signature. --remove-signatures (remove_signatures=True from Python) writes it anyway and removes the signature, for you to sign the project again in the VBE. The signature is found in the zip-based files; in an .xls, .doc, .ppt or Access file it is not recognized, and formatting leaves it out of date. A password-protected project is written, and keeps its password and its lock.

Office files are formatted when named on the command line. Formatting a directory takes module files only, unless an include pattern names Office files too (see configuration).

From Python, format_office_file("Book1.xlsm", write=True) returns one result per module.

Presets

Preset What it does
default What the VBE does to each line (casing, spacing, literal spelling), plus indentation, blank lines and line endings.
vbe Only what the VBE itself does to code it reads in, so the VBE has nothing left to change (the exceptions are listed in docs/vbe-evidence.md).
xlide What XLIDE's Format Document does: indentation, casing and inserted spaces, never removing a space or a line.
strict The defaults plus every style rule: one statement per line, ' comments with a space, no Let, aligned declarations.
minimal Whitespace only: trailing whitespace, the end of the file, line endings.
none Nothing but checking suppression directives; a base for enabling rules one by one.

Rules

Twenty rules, each documented with its options in docs/rules.md: keyword and identifier casing, spacing, numeric and date literal spelling, statement forms, indentation (block structure, Case arms, #If blocks, labels, continuation lines), end-of-line comments, blank lines, trailing whitespace, line endings, and a handful of optional style rules.

Configuration

A pyprettyvba.toml (or a [tool.pyprettyvba] table in pyproject.toml) next to the code, or in any directory above it:

preset = "default"
indent-width = 4
line-ending = "crlf"

[rules]
blank-lines = { max-consecutive = 1 }
comment-space = true
numeric-literals = false

[[overrides]]
files = ["legacy/**"]
rules = { indent = false }

See docs/configuration.md for every key.

Suppression

x   =  1   '@prettyvba-ignore: spacing -- aligned on purpose
'@prettyvba-ignore-next-line: indent
'@prettyvba-ignore-start
    table(0) = Array("id",   "name")
'@prettyvba-ignore-end

See docs/suppression.md.

Safety

Whitespace and letter case are not always meaningless in VBA. s&t is a syntax error where s & t concatenates; Foo .Bar passes a With member where Foo.Bar calls one; a comment ending in _ swallows the next line; a Declare without an Alias looks its entry point up case-sensitively. Before it returns anything, the formatter reduces the input and the output to the statements they run and compares them, and it refuses output that differs. Property-based tests, a corpus of 336 real modules, and the VBE itself (below) check that this never has to happen.

What the VBE does, measured

The rules that claim to do what the VBE does are checked against the VBE: probe modules pasted into Excel and exported again, compile checks, run-time checks, and whole modules imported from files. The recording is replayed by the test suite without Office. See docs/vbe-evidence.md.

Development

pip install -e .[dev]
python -m pytest                 # unit tests, fixtures, properties, oracle replay
python -m pytest -m live         # against a real VBE (Windows, Excel, pyVBAharness)

See CONTRIBUTING.md for the fixture workflow, the corpus and property tests, and how to add a rule.

License

MIT. See LICENSE.

Metadata

Release files for pyprettyvba 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyprettyvba 0.1.0
File Size Uploaded
pyprettyvba-0.1.0.tar.gz 659.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyprettyvba 0.1.0
File Interpreter ABI Platform
pyprettyvba-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.2 MB

Release files / pyprettyvba-0.1.0.tar.gz

Download URL pyprettyvba-0.1.0.tar.gz
Size 659.9 kB
Tags Source
SHA-256 checksum
How to use checksums
77e7412a7b3fb8cc654aec921097083f95ddb7406a30cd0fcbb6dd9a947ae463
BLAKE2b-256 checksum
How to use checksums
a94ae64bf703298b2dd91f94a95d68d0459bc586a5a43e5953c39f8ab6f56726
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / pyprettyvba-0.1.0-py3-none-any.whl

Download URL pyprettyvba-0.1.0-py3-none-any.whl
Size 546.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2cc054c31752ad23ec5f52e03d82bb0c1a22ce8b5fd91460ca078521fadf6449
BLAKE2b-256 checksum
How to use checksums
2d2fc7cd65b91f9a34109018476da8f41468bda90985dcef6de63882e9f5a0a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page