1
- from __future__ import print_function
2
-
3
1
import os
4
2
import pathlib
5
3
import sys
6
4
import textwrap
7
- from collections import defaultdict
5
+ from collections .abc import Callable , Iterable
6
+ from typing import Any , Optional , Type
8
7
8
+ import pandas as pd
9
9
import tabulate
10
10
11
11
12
- class TableWriter (object ):
12
+ def _run_method (method : Callable [..., str ], * args : Any ) -> str :
13
+ val = method (* args )
14
+ return val
15
+
16
+
17
+ def _generate_row (
18
+ * , column_spec : list [tuple [str , Callable [..., str ]]], args : Iterable [Any ]
19
+ ) -> dict [str , str ]:
20
+ row = {}
21
+ for heading , method in column_spec :
22
+ val = _run_method (method , * args )
23
+ row [heading ] = val
24
+ return row
25
+
26
+
27
+ def _generate_table (
28
+ * ,
29
+ input_items : Iterable [Any ],
30
+ column_spec : list [tuple [str , Callable [..., str ]]],
31
+ ) -> pd .DataFrame :
32
+ rows = []
33
+ for args in input_items :
34
+ if not isinstance (args , Iterable ):
35
+ args = [args ]
36
+ line = _generate_row (column_spec = column_spec , args = args )
37
+ rows .append (line )
38
+ df = pd .DataFrame (rows )
39
+ return df
40
+
41
+
42
+ def write_table (
43
+ * ,
44
+ path : str ,
45
+ headers : list [str ],
46
+ lines : list [list [str ]],
47
+ include_table : Optional [str ] = None ,
48
+ ) -> None :
49
+ parent_directory = pathlib .Path (path ).parent
50
+ parent_directory .mkdir (exist_ok = True , parents = True )
51
+ with open (path , "w" ) as f :
52
+ f .write (f'..\n Generated by { sys .argv [0 ].split ("/" )[- 1 ]} \n \n ' )
53
+ if include_table :
54
+ f .write (f".. table:: { include_table } \n \n " )
55
+ tabled = tabulate .tabulate (lines , headers = headers , tablefmt = "rst" )
56
+ if include_table :
57
+ tabled = textwrap .indent (tabled , " " )
58
+ f .write (tabled )
59
+ print ("Wrote " , path )
60
+
61
+
62
+ class TableWriter :
13
63
"""
14
64
For writing tables with easy column switching.
15
65
@@ -18,91 +68,77 @@ class TableWriter(object):
18
68
Filename relative to source.
19
69
"""
20
70
21
- filename = ""
22
- include_table = False
23
- headings = []
24
- preprocess = []
25
- postprocess = []
26
- sort = True
71
+ def __init__ (
72
+ self ,
73
+ column_spec : list [tuple [str , Callable [..., str ]]],
74
+ lines : list [list [str ]],
75
+ filename : str = "" ,
76
+ include_table : Optional [str ] = None ,
77
+ sort : bool = True ,
78
+ input_items : Optional [Iterable [Any ]] = None ,
79
+ ):
80
+ if column_spec :
81
+ assert input_items
82
+ assert (column_spec and not lines ) or (lines and not column_spec )
83
+ stem = os .getcwd ().split ("source" )[0 ]
84
+ self .path = os .path .join (stem , "source" , filename )
85
+ self .filename = filename
86
+ self .include_table = include_table
87
+ self .sort = sort
88
+ self .input_items = input_items
89
+ self .column_spec = column_spec
90
+ self .lines = lines
91
+ self ._df = pd .DataFrame ()
27
92
28
- def __getattr__ (self , key : str ) -> list :
29
- return self .fields [key ]
93
+ @property
94
+ def headers (self ) -> list [str ]:
95
+ return [column_name for column_name , _ in self .column_spec ]
30
96
31
- def __init__ (self , * args , ** kwargs ):
32
- stem = os .getcwd ().split ("source" )[0 ]
33
- self .path = os .path .join (stem , "source" , self .filename )
34
- self .fields = defaultdict (list )
97
+ @property
98
+ def fields (self ) -> pd .DataFrame :
99
+ return self ._df
35
100
36
- parent_directory = pathlib .Path (self .path ).parent
37
- parent_directory .mkdir (exist_ok = True , parents = True )
38
- self .get_lines (* args , ** kwargs )
39
- self .write_table ()
101
+ def generate_lines_and_write_table (self ) -> None :
102
+ df = _generate_table (
103
+ input_items = self .input_items or [],
104
+ column_spec = self .column_spec ,
105
+ )
40
106
41
- def _run_method (self , method , * args , ** kwargs ):
42
- sanitized = self .sanitize_name (method )
43
- meth = getattr (self , sanitized )
44
- val = meth (* args , ** kwargs )
45
- self .fields [method ].append (val )
46
- return val
47
-
48
- @staticmethod
49
- def sanitize_name (name ):
50
- return "_" + name .replace (" " , "_" ).replace ("/" , "_" ).lower ()
51
-
52
- def get_lines (self , * args , ** kwargs ):
53
- lines = []
54
- for items in self ._set_up_input ():
55
- try :
56
- lines .append (self .get_line (* items ))
57
- except TypeError : # one argument
58
- lines .append (self .get_line (items ))
59
- if self .sort :
60
- lines = sorted (lines )
107
+ lines = df .values .tolist ()
108
+ lines = sorted (lines ) if self .sort else lines
61
109
self .lines = lines
110
+ self ._df = df
111
+ self .write_table ()
62
112
63
- def get_line (self , * args ):
64
- line = []
65
- for p in self .preprocess :
66
- self ._run_method (p , * args )
67
- for h in self .headings :
68
- line .append (self ._run_method (h , * args ))
69
- for p in self .postprocess :
70
- self ._run_method (p , * args )
71
- return line
72
-
73
- def write_table (self ):
74
- with open (self .path , "w" ) as f :
75
- f .write (f'..\n Generated by { sys .argv [0 ].split ("/" )[- 1 ]} \n \n ' )
76
- if self .include_table :
77
- f .write (f".. table:: { self .include_table } \n \n " )
78
- tabled = tabulate .tabulate (
79
- self .lines , headers = self .headings , tablefmt = "rst"
80
- )
81
- if self .include_table :
82
- tabled = textwrap .indent (tabled , " " )
83
- f .write (tabled )
84
- print ("Wrote " , self .filename )
85
-
86
- # ==== HELPER FUNCTIONS ==== #
87
-
88
- @staticmethod
89
- def sphinx_class (klass , tilde = True ):
90
- prefix = "~" if tilde else ""
91
- return ":class:`{}{}.{}`" .format (
92
- prefix , klass .__module__ , klass .__name__
113
+ def write_table (self ) -> None :
114
+ write_table (
115
+ path = self .path ,
116
+ headers = self .headers ,
117
+ lines = self .lines ,
118
+ include_table = self .include_table ,
93
119
)
94
120
95
- @staticmethod
96
- def sphinx_meth (meth , tilde = True ):
97
- prefix = "~" if tilde else ""
98
- return ":meth:`{}{}.{}`" .format (
99
- prefix , meth .__module__ , meth .__qualname__
100
- )
101
121
102
- @staticmethod
103
- def sphinx_ref (txt : str , label : str = None , suffix : str = "" ) -> str :
104
- return f":ref:`{ txt } <{ label } { suffix } >`"
122
+ # ==== HELPER FUNCTIONS ==== #
123
+
124
+
125
+ def sphinx_class (* , klass : Type [Any ], tilde : bool = True ) -> str :
126
+ prefix = "~" if tilde else ""
127
+ return f":class:`{ prefix } { klass .__module__ } .{ klass .__name__ } `"
128
+
129
+
130
+ def sphinx_method (* , method : Callable [..., Any ], tilde : bool = True ) -> str :
131
+ prefix = "~" if tilde else ""
132
+ return ":meth:`{}{}.{}`" .format (
133
+ prefix , method .__module__ , method .__qualname__
134
+ )
135
+
136
+
137
+ def sphinx_ref (
138
+ * , txt : str , label : Optional [str ] = None , suffix : str = ""
139
+ ) -> str :
140
+ return f":ref:`{ txt } <{ label } { suffix } >`"
141
+
105
142
106
- @staticmethod
107
- def sphinx_link (txt ):
108
- return "`{}`_" .format (txt )
143
+ def sphinx_link (* , txt : str ) -> str :
144
+ return f"`{ txt } `_"
0 commit comments