@@ -140,6 +140,30 @@ def _pipe_line_with_colons(colwidths, colaligns):
140
140
return "|" + "|" .join (segments ) + "|"
141
141
142
142
143
+ def _grid_segment_with_colons (colwidth , align ):
144
+ """Return a segment of a horizontal line with optional colons which indicate
145
+ column's alignment in a grid table."""
146
+ width = colwidth
147
+ if align == "right" :
148
+ return ("=" * (width - 1 )) + ":"
149
+ elif align == "center" :
150
+ return ":" + ("=" * (width - 2 )) + ":"
151
+ elif align == "left" :
152
+ return ":" + ("=" * (width - 1 ))
153
+ else :
154
+ return "=" * width
155
+
156
+
157
+ def _grid_line_with_colons (colwidths , colaligns ):
158
+ """Return a horizontal line with optional colons to indicate column's alignment
159
+ in a grid table."""
160
+ if not colaligns :
161
+ colaligns = ["" ] * len (colwidths )
162
+ segments = [_grid_segment_with_colons (w , a ) for a , w in zip (colaligns , colwidths )]
163
+ return "+" + "+" .join (segments ) + "+"
164
+
165
+
166
+
143
167
def _mediawiki_row_with_attrs (separator , cell_values , colwidths , colaligns ):
144
168
alignment = {
145
169
"left" : "" ,
@@ -406,6 +430,16 @@ def escape_empty(val):
406
430
padding = 1 ,
407
431
with_header_hide = None ,
408
432
),
433
+ "colon_grid" : TableFormat (
434
+ lineabove = Line ("+" , "-" , "+" , "+" ),
435
+ linebelowheader = _grid_line_with_colons ,
436
+ linebetweenrows = Line ("+" , "-" , "+" , "+" ),
437
+ linebelow = Line ("+" , "-" , "+" , "+" ),
438
+ headerrow = DataRow ("|" , "|" , "|" ),
439
+ datarow = DataRow ("|" , "|" , "|" ),
440
+ padding = 1 ,
441
+ with_header_hide = None ,
442
+ ),
409
443
"outline" : TableFormat (
410
444
lineabove = Line ("+" , "-" , "+" , "+" ),
411
445
linebelowheader = Line ("+" , "=" , "+" , "+" ),
@@ -699,6 +733,7 @@ def escape_empty(val):
699
733
"mixed_grid" : "mixed_grid" ,
700
734
"double_grid" : "double_grid" ,
701
735
"fancy_grid" : "fancy_grid" ,
736
+ "colon_grid" : "colon_grid" ,
702
737
"pipe" : "pipe" ,
703
738
"orgtbl" : "orgtbl" ,
704
739
"jira" : "jira" ,
@@ -1838,6 +1873,30 @@ def tabulate(
1838
1873
│ eggs │ 451 │
1839
1874
╘═══════════╧═══════════╛
1840
1875
1876
+ "colon_grid" is similar to "grid" but uses colons only to define
1877
+ columnwise content alignment, with no whitespace padding:
1878
+
1879
+ >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
1880
+ ... ["strings", "numbers"], "colon_grid"))
1881
+ +-----------+-----------+
1882
+ | strings | numbers |
1883
+ +:==========+:==========+
1884
+ | spam | 41.9999 |
1885
+ +-----------+-----------+
1886
+ | eggs | 451 |
1887
+ +-----------+-----------+
1888
+
1889
+ >>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
1890
+ ... ["strings", "numbers"], "colon_grid",
1891
+ ... colalign=["right", "left"]))
1892
+ +-----------+-----------+
1893
+ | strings | numbers |
1894
+ +==========:+:==========+
1895
+ | spam | 41.9999 |
1896
+ +-----------+-----------+
1897
+ | eggs | 451 |
1898
+ +-----------+-----------+
1899
+
1841
1900
"outline" is the same as the "grid" format but doesn't draw lines between rows:
1842
1901
1843
1902
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
@@ -2154,6 +2213,13 @@ def tabulate(
2154
2213
numalign = "decimal" if numalign == _DEFAULT_ALIGN else numalign
2155
2214
stralign = "left" if stralign == _DEFAULT_ALIGN else stralign
2156
2215
2216
+ # 'colon_grid' uses colons in the line beneath the header to represent a column's
2217
+ # alignment instead of literally aligning the text differently. Hence,
2218
+ # left alignment of the data in the text output is enforced.
2219
+ if tablefmt == "colon_grid" :
2220
+ colglobalalign = "left"
2221
+ headersglobalalign = "left"
2222
+
2157
2223
# optimization: look for ANSI control codes once,
2158
2224
# enable smart width functions only if a control code is found
2159
2225
#
@@ -2222,7 +2288,7 @@ def tabulate(
2222
2288
aligns = [colglobalalign ] * len (cols )
2223
2289
else : # default
2224
2290
aligns = [numalign if ct in [int , float ] else stralign for ct in coltypes ]
2225
- # then specific alignements
2291
+ # then specific alignments
2226
2292
if colalign is not None :
2227
2293
assert isinstance (colalign , Iterable )
2228
2294
if isinstance (colalign , str ):
@@ -2239,9 +2305,14 @@ def tabulate(
2239
2305
minwidths = (
2240
2306
[width_fn (h ) + min_padding for h in headers ] if headers else [0 ] * len (cols )
2241
2307
)
2308
+ aligns_copy = aligns .copy ()
2309
+ # Reset alignments in copy of alignments list to "left" for 'colon_grid' format,
2310
+ # which enforces left alignment in the text output of the data.
2311
+ if tablefmt == "colon_grid" :
2312
+ aligns_copy = ["left" ] * len (cols )
2242
2313
cols = [
2243
2314
_align_column (c , a , minw , has_invisible , enable_widechars , is_multiline )
2244
- for c , a , minw in zip (cols , aligns , minwidths )
2315
+ for c , a , minw in zip (cols , aligns_copy , minwidths )
2245
2316
]
2246
2317
2247
2318
aligns_headers = None
@@ -2253,7 +2324,7 @@ def tabulate(
2253
2324
aligns_headers = [headersglobalalign ] * len (t_cols )
2254
2325
else : # default
2255
2326
aligns_headers = aligns or [stralign ] * len (headers )
2256
- # then specific header alignements
2327
+ # then specific header alignments
2257
2328
if headersalign is not None :
2258
2329
assert isinstance (headersalign , Iterable )
2259
2330
if isinstance (headersalign , str ):
0 commit comments