Skip to content

Commit

Permalink
Merge pull request #180 from myheroyuki/internal_border
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk committed May 5, 2022
2 parents 53d62b3 + 292a8af commit 56baf04
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 8 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,9 @@ whatever you prefer. The `set_style` method just does this automatically for you
The options are these:

- `border` - A boolean option (must be `True` or `False`). Controls whether a border is
drawn around the table.
drawn inside and around the table.
- `preserve_internal_border` - A boolean option (must be `True` or `False`). Controls
whether borders are still drawn within the table even when `border=False`.
- `header` - A boolean option (must be `True` or `False`). Controls whether the first
row of the table is a header showing the names of all the fields.
- `hrules` - Controls printing of horizontal rules after rows. Allowed values: `FRAME`,
Expand Down
69 changes: 62 additions & 7 deletions src/prettytable/prettytable.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ def __init__(self, field_names=None, **kwargs):
header_style - stylisation to apply to field names in header
("cap", "title", "upper", "lower" or None)
border - print a border around the table (True or False)
preserve_internal_border - print a border inside the table even if
border is disabled (True or False)
hrules - controls printing of horizontal rules after rows.
Allowed values: FRAME, HEADER, ALL, NONE
vrules - controls printing of vertical rules between columns.
Expand Down Expand Up @@ -152,6 +154,7 @@ def __init__(self, field_names=None, **kwargs):
"fields",
"header",
"border",
"preserve_internal_border",
"sortby",
"reversesort",
"sort_key",
Expand Down Expand Up @@ -211,6 +214,10 @@ def __init__(self, field_names=None, **kwargs):
self._border = kwargs["border"]
else:
self._border = True
if kwargs["preserve_internal_border"] in (True, False):
self._preserve_internal_border = kwargs["preserve_internal_border"]
else:
self._preserve_internal_border = False
self._hrules = kwargs["hrules"] or FRAME
self._vrules = kwargs["vrules"] or ALL

