mrkeyoor.com_
Sat 19 Sept 08:56 UTC
PyPIDataupdated 19 Sept 2026

openpyxl review

openpyxl 3.1.5 edits the Office Open XML workbook formats `.xlsx`, `.xlsm`, `.xltx`, and `.xltm` from Python. Its objects expose cells, worksheets, styles, formulas, charts, data validation, named ranges, and print settings without starting Excel. Normal mode materializes workbook structure in memory; `read_only` walks existing rows, while `write_only` appends a new workbook with lower memory use. The library stores formulas but has no calculation engine and cannot read legacy `.xls`. Version 3.1.5 fixes NumPy API test compatibility, Excel version metadata, and slow reads involving many named styles.

Verdict

openpyxl 3.1.5 installed in 0.2 seconds as two packages using 2 MB in our sandbox, imported in 0.38 seconds, and had zero audit findings, but it shipped no `py.typed` marker. Use it for cell-level edits to modern Excel files; avoid it when formulas must be calculated, old `.xls` must be read, or every workbook extension must survive unchanged.

We installed it

Lab card: what happened when we installed openpyxlScreenshot of openpyxl documentation
Install✓ · 0.2s2 packages on disk · 2 MB
Importimport openpyxl in 0.38s · pure Python · requires Python >=3.8
Known vulns0(pip-audit)

Answers from our run

Does openpyxl install cleanly?

Yes. In a fresh container with an empty cache, pip install openpyxl finished in 0.2s, leaving 2 packages and 2 MB on disk. pip-audit reported no known vulnerabilities.

What does openpyxl need to run?

Python >=3.8, and nothing compiled: it is pure Python. In our run import openpyxl succeeded in 0.38s.

openpyxl or xlsxwriter: which should you use?

xlsxwriter: Use it to create polished new .xlsx reports when existing workbooks never need to be read or edited. openpyxl 3.1.5 installed in 0.2 seconds as two packages using 2 MB in our sandbox, imported in 0.38 seconds, and had zero audit findings, but it shipped no py.typed marker.

When should you not use openpyxl?

The server must calculate formulas. openpyxl writes expressions and reads cached results, but it does not evaluate them.

API stability5/5The 3.x series continues to use `Workbook`, `load_workbook`, worksheet indexing, one-based cell coordinates, `append`, and `save`, with optimized modes selected at construction or load time. Version 3.1.5 is a bug-fix release for NumPy test changes, workbook version metadata, and named-style read performance rather than an object-model migration. Most compatibility risk sits in Excel features that the model cannot represent, not repeated Python API changes.
Docs4/5The official manual covers loading and saving, optimized modes, formulas, styles, charts, images, tables, validation, dimensions, and workbook properties with executable examples. It documents cached formula values and warns about unsupported objects and XML attacks. The stable Read the Docs site currently labels itself 3.1.3 while PyPI serves 3.1.5, so the separate Heptapod change log is needed to learn what the current package fixed.
Maintenance3/5PyPI published 3.1.5 on June 28, 2024, and the project tracks source and issues on Heptapod rather than GitHub, so GitHub stars and push dates do not apply. That release fixed three concrete problems, including slow workbooks with many named styles and version metadata rejected by Excel. More than two years without a newer PyPI release makes rapid support for new Excel behavior unlikely, even though the mature package still works on our Python 3.12 check.
Ecosystem5/5The current package record carries 80,200,216 weekly downloads. pandas commonly selects openpyxl for `.xlsx` input and output, and Python reporting systems often build on its workbook object model. Separate `types-openpyxl` stubs address the missing `py.typed` marker, while `defusedxml` supplies hardening recommended by the project. Popularity produces many recipes, but it cannot make unsupported Office XML extensions safe to round-trip.

Use it if

  • Python must change values, formulas, styles, validation, or sheet properties in an existing modern Excel workbook.
  • A generated report needs spreadsheet features beyond a plain table, such as merged cells, charts, or print configuration.
  • Rows can be handled sequentially through `read_only` or `write_only` mode when a full in-memory model is too expensive.
  • The job runs on a server where Microsoft Excel automation is unavailable or undesirable.
Skip it if

Setup reality

We installed openpyxl 3.1.5 without a cache in a clean Python 3.12 Bookworm sandbox. The operation took 0.2 seconds and left two packages using 2 MB. openpyxl is pure Python, declares one direct dependency, requires Python 3.8 or later, and uses the MIT license. pip-audit found zero known vulnerabilities. Import succeeded in 0.38 seconds, but the package did not include py.typed.

