
When ordinary text becomes an executable formula
A contact name, order note or visitor comment can eventually appear in a spreadsheet export. Without an explicit type contract, some strings may be interpreted as formulas rather than displayed as ordinary data. The resulting risk extends beyond a cosmetic formatting problem. This tutorial writes the intended cell type explicitly and checks the generated workbook itself, instead of relying on the visual appearance of a value before it reaches the spreadsheet application.
For untrusted fields that are genuinely text, the example creates an XLSX workbook and records each cell as a string. It preserves the submitted value rather than silently removing its first character. CSV import and save behavior differs among spreadsheet applications, so quotation-based escaping is not presented as a universal guarantee. The demonstrated contract is deliberately narrower: typed text in the workbook generated by this controlled exporter and verified by the accompanying checks.
Define a typed-text export contract
Name and note are always textual columns here. A report containing amounts, dates or identifiers needs separately defined types and validation for those fields. Converting everything to text can disrupt legitimate calculations or expected sorting behavior. The example rejects excessively long fields and does not replace an existing destination. A public export endpoint also needs an independent row-count and resource budget instead of treating streaming workbook construction as permission to accept unlimited input.
Save the four files, install openpyxl in a project virtual environment and run python -m unittest test_export.py. This example was tested with openpyxl 3.1.5. Run python demo_export.py to create contacts.xlsx; the structural check should report zero formulas. The basic checks inspect the generated file without launching Excel. Keep the test directory separate from production reports, especially because repeated demonstrations intentionally refuse to overwrite an existing output file.
Tests read several formula-like values and Persian content back from disk, checking both exact values and string cell types. A separate inspection counts formula elements inside worksheet XML. Their absence is useful evidence for this exporter, not a complete assessment of every spreadsheet feature or an arbitrary unknown workbook. The structural inspector is intended for trusted generated output and should not become an unrestricted public archive-analysis endpoint without additional size and archive protections.
Complete executable files
The four files are complete. The article provides prerequisites, command order and verification; code is neither shortened nor hidden.
1. Complete export_text.py
Save this entire file beside the others using the exact filename.
from pathlib import Path
from openpyxl import Workbook
from openpyxl.cell import WriteOnlyCell
def export_rows(rows, destination):
workbook = Workbook(write_only=True)
sheet = workbook.create_sheet('Contacts')
sheet.append(['Name', 'Note'])
for row in rows:
values = []
for field in ('name', 'note'):
value = row[field]
if not isinstance(value, str) or len(value) > 10000:
raise ValueError('Expected short text fields')
cell = WriteOnlyCell(sheet, value=value)
cell.data_type = 's' # Never serialize untrusted text as a formula.
values.append(cell)
sheet.append(values)
with Path(destination).open('xb') as output:
workbook.save(output)The execution section explains this file’s command, expected outcome and limits.
2. Complete validate_xlsx.py
Save this entire file beside the others using the exact filename.
import zipfile
from xml.etree import ElementTree as ET
def formula_count(path):
count = 0
with zipfile.ZipFile(path) as archive:
for name in archive.namelist():
if name.startswith('xl/worksheets/') and name.endswith('.xml'):
root = ET.fromstring(archive.read(name))
count += len(root.findall('.//{http://schemas.openxmlformats.org/spreadsheetml/2006/main}f'))
return countThe execution section explains this file’s command, expected outcome and limits.
3. Complete demo_export.py
Save this entire file beside the others using the exact filename.
from export_text import export_rows
from validate_xlsx import formula_count
if __name__ == '__main__':
export_rows([{'name': 'کاربر نمونه', 'note': '=1+1'}, {'name': 'Example', 'note': '@SUM(A1:A2)'}], 'contacts.xlsx')
print({'formulas': formula_count('contacts.xlsx')})The execution section explains this file’s command, expected outcome and limits.
4. Complete test_export.py
Save this entire file beside the others using the exact filename.
from pathlib import Path
import tempfile
import unittest
from openpyxl import load_workbook
from export_text import export_rows
from validate_xlsx import formula_count
class ExportTests(unittest.TestCase):
def test_formula_like_values_are_literal_strings(self):
with tempfile.TemporaryDirectory() as directory:
target = Path(directory) / 'contacts.xlsx'
values = ['=1+1', '+SUM(A1:A2)', '-1+2', '@SUM(A1:A2)', 'متن فارسی']
export_rows([{'name': 'Example', 'note': v} for v in values], target)
self.assertEqual(formula_count(target), 0)
workbook = load_workbook(target, data_only=False)
try:
cells = list(workbook.active.iter_rows(min_row=2))
self.assertEqual([r[1].value for r in cells], values)
self.assertTrue(all(r[1].data_type == 's' for r in cells))
finally: workbook.close()
if __name__ == '__main__': unittest.main()The execution section explains this file’s command, expected outcome and limits.
Create the workbook and inspect unintended formulas
In a real reporting service, distinguish user-supplied text from intentional formulas authored by the application. Review representative output in the actual destination software, and do not require users to dismiss security warnings simply to read a report. Document column contracts so that a later format change does not silently remove the protection. Success means preserving the intended data and producing the expected structure, rather than merely obtaining a file that opens without an obvious error.
Implementation checklist
- Write untrusted textual fields as string cells.
- Test values and types after saving.
- Separate input text from application-authored formulas.
- Budget export size and unknown-file analysis independently.
References
Editorial guidance: Liyan Knowledge.




