1# this file contains all tools necessary to build the docstrings in _doc.py
2
3from plotext._utility import pad_string, colorize, uncolorize
4from inspect import getfullargspec as args
5from re import sub
6import copy
7
8
9
10method_name_color = 'blue+'
11method_name_style = 'bold'
12alias_style = 'italic'
13
14parameters_title_color = 'none'
15parameters_title_style = 'none'
16
17parameter_name_color = 'red+'
18parameter_name_style = 'bold'
19
20parameter_specs_color = 'orange+'
21parameter_specs_style = 'dim'
22
23parameter_doc_style = 'italic'
24
25return_color = 'orange+'
26return_style = 'bold'
27
28warning = colorize('Warning', 'orange', 'bold')
29
30nl = '\n'
31sp = ' '
32cm = ', '
33sc = '; '
34
35def correct_doc(doc):
36 doc = doc.strip()
37 doc = doc[:1].upper() + doc[1:]
38 doc = doc if len(doc) == 0 or doc[-1] == '.' else doc + '.'
39 doc = sub(' ', ' ', doc)
40 return doc
41
42class parameter_class(): # parameter doc
43 def __init__(self, name, doc = '', type = '', default = ''):
44 self.name = name.lower()
45 self.doc = correct_doc(doc)
46 self.type = None if type is None else str(type)
47 self.set_default(default)
48
49 def set_default(self, default = ''):
50 if default == '':
51 self.default = ''
52 elif isinstance(default, str):
53 self.default = "'" + default + "'"
54 elif isinstance(default, float):
55 self.default = str(round(default, 3))
56 else:
57 self.default = str(default)
58
59 def get_doc(self):
60 name = colorize(self.name, parameter_name_color, parameter_name_style)
61 type = '' if self.type == '' else 'type: ' + str(self.type)
62 default = '' if self.default == '' else 'default: ' + self.default
63 specs = sc.join([spec for spec in [type, default] if spec != ''])
64 specs = nl + colorize(specs, parameter_specs_color, parameter_specs_style) if specs != '' else ''
65 doc = colorize(self.doc, style = parameter_doc_style)
66 return nl + name + sp + doc + specs
67
68 def copy(self, default = ''):
69 par = copy.copy(self)
70 par.set_default(default)
71 return par
72
73
74class parameters_class():
75 def __init__(self):
76 self.list = []
77
78 def append(self, parameter):
79 self.list.append(parameter)
80
81 def add(self, name, doc = '', type = '', default = ''):
82 self.append(parameter_class(name, doc, type, default))
83
84 def get_title(self):
85 lp = len(self.list)
86 title = 'This is its parameter:' if lp == 1 else 'These are its parameters:'
87 return colorize(title, parameters_title_color, parameters_title_style)
88
89 def get_doc(self):
90 docs = [el.get_doc() for el in self.list]
91 return nl + self.get_title() + nl + nl.join(docs) if len(self.list) != 0 else ''
92
93 def get_parameter(self, name):
94 names = [el.name for el in self.list]
95 if name not in names:
96 print(warning, 'no parameter', name, 'found')
97 index = names.index(name) if name in names else None
98 return self.list[index] if name in names else None
99
100
101
102class output_class():
103 def __init__(self, doc = '', type = None):
104 self.type = type
105 self.doc = correct_doc(doc)
106
107 def get_doc(self):
108 title = colorize('Returns', return_color, return_style)
109 type = '' if self.type is None else 'type: ' + str(self.type)
110 type = colorize(type , parameter_specs_color, parameter_specs_style) if type != '' else ''
111 doc = colorize(self.doc, style = parameter_doc_style)
112 return nl + title + sp + doc + nl + type if self.doc != '' else ''
113
114
115class method_class():
116 def __init__(self, name, alias = None):
117 self.name = name.lower()
118 self.set_doc()
119 self.alias = alias
120
121 self.parameters = parameters_class()
122 self.set_output()
123 self.status = False
124
125 def set_doc(self, doc = ''):
126 self.doc = correct_doc(doc)
127
128 def set_output(self, doc = '', type = ''):
129 self.output = output_class(doc, type)
130
131 def append_parameter(self, parameter_object):
132 self.parameters.append(parameter_object)
133
134 def add_parameter(self, name, doc = '', type = '', default = ''):
135 self.parameters.add(name, doc, type, default)
136
137 def get_title(self):
138 return colorize(self.name, method_name_color, method_name_style)
139
140 def get_doc(self):
141 alias = (nl + "The methods " + colorize(self.name + '()', style = alias_style) + ' and ' + colorize(self.alias + '()', style = alias_style) + ' are equivalent.') if self.alias != '' else ''
142 pars = self.parameters.get_doc()
143 out = self.output.get_doc()
144 return nl.join([el for el in [self.doc, alias, pars, out] if el != ''])
145
146 def get_parameters(self):
147 return [el.name for el in self.parameters.list]
148
149 def get_parameter(self, name):
150 return self.parameters.get_parameter(name)
151
152 def show(self):
153 print(self.get_doc())
154
155
156def get_parameters(method):
157 spec = args(method)
158 parameters = ([spec.varargs] if spec.varargs is not None else []) + spec.args + spec.kwonlyargs
159 parameters = [el for el in parameters if el != 'self']
160 #defaults = spec.defaults if spec.defaults is not None else spec.kwonlydefaults.values() if spec.kwonlydefaults is not None else []
161 #lp, ld = len(parameters), len(defaults)
162 #defaults = [None] * (lp - ld) + list(defaults)
163 #return [(parameters[i], defaults[i]) for i in range(lp)]
164 return parameters#, defaults
165
166
167class documentation_class(): # a list of method_class objects
168 "It contains the doc-strings of all the main plotext functions."
169
170 def __init__(self):
171 self._methods = []
172
173 def _add_method(self, name, alias = ''):
174 method = method_class(name, alias)
175 self._methods.append(method)
176 setattr(self, name, method.show)
177
178 def _last(self):
179 return self._methods[-1]
180
181 def _set_doc(self, doc):
182 self._last().set_doc(doc)
183
184 def _add_parameter(self, name, doc = '', type = '', default = ''):
185 self._last().add_parameter(name, doc, type, default)
186
187 def _set_output(self, doc = '', type = ''):
188 self._last().set_output(doc, type)
189
190 def _get_method(self, name):
191 names = [el.name for el in self._methods]
192 if name not in names:
193 print(warning, 'no method', name + '() found')
194 return self._methods[names.index(name)] if name in names else None
195
196 def _get_parameters(self, parameter, method):
197 method = self.get_method(method)
198 return method.get_parameters(parameter) if method is not None else None
199
200 def _add_past_parameter(self, name, method, default = None):
201 method = self._get_method(method)
202 parameter = method.get_parameter(name) if method is not None else None
203 parameter = parameter if default is None else parameter.copy(default)
204 self._last().append_parameter(parameter) if parameter is not None else None
205
206 def _set_past_output(self, method):
207 method = self.get_method(method)
208 output = method.output
209 self._set_output(output.type, output.doc)
210
211 def all(self):
212 docs = (nl * 3).join([el.get_title() + nl + el.get_doc() for el in self._methods if el.status in [0, 1]])
213 print(docs)
214
215 def _add_function(self, function):
216 name = function.__name__
217 method = self._get_method(name)
218 name += '()'
219 if method is None:
220 print(warning, name, "doc not present")
221 return
222 doc = method.get_doc()
223 function.__doc__ = uncolorize(doc)
224 function.doc = lambda: print(doc)
225 parameters_actual = get_parameters(function)
226 parameters_found = method.get_parameters()
227 if parameters_actual != parameters_found:
228 actual = colorize(cm.join(parameters_actual), style = 'italic')
229 found = colorize(cm.join(parameters_found), style = 'italic')
230 print(warning, "the parameters of", name, "are", actual, "not", found + '.')
231
232
233class parameter_types():
234 def __init__(self):
235 self.int = 'an integer'
236 self.float = 'a float'
237 self.num = 'a number'
238 self.str = 'a string'
239 self.bool = 'a Boolean'
240
241
242 self.ints = 'integers'
243 self.floats = 'floats'
244 self.nums = 'numbers'
245 self.strs = 'strings'
246 self.bools = 'Booleans'
247
248 self.list_int = lambda n = 'many': self.plural(self.ints, n)
249 self.list_float = lambda n = 'many': self.plural(self.floats, n)
250 self.list_num = lambda n = 'many': self.plural(self.nums, n)
251 self.list_str = lambda n = 'many': self.plural(self.strs, n)
252 self.list_bool = lambda n = 'many': self.plural(self.bools, n)
253
254 self.fig = 'a plotext figure'
255 self.xy = 'one or two lists of numbers or string dates'
256 self.multiple_xy = 'an optional list of numbers or date strings and a mandatory matrix of numbers'
257 self.x = 'a list of numbers or string dates'
258 self.marker = 'a string or a list of strings'
259 self.color = 'a string or an integer (from 0 to 255) or a tuple of 3 integers (from 0 to 255)'
260 self.colors = 'strings or integers (from 0 to 255) or tuples of 3 integers (from 0 to 255)'
261 self.list_color = lambda n = 'many': self.plural(self.colors, n)
262 self.color_list = self.color + ' or a list of those'
263 self.str_list = self.mix(self.str, self.list_str())
264
265 self.str_int = self.mix(self.str, self.int)
266 self.str_num = self.mix(self.str, self.num)
267 self.list_str_num = lambda n = 'many': self.plural(self.mix(self.strs, self.nums), n)
268 self.list_num_bool = lambda n = 'many': self.plural(self.mix(self.nums, self.bools), n)
269 self.bool_num_str = self.mix(self.bool, self.num, self.str)
270 self.dic = "a dictionary with mandatory keys: 'Open', 'Close', 'High', 'Low'; each value should be a list of numbers."
271 self.matrix = 'a list of numbers or a list of tuples 3 integers (from 0 to 255)'
272 self.datetime = 'a datetime object'
273 self.list_datetime = self.plural(self.datetime)
274 self.data = 'a 2 dimensional matrix of numbers or strings'
275
276 def plural(self, type, n = 'many'):
277 return 'a list of ' + (str(n) + sp if not isinstance(n, str) else '') + type
278
279 def mix(self, *types):
280 return ' or '.join(types)
281
282documentation = documentation_class()
283method = documentation._add_method
284doc = documentation._set_doc
285par = documentation._add_parameter
286past = documentation._add_past_parameter
287out = documentation._set_output
288past_out = documentation._set_past_output
289add = documentation._add_function
290
291t = parameter_types()