Default load_workbook() creates cells and worksheet objects in memory, so RAM use does not track the compressed .xlsx file size. read_only=True favors ordered iteration and should be closed when finished. Workbook(write_only=True) lets a producer append rows with low memory use, but an appended row cannot be read or edited afterward. For hostile XML input, install defusedxml; the project's own PyPI page says the default parser does not guard against quadratic blowup or billion-laughs attacks.

Coordinates are one-based. Styles are immutable shared values, so assign a new Font, Fill, or copied style to each target cell instead of changing a component in place. save() overwrites the destination path without confirmation. Write a new file when the source must remain recoverable. Load macro workbooks with keep_vba=True to retain VBA parts; openpyxl does not inspect or modify the macro code. The missing py.typed marker also means strict typing often relies on the separate types-openpyxl stubs.

A formula cell stores an expression. With data_only=True, loading returns the last cached result written by Excel or another calculation engine, and a newly created formula can read back as None. Version 3.1.5 improves reads with many named styles and fixes workbook version metadata that Excel validates strictly. Those fixes do not promise full document fidelity: unsupported drawings or extensions may disappear after saving. Run a representative production workbook through load, save, reopen, and the target spreadsheet application before adopting the pipeline.

Patterns

Read cells by name or coordinates read-workbook

from openpyxl import load_workbook

wb = load_workbook("report.xlsx")
ws = wb["Sheet1"]  # or wb.active

print(ws["A1"].value)
print(ws.cell(row=2, column=3).value)

Rows and columns are numbered from 1, so `A1` and `cell(row=1, column=1)` select the same cell.

Save a new workbook write-workbook

from openpyxl import Workbook

wb = Workbook()
ws = wb.active
ws.title = "Data"
ws["A1"] = "name"
ws["B1"] = "qty"
wb.save("out.xlsx")

`save()` replaces an existing destination. Write to another path when the original workbook must remain recoverable.

Iterate row values without Cell objects iterate-rows

for row in ws.iter_rows(min_row=2, max_col=3, values_only=True):
    name, qty, price = row
    print(name, qty, price)

`values_only=True` is suited to scans that do not need formatting, comments, or cell coordinates.

Add records after existing content append-rows

rows = [
    ("widget", 3, 9.99),
    ("gadget", 7, 24.50),
]
for r in rows:
    ws.append(r)

A populated cell far below the visible table affects the used range and can move where `append()` writes the next row.

Apply formatting to a cell style-cells

from openpyxl.styles import Font, PatternFill, Alignment

cell = ws["A1"]
cell.font = Font(bold=True, color="FFFFFF")
cell.fill = PatternFill("solid", fgColor="4472C4")
cell.alignment = Alignment(horizontal="center")

Style objects are shared and immutable in practice. Assign styles cell by cell when formatting a complete header row.

Write a formula and read its cached value write-formula

ws["B10"] = "=SUM(B2:B9)"
wb.save("out.xlsx")

# later, to read cached results instead of formula strings:
from openpyxl import load_workbook
wb2 = load_workbook("out.xlsx", data_only=True)
print(wb2.active["B10"].value)

openpyxl never calculates the expression; `data_only=True` can return `None` until a spreadsheet engine saves a result.

Scan a large workbook in read-only mode read-large-file

from openpyxl import load_workbook

wb = load_workbook("big.xlsx", read_only=True)
ws = wb.active
for row in ws.iter_rows(values_only=True):
    process(row)
wb.close()

Process rows in order and close the workbook. Random coordinate access conflicts with the streaming design.

Produce a large workbook in write-only mode write-large-file

from openpyxl import Workbook

wb = Workbook(write_only=True)
ws = wb.create_sheet()
for row in generate_rows():
    ws.append(row)
wb.save("big.xlsx")

Once a row is appended, write-only mode cannot read it back or revise its cells, so finish the row first.

Alternatives

PackageRegistryPick it when
xlsxwriterPyPIUse it to create polished new `.xlsx` reports when existing workbooks never need to be read or edited.
python-calaminePyPIUse it for fast read-only access across `.xlsx`, `.xls`, `.xlsb`, and OpenDocument formats.
pandasPyPIUse it when each sheet is a table and DataFrame transformation matters more than workbook objects.

More data guides

numpy · fsspec · pandas · pyarrow · sqlalchemy · s3fs · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.