Mercurial > repos > fubar > tool_factory_2
comparison toolfactory/galaxyxml/tool/parameters/__init__.py @ 35:5d38cb3d9be8 draft
added patched galaxyxml code temporarily until PR accepted
author | fubar |
---|---|
date | Sat, 08 Aug 2020 19:55:55 -0400 |
parents | |
children | ce2b1f8ea68d |
comparison
equal
deleted
inserted
replaced
34:5052ac89c036 | 35:5d38cb3d9be8 |
---|---|
1 from builtins import str | |
2 from builtins import object | |
3 from lxml import etree | |
4 from galaxyxml import Util | |
5 | |
6 | |
7 class XMLParam(object): | |
8 name = 'node' | |
9 | |
10 def __init__(self, *args, **kwargs): | |
11 # http://stackoverflow.com/a/12118700 | |
12 self.children = [] | |
13 kwargs = {k: v for k, v in list(kwargs.items()) if v is not None} | |
14 kwargs = Util.coerce(kwargs, kill_lists=True) | |
15 kwargs = Util.clean_kwargs(kwargs, final=True) | |
16 self.node = etree.Element(self.name, **kwargs) | |
17 | |
18 def append(self, sub_node): | |
19 if self.acceptable_child(sub_node): | |
20 # If one of ours, they aren't etree nodes, they're custom objects | |
21 if issubclass(type(sub_node), XMLParam): | |
22 self.node.append(sub_node.node) | |
23 self.children.append(sub_node) | |
24 else: | |
25 raise Exception("Child was unacceptable to parent (%s is not appropriate for %s)" % ( | |
26 type(self), type(sub_node))) | |
27 else: | |
28 raise Exception("Child was unacceptable to parent (%s is not appropriate for %s)" % ( | |
29 type(self), type(sub_node))) | |
30 | |
31 def validate(self): | |
32 # Very few need validation, but some nodes we may want to have | |
33 # validation routines on. Should only be called when DONE. | |
34 for child in self.children: | |
35 # If any child fails to validate return false. | |
36 if not child.validate(): | |
37 return False | |
38 return True | |
39 | |
40 def cli(self): | |
41 lines = [] | |
42 for child in self.children: | |
43 lines.append(child.command_line()) | |
44 # lines += child.command_line() | |
45 return '\n'.join(lines) | |
46 | |
47 def command_line(self): | |
48 return None | |
49 | |
50 | |
51 class RequestParamTranslation(XMLParam): | |
52 name = 'request_param_translation' | |
53 | |
54 def __init__(self, **kwargs): | |
55 self.node = etree.Element(self.name) | |
56 | |
57 def acceptable_child(self, child): | |
58 return isinstance(child, RequestParamTranslation) | |
59 | |
60 | |
61 class RequestParam(XMLParam): | |
62 name = 'request_param' | |
63 | |
64 def __init__(self, galaxy_name, remote_name, missing, **kwargs): | |
65 # TODO: bulk copy locals into self.attr? | |
66 self.galaxy_name = galaxy_name | |
67 # http://stackoverflow.com/a/1408860 | |
68 params = Util.clean_kwargs(locals().copy()) | |
69 super(RequestParam, self).__init__(**params) | |
70 | |
71 def acceptable_child(self, child): | |
72 return isinstance(child, AppendParam) and self.galaxy_name == "URL" | |
73 | |
74 | |
75 class AppendParam(XMLParam): | |
76 name = 'append_param' | |
77 | |
78 def __init__(self, separator="&", first_separator="?", join="=", **kwargs): | |
79 params = Util.clean_kwargs(locals().copy()) | |
80 super(AppendParam, self).__init__(**params) | |
81 | |
82 def acceptable_child(self, child): | |
83 return isinstance(child, AppendParamValue) | |
84 | |
85 | |
86 class AppendParamValue(XMLParam): | |
87 name = 'value' | |
88 | |
89 def __init__(self, name="_export", missing="1", **kwargs): | |
90 params = Util.clean_kwargs(locals().copy()) | |
91 super(AppendParamValue, self).__init__(**params) | |
92 | |
93 def acceptable_child(self, child): | |
94 return False | |
95 | |
96 | |
97 class EdamOperations(XMLParam): | |
98 name = 'edam_operations' | |
99 | |
100 def acceptable_child(self, child): | |
101 return issubclass(type(child), EdamOperation) | |
102 | |
103 def has_operation(self, edam_operation): | |
104 """ | |
105 Check the presence of a given edam_operation. | |
106 | |
107 :type edam_operation: STRING | |
108 """ | |
109 for operation in self.children: | |
110 if operation.node.text == edam_operation: | |
111 return True | |
112 return False | |
113 | |
114 | |
115 class EdamOperation(XMLParam): | |
116 name = 'edam_operation' | |
117 | |
118 def __init__(self, value): | |
119 super(EdamOperation, self).__init__() | |
120 self.node.text = str(value) | |
121 | |
122 | |
123 class EdamTopics(XMLParam): | |
124 name = 'edam_topics' | |
125 | |
126 def acceptable_child(self, child): | |
127 return issubclass(type(child), EdamTopic) | |
128 | |
129 def has_topic(self, edam_topic): | |
130 """ | |
131 Check the presence of a given edam_topic. | |
132 | |
133 :type edam_topic: STRING | |
134 """ | |
135 for topic in self.children: | |
136 if topic.node.text == edam_topic: | |
137 return True | |
138 return False | |
139 | |
140 | |
141 class EdamTopic(XMLParam): | |
142 name = 'edam_topic' | |
143 | |
144 def __init__(self, value): | |
145 super(EdamTopic, self).__init__() | |
146 self.node.text = str(value) | |
147 | |
148 | |
149 class Requirements(XMLParam): | |
150 name = 'requirements' | |
151 # This bodes to be an issue -__- | |
152 | |
153 def acceptable_child(self, child): | |
154 return issubclass(type(child), Requirement) or issubclass(type(child), Container) | |
155 | |
156 | |
157 class Requirement(XMLParam): | |
158 name = 'requirement' | |
159 | |
160 def __init__(self, type, value, version=None, **kwargs): | |
161 params = Util.clean_kwargs(locals().copy()) | |
162 passed_kwargs = {} | |
163 passed_kwargs['version'] = params['version'] | |
164 passed_kwargs['type'] = params['type'] | |
165 super(Requirement, self).__init__(**passed_kwargs) | |
166 self.node.text = str(value) | |
167 | |
168 | |
169 class Container(XMLParam): | |
170 name = 'container' | |
171 | |
172 def __init__(self, type, value, **kwargs): | |
173 params = Util.clean_kwargs(locals().copy()) | |
174 passed_kwargs = {} | |
175 passed_kwargs['type'] = params['type'] | |
176 super(Container, self).__init__(**passed_kwargs) | |
177 self.node.text = str(value) | |
178 | |
179 | |
180 class Configfiles(XMLParam): | |
181 name = 'configfiles' | |
182 | |
183 def acceptable_child(self, child): | |
184 return issubclass(type(child), Configfile) or issubclass(type(child), ConfigfileDefaultInputs) | |
185 | |
186 | |
187 class Configfile(XMLParam): | |
188 name = 'configfile' | |
189 | |
190 def __init__(self, name, text, **kwargs): | |
191 params = Util.clean_kwargs(locals().copy()) | |
192 passed_kwargs = {} | |
193 passed_kwargs['name'] = params['name'] | |
194 super(Configfile, self).__init__(**passed_kwargs) | |
195 self.node.text = etree.CDATA(str(text)) | |
196 | |
197 | |
198 class ConfigfileDefaultInputs(XMLParam): | |
199 name = 'inputs' | |
200 | |
201 def __init__(self, name, **kwargs): | |
202 params = Util.clean_kwargs(locals().copy()) | |
203 passed_kwargs = {} | |
204 passed_kwargs['name'] = params['name'] | |
205 super(ConfigfileDefaultInputs, self).__init__(**passed_kwargs) | |
206 | |
207 | |
208 class Inputs(XMLParam): | |
209 name = 'inputs' | |
210 # This bodes to be an issue -__- | |
211 | |
212 def __init__(self, action=None, check_value=None, method=None, | |
213 target=None, nginx_upload=None, **kwargs): | |
214 params = Util.clean_kwargs(locals().copy()) | |
215 super(Inputs, self).__init__(**params) | |
216 | |
217 def acceptable_child(self, child): | |
218 return issubclass(type(child), InputParameter) | |
219 | |
220 | |
221 class InputParameter(XMLParam): | |
222 | |
223 def __init__(self, name, **kwargs): | |
224 # TODO: look at | |
225 self.mako_identifier = name | |
226 # We use kwargs instead of the usual locals(), so manually copy the | |
227 # name to kwargs | |
228 if name is not None: | |
229 kwargs['name'] = name | |
230 | |
231 # Handle positional parameters | |
232 if 'positional' in kwargs and kwargs['positional']: | |
233 self.positional = True | |
234 else: | |
235 self.positional = False | |
236 | |
237 if 'num_dashes' in kwargs: | |
238 self.num_dashes = kwargs['num_dashes'] | |
239 del kwargs['num_dashes'] | |
240 else: | |
241 self.num_dashes = 0 | |
242 | |
243 self.space_between_arg = " " | |
244 | |
245 # Not sure about this :( | |
246 # https://wiki.galaxyproject.org/Tools/BestPractices#Parameter_help | |
247 if 'label' in kwargs: | |
248 # TODO: replace with positional attribute | |
249 if len(self.flag()) > 0: | |
250 if kwargs['label'] is None: | |
251 kwargs[ | |
252 'label'] = 'Author did not provide help for this parameter... ' | |
253 if not self.positional: | |
254 kwargs['argument'] = self.flag() | |
255 | |
256 super(InputParameter, self).__init__(**kwargs) | |
257 | |
258 def command_line(self): | |
259 before = self.command_line_before() | |
260 cli = self.command_line_actual() | |
261 after = self.command_line_after() | |
262 | |
263 complete = [x for x in (before, cli, after) if x is not None] | |
264 return '\n'.join(complete) | |
265 | |
266 def command_line_before(self): | |
267 try: | |
268 return self.command_line_before_override | |
269 except Exception: | |
270 return None | |
271 | |
272 def command_line_after(self): | |
273 try: | |
274 return self.command_line_after_override | |
275 except Exception: | |
276 return None | |
277 | |
278 def command_line_actual(self): | |
279 try: | |
280 return self.command_line_override | |
281 except Exception: | |
282 if self.positional: | |
283 return self.mako_name() | |
284 else: | |
285 return "%s%s%s" % (self.flag(), self.space_between_arg, self.mako_name()) | |
286 | |
287 def mako_name(self): | |
288 # TODO: enhance logic to check up parents for things like | |
289 # repeat>condotion>param | |
290 return '$' + self.mako_identifier | |
291 | |
292 def flag(self): | |
293 flag = '-' * self.num_dashes | |
294 return flag + self.mako_identifier | |
295 | |
296 | |
297 class Section(InputParameter): | |
298 name = 'section' | |
299 | |
300 def __init__(self, name, title, expanded=None, help=None, **kwargs): | |
301 params = Util.clean_kwargs(locals().copy()) | |
302 super(Section, self).__init__(**params) | |
303 | |
304 def acceptable_child(self, child): | |
305 return issubclass(type(child), InputParameter) | |
306 | |
307 | |
308 class Repeat(InputParameter): | |
309 name = 'repeat' | |
310 | |
311 def __init__(self, name, title, min=None, max=None, default=None, | |
312 **kwargs): | |
313 params = Util.clean_kwargs(locals().copy()) | |
314 # Allow overriding | |
315 self.command_line_before_override = '#for $i in $%s:' % name | |
316 self.command_line_after_override = '#end for' | |
317 # self.command_line_override | |
318 super(Repeat, self).__init__(**params) | |
319 | |
320 def acceptable_child(self, child): | |
321 return issubclass(type(child), InputParameter) | |
322 | |
323 def command_line_actual(self): | |
324 if hasattr(self, 'command_line_override'): | |
325 return self.command_line_override | |
326 else: | |
327 return "%s" % self.mako_name() | |
328 | |
329 | |
330 class Conditional(InputParameter): | |
331 name = 'conditional' | |
332 | |
333 def __init__(self, name, **kwargs): | |
334 params = Util.clean_kwargs(locals().copy()) | |
335 super(Conditional, self).__init__(**params) | |
336 | |
337 def acceptable_child(self, child): | |
338 return issubclass(type(child), InputParameter) \ | |
339 and not isinstance(child, Conditional) | |
340 | |
341 def validate(self): | |
342 # Find a way to check if one of the kids is a WHEN | |
343 pass | |
344 | |
345 | |
346 class When(InputParameter): | |
347 name = 'when' | |
348 | |
349 def __init__(self, value): | |
350 params = Util.clean_kwargs(locals().copy()) | |
351 super(When, self).__init__(None, **params) | |
352 | |
353 def acceptable_child(self, child): | |
354 return issubclass(type(child), InputParameter) | |
355 | |
356 | |
357 class Param(InputParameter): | |
358 name = 'param' | |
359 | |
360 # This...isn't really valid as-is, and shouldn't be used. | |
361 def __init__(self, name, optional=None, label=None, help=None, **kwargs): | |
362 params = Util.clean_kwargs(locals().copy()) | |
363 params['type'] = self.type | |
364 super(Param, self).__init__(**params) | |
365 | |
366 if type(self) == Param: | |
367 raise Exception( | |
368 "Param class is not an actual parameter type, use a subclass of Param") | |
369 | |
370 def acceptable_child(self, child): | |
371 return issubclass(type(child, InputParameter) or isinstance(child), ValidatorParam) | |
372 | |
373 | |
374 class TextParam(Param): | |
375 type = 'text' | |
376 | |
377 def __init__(self, name, optional=None, label=None, help=None, | |
378 value=None, **kwargs): | |
379 params = Util.clean_kwargs(locals().copy()) | |
380 super(TextParam, self).__init__(**params) | |
381 | |
382 def command_line_actual(self): | |
383 try: | |
384 return self.command_line_override | |
385 except Exception: | |
386 if self.positional: | |
387 return self.mako_name() | |
388 else: | |
389 return f"{self.flag}{self.space_between_arg}'{self.mako_name()}'" | |
390 | |
391 | |
392 class _NumericParam(Param): | |
393 | |
394 def __init__(self, name, value, optional=None, label=None, help=None, | |
395 min=None, max=None, **kwargs): | |
396 params = Util.clean_kwargs(locals().copy()) | |
397 super(_NumericParam, self).__init__(**params) | |
398 | |
399 | |
400 class IntegerParam(_NumericParam): | |
401 type = 'integer' | |
402 | |
403 | |
404 class FloatParam(_NumericParam): | |
405 type = 'float' | |
406 | |
407 | |
408 class BooleanParam(Param): | |
409 type = 'boolean' | |
410 | |
411 def __init__(self, name, optional=None, label=None, help=None, | |
412 checked=False, truevalue=None, falsevalue=None, **kwargs): | |
413 params = Util.clean_kwargs(locals().copy()) | |
414 | |
415 super(BooleanParam, self).__init__(**params) | |
416 if truevalue is None: | |
417 # If truevalue and falsevalue are None, then we use "auto", the IUC | |
418 # recommended default. | |
419 # | |
420 # truevalue is set to the parameter's value, and falsevalue is not. | |
421 # | |
422 # Unfortunately, mako_identifier is set as a result of the super | |
423 # call, which we shouldn't call TWICE, so we'll just hack around this :( | |
424 # params['truevalue'] = '%s%s' % (self.) | |
425 self.node.attrib['truevalue'] = self.flag() | |
426 | |
427 if falsevalue is None: | |
428 self.node.attrib['falsevalue'] = "" | |
429 | |
430 def command_line_actual(self): | |
431 if hasattr(self, 'command_line_override'): | |
432 return self.command_line_override | |
433 else: | |
434 return "%s" % self.mako_name() | |
435 | |
436 | |
437 class DataParam(Param): | |
438 type = 'data' | |
439 | |
440 def __init__(self, name, optional=None, label=None, help=None, format=None, | |
441 multiple=None, **kwargs): | |
442 params = Util.clean_kwargs(locals().copy()) | |
443 super(DataParam, self).__init__(**params) | |
444 | |
445 | |
446 class SelectParam(Param): | |
447 type = 'select' | |
448 | |
449 def __init__(self, name, optional=None, label=None, help=None, | |
450 data_ref=None, display=None, multiple=None, options=None, | |
451 default=None, **kwargs): | |
452 params = Util.clean_kwargs(locals().copy()) | |
453 del params['options'] | |
454 del params['default'] | |
455 | |
456 super(SelectParam, self).__init__(**params) | |
457 | |
458 if options is not None and default is not None: | |
459 if default not in options: | |
460 raise Exception("Specified a default that isn't in options") | |
461 | |
462 if options: | |
463 for k, v in list(sorted(options.items())): | |
464 selected = (k == default) | |
465 self.append(SelectOption(k, v, selected=selected)) | |
466 | |
467 def acceptable_child(self, child): | |
468 return issubclass(type(child), SelectOption) \ | |
469 or issubclass(type(child), Options) | |
470 | |
471 | |
472 class SelectOption(InputParameter): | |
473 name = 'option' | |
474 | |
475 def __init__(self, value, text, selected=False, **kwargs): | |
476 params = Util.clean_kwargs(locals().copy()) | |
477 | |
478 passed_kwargs = {} | |
479 if selected: | |
480 passed_kwargs['selected'] = "true" | |
481 passed_kwargs['value'] = params['value'] | |
482 | |
483 super(SelectOption, self).__init__(None, **passed_kwargs) | |
484 self.node.text = str(text) | |
485 | |
486 | |
487 class Options(InputParameter): | |
488 name = 'options' | |
489 | |
490 def __init__(self, from_dataset=None, from_file=None, from_data_table=None, | |
491 from_parameter=None, **kwargs): | |
492 params = Util.clean_kwargs(locals().copy()) | |
493 super(Options, self).__init__(None, **params) | |
494 | |
495 def acceptable_child(self, child): | |
496 return issubclass(type(child), Column) or issubclass(type(child), Filter) | |
497 | |
498 | |
499 class Column(InputParameter): | |
500 name = 'column' | |
501 | |
502 def __init__(self, name, index, **kwargs): | |
503 params = Util.clean_kwargs(locals().copy()) | |
504 super(Column, self).__init__(**params) | |
505 | |
506 | |
507 class Filter(InputParameter): | |
508 name = 'filter' | |
509 | |
510 def __init__(self, type, column=None, name=None, ref=None, key=None, | |
511 multiple=None, separator=None, keep=None, value=None, | |
512 ref_attribute=None, index=None, **kwargs): | |
513 params = Util.clean_kwargs(locals().copy()) | |
514 super(Filter, self).__init__(**params) | |
515 | |
516 | |
517 class ValidatorParam(InputParameter): | |
518 name = 'validator' | |
519 | |
520 def __init__(self, type, message=None, filename=None, metadata_name=None, | |
521 metadata_column=None, line_startswith=None, min=None, | |
522 max=None, **kwargs): | |
523 params = Util.clean_kwargs(locals().copy()) | |
524 super(ValidatorParam, self).__init__(**params) | |
525 | |
526 | |
527 class Outputs(XMLParam): | |
528 name = 'outputs' | |
529 | |
530 def acceptable_child(self, child): | |
531 return isinstance(child, OutputData) or isinstance(child, OutputCollection) | |
532 | |
533 | |
534 class OutputData(XMLParam): | |
535 """Copypasta of InputParameter, needs work | |
536 """ | |
537 name = 'data' | |
538 | |
539 def __init__(self, name, format, format_source=None, metadata_source=None, | |
540 label=None, from_work_dir=None, hidden=False, **kwargs): | |
541 # TODO: validate format_source&metadata_source against something in the | |
542 # XMLParam children tree. | |
543 self.mako_identifier = name | |
544 if 'num_dashes' in kwargs: | |
545 self.num_dashes = kwargs['num_dashes'] | |
546 del kwargs['num_dashes'] | |
547 else: | |
548 self.num_dashes = 0 | |
549 self.space_between_arg = " " | |
550 params = Util.clean_kwargs(locals().copy()) | |
551 | |
552 super(OutputData, self).__init__(**params) | |
553 | |
554 def command_line(self): | |
555 if hasattr(self, 'command_line_override'): | |
556 return self.command_line_override | |
557 else: | |
558 return "%s%s%s" % (self.flag(), self.space_between_arg, self.mako_name()) | |
559 | |
560 def mako_name(self): | |
561 return '$' + self.mako_identifier | |
562 | |
563 def flag(self): | |
564 flag = '-' * self.num_dashes | |
565 return flag + self.mako_identifier | |
566 | |
567 def acceptable_child(self, child): | |
568 return isinstance(child, OutputFilter) or \ | |
569 isinstance(child, ChangeFormat) or \ | |
570 isinstance(child, DiscoverDatasets) | |
571 | |
572 | |
573 class OutputFilter(XMLParam): | |
574 name = 'filter' | |
575 | |
576 def __init__(self, text, **kwargs): | |
577 params = Util.clean_kwargs(locals().copy()) | |
578 del params['text'] | |
579 super(OutputFilter, self).__init__(**params) | |
580 self.node.text = text | |
581 | |
582 def acceptable_child(self, child): | |
583 return False | |
584 | |
585 | |
586 class ChangeFormat(XMLParam): | |
587 name = 'change_format' | |
588 | |
589 def __init__(self, **kwargs): | |
590 params = Util.clean_kwargs(locals().copy()) | |
591 super(ChangeFormat, self).__init__(**params) | |
592 | |
593 def acceptable_child(self, child): | |
594 return isinstance(child, ChangeFormatWhen) | |
595 | |
596 | |
597 class ChangeFormatWhen(XMLParam): | |
598 name = 'when' | |
599 | |
600 def __init__(self, input, format, value, **kwargs): | |
601 params = Util.clean_kwargs(locals().copy()) | |
602 super(ChangeFormatWhen, self).__init__(**params) | |
603 | |
604 def acceptable_child(self, child): | |
605 return False | |
606 | |
607 | |
608 class OutputCollection(XMLParam): | |
609 name = 'collection' | |
610 | |
611 def __init__(self, name, type=None, label=None, format_source=None, | |
612 type_source=None, structured_like=None, inherit_format=None, **kwargs): | |
613 params = Util.clean_kwargs(locals().copy()) | |
614 super(OutputCollection, self).__init__(**params) | |
615 | |
616 def acceptable_child(self, child): | |
617 return isinstance(child, OutputData) or isinstance(child, OutputFilter) \ | |
618 or isinstance(child, DiscoverDatasets) | |
619 | |
620 | |
621 class DiscoverDatasets(XMLParam): | |
622 name = 'discover_datasets' | |
623 | |
624 def __init__(self, pattern, directory=None, format=None, ext=None, | |
625 visible=None, **kwargs): | |
626 params = Util.clean_kwargs(locals().copy()) | |
627 super(DiscoverDatasets, self).__init__(**params) | |
628 | |
629 | |
630 class Tests(XMLParam): | |
631 name = 'tests' | |
632 | |
633 def acceptable_child(self, child): | |
634 return issubclass(type(child), Test) | |
635 | |
636 | |
637 class Test(XMLParam): | |
638 name = 'test' | |
639 | |
640 def acceptable_child(self, child): | |
641 return isinstance(child, TestParam) or isinstance(child, TestOutput) | |
642 | |
643 | |
644 class TestParam(XMLParam): | |
645 name = 'param' | |
646 | |
647 def __init__(self, name, value=None, ftype=None, dbkey=None, **kwargs): | |
648 params = Util.clean_kwargs(locals().copy()) | |
649 super(TestParam, self).__init__(**params) | |
650 | |
651 | |
652 class TestOutput(XMLParam): | |
653 name = 'output' | |
654 | |
655 def __init__(self, name=None, file=None, ftype=None, sort=None, value=None, | |
656 md5=None, checksum=None, compare=None, lines_diff=None, | |
657 delta=None, **kwargs): | |
658 params = Util.clean_kwargs(locals().copy()) | |
659 super(TestOutput, self).__init__(**params) | |
660 | |
661 | |
662 class Citations(XMLParam): | |
663 name = 'citations' | |
664 | |
665 def acceptable_child(self, child): | |
666 return issubclass(type(child), Citation) | |
667 | |
668 def has_citation(self, type, value): | |
669 """ | |
670 Check the presence of a given citation. | |
671 | |
672 :type type: STRING | |
673 :type value: STRING | |
674 """ | |
675 for citation in self.children: | |
676 if citation.node.attrib['type'] == type and citation.node.text == value: | |
677 return True | |
678 return False | |
679 | |
680 | |
681 class Citation(XMLParam): | |
682 name = 'citation' | |
683 | |
684 def __init__(self, type, value): | |
685 passed_kwargs = {} | |
686 passed_kwargs['type'] = type | |
687 super(Citation, self).__init__(**passed_kwargs) | |
688 self.node.text = str(value) |