tabulate.py 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879
  1. # -*- coding: utf-8 -*-
  2. """Pretty-print tabular data."""
  3. from __future__ import print_function
  4. from __future__ import unicode_literals
  5. from collections import namedtuple
  6. import sys
  7. import re
  8. import math
  9. if sys.version_info >= (3, 3):
  10. from collections.abc import Iterable
  11. else:
  12. from collections import Iterable
  13. if sys.version_info[0] < 3:
  14. from itertools import izip_longest
  15. from functools import partial
  16. _none_type = type(None)
  17. _bool_type = bool
  18. _int_type = int
  19. _long_type = long # noqa
  20. _float_type = float
  21. _text_type = unicode # noqa
  22. _binary_type = str
  23. def _is_file(f):
  24. return hasattr(f, "read")
  25. else:
  26. from itertools import zip_longest as izip_longest
  27. from functools import reduce, partial
  28. _none_type = type(None)
  29. _bool_type = bool
  30. _int_type = int
  31. _long_type = int
  32. _float_type = float
  33. _text_type = str
  34. _binary_type = bytes
  35. basestring = str
  36. import io
  37. def _is_file(f):
  38. return isinstance(f, io.IOBase)
  39. try:
  40. import wcwidth # optional wide-character (CJK) support
  41. except ImportError:
  42. wcwidth = None
  43. try:
  44. from html import escape as htmlescape
  45. except ImportError:
  46. from cgi import escape as htmlescape
  47. __all__ = ["tabulate", "tabulate_formats", "simple_separated_format"]
  48. __version__ = "0.8.9"
  49. # minimum extra space in headers
  50. MIN_PADDING = 2
  51. # Whether or not to preserve leading/trailing whitespace in data.
  52. PRESERVE_WHITESPACE = False
  53. _DEFAULT_FLOATFMT = "g"
  54. _DEFAULT_MISSINGVAL = ""
  55. # default align will be overwritten by "left", "center" or "decimal"
  56. # depending on the formatter
  57. _DEFAULT_ALIGN = "default"
  58. # if True, enable wide-character (CJK) support
  59. WIDE_CHARS_MODE = wcwidth is not None
  60. Line = namedtuple("Line", ["begin", "hline", "sep", "end"])
  61. DataRow = namedtuple("DataRow", ["begin", "sep", "end"])
  62. # A table structure is suppposed to be:
  63. #
  64. # --- lineabove ---------
  65. # headerrow
  66. # --- linebelowheader ---
  67. # datarow
  68. # --- linebetweenrows ---
  69. # ... (more datarows) ...
  70. # --- linebetweenrows ---
  71. # last datarow
  72. # --- linebelow ---------
  73. #
  74. # TableFormat's line* elements can be
  75. #
  76. # - either None, if the element is not used,
  77. # - or a Line tuple,
  78. # - or a function: [col_widths], [col_alignments] -> string.
  79. #
  80. # TableFormat's *row elements can be
  81. #
  82. # - either None, if the element is not used,
  83. # - or a DataRow tuple,
  84. # - or a function: [cell_values], [col_widths], [col_alignments] -> string.
  85. #
  86. # padding (an integer) is the amount of white space around data values.
  87. #
  88. # with_header_hide:
  89. #
  90. # - either None, to display all table elements unconditionally,
  91. # - or a list of elements not to be displayed if the table has column headers.
  92. #
  93. TableFormat = namedtuple(
  94. "TableFormat",
  95. [
  96. "lineabove",
  97. "linebelowheader",
  98. "linebetweenrows",
  99. "linebelow",
  100. "headerrow",
  101. "datarow",
  102. "padding",
  103. "with_header_hide",
  104. ],
  105. )
  106. def _pipe_segment_with_colons(align, colwidth):
  107. """Return a segment of a horizontal line with optional colons which
  108. indicate column's alignment (as in `pipe` output format)."""
  109. w = colwidth
  110. if align in ["right", "decimal"]:
  111. return ("-" * (w - 1)) + ":"
  112. elif align == "center":
  113. return ":" + ("-" * (w - 2)) + ":"
  114. elif align == "left":
  115. return ":" + ("-" * (w - 1))
  116. else:
  117. return "-" * w
  118. def _pipe_line_with_colons(colwidths, colaligns):
  119. """Return a horizontal line with optional colons to indicate column's
  120. alignment (as in `pipe` output format)."""
  121. if not colaligns: # e.g. printing an empty data frame (github issue #15)
  122. colaligns = [""] * len(colwidths)
  123. segments = [_pipe_segment_with_colons(a, w) for a, w in zip(colaligns, colwidths)]
  124. return "|" + "|".join(segments) + "|"
  125. def _mediawiki_row_with_attrs(separator, cell_values, colwidths, colaligns):
  126. alignment = {
  127. "left": "",
  128. "right": 'align="right"| ',
  129. "center": 'align="center"| ',
  130. "decimal": 'align="right"| ',
  131. }
  132. # hard-coded padding _around_ align attribute and value together
  133. # rather than padding parameter which affects only the value
  134. values_with_attrs = [
  135. " " + alignment.get(a, "") + c + " " for c, a in zip(cell_values, colaligns)
  136. ]
  137. colsep = separator * 2
  138. return (separator + colsep.join(values_with_attrs)).rstrip()
  139. def _textile_row_with_attrs(cell_values, colwidths, colaligns):
  140. cell_values[0] += " "
  141. alignment = {"left": "<.", "right": ">.", "center": "=.", "decimal": ">."}
  142. values = (alignment.get(a, "") + v for a, v in zip(colaligns, cell_values))
  143. return "|" + "|".join(values) + "|"
  144. def _html_begin_table_without_header(colwidths_ignore, colaligns_ignore):
  145. # this table header will be suppressed if there is a header row
  146. return "<table>\n<tbody>"
  147. def _html_row_with_attrs(celltag, unsafe, cell_values, colwidths, colaligns):
  148. alignment = {
  149. "left": "",
  150. "right": ' style="text-align: right;"',
  151. "center": ' style="text-align: center;"',
  152. "decimal": ' style="text-align: right;"',
  153. }
  154. if unsafe:
  155. values_with_attrs = [
  156. "<{0}{1}>{2}</{0}>".format(celltag, alignment.get(a, ""), c)
  157. for c, a in zip(cell_values, colaligns)
  158. ]
  159. else:
  160. values_with_attrs = [
  161. "<{0}{1}>{2}</{0}>".format(celltag, alignment.get(a, ""), htmlescape(c))
  162. for c, a in zip(cell_values, colaligns)
  163. ]
  164. rowhtml = "<tr>{}</tr>".format("".join(values_with_attrs).rstrip())
  165. if celltag == "th": # it's a header row, create a new table header
  166. rowhtml = "<table>\n<thead>\n{}\n</thead>\n<tbody>".format(rowhtml)
  167. return rowhtml
  168. def _moin_row_with_attrs(celltag, cell_values, colwidths, colaligns, header=""):
  169. alignment = {
  170. "left": "",
  171. "right": '<style="text-align: right;">',
  172. "center": '<style="text-align: center;">',
  173. "decimal": '<style="text-align: right;">',
  174. }
  175. values_with_attrs = [
  176. "{0}{1} {2} ".format(celltag, alignment.get(a, ""), header + c + header)
  177. for c, a in zip(cell_values, colaligns)
  178. ]
  179. return "".join(values_with_attrs) + "||"
  180. def _latex_line_begin_tabular(colwidths, colaligns, booktabs=False, longtable=False):
  181. alignment = {"left": "l", "right": "r", "center": "c", "decimal": "r"}
  182. tabular_columns_fmt = "".join([alignment.get(a, "l") for a in colaligns])
  183. return "\n".join(
  184. [
  185. ("\\begin{tabular}{" if not longtable else "\\begin{longtable}{")
  186. + tabular_columns_fmt
  187. + "}",
  188. "\\toprule" if booktabs else "\\hline",
  189. ]
  190. )
  191. LATEX_ESCAPE_RULES = {
  192. r"&": r"\&",
  193. r"%": r"\%",
  194. r"$": r"\$",
  195. r"#": r"\#",
  196. r"_": r"\_",
  197. r"^": r"\^{}",
  198. r"{": r"\{",
  199. r"}": r"\}",
  200. r"~": r"\textasciitilde{}",
  201. "\\": r"\textbackslash{}",
  202. r"<": r"\ensuremath{<}",
  203. r">": r"\ensuremath{>}",
  204. }
  205. def _latex_row(cell_values, colwidths, colaligns, escrules=LATEX_ESCAPE_RULES):
  206. def escape_char(c):
  207. return escrules.get(c, c)
  208. escaped_values = ["".join(map(escape_char, cell)) for cell in cell_values]
  209. rowfmt = DataRow("", "&", "\\\\")
  210. return _build_simple_row(escaped_values, rowfmt)
  211. def _rst_escape_first_column(rows, headers):
  212. def escape_empty(val):
  213. if isinstance(val, (_text_type, _binary_type)) and not val.strip():
  214. return ".."
  215. else:
  216. return val
  217. new_headers = list(headers)
  218. new_rows = []
  219. if headers:
  220. new_headers[0] = escape_empty(headers[0])
  221. for row in rows:
  222. new_row = list(row)
  223. if new_row:
  224. new_row[0] = escape_empty(row[0])
  225. new_rows.append(new_row)
  226. return new_rows, new_headers
  227. _table_formats = {
  228. "simple": TableFormat(
  229. lineabove=Line("", "-", " ", ""),
  230. linebelowheader=Line("", "-", " ", ""),
  231. linebetweenrows=None,
  232. linebelow=Line("", "-", " ", ""),
  233. headerrow=DataRow("", " ", ""),
  234. datarow=DataRow("", " ", ""),
  235. padding=0,
  236. with_header_hide=["lineabove", "linebelow"],
  237. ),
  238. "plain": TableFormat(
  239. lineabove=None,
  240. linebelowheader=None,
  241. linebetweenrows=None,
  242. linebelow=None,
  243. headerrow=DataRow("", " ", ""),
  244. datarow=DataRow("", " ", ""),
  245. padding=0,
  246. with_header_hide=None,
  247. ),
  248. "grid": TableFormat(
  249. lineabove=Line("+", "-", "+", "+"),
  250. linebelowheader=Line("+", "=", "+", "+"),
  251. linebetweenrows=Line("+", "-", "+", "+"),
  252. linebelow=Line("+", "-", "+", "+"),
  253. headerrow=DataRow("|", "|", "|"),
  254. datarow=DataRow("|", "|", "|"),
  255. padding=1,
  256. with_header_hide=None,
  257. ),
  258. "fancy_grid": TableFormat(
  259. lineabove=Line("╒", "═", "╤", "╕"),
  260. linebelowheader=Line("╞", "═", "╪", "╡"),
  261. linebetweenrows=Line("├", "─", "┼", "┤"),
  262. linebelow=Line("╘", "═", "╧", "╛"),
  263. headerrow=DataRow("│", "│", "│"),
  264. datarow=DataRow("│", "│", "│"),
  265. padding=1,
  266. with_header_hide=None,
  267. ),
  268. "fancy_outline": TableFormat(
  269. lineabove=Line("╒", "═", "╤", "╕"),
  270. linebelowheader=Line("╞", "═", "╪", "╡"),
  271. linebetweenrows=None,
  272. linebelow=Line("╘", "═", "╧", "╛"),
  273. headerrow=DataRow("│", "│", "│"),
  274. datarow=DataRow("│", "│", "│"),
  275. padding=1,
  276. with_header_hide=None,
  277. ),
  278. "github": TableFormat(
  279. lineabove=Line("|", "-", "|", "|"),
  280. linebelowheader=Line("|", "-", "|", "|"),
  281. linebetweenrows=None,
  282. linebelow=None,
  283. headerrow=DataRow("|", "|", "|"),
  284. datarow=DataRow("|", "|", "|"),
  285. padding=1,
  286. with_header_hide=["lineabove"],
  287. ),
  288. "pipe": TableFormat(
  289. lineabove=_pipe_line_with_colons,
  290. linebelowheader=_pipe_line_with_colons,
  291. linebetweenrows=None,
  292. linebelow=None,
  293. headerrow=DataRow("|", "|", "|"),
  294. datarow=DataRow("|", "|", "|"),
  295. padding=1,
  296. with_header_hide=["lineabove"],
  297. ),
  298. "orgtbl": TableFormat(
  299. lineabove=None,
  300. linebelowheader=Line("|", "-", "+", "|"),
  301. linebetweenrows=None,
  302. linebelow=None,
  303. headerrow=DataRow("|", "|", "|"),
  304. datarow=DataRow("|", "|", "|"),
  305. padding=1,
  306. with_header_hide=None,
  307. ),
  308. "jira": TableFormat(
  309. lineabove=None,
  310. linebelowheader=None,
  311. linebetweenrows=None,
  312. linebelow=None,
  313. headerrow=DataRow("||", "||", "||"),
  314. datarow=DataRow("|", "|", "|"),
  315. padding=1,
  316. with_header_hide=None,
  317. ),
  318. "presto": TableFormat(
  319. lineabove=None,
  320. linebelowheader=Line("", "-", "+", ""),
  321. linebetweenrows=None,
  322. linebelow=None,
  323. headerrow=DataRow("", "|", ""),
  324. datarow=DataRow("", "|", ""),
  325. padding=1,
  326. with_header_hide=None,
  327. ),
  328. "pretty": TableFormat(
  329. lineabove=Line("+", "-", "+", "+"),
  330. linebelowheader=Line("+", "-", "+", "+"),
  331. linebetweenrows=None,
  332. linebelow=Line("+", "-", "+", "+"),
  333. headerrow=DataRow("|", "|", "|"),
  334. datarow=DataRow("|", "|", "|"),
  335. padding=1,
  336. with_header_hide=None,
  337. ),
  338. "psql": TableFormat(
  339. lineabove=Line("+", "-", "+", "+"),
  340. linebelowheader=Line("|", "-", "+", "|"),
  341. linebetweenrows=None,
  342. linebelow=Line("+", "-", "+", "+"),
  343. headerrow=DataRow("|", "|", "|"),
  344. datarow=DataRow("|", "|", "|"),
  345. padding=1,
  346. with_header_hide=None,
  347. ),
  348. "rst": TableFormat(
  349. lineabove=Line("", "=", " ", ""),
  350. linebelowheader=Line("", "=", " ", ""),
  351. linebetweenrows=None,
  352. linebelow=Line("", "=", " ", ""),
  353. headerrow=DataRow("", " ", ""),
  354. datarow=DataRow("", " ", ""),
  355. padding=0,
  356. with_header_hide=None,
  357. ),
  358. "mediawiki": TableFormat(
  359. lineabove=Line(
  360. '{| class="wikitable" style="text-align: left;"',
  361. "",
  362. "",
  363. "\n|+ <!-- caption -->\n|-",
  364. ),
  365. linebelowheader=Line("|-", "", "", ""),
  366. linebetweenrows=Line("|-", "", "", ""),
  367. linebelow=Line("|}", "", "", ""),
  368. headerrow=partial(_mediawiki_row_with_attrs, "!"),
  369. datarow=partial(_mediawiki_row_with_attrs, "|"),
  370. padding=0,
  371. with_header_hide=None,
  372. ),
  373. "moinmoin": TableFormat(
  374. lineabove=None,
  375. linebelowheader=None,
  376. linebetweenrows=None,
  377. linebelow=None,
  378. headerrow=partial(_moin_row_with_attrs, "||", header="'''"),
  379. datarow=partial(_moin_row_with_attrs, "||"),
  380. padding=1,
  381. with_header_hide=None,
  382. ),
  383. "youtrack": TableFormat(
  384. lineabove=None,
  385. linebelowheader=None,
  386. linebetweenrows=None,
  387. linebelow=None,
  388. headerrow=DataRow("|| ", " || ", " || "),
  389. datarow=DataRow("| ", " | ", " |"),
  390. padding=1,
  391. with_header_hide=None,
  392. ),
  393. "html": TableFormat(
  394. lineabove=_html_begin_table_without_header,
  395. linebelowheader="",
  396. linebetweenrows=None,
  397. linebelow=Line("</tbody>\n</table>", "", "", ""),
  398. headerrow=partial(_html_row_with_attrs, "th", False),
  399. datarow=partial(_html_row_with_attrs, "td", False),
  400. padding=0,
  401. with_header_hide=["lineabove"],
  402. ),
  403. "unsafehtml": TableFormat(
  404. lineabove=_html_begin_table_without_header,
  405. linebelowheader="",
  406. linebetweenrows=None,
  407. linebelow=Line("</tbody>\n</table>", "", "", ""),
  408. headerrow=partial(_html_row_with_attrs, "th", True),
  409. datarow=partial(_html_row_with_attrs, "td", True),
  410. padding=0,
  411. with_header_hide=["lineabove"],
  412. ),
  413. "latex": TableFormat(
  414. lineabove=_latex_line_begin_tabular,
  415. linebelowheader=Line("\\hline", "", "", ""),
  416. linebetweenrows=None,
  417. linebelow=Line("\\hline\n\\end{tabular}", "", "", ""),
  418. headerrow=_latex_row,
  419. datarow=_latex_row,
  420. padding=1,
  421. with_header_hide=None,
  422. ),
  423. "latex_raw": TableFormat(
  424. lineabove=_latex_line_begin_tabular,
  425. linebelowheader=Line("\\hline", "", "", ""),
  426. linebetweenrows=None,
  427. linebelow=Line("\\hline\n\\end{tabular}", "", "", ""),
  428. headerrow=partial(_latex_row, escrules={}),
  429. datarow=partial(_latex_row, escrules={}),
  430. padding=1,
  431. with_header_hide=None,
  432. ),
  433. "latex_booktabs": TableFormat(
  434. lineabove=partial(_latex_line_begin_tabular, booktabs=True),
  435. linebelowheader=Line("\\midrule", "", "", ""),
  436. linebetweenrows=None,
  437. linebelow=Line("\\bottomrule\n\\end{tabular}", "", "", ""),
  438. headerrow=_latex_row,
  439. datarow=_latex_row,
  440. padding=1,
  441. with_header_hide=None,
  442. ),
  443. "latex_longtable": TableFormat(
  444. lineabove=partial(_latex_line_begin_tabular, longtable=True),
  445. linebelowheader=Line("\\hline\n\\endhead", "", "", ""),
  446. linebetweenrows=None,
  447. linebelow=Line("\\hline\n\\end{longtable}", "", "", ""),
  448. headerrow=_latex_row,
  449. datarow=_latex_row,
  450. padding=1,
  451. with_header_hide=None,
  452. ),
  453. "tsv": TableFormat(
  454. lineabove=None,
  455. linebelowheader=None,
  456. linebetweenrows=None,
  457. linebelow=None,
  458. headerrow=DataRow("", "\t", ""),
  459. datarow=DataRow("", "\t", ""),
  460. padding=0,
  461. with_header_hide=None,
  462. ),
  463. "textile": TableFormat(
  464. lineabove=None,
  465. linebelowheader=None,
  466. linebetweenrows=None,
  467. linebelow=None,
  468. headerrow=DataRow("|_. ", "|_.", "|"),
  469. datarow=_textile_row_with_attrs,
  470. padding=1,
  471. with_header_hide=None,
  472. ),
  473. }
  474. tabulate_formats = list(sorted(_table_formats.keys()))
  475. # The table formats for which multiline cells will be folded into subsequent
  476. # table rows. The key is the original format specified at the API. The value is
  477. # the format that will be used to represent the original format.
  478. multiline_formats = {
  479. "plain": "plain",
  480. "simple": "simple",
  481. "grid": "grid",
  482. "fancy_grid": "fancy_grid",
  483. "pipe": "pipe",
  484. "orgtbl": "orgtbl",
  485. "jira": "jira",
  486. "presto": "presto",
  487. "pretty": "pretty",
  488. "psql": "psql",
  489. "rst": "rst",
  490. }
  491. # TODO: Add multiline support for the remaining table formats:
  492. # - mediawiki: Replace \n with <br>
  493. # - moinmoin: TBD
  494. # - youtrack: TBD
  495. # - html: Replace \n with <br>
  496. # - latex*: Use "makecell" package: In header, replace X\nY with
  497. # \thead{X\\Y} and in data row, replace X\nY with \makecell{X\\Y}
  498. # - tsv: TBD
  499. # - textile: Replace \n with <br/> (must be well-formed XML)
  500. _multiline_codes = re.compile(r"\r|\n|\r\n")
  501. _multiline_codes_bytes = re.compile(b"\r|\n|\r\n")
  502. _invisible_codes = re.compile(
  503. r"\x1b\[\d+[;\d]*m|\x1b\[\d*\;\d*\;\d*m|\x1b\]8;;(.*?)\x1b\\"
  504. ) # ANSI color codes
  505. _invisible_codes_bytes = re.compile(
  506. b"\x1b\\[\\d+\\[;\\d]*m|\x1b\\[\\d*;\\d*;\\d*m|\\x1b\\]8;;(.*?)\\x1b\\\\"
  507. ) # ANSI color codes
  508. _invisible_codes_link = re.compile(
  509. r"\x1B]8;[a-zA-Z0-9:]*;[^\x1B]+\x1B\\([^\x1b]+)\x1B]8;;\x1B\\"
  510. ) # Terminal hyperlinks
  511. def simple_separated_format(separator):
  512. """Construct a simple TableFormat with columns separated by a separator.
  513. >>> tsv = simple_separated_format("\\t") ; \
  514. tabulate([["foo", 1], ["spam", 23]], tablefmt=tsv) == 'foo \\t 1\\nspam\\t23'
  515. True
  516. """
  517. return TableFormat(
  518. None,
  519. None,
  520. None,
  521. None,
  522. headerrow=DataRow("", separator, ""),
  523. datarow=DataRow("", separator, ""),
  524. padding=0,
  525. with_header_hide=None,
  526. )
  527. def _isconvertible(conv, string):
  528. try:
  529. conv(string)
  530. return True
  531. except (ValueError, TypeError):
  532. return False
  533. def _isnumber(string):
  534. """
  535. >>> _isnumber("123.45")
  536. True
  537. >>> _isnumber("123")
  538. True
  539. >>> _isnumber("spam")
  540. False
  541. >>> _isnumber("123e45678")
  542. False
  543. >>> _isnumber("inf")
  544. True
  545. """
  546. if not _isconvertible(float, string):
  547. return False
  548. elif isinstance(string, (_text_type, _binary_type)) and (
  549. math.isinf(float(string)) or math.isnan(float(string))
  550. ):
  551. return string.lower() in ["inf", "-inf", "nan"]
  552. return True
  553. def _isint(string, inttype=int):
  554. """
  555. >>> _isint("123")
  556. True
  557. >>> _isint("123.45")
  558. False
  559. """
  560. return (
  561. type(string) is inttype
  562. or (isinstance(string, _binary_type) or isinstance(string, _text_type))
  563. and _isconvertible(inttype, string)
  564. )
  565. def _isbool(string):
  566. """
  567. >>> _isbool(True)
  568. True
  569. >>> _isbool("False")
  570. True
  571. >>> _isbool(1)
  572. False
  573. """
  574. return type(string) is _bool_type or (
  575. isinstance(string, (_binary_type, _text_type)) and string in ("True", "False")
  576. )
  577. def _type(string, has_invisible=True, numparse=True):
  578. """The least generic type (type(None), int, float, str, unicode).
  579. >>> _type(None) is type(None)
  580. True
  581. >>> _type("foo") is type("")
  582. True
  583. >>> _type("1") is type(1)
  584. True
  585. >>> _type('\x1b[31m42\x1b[0m') is type(42)
  586. True
  587. >>> _type('\x1b[31m42\x1b[0m') is type(42)
  588. True
  589. """
  590. if has_invisible and (
  591. isinstance(string, _text_type) or isinstance(string, _binary_type)
  592. ):
  593. string = _strip_invisible(string)
  594. if string is None:
  595. return _none_type
  596. elif hasattr(string, "isoformat"): # datetime.datetime, date, and time
  597. return _text_type
  598. elif _isbool(string):
  599. return _bool_type
  600. elif _isint(string) and numparse:
  601. return int
  602. elif _isint(string, _long_type) and numparse:
  603. return int
  604. elif _isnumber(string) and numparse:
  605. return float
  606. elif isinstance(string, _binary_type):
  607. return _binary_type
  608. else:
  609. return _text_type
  610. def _afterpoint(string):
  611. """Symbols after a decimal point, -1 if the string lacks the decimal point.
  612. >>> _afterpoint("123.45")
  613. 2
  614. >>> _afterpoint("1001")
  615. -1
  616. >>> _afterpoint("eggs")
  617. -1
  618. >>> _afterpoint("123e45")
  619. 2
  620. """
  621. if _isnumber(string):
  622. if _isint(string):
  623. return -1
  624. else:
  625. pos = string.rfind(".")
  626. pos = string.lower().rfind("e") if pos < 0 else pos
  627. if pos >= 0:
  628. return len(string) - pos - 1
  629. else:
  630. return -1 # no point
  631. else:
  632. return -1 # not a number
  633. def _padleft(width, s):
  634. """Flush right.
  635. >>> _padleft(6, '\u044f\u0439\u0446\u0430') == ' \u044f\u0439\u0446\u0430'
  636. True
  637. """
  638. fmt = "{0:>%ds}" % width
  639. return fmt.format(s)
  640. def _padright(width, s):
  641. """Flush left.
  642. >>> _padright(6, '\u044f\u0439\u0446\u0430') == '\u044f\u0439\u0446\u0430 '
  643. True
  644. """
  645. fmt = "{0:<%ds}" % width
  646. return fmt.format(s)
  647. def _padboth(width, s):
  648. """Center string.
  649. >>> _padboth(6, '\u044f\u0439\u0446\u0430') == ' \u044f\u0439\u0446\u0430 '
  650. True
  651. """
  652. fmt = "{0:^%ds}" % width
  653. return fmt.format(s)
  654. def _padnone(ignore_width, s):
  655. return s
  656. def _strip_invisible(s):
  657. r"""Remove invisible ANSI color codes.
  658. >>> str(_strip_invisible('\x1B]8;;https://example.com\x1B\\This is a link\x1B]8;;\x1B\\'))
  659. 'This is a link'
  660. """
  661. if isinstance(s, _text_type):
  662. links_removed = re.sub(_invisible_codes_link, "\\1", s)
  663. return re.sub(_invisible_codes, "", links_removed)
  664. else: # a bytestring
  665. return re.sub(_invisible_codes_bytes, "", s)
  666. def _visible_width(s):
  667. """Visible width of a printed string. ANSI color codes are removed.
  668. >>> _visible_width('\x1b[31mhello\x1b[0m'), _visible_width("world")
  669. (5, 5)
  670. """
  671. # optional wide-character support
  672. if wcwidth is not None and WIDE_CHARS_MODE:
  673. len_fn = wcwidth.wcswidth
  674. else:
  675. len_fn = len
  676. if isinstance(s, _text_type) or isinstance(s, _binary_type):
  677. return len_fn(_strip_invisible(s))
  678. else:
  679. return len_fn(_text_type(s))
  680. def _is_multiline(s):
  681. if isinstance(s, _text_type):
  682. return bool(re.search(_multiline_codes, s))
  683. else: # a bytestring
  684. return bool(re.search(_multiline_codes_bytes, s))
  685. def _multiline_width(multiline_s, line_width_fn=len):
  686. """Visible width of a potentially multiline content."""
  687. return max(map(line_width_fn, re.split("[\r\n]", multiline_s)))
  688. def _choose_width_fn(has_invisible, enable_widechars, is_multiline):
  689. """Return a function to calculate visible cell width."""
  690. if has_invisible:
  691. line_width_fn = _visible_width
  692. elif enable_widechars: # optional wide-character support if available
  693. line_width_fn = wcwidth.wcswidth
  694. else:
  695. line_width_fn = len
  696. if is_multiline:
  697. width_fn = lambda s: _multiline_width(s, line_width_fn) # noqa
  698. else:
  699. width_fn = line_width_fn
  700. return width_fn
  701. def _align_column_choose_padfn(strings, alignment, has_invisible):
  702. if alignment == "right":
  703. if not PRESERVE_WHITESPACE:
  704. strings = [s.strip() for s in strings]
  705. padfn = _padleft
  706. elif alignment == "center":
  707. if not PRESERVE_WHITESPACE:
  708. strings = [s.strip() for s in strings]
  709. padfn = _padboth
  710. elif alignment == "decimal":
  711. if has_invisible:
  712. decimals = [_afterpoint(_strip_invisible(s)) for s in strings]
  713. else:
  714. decimals = [_afterpoint(s) for s in strings]
  715. maxdecimals = max(decimals)
  716. strings = [s + (maxdecimals - decs) * " " for s, decs in zip(strings, decimals)]
  717. padfn = _padleft
  718. elif not alignment:
  719. padfn = _padnone
  720. else:
  721. if not PRESERVE_WHITESPACE:
  722. strings = [s.strip() for s in strings]
  723. padfn = _padright
  724. return strings, padfn
  725. def _align_column_choose_width_fn(has_invisible, enable_widechars, is_multiline):
  726. if has_invisible:
  727. line_width_fn = _visible_width
  728. elif enable_widechars: # optional wide-character support if available
  729. line_width_fn = wcwidth.wcswidth
  730. else:
  731. line_width_fn = len
  732. if is_multiline:
  733. width_fn = lambda s: _align_column_multiline_width(s, line_width_fn) # noqa
  734. else:
  735. width_fn = line_width_fn
  736. return width_fn
  737. def _align_column_multiline_width(multiline_s, line_width_fn=len):
  738. """Visible width of a potentially multiline content."""
  739. return list(map(line_width_fn, re.split("[\r\n]", multiline_s)))
  740. def _flat_list(nested_list):
  741. ret = []
  742. for item in nested_list:
  743. if isinstance(item, list):
  744. for subitem in item:
  745. ret.append(subitem)
  746. else:
  747. ret.append(item)
  748. return ret
  749. def _align_column(
  750. strings,
  751. alignment,
  752. minwidth=0,
  753. has_invisible=True,
  754. enable_widechars=False,
  755. is_multiline=False,
  756. ):
  757. """[string] -> [padded_string]"""
  758. strings, padfn = _align_column_choose_padfn(strings, alignment, has_invisible)
  759. width_fn = _align_column_choose_width_fn(
  760. has_invisible, enable_widechars, is_multiline
  761. )
  762. s_widths = list(map(width_fn, strings))
  763. maxwidth = max(max(_flat_list(s_widths)), minwidth)
  764. # TODO: refactor column alignment in single-line and multiline modes
  765. if is_multiline:
  766. if not enable_widechars and not has_invisible:
  767. padded_strings = [
  768. "\n".join([padfn(maxwidth, s) for s in ms.splitlines()])
  769. for ms in strings
  770. ]
  771. else:
  772. # enable wide-character width corrections
  773. s_lens = [[len(s) for s in re.split("[\r\n]", ms)] for ms in strings]
  774. visible_widths = [
  775. [maxwidth - (w - l) for w, l in zip(mw, ml)]
  776. for mw, ml in zip(s_widths, s_lens)
  777. ]
  778. # wcswidth and _visible_width don't count invisible characters;
  779. # padfn doesn't need to apply another correction
  780. padded_strings = [
  781. "\n".join([padfn(w, s) for s, w in zip((ms.splitlines() or ms), mw)])
  782. for ms, mw in zip(strings, visible_widths)
  783. ]
  784. else: # single-line cell values
  785. if not enable_widechars and not has_invisible:
  786. padded_strings = [padfn(maxwidth, s) for s in strings]
  787. else:
  788. # enable wide-character width corrections
  789. s_lens = list(map(len, strings))
  790. visible_widths = [maxwidth - (w - l) for w, l in zip(s_widths, s_lens)]
  791. # wcswidth and _visible_width don't count invisible characters;
  792. # padfn doesn't need to apply another correction
  793. padded_strings = [padfn(w, s) for s, w in zip(strings, visible_widths)]
  794. return padded_strings
  795. def _more_generic(type1, type2):
  796. types = {
  797. _none_type: 0,
  798. _bool_type: 1,
  799. int: 2,
  800. float: 3,
  801. _binary_type: 4,
  802. _text_type: 5,
  803. }
  804. invtypes = {
  805. 5: _text_type,
  806. 4: _binary_type,
  807. 3: float,
  808. 2: int,
  809. 1: _bool_type,
  810. 0: _none_type,
  811. }
  812. moregeneric = max(types.get(type1, 5), types.get(type2, 5))
  813. return invtypes[moregeneric]
  814. def _column_type(strings, has_invisible=True, numparse=True):
  815. """The least generic type all column values are convertible to.
  816. >>> _column_type([True, False]) is _bool_type
  817. True
  818. >>> _column_type(["1", "2"]) is _int_type
  819. True
  820. >>> _column_type(["1", "2.3"]) is _float_type
  821. True
  822. >>> _column_type(["1", "2.3", "four"]) is _text_type
  823. True
  824. >>> _column_type(["four", '\u043f\u044f\u0442\u044c']) is _text_type
  825. True
  826. >>> _column_type([None, "brux"]) is _text_type
  827. True
  828. >>> _column_type([1, 2, None]) is _int_type
  829. True
  830. >>> import datetime as dt
  831. >>> _column_type([dt.datetime(1991,2,19), dt.time(17,35)]) is _text_type
  832. True
  833. """
  834. types = [_type(s, has_invisible, numparse) for s in strings]
  835. return reduce(_more_generic, types, _bool_type)
  836. def _format(val, valtype, floatfmt, missingval="", has_invisible=True):
  837. """Format a value according to its type.
  838. Unicode is supported:
  839. >>> hrow = ['\u0431\u0443\u043a\u0432\u0430', '\u0446\u0438\u0444\u0440\u0430'] ; \
  840. tbl = [['\u0430\u0437', 2], ['\u0431\u0443\u043a\u0438', 4]] ; \
  841. good_result = '\\u0431\\u0443\\u043a\\u0432\\u0430 \\u0446\\u0438\\u0444\\u0440\\u0430\\n------- -------\\n\\u0430\\u0437 2\\n\\u0431\\u0443\\u043a\\u0438 4' ; \
  842. tabulate(tbl, headers=hrow) == good_result
  843. True
  844. """ # noqa
  845. if val is None:
  846. return missingval
  847. if valtype in [int, _text_type]:
  848. return "{0}".format(val)
  849. elif valtype is _binary_type:
  850. try:
  851. return _text_type(val, "ascii")
  852. except TypeError:
  853. return _text_type(val)
  854. elif valtype is float:
  855. is_a_colored_number = has_invisible and isinstance(
  856. val, (_text_type, _binary_type)
  857. )
  858. if is_a_colored_number:
  859. raw_val = _strip_invisible(val)
  860. formatted_val = format(float(raw_val), floatfmt)
  861. return val.replace(raw_val, formatted_val)
  862. else:
  863. return format(float(val), floatfmt)
  864. else:
  865. return "{0}".format(val)
  866. def _align_header(
  867. header, alignment, width, visible_width, is_multiline=False, width_fn=None
  868. ):
  869. "Pad string header to width chars given known visible_width of the header."
  870. if is_multiline:
  871. header_lines = re.split(_multiline_codes, header)
  872. padded_lines = [
  873. _align_header(h, alignment, width, width_fn(h)) for h in header_lines
  874. ]
  875. return "\n".join(padded_lines)
  876. # else: not multiline
  877. ninvisible = len(header) - visible_width
  878. width += ninvisible
  879. if alignment == "left":
  880. return _padright(width, header)
  881. elif alignment == "center":
  882. return _padboth(width, header)
  883. elif not alignment:
  884. return "{0}".format(header)
  885. else:
  886. return _padleft(width, header)
  887. def _prepend_row_index(rows, index):
  888. """Add a left-most index column."""
  889. if index is None or index is False:
  890. return rows
  891. if len(index) != len(rows):
  892. print("index=", index)
  893. print("rows=", rows)
  894. raise ValueError("index must be as long as the number of data rows")
  895. rows = [[v] + list(row) for v, row in zip(index, rows)]
  896. return rows
  897. def _bool(val):
  898. "A wrapper around standard bool() which doesn't throw on NumPy arrays"
  899. try:
  900. return bool(val)
  901. except ValueError: # val is likely to be a numpy array with many elements
  902. return False
  903. def _normalize_tabular_data(tabular_data, headers, showindex="default"):
  904. """Transform a supported data type to a list of lists, and a list of headers.
  905. Supported tabular data types:
  906. * list-of-lists or another iterable of iterables
  907. * list of named tuples (usually used with headers="keys")
  908. * list of dicts (usually used with headers="keys")
  909. * list of OrderedDicts (usually used with headers="keys")
  910. * 2D NumPy arrays
  911. * NumPy record arrays (usually used with headers="keys")
  912. * dict of iterables (usually used with headers="keys")
  913. * pandas.DataFrame (usually used with headers="keys")
  914. The first row can be used as headers if headers="firstrow",
  915. column indices can be used as headers if headers="keys".
  916. If showindex="default", show row indices of the pandas.DataFrame.
  917. If showindex="always", show row indices for all types of data.
  918. If showindex="never", don't show row indices for all types of data.
  919. If showindex is an iterable, show its values as row indices.
  920. """
  921. try:
  922. bool(headers)
  923. is_headers2bool_broken = False # noqa
  924. except ValueError: # numpy.ndarray, pandas.core.index.Index, ...
  925. is_headers2bool_broken = True # noqa
  926. headers = list(headers)
  927. index = None
  928. if hasattr(tabular_data, "keys") and hasattr(tabular_data, "values"):
  929. # dict-like and pandas.DataFrame?
  930. if hasattr(tabular_data.values, "__call__"):
  931. # likely a conventional dict
  932. keys = tabular_data.keys()
  933. rows = list(
  934. izip_longest(*tabular_data.values())
  935. ) # columns have to be transposed
  936. elif hasattr(tabular_data, "index"):
  937. # values is a property, has .index => it's likely a pandas.DataFrame (pandas 0.11.0)
  938. keys = list(tabular_data)
  939. if (
  940. showindex in ["default", "always", True]
  941. and tabular_data.index.name is not None
  942. ):
  943. if isinstance(tabular_data.index.name, list):
  944. keys[:0] = tabular_data.index.name
  945. else:
  946. keys[:0] = [tabular_data.index.name]
  947. vals = tabular_data.values # values matrix doesn't need to be transposed
  948. # for DataFrames add an index per default
  949. index = list(tabular_data.index)
  950. rows = [list(row) for row in vals]
  951. else:
  952. raise ValueError("tabular data doesn't appear to be a dict or a DataFrame")
  953. if headers == "keys":
  954. headers = list(map(_text_type, keys)) # headers should be strings
  955. else: # it's a usual an iterable of iterables, or a NumPy array
  956. rows = list(tabular_data)
  957. if headers == "keys" and not rows:
  958. # an empty table (issue #81)
  959. headers = []
  960. elif (
  961. headers == "keys"
  962. and hasattr(tabular_data, "dtype")
  963. and getattr(tabular_data.dtype, "names")
  964. ):
  965. # numpy record array
  966. headers = tabular_data.dtype.names
  967. elif (
  968. headers == "keys"
  969. and len(rows) > 0
  970. and isinstance(rows[0], tuple)
  971. and hasattr(rows[0], "_fields")
  972. ):
  973. # namedtuple
  974. headers = list(map(_text_type, rows[0]._fields))
  975. elif len(rows) > 0 and hasattr(rows[0], "keys") and hasattr(rows[0], "values"):
  976. # dict-like object
  977. uniq_keys = set() # implements hashed lookup
  978. keys = [] # storage for set
  979. if headers == "firstrow":
  980. firstdict = rows[0] if len(rows) > 0 else {}
  981. keys.extend(firstdict.keys())
  982. uniq_keys.update(keys)
  983. rows = rows[1:]
  984. for row in rows:
  985. for k in row.keys():
  986. # Save unique items in input order
  987. if k not in uniq_keys:
  988. keys.append(k)
  989. uniq_keys.add(k)
  990. if headers == "keys":
  991. headers = keys
  992. elif isinstance(headers, dict):
  993. # a dict of headers for a list of dicts
  994. headers = [headers.get(k, k) for k in keys]
  995. headers = list(map(_text_type, headers))
  996. elif headers == "firstrow":
  997. if len(rows) > 0:
  998. headers = [firstdict.get(k, k) for k in keys]
  999. headers = list(map(_text_type, headers))
  1000. else:
  1001. headers = []
  1002. elif headers:
  1003. raise ValueError(
  1004. "headers for a list of dicts is not a dict or a keyword"
  1005. )
  1006. rows = [[row.get(k) for k in keys] for row in rows]
  1007. elif (
  1008. headers == "keys"
  1009. and hasattr(tabular_data, "description")
  1010. and hasattr(tabular_data, "fetchone")
  1011. and hasattr(tabular_data, "rowcount")
  1012. ):
  1013. # Python Database API cursor object (PEP 0249)
  1014. # print tabulate(cursor, headers='keys')
  1015. headers = [column[0] for column in tabular_data.description]
  1016. elif headers == "keys" and len(rows) > 0:
  1017. # keys are column indices
  1018. headers = list(map(_text_type, range(len(rows[0]))))
  1019. # take headers from the first row if necessary
  1020. if headers == "firstrow" and len(rows) > 0:
  1021. if index is not None:
  1022. headers = [index[0]] + list(rows[0])
  1023. index = index[1:]
  1024. else:
  1025. headers = rows[0]
  1026. headers = list(map(_text_type, headers)) # headers should be strings
  1027. rows = rows[1:]
  1028. headers = list(map(_text_type, headers))
  1029. rows = list(map(list, rows))
  1030. # add or remove an index column
  1031. showindex_is_a_str = type(showindex) in [_text_type, _binary_type]
  1032. if showindex == "default" and index is not None:
  1033. rows = _prepend_row_index(rows, index)
  1034. elif isinstance(showindex, Iterable) and not showindex_is_a_str:
  1035. rows = _prepend_row_index(rows, list(showindex))
  1036. elif showindex == "always" or (_bool(showindex) and not showindex_is_a_str):
  1037. if index is None:
  1038. index = list(range(len(rows)))
  1039. rows = _prepend_row_index(rows, index)
  1040. elif showindex == "never" or (not _bool(showindex) and not showindex_is_a_str):
  1041. pass
  1042. # pad with empty headers for initial columns if necessary
  1043. if headers and len(rows) > 0:
  1044. nhs = len(headers)
  1045. ncols = len(rows[0])
  1046. if nhs < ncols:
  1047. headers = [""] * (ncols - nhs) + headers
  1048. return rows, headers
  1049. def tabulate(
  1050. tabular_data,
  1051. headers=(),
  1052. tablefmt="simple",
  1053. floatfmt=_DEFAULT_FLOATFMT,
  1054. numalign=_DEFAULT_ALIGN,
  1055. stralign=_DEFAULT_ALIGN,
  1056. missingval=_DEFAULT_MISSINGVAL,
  1057. showindex="default",
  1058. disable_numparse=False,
  1059. colalign=None,
  1060. ):
  1061. """Format a fixed width table for pretty printing.
  1062. >>> print(tabulate([[1, 2.34], [-56, "8.999"], ["2", "10001"]]))
  1063. --- ---------
  1064. 1 2.34
  1065. -56 8.999
  1066. 2 10001
  1067. --- ---------
  1068. The first required argument (`tabular_data`) can be a
  1069. list-of-lists (or another iterable of iterables), a list of named
  1070. tuples, a dictionary of iterables, an iterable of dictionaries,
  1071. a two-dimensional NumPy array, NumPy record array, or a Pandas'
  1072. dataframe.
  1073. Table headers
  1074. -------------
  1075. To print nice column headers, supply the second argument (`headers`):
  1076. - `headers` can be an explicit list of column headers
  1077. - if `headers="firstrow"`, then the first row of data is used
  1078. - if `headers="keys"`, then dictionary keys or column indices are used
  1079. Otherwise a headerless table is produced.
  1080. If the number of headers is less than the number of columns, they
  1081. are supposed to be names of the last columns. This is consistent
  1082. with the plain-text format of R and Pandas' dataframes.
  1083. >>> print(tabulate([["sex","age"],["Alice","F",24],["Bob","M",19]],
  1084. ... headers="firstrow"))
  1085. sex age
  1086. ----- ----- -----
  1087. Alice F 24
  1088. Bob M 19
  1089. By default, pandas.DataFrame data have an additional column called
  1090. row index. To add a similar column to all other types of data,
  1091. use `showindex="always"` or `showindex=True`. To suppress row indices
  1092. for all types of data, pass `showindex="never" or `showindex=False`.
  1093. To add a custom row index column, pass `showindex=some_iterable`.
  1094. >>> print(tabulate([["F",24],["M",19]], showindex="always"))
  1095. - - --
  1096. 0 F 24
  1097. 1 M 19
  1098. - - --
  1099. Column alignment
  1100. ----------------
  1101. `tabulate` tries to detect column types automatically, and aligns
  1102. the values properly. By default it aligns decimal points of the
  1103. numbers (or flushes integer numbers to the right), and flushes
  1104. everything else to the left. Possible column alignments
  1105. (`numalign`, `stralign`) are: "right", "center", "left", "decimal"
  1106. (only for `numalign`), and None (to disable alignment).
  1107. Table formats
  1108. -------------
  1109. `floatfmt` is a format specification used for columns which
  1110. contain numeric data with a decimal point. This can also be
  1111. a list or tuple of format strings, one per column.
  1112. `None` values are replaced with a `missingval` string (like
  1113. `floatfmt`, this can also be a list of values for different
  1114. columns):
  1115. >>> print(tabulate([["spam", 1, None],
  1116. ... ["eggs", 42, 3.14],
  1117. ... ["other", None, 2.7]], missingval="?"))
  1118. ----- -- ----
  1119. spam 1 ?
  1120. eggs 42 3.14
  1121. other ? 2.7
  1122. ----- -- ----
  1123. Various plain-text table formats (`tablefmt`) are supported:
  1124. 'plain', 'simple', 'grid', 'pipe', 'orgtbl', 'rst', 'mediawiki',
  1125. 'latex', 'latex_raw', 'latex_booktabs', 'latex_longtable' and tsv.
  1126. Variable `tabulate_formats`contains the list of currently supported formats.
  1127. "plain" format doesn't use any pseudographics to draw tables,
  1128. it separates columns with a double space:
  1129. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
  1130. ... ["strings", "numbers"], "plain"))
  1131. strings numbers
  1132. spam 41.9999
  1133. eggs 451
  1134. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="plain"))
  1135. spam 41.9999
  1136. eggs 451
  1137. "simple" format is like Pandoc simple_tables:
  1138. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
  1139. ... ["strings", "numbers"], "simple"))
  1140. strings numbers
  1141. --------- ---------
  1142. spam 41.9999
  1143. eggs 451
  1144. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="simple"))
  1145. ---- --------
  1146. spam 41.9999
  1147. eggs 451
  1148. ---- --------
  1149. "grid" is similar to tables produced by Emacs table.el package or
  1150. Pandoc grid_tables:
  1151. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
  1152. ... ["strings", "numbers"], "grid"))
  1153. +-----------+-----------+
  1154. | strings | numbers |
  1155. +===========+===========+
  1156. | spam | 41.9999 |
  1157. +-----------+-----------+
  1158. | eggs | 451 |
  1159. +-----------+-----------+
  1160. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="grid"))
  1161. +------+----------+
  1162. | spam | 41.9999 |
  1163. +------+----------+
  1164. | eggs | 451 |
  1165. +------+----------+
  1166. "fancy_grid" draws a grid using box-drawing characters:
  1167. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
  1168. ... ["strings", "numbers"], "fancy_grid"))
  1169. ╒═══════════╤═══════════╕
  1170. │ strings │ numbers │
  1171. ╞═══════════╪═══════════╡
  1172. │ spam │ 41.9999 │
  1173. ├───────────┼───────────┤
  1174. │ eggs │ 451 │
  1175. ╘═══════════╧═══════════╛
  1176. "pipe" is like tables in PHP Markdown Extra extension or Pandoc
  1177. pipe_tables:
  1178. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
  1179. ... ["strings", "numbers"], "pipe"))
  1180. | strings | numbers |
  1181. |:----------|----------:|
  1182. | spam | 41.9999 |
  1183. | eggs | 451 |
  1184. "presto" is like tables produce by the Presto CLI:
  1185. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
  1186. ... ["strings", "numbers"], "presto"))
  1187. strings | numbers
  1188. -----------+-----------
  1189. spam | 41.9999
  1190. eggs | 451
  1191. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="pipe"))
  1192. |:-----|---------:|
  1193. | spam | 41.9999 |
  1194. | eggs | 451 |
  1195. "orgtbl" is like tables in Emacs org-mode and orgtbl-mode. They
  1196. are slightly different from "pipe" format by not using colons to
  1197. define column alignment, and using a "+" sign to indicate line
  1198. intersections:
  1199. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
  1200. ... ["strings", "numbers"], "orgtbl"))
  1201. | strings | numbers |
  1202. |-----------+-----------|
  1203. | spam | 41.9999 |
  1204. | eggs | 451 |
  1205. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="orgtbl"))
  1206. | spam | 41.9999 |
  1207. | eggs | 451 |
  1208. "rst" is like a simple table format from reStructuredText; please
  1209. note that reStructuredText accepts also "grid" tables:
  1210. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
  1211. ... ["strings", "numbers"], "rst"))
  1212. ========= =========
  1213. strings numbers
  1214. ========= =========
  1215. spam 41.9999
  1216. eggs 451
  1217. ========= =========
  1218. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="rst"))
  1219. ==== ========
  1220. spam 41.9999
  1221. eggs 451
  1222. ==== ========
  1223. "mediawiki" produces a table markup used in Wikipedia and on other
  1224. MediaWiki-based sites:
  1225. >>> print(tabulate([["strings", "numbers"], ["spam", 41.9999], ["eggs", "451.0"]],
  1226. ... headers="firstrow", tablefmt="mediawiki"))
  1227. {| class="wikitable" style="text-align: left;"
  1228. |+ <!-- caption -->
  1229. |-
  1230. ! strings !! align="right"| numbers
  1231. |-
  1232. | spam || align="right"| 41.9999
  1233. |-
  1234. | eggs || align="right"| 451
  1235. |}
  1236. "html" produces HTML markup as an html.escape'd str
  1237. with a ._repr_html_ method so that Jupyter Lab and Notebook display the HTML
  1238. and a .str property so that the raw HTML remains accessible
  1239. the unsafehtml table format can be used if an unescaped HTML format is required:
  1240. >>> print(tabulate([["strings", "numbers"], ["spam", 41.9999], ["eggs", "451.0"]],
  1241. ... headers="firstrow", tablefmt="html"))
  1242. <table>
  1243. <thead>
  1244. <tr><th>strings </th><th style="text-align: right;"> numbers</th></tr>
  1245. </thead>
  1246. <tbody>
  1247. <tr><td>spam </td><td style="text-align: right;"> 41.9999</td></tr>
  1248. <tr><td>eggs </td><td style="text-align: right;"> 451 </td></tr>
  1249. </tbody>
  1250. </table>
  1251. "latex" produces a tabular environment of LaTeX document markup:
  1252. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="latex"))
  1253. \\begin{tabular}{lr}
  1254. \\hline
  1255. spam & 41.9999 \\\\
  1256. eggs & 451 \\\\
  1257. \\hline
  1258. \\end{tabular}
  1259. "latex_raw" is similar to "latex", but doesn't escape special characters,
  1260. such as backslash and underscore, so LaTeX commands may embedded into
  1261. cells' values:
  1262. >>> print(tabulate([["spam$_9$", 41.9999], ["\\\\emph{eggs}", "451.0"]], tablefmt="latex_raw"))
  1263. \\begin{tabular}{lr}
  1264. \\hline
  1265. spam$_9$ & 41.9999 \\\\
  1266. \\emph{eggs} & 451 \\\\
  1267. \\hline
  1268. \\end{tabular}
  1269. "latex_booktabs" produces a tabular environment of LaTeX document markup
  1270. using the booktabs.sty package:
  1271. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="latex_booktabs"))
  1272. \\begin{tabular}{lr}
  1273. \\toprule
  1274. spam & 41.9999 \\\\
  1275. eggs & 451 \\\\
  1276. \\bottomrule
  1277. \\end{tabular}
  1278. "latex_longtable" produces a tabular environment that can stretch along
  1279. multiple pages, using the longtable package for LaTeX.
  1280. >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tablefmt="latex_longtable"))
  1281. \\begin{longtable}{lr}
  1282. \\hline
  1283. spam & 41.9999 \\\\
  1284. eggs & 451 \\\\
  1285. \\hline
  1286. \\end{longtable}
  1287. Number parsing
  1288. --------------
  1289. By default, anything which can be parsed as a number is a number.
  1290. This ensures numbers represented as strings are aligned properly.
  1291. This can lead to weird results for particular strings such as
  1292. specific git SHAs e.g. "42992e1" will be parsed into the number
  1293. 429920 and aligned as such.
  1294. To completely disable number parsing (and alignment), use
  1295. `disable_numparse=True`. For more fine grained control, a list column
  1296. indices is used to disable number parsing only on those columns
  1297. e.g. `disable_numparse=[0, 2]` would disable number parsing only on the
  1298. first and third columns.
  1299. """
  1300. if tabular_data is None:
  1301. tabular_data = []
  1302. list_of_lists, headers = _normalize_tabular_data(
  1303. tabular_data, headers, showindex=showindex
  1304. )
  1305. # empty values in the first column of RST tables should be escaped (issue #82)
  1306. # "" should be escaped as "\\ " or ".."
  1307. if tablefmt == "rst":
  1308. list_of_lists, headers = _rst_escape_first_column(list_of_lists, headers)
  1309. # PrettyTable formatting does not use any extra padding.
  1310. # Numbers are not parsed and are treated the same as strings for alignment.
  1311. # Check if pretty is the format being used and override the defaults so it
  1312. # does not impact other formats.
  1313. min_padding = MIN_PADDING
  1314. if tablefmt == "pretty":
  1315. min_padding = 0
  1316. disable_numparse = True
  1317. numalign = "center" if numalign == _DEFAULT_ALIGN else numalign
  1318. stralign = "center" if stralign == _DEFAULT_ALIGN else stralign
  1319. else:
  1320. numalign = "decimal" if numalign == _DEFAULT_ALIGN else numalign
  1321. stralign = "left" if stralign == _DEFAULT_ALIGN else stralign
  1322. # optimization: look for ANSI control codes once,
  1323. # enable smart width functions only if a control code is found
  1324. plain_text = "\t".join(
  1325. ["\t".join(map(_text_type, headers))]
  1326. + ["\t".join(map(_text_type, row)) for row in list_of_lists]
  1327. )
  1328. has_invisible = re.search(_invisible_codes, plain_text)
  1329. if not has_invisible:
  1330. has_invisible = re.search(_invisible_codes_link, plain_text)
  1331. enable_widechars = wcwidth is not None and WIDE_CHARS_MODE
  1332. if (
  1333. not isinstance(tablefmt, TableFormat)
  1334. and tablefmt in multiline_formats
  1335. and _is_multiline(plain_text)
  1336. ):
  1337. tablefmt = multiline_formats.get(tablefmt, tablefmt)
  1338. is_multiline = True
  1339. else:
  1340. is_multiline = False
  1341. width_fn = _choose_width_fn(has_invisible, enable_widechars, is_multiline)
  1342. # format rows and columns, convert numeric values to strings
  1343. cols = list(izip_longest(*list_of_lists))
  1344. numparses = _expand_numparse(disable_numparse, len(cols))
  1345. coltypes = [_column_type(col, numparse=np) for col, np in zip(cols, numparses)]
  1346. if isinstance(floatfmt, basestring): # old version
  1347. float_formats = len(cols) * [
  1348. floatfmt
  1349. ] # just duplicate the string to use in each column
  1350. else: # if floatfmt is list, tuple etc we have one per column
  1351. float_formats = list(floatfmt)
  1352. if len(float_formats) < len(cols):
  1353. float_formats.extend((len(cols) - len(float_formats)) * [_DEFAULT_FLOATFMT])
  1354. if isinstance(missingval, basestring):
  1355. missing_vals = len(cols) * [missingval]
  1356. else:
  1357. missing_vals = list(missingval)
  1358. if len(missing_vals) < len(cols):
  1359. missing_vals.extend((len(cols) - len(missing_vals)) * [_DEFAULT_MISSINGVAL])
  1360. cols = [
  1361. [_format(v, ct, fl_fmt, miss_v, has_invisible) for v in c]
  1362. for c, ct, fl_fmt, miss_v in zip(cols, coltypes, float_formats, missing_vals)
  1363. ]
  1364. # align columns
  1365. aligns = [numalign if ct in [int, float] else stralign for ct in coltypes]
  1366. if colalign is not None:
  1367. assert isinstance(colalign, Iterable)
  1368. for idx, align in enumerate(colalign):
  1369. aligns[idx] = align
  1370. minwidths = (
  1371. [width_fn(h) + min_padding for h in headers] if headers else [0] * len(cols)
  1372. )
  1373. cols = [
  1374. _align_column(c, a, minw, has_invisible, enable_widechars, is_multiline)
  1375. for c, a, minw in zip(cols, aligns, minwidths)
  1376. ]
  1377. if headers:
  1378. # align headers and add headers
  1379. t_cols = cols or [[""]] * len(headers)
  1380. t_aligns = aligns or [stralign] * len(headers)
  1381. minwidths = [
  1382. max(minw, max(width_fn(cl) for cl in c))
  1383. for minw, c in zip(minwidths, t_cols)
  1384. ]
  1385. headers = [
  1386. _align_header(h, a, minw, width_fn(h), is_multiline, width_fn)
  1387. for h, a, minw in zip(headers, t_aligns, minwidths)
  1388. ]
  1389. rows = list(zip(*cols))
  1390. else:
  1391. minwidths = [max(width_fn(cl) for cl in c) for c in cols]
  1392. rows = list(zip(*cols))
  1393. if not isinstance(tablefmt, TableFormat):
  1394. tablefmt = _table_formats.get(tablefmt, _table_formats["simple"])
  1395. return _format_table(tablefmt, headers, rows, minwidths, aligns, is_multiline)
  1396. def _expand_numparse(disable_numparse, column_count):
  1397. """
  1398. Return a list of bools of length `column_count` which indicates whether
  1399. number parsing should be used on each column.
  1400. If `disable_numparse` is a list of indices, each of those indices are False,
  1401. and everything else is True.
  1402. If `disable_numparse` is a bool, then the returned list is all the same.
  1403. """
  1404. if isinstance(disable_numparse, Iterable):
  1405. numparses = [True] * column_count
  1406. for index in disable_numparse:
  1407. numparses[index] = False
  1408. return numparses
  1409. else:
  1410. return [not disable_numparse] * column_count
  1411. def _pad_row(cells, padding):
  1412. if cells:
  1413. pad = " " * padding
  1414. padded_cells = [pad + cell + pad for cell in cells]
  1415. return padded_cells
  1416. else:
  1417. return cells
  1418. def _build_simple_row(padded_cells, rowfmt):
  1419. "Format row according to DataRow format without padding."
  1420. begin, sep, end = rowfmt
  1421. return (begin + sep.join(padded_cells) + end).rstrip()
  1422. def _build_row(padded_cells, colwidths, colaligns, rowfmt):
  1423. "Return a string which represents a row of data cells."
  1424. if not rowfmt:
  1425. return None
  1426. if hasattr(rowfmt, "__call__"):
  1427. return rowfmt(padded_cells, colwidths, colaligns)
  1428. else:
  1429. return _build_simple_row(padded_cells, rowfmt)
  1430. def _append_basic_row(lines, padded_cells, colwidths, colaligns, rowfmt):
  1431. lines.append(_build_row(padded_cells, colwidths, colaligns, rowfmt))
  1432. return lines
  1433. def _append_multiline_row(
  1434. lines, padded_multiline_cells, padded_widths, colaligns, rowfmt, pad
  1435. ):
  1436. colwidths = [w - 2 * pad for w in padded_widths]
  1437. cells_lines = [c.splitlines() for c in padded_multiline_cells]
  1438. nlines = max(map(len, cells_lines)) # number of lines in the row
  1439. # vertically pad cells where some lines are missing
  1440. cells_lines = [
  1441. (cl + [" " * w] * (nlines - len(cl))) for cl, w in zip(cells_lines, colwidths)
  1442. ]
  1443. lines_cells = [[cl[i] for cl in cells_lines] for i in range(nlines)]
  1444. for ln in lines_cells:
  1445. padded_ln = _pad_row(ln, pad)
  1446. _append_basic_row(lines, padded_ln, colwidths, colaligns, rowfmt)
  1447. return lines
  1448. def _build_line(colwidths, colaligns, linefmt):
  1449. "Return a string which represents a horizontal line."
  1450. if not linefmt:
  1451. return None
  1452. if hasattr(linefmt, "__call__"):
  1453. return linefmt(colwidths, colaligns)
  1454. else:
  1455. begin, fill, sep, end = linefmt
  1456. cells = [fill * w for w in colwidths]
  1457. return _build_simple_row(cells, (begin, sep, end))
  1458. def _append_line(lines, colwidths, colaligns, linefmt):
  1459. lines.append(_build_line(colwidths, colaligns, linefmt))
  1460. return lines
  1461. class JupyterHTMLStr(str):
  1462. """Wrap the string with a _repr_html_ method so that Jupyter
  1463. displays the HTML table"""
  1464. def _repr_html_(self):
  1465. return self
  1466. @property
  1467. def str(self):
  1468. """add a .str property so that the raw string is still accessible"""
  1469. return self
  1470. def _format_table(fmt, headers, rows, colwidths, colaligns, is_multiline):
  1471. """Produce a plain-text representation of the table."""
  1472. lines = []
  1473. hidden = fmt.with_header_hide if (headers and fmt.with_header_hide) else []
  1474. pad = fmt.padding
  1475. headerrow = fmt.headerrow
  1476. padded_widths = [(w + 2 * pad) for w in colwidths]
  1477. if is_multiline:
  1478. pad_row = lambda row, _: row # noqa do it later, in _append_multiline_row
  1479. append_row = partial(_append_multiline_row, pad=pad)
  1480. else:
  1481. pad_row = _pad_row
  1482. append_row = _append_basic_row
  1483. padded_headers = pad_row(headers, pad)
  1484. padded_rows = [pad_row(row, pad) for row in rows]
  1485. if fmt.lineabove and "lineabove" not in hidden:
  1486. _append_line(lines, padded_widths, colaligns, fmt.lineabove)
  1487. if padded_headers:
  1488. append_row(lines, padded_headers, padded_widths, colaligns, headerrow)
  1489. if fmt.linebelowheader and "linebelowheader" not in hidden:
  1490. _append_line(lines, padded_widths, colaligns, fmt.linebelowheader)
  1491. if padded_rows and fmt.linebetweenrows and "linebetweenrows" not in hidden:
  1492. # initial rows with a line below
  1493. for row in padded_rows[:-1]:
  1494. append_row(lines, row, padded_widths, colaligns, fmt.datarow)
  1495. _append_line(lines, padded_widths, colaligns, fmt.linebetweenrows)
  1496. # the last row without a line below
  1497. append_row(lines, padded_rows[-1], padded_widths, colaligns, fmt.datarow)
  1498. else:
  1499. for row in padded_rows:
  1500. append_row(lines, row, padded_widths, colaligns, fmt.datarow)
  1501. if fmt.linebelow and "linebelow" not in hidden:
  1502. _append_line(lines, padded_widths, colaligns, fmt.linebelow)
  1503. if headers or rows:
  1504. output = "\n".join(lines)
  1505. if fmt.lineabove == _html_begin_table_without_header:
  1506. return JupyterHTMLStr(output)
  1507. else:
  1508. return output
  1509. else: # a completely empty table
  1510. return ""
  1511. def _main():
  1512. """\
  1513. Usage: tabulate [options] [FILE ...]
  1514. Pretty-print tabular data.
  1515. See also https://github.com/astanin/python-tabulate
  1516. FILE a filename of the file with tabular data;
  1517. if "-" or missing, read data from stdin.
  1518. Options:
  1519. -h, --help show this message
  1520. -1, --header use the first row of data as a table header
  1521. -o FILE, --output FILE print table to FILE (default: stdout)
  1522. -s REGEXP, --sep REGEXP use a custom column separator (default: whitespace)
  1523. -F FPFMT, --float FPFMT floating point number format (default: g)
  1524. -f FMT, --format FMT set output table format; supported formats:
  1525. plain, simple, grid, fancy_grid, pipe, orgtbl,
  1526. rst, mediawiki, html, latex, latex_raw,
  1527. latex_booktabs, latex_longtable, tsv
  1528. (default: simple)
  1529. """
  1530. import getopt
  1531. import sys
  1532. import textwrap
  1533. usage = textwrap.dedent(_main.__doc__)
  1534. try:
  1535. opts, args = getopt.getopt(
  1536. sys.argv[1:],
  1537. "h1o:s:F:A:f:",
  1538. ["help", "header", "output", "sep=", "float=", "align=", "format="],
  1539. )
  1540. except getopt.GetoptError as e:
  1541. print(e)
  1542. print(usage)
  1543. sys.exit(2)
  1544. headers = []
  1545. floatfmt = _DEFAULT_FLOATFMT
  1546. colalign = None
  1547. tablefmt = "simple"
  1548. sep = r"\s+"
  1549. outfile = "-"
  1550. for opt, value in opts:
  1551. if opt in ["-1", "--header"]:
  1552. headers = "firstrow"
  1553. elif opt in ["-o", "--output"]:
  1554. outfile = value
  1555. elif opt in ["-F", "--float"]:
  1556. floatfmt = value
  1557. elif opt in ["-C", "--colalign"]:
  1558. colalign = value.split()
  1559. elif opt in ["-f", "--format"]:
  1560. if value not in tabulate_formats:
  1561. print("%s is not a supported table format" % value)
  1562. print(usage)
  1563. sys.exit(3)
  1564. tablefmt = value
  1565. elif opt in ["-s", "--sep"]:
  1566. sep = value
  1567. elif opt in ["-h", "--help"]:
  1568. print(usage)
  1569. sys.exit(0)
  1570. files = [sys.stdin] if not args else args
  1571. with (sys.stdout if outfile == "-" else open(outfile, "w")) as out:
  1572. for f in files:
  1573. if f == "-":
  1574. f = sys.stdin
  1575. if _is_file(f):
  1576. _pprint_file(
  1577. f,
  1578. headers=headers,
  1579. tablefmt=tablefmt,
  1580. sep=sep,
  1581. floatfmt=floatfmt,
  1582. file=out,
  1583. colalign=colalign,
  1584. )
  1585. else:
  1586. with open(f) as fobj:
  1587. _pprint_file(
  1588. fobj,
  1589. headers=headers,
  1590. tablefmt=tablefmt,
  1591. sep=sep,
  1592. floatfmt=floatfmt,
  1593. file=out,
  1594. colalign=colalign,
  1595. )
  1596. def _pprint_file(fobject, headers, tablefmt, sep, floatfmt, file, colalign):
  1597. rows = fobject.readlines()
  1598. table = [re.split(sep, r.rstrip()) for r in rows if r.strip()]
  1599. print(
  1600. tabulate(table, headers, tablefmt, floatfmt=floatfmt, colalign=colalign),
  1601. file=file,
  1602. )
  1603. if __name__ == "__main__":
  1604. _main()