File indexing completed on 2026-04-09 07:48:50
0001
0002
0003 import logging, re
0004 import numpy as np
0005 log = logging.getLogger(__name__)
0006
0007 class RSTTable(object):
0008 def divider(self, widths, char="-"):
0009 """+----+----------------+----------------+"""
0010 return "+".join([""]+list(map(lambda j:char*widths[j], range(len(widths))))+[""])
0011
0012
0013 @classmethod
0014 def Render_(cls, t, labels, wids, hfmt, rfmt, pre, post):
0015 """
0016 :param t: 2D array of np.object that are the content of the table
0017 :param labels: list of string labels for the header
0018 :param wids: list of integer widths
0019 :param hfmt: list of header format strings
0020 :param rfmt: list of row format strings
0021 :param pre: list of strings that prepend the header and row formats
0022 :param post: list of strings that postpend the header and row formats
0023 """
0024 tab = RSTTable(t)
0025 tab.labels = labels
0026 tab.pre = np.array( list(map(len,pre)), dtype=np.int32 )
0027 tab.post = np.array( list(map(len,post)), dtype=np.int32 )
0028 tab.wids = np.array( list(map(int,wids)), dtype=np.int32 )
0029 tab.hfmt = [ pre[i]+hfmt[i]+post[i] for i in range(len(hfmt)) ]
0030 tab.rfmt = [ pre[i]+rfmt[i]+post[i] for i in range(len(rfmt)) ]
0031 tab.wids += tab.pre
0032 tab.wids += tab.post
0033 return tab
0034
0035 @classmethod
0036 def Render(cls, t, labels, wids, hfmt, rfmt, pre, post):
0037 tab = cls.Render_(t, labels, wids, hfmt, rfmt, pre, post)
0038 return str(tab)
0039
0040 @classmethod
0041 def Rdr_(cls, t, labels, wid=10, hfm="%10s", rfm="%10.4f", pre_="", post_="", left_wid=None, left_rfm=None, left_hfm=None ):
0042 """
0043 :param t: 2D array "table" of np.object items to populate the RST table
0044 :param labels: list of labels
0045 :param wid: int width that is repeated across all columns
0046 :param hfm: string header format string
0047 :param rfm: string row format string
0048 :param pre_: string that prepends the header and row formats
0049 :param post_: string that postpends the header and row formats
0050
0051 *Rdr* provides a simpler interface to creating RST tables than *Render*, which it uses
0052 via *np.repeat* repetitions to generate the detailed input arrays needed by *Render*
0053 """
0054 nlab = len(labels)
0055
0056 wids = np.repeat( wid, nlab )
0057 hfmt = np.repeat( hfm, nlab )
0058 rfmt = np.repeat( rfm, nlab )
0059 pre = np.repeat( pre_, nlab )
0060 post = np.repeat( post_, nlab )
0061
0062 if not left_wid is None:
0063 wids[0] = left_wid
0064 pass
0065 if not left_rfm is None:
0066 rfmt[0] = left_rfm
0067 pass
0068 if not left_hfm is None:
0069 hfmt[0] = left_hfm
0070 pass
0071 return cls.Render_(t, labels, wids, hfmt, rfmt, pre, post )
0072
0073 @classmethod
0074 def Rdr(cls, t, labels, wid=10, hfm="%10s", rfm="%10.4f", pre_="", post_="", left_wid=None, left_rfm=None, left_hfm=None ):
0075 tab = cls.Rdr_(t, labels, wid, hfm, rfm, pre_, post_, left_wid, left_rfm, left_hfm)
0076 return str(tab)
0077
0078
0079 def __init__(self, t):
0080 self.t = t
0081
0082 elem_ptn = re.compile("^(\s*)(.*?)(\s*)$")
0083
0084 def color_elem(self, elem, color):
0085 """
0086 :param elem: string cell of an RST table, NB relies on free space at both sides
0087 :param color: color string eg "r" "g" "b"
0088 :return elem2: string of same length with prefix and suffix RST coloring codes
0089 """
0090 if len(elem) == 0 or color is None: return elem
0091
0092 elem_match = self.elem_ptn.match(elem)
0093 assert not elem_match is None
0094 elem_groups = elem_match.groups()
0095 assert len(elem_groups) == 3
0096
0097 pre,body,post = elem_groups
0098
0099 lhs = ":%s:`" % color
0100 rhs = "`"
0101
0102 lpre = list(pre)
0103 lpost = list(post)
0104
0105 assert len(lpre) >= len(lhs)
0106 for i in range(len(lhs)):
0107 lpre[len(lpre)-len(lhs)+i] = lhs[i]
0108 pass
0109 assert len(lpost) >= len(rhs)
0110 for i in range(len(rhs)):
0111 lpost[i] = rhs[i]
0112 pass
0113 pre = "".join(lpre)
0114 post = "".join(lpost)
0115
0116 elem2 = "%s%s%s" % (pre,body,post)
0117 return elem2
0118
0119 def color_line(self, line, color):
0120 """
0121 :param line:
0122 :param color: code eg "r"
0123 :return line2: of same length
0124
0125 | CSG_GGeo | 3/2/0/0 | GGeo to CSG geometry translation |
0126 | :r:`CSG_GGeo` | :r:`3/2/0/0` | :r:`GGeo to CSG geometry translation` |
0127 """
0128 if color is None: return line
0129 elems = line.split("|")
0130 elems2 = []
0131 for elem in elems:
0132 elems2.append(self.color_elem(elem,color))
0133 pass
0134 line2 = "|".join(elems2)
0135
0136 return line2
0137
0138 def __str__(self):
0139 """
0140 Builds the RST table line by line
0141 """
0142 nrow = self.t.shape[0]
0143 ncol = self.t.shape[1]
0144
0145 assert len(self.hfmt) == ncol
0146 assert len(self.rfmt) == ncol
0147 assert len(self.labels) == ncol
0148 assert len(self.wids) == ncol
0149 assert len(self.pre) == ncol
0150
0151 hfmt = "|".join( [""]+self.hfmt+[""])
0152 rfmt = "|".join( [""]+self.rfmt+[""])
0153
0154 lines = []
0155 lines.append(self.divider(self.wids, "-"))
0156 lines.append(hfmt % tuple(self.labels))
0157 lines.append(self.divider(self.wids, "="))
0158 for i in range(nrow):
0159 line = rfmt % tuple(self.t[i])
0160
0161 key = self.t[i,0]
0162 color = self.colormap.get(key, None) if hasattr(self, "colormap") else None
0163 lines.append(self.color_line(line, color))
0164 lines.append(self.divider(self.wids,"-"))
0165 pass
0166 self._hfmt = hfmt
0167 self._rfmt = rfmt
0168 return "\n".join(lines)
0169
0170
0171
0172 def test_Render():
0173 log.info("test_Render")
0174 t = np.empty( [2,2], dtype=np.object )
0175 t[0] = ["a", "b" ]
0176 t[1] = ["c", "d" ]
0177
0178 labels = ["A", "B"]
0179 wids = [ 10, 10]
0180 hfmt = [ "%10s", "%10s" ]
0181 rfmt = [ "%10s", "%10s" ]
0182 pre = [ "" , " " ]
0183 post = [ "" , " " ]
0184
0185 rst = RSTTable.Render(t, labels, wids, hfmt, rfmt, pre, post )
0186 print(rst)
0187
0188
0189 def test_Rdr2x2():
0190 log.info("test_Rdr2x2")
0191 t = np.zeros( [2,2], dtype=np.object )
0192 t[0] = [ 1.1 , 1.2 ]
0193 t[1] = [ 2.2 , 3.1 ]
0194 labels = ["A", "B"]
0195 rst = RSTTable.Rdr(t, labels )
0196 print(rst)
0197
0198
0199 def test_Rdr3x3():
0200 log.info("test_Rdr3x3")
0201 t = np.zeros( [3,3], dtype=np.object )
0202 t[0] = [ 1.1 , 1.2 , 1.3 ]
0203 t[1] = [ 2.2 , 3.1 , 4.1 ]
0204 t[2] = [ 3.2 , 4.1 , 5.1 ]
0205 labels = ["A", "B", "C" ]
0206 rst = RSTTable.Rdr(t, labels )
0207 print(rst)
0208
0209
0210 def test_Rdr3x4():
0211 log.info("test_Rdr3x4")
0212 t = np.zeros( [3,4], dtype=np.object )
0213 t[0] = [ "one", 1.1 , 1.2 , 1.3 ]
0214 t[1] = [ "two", 2.2 , 3.1 , 4.1 ]
0215 t[2] = [ "three", 3.2 , 4.1 , 5.1 ]
0216 labels = ["key", "A", "B", "C" ]
0217 rst = RSTTable.Rdr(t, labels, left_wid=15, left_rfm="%15s", left_hfm="%15s" )
0218 print(rst)
0219
0220
0221 if __name__ == '__main__':
0222 logging.basicConfig(level=logging.INFO)
0223
0224
0225
0226 test_Rdr3x4()
0227
0228