texttable
texttable builds plain-text tables from Python rows. You can set headers, column widths, horizontal and vertical alignment, numeric formatting, precision, border characters, and which lines are drawn, then call draw to receive one complete string. Cells may contain newlines and tabs, and optional cjkwrap or wcwidth installations improve CJK and emoji width handling. It is a compact terminal-report formatter, not an interactive widget, streaming renderer, rich terminal framework, or exporter for Markdown, HTML, CSV, or spreadsheets.
Still a fine tiny formatter for stable, small ASCII reports. New CLI applications that need rich output or multiple export formats should start with Rich, tabulate, or PrettyTable instead.
Use it if
- You need a small dependency-free ASCII table for logs, tests, reports, or a simple command-line script
- You want explicit per-column alignment, vertical alignment, width, numeric type, and border control
- Your table is small enough to build as one string before printing
- You maintain existing code or a dependency that already uses Texttable's long-stable API
- You need colored cells, hyperlinks, live updates, progress bars, or terminal capability handling: the documented API only formats static text tables, while Rich is built for those interfaces
- You need Markdown, HTML, LaTeX, CSV, or spreadsheet output from the same rows: draw returns one ASCII-style string and the README points to a separate LaTeX fork
- You require deterministic CJK and emoji layout with no optional environment differences: the README says cjkwrap and wcwidth are used only when available, so installed extras can change wrapping and measured width
- You want strong modern typing and reference documentation: version 1.6.7 removed the packaged stub file, and the README's API section is generated in an old Python 2-era pydoc style
- You want active feature development: version 1.7.0 and the last repository push were both in October 2023, with the latest release adding only boolean formatting
Setup reality
pip install texttable has no required runtime dependencies and imports as from texttable import Texttable. The defaults still carry behavioral choices: max_width is 80, all decorations are enabled, automatic typing can reinterpret numeric-looking values, float precision is 3, and add_rows treats the first row as a header unless header=False. A mismatched header, alignment, width, datatype, or row length can raise ArraySizeError, so determine the schema before adding rows. set_cols_dtype accepts a, t, f, e, i, b, or a callable; use t for identifiers such as ZIP codes and invoice numbers or automatic formatting may strip leading zeros or choose numeric notation. set_cols_width gives exact wrapping widths, while max_width controls the overall table and 0 disables automatic wrapping. Multiline cells are supported and vertical alignment decides where shorter cells sit. Unicode width is environment-sensitive: cjkwrap improves CJK wrapping if installed, and wcwidth improves emoji width if installed, but neither is mandatory, so CI and production can draw differently unless you pin the same extras. The library returns the entire table from draw, which is fine for small reports but duplicates memory and prevents incremental output for large result sets. It also knows nothing about ANSI escape sequences, terminal resizing, sorting, pagination, or safe serialization to other formats. Build plain values first, apply explicit column types and widths, test representative Unicode, and treat styling characters as presentation rather than a machine-readable contract.
Patterns
Build a table with a header and rowsdraw-basic-table
from texttable import Texttable
table = Texttable()
table.header(['Name', 'Age'])
table.add_row(['Ada', 36])
table.add_row(['Grace', 40])
print(table.draw())draw returns the whole table as a string; print or log it once to avoid adding an extra prefix to every line.
Add a batch without consuming its first row as a headeradd-rows-without-header
table = Texttable()
table.header(['Name', 'Score'])
table.add_rows([['Ada', 98], ['Grace', 95]], header=False)add_rows defaults header=True, so omitting the flag here would replace the intended first data row with a header.
Set horizontal and vertical alignmentalign-columns
table.set_cols_align(['l', 'r', 'c'])
table.set_cols_valign(['t', 'm', 'b'])The configuration array must have exactly one entry per column or Texttable raises ArraySizeError.
Control numeric and text formattingformat-column-types
table.set_cols_dtype([
't', # preserve identifiers as text
'i', # integer
'f', # fixed-point float
'b', # boolean
])
table.set_precision(2)Automatic typing can reinterpret numeric strings; mark values such as 00123 as text when leading zeros matter.
Wrap cells at explicit column widthsset-column-widths
table = Texttable(max_width=0)
table.set_cols_width([12, 36, 8])max_width=0 disables the overall automatic limit, while set_cols_width still wraps each configured column.
Draw only borders and a header separatorchoose-decoration
from texttable import Texttable
table.set_deco(Texttable.BORDER | Texttable.HEADER)Decoration flags combine with bitwise OR; leaving out VLINES and HLINES removes internal cell and row rules.
Change the four drawing characterscustomize-border-characters
table.set_chars(['-', '|', '+', '='])The array order is horizontal, vertical, corner, header; multi-column glyphs can break width assumptions.
Use a callable as a column datatypeformat-with-callable
def dollars(value):
return f'${value:,.2f}'
table.set_cols_dtype(['t', dollars])
table.add_rows([['Item', 'Price'], ['Cable', 12.5]])The callable must return a string for every value it receives, including any null-like values in your rows.
Reset rows and header before another reportreuse-table
print(table.draw())
table.reset()
table.header(['Key', 'Value'])
table.add_row(['status', 'ok'])reset clears rows and the header; reapply the next report's schema before adding values.
Catch a mismatched row shapehandle-schema-error
from texttable import ArraySizeError
try:
table.add_row(['too', 'many', 'cells'])
except ArraySizeError as exc:
raise ValueError('row does not match table columns') from excIt is usually better to validate and normalize records before rendering, since one malformed row prevents the final table.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| tabulate | PyPI | You want many output formats, including Markdown, HTML, grid, and database-style tables |
| prettytable | PyPI | You want a fuller table object with sorting, field selection, and several text or markup styles |
| rich | PyPI | You are building a modern terminal UI with color, wrapping, live updates, and console-aware rendering |