urwid 2.6.0.post0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of urwid might be problematic. Click here for more details.
- urwid/__init__.py +333 -0
- urwid/canvas.py +1413 -0
- urwid/command_map.py +137 -0
- urwid/container.py +59 -0
- urwid/decoration.py +65 -0
- urwid/display/__init__.py +97 -0
- urwid/display/_posix_raw_display.py +413 -0
- urwid/display/_raw_display_base.py +914 -0
- urwid/display/_web.css +12 -0
- urwid/display/_web.js +462 -0
- urwid/display/_win32.py +171 -0
- urwid/display/_win32_raw_display.py +269 -0
- urwid/display/common.py +1219 -0
- urwid/display/curses.py +690 -0
- urwid/display/escape.py +624 -0
- urwid/display/html_fragment.py +251 -0
- urwid/display/lcd.py +518 -0
- urwid/display/raw.py +37 -0
- urwid/display/web.py +636 -0
- urwid/event_loop/__init__.py +55 -0
- urwid/event_loop/abstract_loop.py +175 -0
- urwid/event_loop/asyncio_loop.py +231 -0
- urwid/event_loop/glib_loop.py +294 -0
- urwid/event_loop/main_loop.py +721 -0
- urwid/event_loop/select_loop.py +230 -0
- urwid/event_loop/tornado_loop.py +206 -0
- urwid/event_loop/trio_loop.py +302 -0
- urwid/event_loop/twisted_loop.py +269 -0
- urwid/event_loop/zmq_loop.py +275 -0
- urwid/font.py +695 -0
- urwid/graphics.py +96 -0
- urwid/highlight.css +19 -0
- urwid/listbox.py +1899 -0
- urwid/monitored_list.py +522 -0
- urwid/numedit.py +376 -0
- urwid/signals.py +330 -0
- urwid/split_repr.py +130 -0
- urwid/str_util.py +358 -0
- urwid/text_layout.py +632 -0
- urwid/treetools.py +515 -0
- urwid/util.py +557 -0
- urwid/version.py +16 -0
- urwid/vterm.py +1806 -0
- urwid/widget/__init__.py +181 -0
- urwid/widget/attr_map.py +161 -0
- urwid/widget/attr_wrap.py +140 -0
- urwid/widget/bar_graph.py +649 -0
- urwid/widget/big_text.py +77 -0
- urwid/widget/box_adapter.py +126 -0
- urwid/widget/columns.py +1145 -0
- urwid/widget/constants.py +574 -0
- urwid/widget/container.py +227 -0
- urwid/widget/divider.py +110 -0
- urwid/widget/edit.py +718 -0
- urwid/widget/filler.py +403 -0
- urwid/widget/frame.py +539 -0
- urwid/widget/grid_flow.py +539 -0
- urwid/widget/line_box.py +194 -0
- urwid/widget/overlay.py +829 -0
- urwid/widget/padding.py +597 -0
- urwid/widget/pile.py +971 -0
- urwid/widget/popup.py +170 -0
- urwid/widget/progress_bar.py +141 -0
- urwid/widget/scrollable.py +597 -0
- urwid/widget/solid_fill.py +44 -0
- urwid/widget/text.py +354 -0
- urwid/widget/widget.py +852 -0
- urwid/widget/widget_decoration.py +166 -0
- urwid/widget/wimp.py +792 -0
- urwid/wimp.py +23 -0
- urwid-2.6.0.post0.dist-info/COPYING +504 -0
- urwid-2.6.0.post0.dist-info/METADATA +332 -0
- urwid-2.6.0.post0.dist-info/RECORD +75 -0
- urwid-2.6.0.post0.dist-info/WHEEL +5 -0
- urwid-2.6.0.post0.dist-info/top_level.txt +1 -0
urwid/numedit.py
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Urwid basic widget classes
|
|
3
|
+
# Copyright (C) 2004-2012 Ian Ward
|
|
4
|
+
#
|
|
5
|
+
# This library is free software; you can redistribute it and/or
|
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
7
|
+
# License as published by the Free Software Foundation; either
|
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13
|
+
# Lesser General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
|
16
|
+
# License along with this library; if not, write to the Free Software
|
|
17
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
18
|
+
#
|
|
19
|
+
# Urwid web site: https://urwid.org/
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import re
|
|
25
|
+
import warnings
|
|
26
|
+
from decimal import Decimal
|
|
27
|
+
from typing import TYPE_CHECKING
|
|
28
|
+
|
|
29
|
+
from urwid import Edit
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from collections.abc import Container
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NumEdit(Edit):
|
|
36
|
+
"""NumEdit - edit numerical types
|
|
37
|
+
|
|
38
|
+
based on the characters in 'allowed' different numerical types
|
|
39
|
+
can be edited:
|
|
40
|
+
+ regular int: 0123456789
|
|
41
|
+
+ regular float: 0123456789.
|
|
42
|
+
+ regular oct: 01234567
|
|
43
|
+
+ regular hex: 0123456789abcdef
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
ALLOWED = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
allowed: Container[str],
|
|
51
|
+
caption,
|
|
52
|
+
default: str | bytes,
|
|
53
|
+
trimLeadingZeros: bool | None = None,
|
|
54
|
+
*,
|
|
55
|
+
trim_leading_zeros: bool = True,
|
|
56
|
+
allow_negative: bool = False,
|
|
57
|
+
):
|
|
58
|
+
super().__init__(caption, default)
|
|
59
|
+
self._allowed = allowed
|
|
60
|
+
self._trim_leading_zeros = trim_leading_zeros
|
|
61
|
+
self._allow_negative = allow_negative
|
|
62
|
+
|
|
63
|
+
if trimLeadingZeros is not None:
|
|
64
|
+
warnings.warn(
|
|
65
|
+
"'trimLeadingZeros' argument is deprecated. Use 'trim_leading_zeros' keyword argument",
|
|
66
|
+
DeprecationWarning,
|
|
67
|
+
stacklevel=3,
|
|
68
|
+
)
|
|
69
|
+
self._trim_leading_zeros = trimLeadingZeros
|
|
70
|
+
|
|
71
|
+
def valid_char(self, ch: str) -> bool:
|
|
72
|
+
"""
|
|
73
|
+
Return true for allowed characters.
|
|
74
|
+
"""
|
|
75
|
+
if len(ch) == 1:
|
|
76
|
+
if ch.upper() in self._allowed:
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
return self._allow_negative and ch == "-" and self.edit_pos == 0 and "-" not in self.edit_text
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
def keypress(self, size: tuple[int], key: str) -> str | None:
|
|
83
|
+
"""
|
|
84
|
+
Handle editing keystrokes. Remove leading zeros.
|
|
85
|
+
|
|
86
|
+
>>> e, size = NumEdit("0123456789", "", "5002"), (10,)
|
|
87
|
+
>>> e.keypress(size, 'home')
|
|
88
|
+
>>> e.keypress(size, 'delete')
|
|
89
|
+
>>> assert e.edit_text == "002"
|
|
90
|
+
>>> e.keypress(size, 'end')
|
|
91
|
+
>>> assert e.edit_text == "2"
|
|
92
|
+
>>> # binary only
|
|
93
|
+
>>> e, size = NumEdit("01", "", ""), (10,)
|
|
94
|
+
>>> assert e.edit_text == ""
|
|
95
|
+
>>> e.keypress(size, '1')
|
|
96
|
+
>>> e.keypress(size, '0')
|
|
97
|
+
>>> e.keypress(size, '1')
|
|
98
|
+
>>> assert e.edit_text == "101"
|
|
99
|
+
>>> e, size = NumEdit("0123456789", "", "", allow_negative=True), (10,)
|
|
100
|
+
>>> e.keypress(size, "-")
|
|
101
|
+
>>> e.keypress(size, '1')
|
|
102
|
+
>>> e.edit_text
|
|
103
|
+
'-1'
|
|
104
|
+
>>> e.keypress(size, 'home')
|
|
105
|
+
>>> e.keypress(size, 'delete')
|
|
106
|
+
>>> e.edit_text
|
|
107
|
+
'1'
|
|
108
|
+
>>> e.keypress(size, 'end')
|
|
109
|
+
>>> e.keypress(size, "-")
|
|
110
|
+
'-'
|
|
111
|
+
>>> e.edit_text
|
|
112
|
+
'1'
|
|
113
|
+
"""
|
|
114
|
+
unhandled = super().keypress(size, key)
|
|
115
|
+
|
|
116
|
+
if not unhandled and self._trim_leading_zeros:
|
|
117
|
+
# trim leading zeros
|
|
118
|
+
while self.edit_pos > 0 and self.edit_text[:1] == "0":
|
|
119
|
+
self.set_edit_pos(self.edit_pos - 1)
|
|
120
|
+
self.set_edit_text(self.edit_text[1:])
|
|
121
|
+
|
|
122
|
+
return unhandled
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class IntegerEdit(NumEdit):
|
|
126
|
+
"""Edit widget for integer values"""
|
|
127
|
+
|
|
128
|
+
def __init__(
|
|
129
|
+
self,
|
|
130
|
+
caption="",
|
|
131
|
+
default: int | str | Decimal | None = None,
|
|
132
|
+
base: int = 10,
|
|
133
|
+
*,
|
|
134
|
+
allow_negative: bool = False,
|
|
135
|
+
) -> None:
|
|
136
|
+
"""
|
|
137
|
+
caption -- caption markup
|
|
138
|
+
default -- default edit value
|
|
139
|
+
|
|
140
|
+
>>> IntegerEdit(u"", 42)
|
|
141
|
+
<IntegerEdit selectable flow widget '42' edit_pos=2>
|
|
142
|
+
>>> e, size = IntegerEdit(u"", "5002"), (10,)
|
|
143
|
+
>>> e.keypress(size, 'home')
|
|
144
|
+
>>> e.keypress(size, 'delete')
|
|
145
|
+
>>> assert e.edit_text == "002"
|
|
146
|
+
>>> e.keypress(size, 'end')
|
|
147
|
+
>>> assert e.edit_text == "2"
|
|
148
|
+
>>> e.keypress(size, '9')
|
|
149
|
+
>>> e.keypress(size, '0')
|
|
150
|
+
>>> assert e.edit_text == "290"
|
|
151
|
+
>>> e, size = IntegerEdit("", ""), (10,)
|
|
152
|
+
>>> assert e.value() is None
|
|
153
|
+
>>> # binary
|
|
154
|
+
>>> e, size = IntegerEdit(u"", "1010", base=2), (10,)
|
|
155
|
+
>>> e.keypress(size, 'end')
|
|
156
|
+
>>> e.keypress(size, '1')
|
|
157
|
+
>>> assert e.edit_text == "10101"
|
|
158
|
+
>>> assert e.value() == Decimal("21")
|
|
159
|
+
>>> # HEX
|
|
160
|
+
>>> e, size = IntegerEdit(u"", "10", base=16), (10,)
|
|
161
|
+
>>> e.keypress(size, 'end')
|
|
162
|
+
>>> e.keypress(size, 'F')
|
|
163
|
+
>>> e.keypress(size, 'f')
|
|
164
|
+
>>> assert e.edit_text == "10Ff"
|
|
165
|
+
>>> assert e.keypress(size, 'G') == 'G' # unhandled key
|
|
166
|
+
>>> assert e.edit_text == "10Ff"
|
|
167
|
+
>>> # keep leading 0's when not base 10
|
|
168
|
+
>>> e, size = IntegerEdit(u"", "10FF", base=16), (10,)
|
|
169
|
+
>>> assert e.edit_text == "10FF"
|
|
170
|
+
>>> assert e.value() == Decimal("4351")
|
|
171
|
+
>>> e.keypress(size, 'home')
|
|
172
|
+
>>> e.keypress(size, 'delete')
|
|
173
|
+
>>> e.keypress(size, '0')
|
|
174
|
+
>>> assert e.edit_text == "00FF"
|
|
175
|
+
>>> # test exception on incompatible value for base
|
|
176
|
+
>>> e, size = IntegerEdit(u"", "10FG", base=16), (10,)
|
|
177
|
+
Traceback (most recent call last):
|
|
178
|
+
...
|
|
179
|
+
ValueError: invalid value: 10FG for base 16
|
|
180
|
+
>>> # test exception on float init value
|
|
181
|
+
>>> e, size = IntegerEdit(u"", 10.0), (10,)
|
|
182
|
+
Traceback (most recent call last):
|
|
183
|
+
...
|
|
184
|
+
ValueError: default: Only 'str', 'int', 'long' or Decimal input allowed
|
|
185
|
+
>>> e, size = IntegerEdit(u"", Decimal("10.0")), (10,)
|
|
186
|
+
Traceback (most recent call last):
|
|
187
|
+
...
|
|
188
|
+
ValueError: not an 'integer Decimal' instance
|
|
189
|
+
"""
|
|
190
|
+
self.base = base
|
|
191
|
+
val = ""
|
|
192
|
+
allowed_chars = self.ALLOWED[: self.base]
|
|
193
|
+
if default is not None:
|
|
194
|
+
if not isinstance(default, (int, str, Decimal)):
|
|
195
|
+
raise ValueError("default: Only 'str', 'int' or Decimal input allowed")
|
|
196
|
+
|
|
197
|
+
# convert to a long first, this will raise a ValueError
|
|
198
|
+
# in case a float is passed or some other error
|
|
199
|
+
if isinstance(default, str) and len(default):
|
|
200
|
+
# check if it is a valid initial value
|
|
201
|
+
validation_re = f"^[{allowed_chars}]+$"
|
|
202
|
+
if not re.match(validation_re, str(default), re.IGNORECASE):
|
|
203
|
+
raise ValueError(f"invalid value: {default} for base {base}")
|
|
204
|
+
|
|
205
|
+
elif isinstance(default, Decimal) and default.as_tuple()[2] != 0:
|
|
206
|
+
# a Decimal instance with no fractional part
|
|
207
|
+
raise ValueError("not an 'integer Decimal' instance")
|
|
208
|
+
|
|
209
|
+
# convert possible int, long or Decimal to str
|
|
210
|
+
val = str(default)
|
|
211
|
+
|
|
212
|
+
super().__init__(
|
|
213
|
+
allowed_chars,
|
|
214
|
+
caption,
|
|
215
|
+
val,
|
|
216
|
+
trim_leading_zeros=(self.base == 10),
|
|
217
|
+
allow_negative=allow_negative,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def value(self) -> Decimal | None:
|
|
221
|
+
"""
|
|
222
|
+
Return the numeric value of self.edit_text.
|
|
223
|
+
|
|
224
|
+
>>> e, size = IntegerEdit(), (10,)
|
|
225
|
+
>>> e.keypress(size, '5')
|
|
226
|
+
>>> e.keypress(size, '1')
|
|
227
|
+
>>> assert e.value() == 51
|
|
228
|
+
"""
|
|
229
|
+
if self.edit_text:
|
|
230
|
+
return Decimal(int(self.edit_text, self.base))
|
|
231
|
+
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
def __int__(self) -> int:
|
|
235
|
+
"""Enforced int value return.
|
|
236
|
+
|
|
237
|
+
>>> e, size = IntegerEdit(allow_negative=True), (10,)
|
|
238
|
+
>>> assert int(e) == 0
|
|
239
|
+
>>> e.keypress(size, '-')
|
|
240
|
+
>>> e.keypress(size, '4')
|
|
241
|
+
>>> e.keypress(size, '2')
|
|
242
|
+
>>> assert int(e) == -42
|
|
243
|
+
"""
|
|
244
|
+
if self.edit_text:
|
|
245
|
+
return int(self.edit_text, self.base)
|
|
246
|
+
return 0
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class FloatEdit(NumEdit):
|
|
250
|
+
"""Edit widget for float values."""
|
|
251
|
+
|
|
252
|
+
def __init__(
|
|
253
|
+
self,
|
|
254
|
+
caption="",
|
|
255
|
+
default: str | int | Decimal | None = None,
|
|
256
|
+
preserveSignificance: bool | None = None,
|
|
257
|
+
decimalSeparator: str | None = None,
|
|
258
|
+
*,
|
|
259
|
+
preserve_significance: bool = True,
|
|
260
|
+
decimal_separator: str = ".",
|
|
261
|
+
allow_negative: bool = False,
|
|
262
|
+
) -> None:
|
|
263
|
+
"""
|
|
264
|
+
caption -- caption markup
|
|
265
|
+
default -- default edit value
|
|
266
|
+
preserve_significance -- return value has the same signif. as default
|
|
267
|
+
decimal_separator -- use '.' as separator by default, optionally a ','
|
|
268
|
+
|
|
269
|
+
>>> FloatEdit(u"", "1.065434")
|
|
270
|
+
<FloatEdit selectable flow widget '1.065434' edit_pos=8>
|
|
271
|
+
>>> e, size = FloatEdit(u"", "1.065434"), (10,)
|
|
272
|
+
>>> e.keypress(size, 'home')
|
|
273
|
+
>>> e.keypress(size, 'delete')
|
|
274
|
+
>>> assert e.edit_text == ".065434"
|
|
275
|
+
>>> e.keypress(size, 'end')
|
|
276
|
+
>>> e.keypress(size, 'backspace')
|
|
277
|
+
>>> assert e.edit_text == ".06543"
|
|
278
|
+
>>> e, size = FloatEdit(), (10,)
|
|
279
|
+
>>> e.keypress(size, '5')
|
|
280
|
+
>>> e.keypress(size, '1')
|
|
281
|
+
>>> e.keypress(size, '.')
|
|
282
|
+
>>> e.keypress(size, '5')
|
|
283
|
+
>>> e.keypress(size, '1')
|
|
284
|
+
>>> assert e.value() == Decimal("51.51"), e.value()
|
|
285
|
+
>>> e, size = FloatEdit(decimal_separator=":"), (10,)
|
|
286
|
+
Traceback (most recent call last):
|
|
287
|
+
...
|
|
288
|
+
ValueError: invalid decimal separator: :
|
|
289
|
+
>>> e, size = FloatEdit(decimal_separator=","), (10,)
|
|
290
|
+
>>> e.keypress(size, '5')
|
|
291
|
+
>>> e.keypress(size, '1')
|
|
292
|
+
>>> e.keypress(size, ',')
|
|
293
|
+
>>> e.keypress(size, '5')
|
|
294
|
+
>>> e.keypress(size, '1')
|
|
295
|
+
>>> assert e.edit_text == "51,51"
|
|
296
|
+
>>> e, size = FloatEdit("", "3.1415", preserve_significance=True), (10,)
|
|
297
|
+
>>> e.keypress(size, 'end')
|
|
298
|
+
>>> e.keypress(size, 'backspace')
|
|
299
|
+
>>> e.keypress(size, 'backspace')
|
|
300
|
+
>>> assert e.edit_text == "3.14"
|
|
301
|
+
>>> assert e.value() == Decimal("3.1400")
|
|
302
|
+
>>> e.keypress(size, '1')
|
|
303
|
+
>>> e.keypress(size, '5')
|
|
304
|
+
>>> e.keypress(size, '9')
|
|
305
|
+
>>> assert e.value() == Decimal("3.1416"), e.value()
|
|
306
|
+
>>> e, size = FloatEdit("", ""), (10,)
|
|
307
|
+
>>> assert e.value() is None
|
|
308
|
+
>>> e, size = FloatEdit(u"", 10.0), (10,)
|
|
309
|
+
Traceback (most recent call last):
|
|
310
|
+
...
|
|
311
|
+
ValueError: default: Only 'str', 'int', 'long' or Decimal input allowed
|
|
312
|
+
"""
|
|
313
|
+
self.significance = None
|
|
314
|
+
self._decimal_separator = decimal_separator
|
|
315
|
+
if decimalSeparator is not None:
|
|
316
|
+
warnings.warn(
|
|
317
|
+
"'decimalSeparator' argument is deprecated. Use 'decimal_separator' keyword argument",
|
|
318
|
+
DeprecationWarning,
|
|
319
|
+
stacklevel=3,
|
|
320
|
+
)
|
|
321
|
+
self._decimal_separator = decimalSeparator
|
|
322
|
+
|
|
323
|
+
if self._decimal_separator not in {".", ","}:
|
|
324
|
+
raise ValueError(f"invalid decimal separator: {self._decimal_separator}")
|
|
325
|
+
|
|
326
|
+
if preserveSignificance is not None:
|
|
327
|
+
warnings.warn(
|
|
328
|
+
"'preserveSignificance' argument is deprecated. Use 'preserve_significance' keyword argument",
|
|
329
|
+
DeprecationWarning,
|
|
330
|
+
stacklevel=3,
|
|
331
|
+
)
|
|
332
|
+
preserve_significance = preserveSignificance
|
|
333
|
+
|
|
334
|
+
val = ""
|
|
335
|
+
if default is not None and default != "": # noqa: PLC1901,RUF100
|
|
336
|
+
if not isinstance(default, (int, str, Decimal)):
|
|
337
|
+
raise ValueError("default: Only 'str', 'int' or Decimal input allowed")
|
|
338
|
+
|
|
339
|
+
if isinstance(default, str) and default:
|
|
340
|
+
# check if it is a float, raises a ValueError otherwise
|
|
341
|
+
float(default)
|
|
342
|
+
default = Decimal(default)
|
|
343
|
+
|
|
344
|
+
if preserve_significance and isinstance(default, Decimal):
|
|
345
|
+
self.significance = default
|
|
346
|
+
|
|
347
|
+
val = str(default)
|
|
348
|
+
|
|
349
|
+
super().__init__(self.ALLOWED[0:10] + self._decimal_separator, caption, val, allow_negative=allow_negative)
|
|
350
|
+
|
|
351
|
+
def value(self) -> Decimal | None:
|
|
352
|
+
"""
|
|
353
|
+
Return the numeric value of self.edit_text.
|
|
354
|
+
"""
|
|
355
|
+
if self.edit_text:
|
|
356
|
+
normalized = Decimal(self.edit_text.replace(self._decimal_separator, "."))
|
|
357
|
+
if self.significance is not None:
|
|
358
|
+
return normalized.quantize(self.significance)
|
|
359
|
+
return normalized
|
|
360
|
+
|
|
361
|
+
return None
|
|
362
|
+
|
|
363
|
+
def __float__(self) -> float:
|
|
364
|
+
"""Enforced float value return.
|
|
365
|
+
|
|
366
|
+
>>> e, size = FloatEdit(allow_negative=True), (10,)
|
|
367
|
+
>>> assert float(e) == 0.
|
|
368
|
+
>>> e.keypress(size, '-')
|
|
369
|
+
>>> e.keypress(size, '4')
|
|
370
|
+
>>> e.keypress(size, '.')
|
|
371
|
+
>>> e.keypress(size, '2')
|
|
372
|
+
>>> assert float(e) == -4.2
|
|
373
|
+
"""
|
|
374
|
+
if self.edit_text:
|
|
375
|
+
return float(self.edit_text.replace(self._decimal_separator, "."))
|
|
376
|
+
return 0.0
|