VBAtoPython
Start Conversion
← All articles

What VBAtoPython Handles — And What It Doesn't

Honest coverage guide for the VBAtoPython converter. Exactly which VBA patterns translate cleanly, which need review, and which we won't touch — based on a 374-file real-world corpus.

Most VBA-to-Python converters either quietly produce broken code or hallucinate an idiomatic pandas rewrite and hope you don't notice the behavior change. We don't. This page is the honest reference for exactly what VBAtoPython handles, what it handles with warnings, and what it won't touch.

Our converter is deterministic and rule-based. It translates your VBA line-by-line — the output preserves the structure and control flow of your original code so you can audit it. That's the pitch for finance, payroll, and accounting teams who need to know their macro still does the same thing after the port.

We benchmark against a 374-file corpus of real-world VBA projects scraped from GitHub. Today we pass 76% cleanly (285 of 374 files produce syntactically valid, runnable Python), and the categories below are based on exactly what's in that corpus plus what real customers submit.

✅ Handles cleanly — no review needed

These patterns produce Python that runs without edits. Faithful line-by-line translation, preserved control flow, named variables kept intact.

Procedures and control flow

  • Sub / Function / Property Get/Let/Set with Dim, type hints, parameters, ByRef / ByVal
  • If / ElseIf / Else / End If
  • Select Caseif/elif/else
  • For/Next, For Each/Next, Do/While/Loop, Exit Sub/Exit Function
  • With blocks flattened

Standard modules and class modules

  • .bas standard modules with Sub/Function definitions
  • .cls class modules with private/public members, Property Get/Let/Set
  • Multi-module projects — class modules talking to standard modules via method calls

Strings and numbers

  • Trim, Left, Right, Mid, Len, InStr, Replace, LCase, UCase
  • Format (common patterns: currency, dates, numbers)
  • Arithmetic, comparison, Like operator
  • Val, CStr, CInt, CDbl, CDate type conversions

Collections and dictionaries

  • Scripting.Dictionary → Python dict
  • Collection → Python list or dict (depending on usage)
  • Add, Remove, Count, Exists, iteration

Basic Excel I/O

  • Range(...), Cells(r, c), .Value
  • Bulk array reads/writes: Range(Cells(1,1), Cells(n,m)).Valuews.iter_rows(...) or per-cell writes
  • Sheets(N) / Worksheets("Name")workbook.worksheets[N-1] / workbook["Name"]
  • Workbooks.Add, .Open, .Save, .SaveAs, .Closeopenpyxl.Workbook(), load_workbook(), .save(), .close()

File I/O

  • Open / Close / Print # / Input # / Line Input #
  • Dir(pattern) file iteration → glob.glob() with proper next() iterator state
  • Basic FileSystemObject operations → pathlib.Path

Error handling (basic cases)

  • On Error GoTo Labeltry / except with labeled handler
  • On Error GoTo 0 → reset handler
  • Simple On Error Resume Next blocks

⚠️ Translates with warnings — review before running

The converter produces working Python, but the output may behave differently from your VBA. Every conversion comes with a warning report listing these patterns explicitly. Always test before running on production data.

Financial math — the important one

VBA's Currency type is fixed-point (scaled to 4 decimal places). Python's float isn't. For payroll, accounting, or anything involving cents, the translated code may be off by a rounding unit on edge cases. We flag this with a warning. Recommended: wrap money calculations in decimal.Decimal with quantize() before comparing or storing. If your macro processes payroll or reconciles accounts, review these lines by hand.

ByRef parameter mutations

Python primitives are pass-by-value. If your VBA sub modifies a ByRef Long or String and expects the change to persist in the caller, the Python version won't. We warn on every case. Fix: refactor the sub to return the new value and assign it at the call site.

On Error Resume Next + If Err.Number checks

The "silent fail, then check Err.Number" idiom doesn't round-trip cleanly. In VBA, the bad line is silently skipped; in the generated Python, it'll raise unless you've explicitly wrapped it. We emit the try/except shell, but the semantics differ. Review these blocks before running on real data.

Range.Value vs Range.Value2

We treat them identically. If your macro relies on .Value2 returning raw unformatted doubles (common for performance-critical date or currency reads), you'll see type mismatches downstream. We flag the distinction.

Application.WorksheetFunction. (SumIfs, VLookup, Index/Match)*

We emit a # TODO comment with a pandas suggestion, but don't auto-translate. These need a manual rewrite to df.query(), df.merge(), df.lookup(), or the equivalent — the semantics are too context-dependent to translate mechanically.

ListObjects (Excel Tables) and SpecialCells

openpyxl has partial Table support, but methods like DataBodyRange, ListRows.Add, and SpecialCells(xlCellTypeVisible) don't have direct equivalents. We emit guidance in comments; you'll need to adapt.

❌ Not supported — by design

These categories are out of scope. We document them clearly so you don't send them through expecting magic. If your macro is mostly one of these, our converter isn't the right tool.

UI code — UserForms, ActiveX controls. No pandas/openpyxl equivalent exists. Rebuild in Tkinter, PyQt, or a web framework.

Event handlersWorkbook_Open, Worksheet_Change, Application.OnTime. Python has no Excel event model. If you trigger logic on workbook open, convert to a scheduled script or a file-watcher.

PivotTables — pandas pivot_table is the target, but the translation isn't mechanical. The source/pivot-field/aggregation structure needs a human to redesign.

Charts and Conditional Formatting — openpyxl's API exists but differs enough that a literal translation is non-idiomatic. Easier to rebuild the chart in openpyxl or matplotlib from the translated data-prep code.

Win32 Declare PtrSafe statements — kernel32, user32 DLL calls don't translate. Use ctypes manually in Python if you actually need them.

COM automation / late bindingCreateObject("Outlook.Application"), Wscript.Shell, CDO.Message. These need pywin32 and a manual rewrite; the semantics aren't preservable through automatic translation.

Full ADO/ADODB Recordset navigation.MovePrevious, .Bookmark, multi-resultset navigation. Basic Open / Execute / Fields / MoveNext are partially supported, but complex cursor logic needs a manual port to sqlite3 or SQLAlchemy.

Data Validation rules — Excel-specific constraint system, not in openpyxl's core API.

What this means for you

If your VBA is a typical finance or reporting macro — data prep, aggregation, formatting, report generation — you're in the 80% that translates cleanly. Paste it in and you'll get Python you can read, audit, and adapt.

If your VBA is heavy on UserForms, PivotTables, events, or COM automation, the converter won't save you the rewrite. But the parts of your code that are data-prep and business logic will still translate — and that's usually the part that takes the longest to rewrite by hand.

Every conversion includes a warning report. If you're migrating a critical macro, read the warnings before running the output. We'd rather be accused of being too cautious than of silently breaking someone's payroll run.