Expand Down Expand Up @@ -373,6 +380,7 @@ def _validate_option(self, option, val):
elif option in (
"header",
"border",
"preserve_internal_border",
"reversesort",
"xhtml",
"print_empty",
Expand Down Expand Up @@ -850,6 +858,21 @@ def border(self, val):
self._validate_option("border", val)
self._border = val

@property
def preserve_internal_border(self):
"""Controls printing of border inside table
Arguments:
preserve_internal_border - print a border inside the table even if
border is disabled (True or False)"""
return self._preserve_internal_border

@preserve_internal_border.setter
def preserve_internal_border(self, val):
self._validate_option("preserve_internal_border", val)
self._preserve_internal_border = val

@property
def hrules(self):
"""Controls printing of horizontal rules after rows
Expand Down Expand Up @@ -1355,6 +1378,7 @@ def _set_random_style(self):
self.vertical_char = random.choice(r"~!@#$%^&*()_+|-=\{}[];':\",./;<>?")
self.horizontal_char = random.choice(r"~!@#$%^&*()_+|-=\{}[];':\",./;<>?")
self.junction_char = random.choice(r"~!@#$%^&*()_+|-=\{}[];':\",./;<>?")
self.preserve_internal_border = random.choice((True, False))

##############################
# DATA INPUT METHODS #
Expand Down Expand Up @@ -1626,6 +1650,8 @@ def get_string(self, **kwargs):
fields - names of fields (columns) to include
header - print a header showing field names (True or False)
border - print a border around the table (True or False)
preserve_internal_border - print a border inside the table even if
border is disabled (True or False)
hrules - controls printing of horizontal rules after rows.
Allowed values: ALL, FRAME, HEADER, NONE
vrules - controls printing of vertical rules between columns.
Expand Down Expand Up @@ -1721,7 +1747,7 @@ def get_string(self, **kwargs):

def _stringify_hrule(self, options, where=""):

if not options["border"]:
if not options["border"] and not options["preserve_internal_border"]:
return ""
lpad, rpad = self._get_padding_widths(options)
if options["vrules"] in (ALL, FRAME):
Expand Down Expand Up @@ -1753,6 +1779,10 @@ def _stringify_hrule(self, options, where=""):
if options["vrules"] in (ALL, FRAME):
bits.pop()
bits.append(options[where + "right_junction_char"])

if options["preserve_internal_border"] and not options["border"]:
bits = bits[1:-1]

return "".join(bits)

def _stringify_title(self, title, options):
Expand All @@ -1768,7 +1798,9 @@ def _stringify_title(self, title, options):
lines.append(self._stringify_hrule(options, "top_"))
bits = []
endpoint = (
options["vertical_char"] if options["vrules"] in (ALL, FRAME) else " "
options["vertical_char"]
if options["vrules"] in (ALL, FRAME) and options["border"]
else " "
)
bits.append(endpoint)
title = " " * lpad + title + " " * rpad
Expand Down Expand Up @@ -1821,17 +1853,25 @@ def _stringify_header(self, options):
+ self._justify(fieldname, width, self._align[field])
+ " " * rpad
)
if options["border"]:
if options["border"] or options["preserve_internal_border"]:
if options["vrules"] == ALL:
bits.append(options["vertical_char"])
else:
bits.append(" ")

# If only preserve_internal_border is true, then we just appended
# a vertical character at the end when we wanted a space
if not options["border"] and options["preserve_internal_border"]:
bits.pop()
bits.append(" ")
# If vrules is FRAME, then we just appended a space at the end
# of the last field, when we really want a vertical character
if options["border"] and options["vrules"] == FRAME:
bits.pop()
bits.append(options["vertical_char"])
if options["border"] and options["hrules"] != NONE:
if (options["border"] or options["preserve_internal_border"]) and options[
"hrules"
] != NONE:
bits.append("\n")
bits.append(self._hrule)
return "".join(bits)
Expand Down Expand Up @@ -1897,13 +1937,19 @@ def _stringify_row(self, row, options, hrule):
+ self._justify(line, width, self._align[field])
+ " " * rpad
)
if options["border"]:
if options["border"] or options["preserve_internal_border"]:
if options["vrules"] == ALL:
bits[y].append(self.vertical_char)
else:
bits[y].append(" ")
y += 1

# If only preserve_internal_border is true, then we just appended
# a vertical character at the end when we wanted a space
if not options["border"] and options["preserve_internal_border"]:
bits[-1].pop()
bits[-1].append(" ")

# If vrules is FRAME, then we just appended a space at the end
# of the last field, when we really want a vertical character
for y in range(0, row_height):
Expand Down Expand Up @@ -2005,6 +2051,8 @@ def get_html_string(self, **kwargs):
fields - names of fields (columns) to include
header - print a header showing field names (True or False)
border - print a border around the table (True or False)
preserve_internal_border - print a border inside the table even if
border is disabled (True or False)
hrules - controls printing of horizontal rules after rows.
Allowed values: ALL, FRAME, HEADER, NONE
vrules - controls printing of vertical rules between columns.
Expand Down Expand Up @@ -2109,6 +2157,8 @@ def _get_formatted_html_string(self, options):
open_tag.append(' frame="vsides"')
elif options["vrules"] == ALL:
open_tag.append(' frame="vsides" rules="cols"')
if not options["border"] and options["preserve_internal_border"]:
open_tag.append(' rules="cols"')
if options["attributes"]:
for attr_name in options["attributes"]:
open_tag.append(f' {attr_name}="{options["attributes"][attr_name]}"')
Expand Down Expand Up @@ -2185,6 +2235,8 @@ def get_latex_string(self, **kwargs):
fields - names of fields (columns) to include
header - print a header showing field names (True or False)
border - print a border around the table (True or False)
preserve_internal_border - print a border inside the table even if
border is disabled (True or False)
hrules - controls printing of horizontal rules after rows.
Allowed values: ALL, FRAME, HEADER, NONE
vrules - controls printing of vertical rules between columns.
Expand Down Expand Up @@ -2251,6 +2303,8 @@ def _get_formatted_latex_string(self, options):
wanted_alignments = [self._align[field] for field in wanted_fields]
if options["border"] and options["vrules"] == ALL:
alignment_str = "|".join(wanted_alignments)
elif not options["border"] and options["preserve_internal_border"]:
alignment_str = "|".join(wanted_alignments)
else:
alignment_str = "".join(wanted_alignments)

Expand All @@ -2259,14 +2313,15 @@ def _get_formatted_latex_string(self, options):

begin_cmd = "\\begin{tabular}{%s}" % alignment_str
lines.append(begin_cmd)

if options["border"] and options["hrules"] in [ALL, FRAME]:
lines.append("\\hline")

# Headers
if options["header"]:
lines.append(" & ".join(wanted_fields) + " \\\\")
if options["border"] and options["hrules"] in [ALL, HEADER]:
if (options["border"] or options["preserve_internal_border"]) and options[
"hrules"
] in [ALL, HEADER]:
lines.append("\\hline")

# Data
Expand Down
75 changes: 75 additions & 0 deletions tests/test_prettytable.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,10 @@ def test_set_for_one_column(self, city_data_prettytable):
city_data_prettytable.get_string() == city_data_prettytable[:].get_string()
)

def test_preserve_internal_border(self):
pt = PrettyTable(preserve_internal_border=True)
assert pt.preserve_internal_border is True


@pytest.fixture(scope="module")
def db_cursor():
Expand Down Expand Up @@ -1908,3 +1912,74 @@ def test_max_table_width(self):
+-----+
""".strip()
)


class TestPreservingInternalBorders:
def test_internal_border_preserved(self):
pt = helper_table(3)
pt.border = False
pt.preserve_internal_border = True

assert (
pt.get_string().strip()
== """
Field 1 | Field 2 | Field 3
---------+---------+---------
value 1 | value2 | value3
value 4 | value5 | value6
value 7 | value8 | value9
""".strip() # noqa: W291
)

def test_internal_border_preserved_latex(self):
pt = helper_table(3)
pt.border = False
pt.format = True
pt.preserve_internal_border = True

assert pt.get_latex_string().strip() == (
"\\begin{tabular}{c|c|c}\r\n"
"Field 1 & Field 2 & Field 3 \\\\\r\n"
"value 1 & value2 & value3 \\\\\r\n"
"value 4 & value5 & value6 \\\\\r\n"
"value 7 & value8 & value9 \\\\\r\n"
"\\end{tabular}"
)

def test_internal_border_preserved_html(self):
pt = helper_table(3)
pt.format = True
pt.border = False
pt.preserve_internal_border = True

assert (
pt.get_html_string().strip()
== """
<table rules="cols">
<thead>
<tr>
<th style="padding-left: 1em; padding-right: 1em; text-align: center">Field 1</th>
<th style="padding-left: 1em; padding-right: 1em; text-align: center">Field 2</th>
<th style="padding-left: 1em; padding-right: 1em; text-align: center">Field 3</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 1</td>
<td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value2</td>
<td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value3</td>
</tr>
<tr>
<td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 4</td>
<td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value5</td>
<td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value6</td>
</tr>
<tr>
<td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value 7</td>
<td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value8</td>
<td style="padding-left: 1em; padding-right: 1em; text-align: center; vertical-align: top">value9</td>
</tr>
</tbody>
</table>
""".strip() # noqa: E501
)

0 comments on commit 56baf04

Please sign in to comment.