tinybird 0.0.1.dev0__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 tinybird might be problematic. Click here for more details.
- tinybird/__cli__.py +8 -0
- tinybird/ch_utils/constants.py +244 -0
- tinybird/ch_utils/engine.py +855 -0
- tinybird/check_pypi.py +25 -0
- tinybird/client.py +1281 -0
- tinybird/config.py +117 -0
- tinybird/connectors.py +428 -0
- tinybird/context.py +23 -0
- tinybird/datafile.py +5589 -0
- tinybird/datatypes.py +434 -0
- tinybird/feedback_manager.py +1022 -0
- tinybird/git_settings.py +145 -0
- tinybird/sql.py +865 -0
- tinybird/sql_template.py +2343 -0
- tinybird/sql_template_fmt.py +281 -0
- tinybird/sql_toolset.py +350 -0
- tinybird/syncasync.py +682 -0
- tinybird/tb_cli.py +25 -0
- tinybird/tb_cli_modules/auth.py +252 -0
- tinybird/tb_cli_modules/branch.py +1043 -0
- tinybird/tb_cli_modules/cicd.py +434 -0
- tinybird/tb_cli_modules/cli.py +1571 -0
- tinybird/tb_cli_modules/common.py +2082 -0
- tinybird/tb_cli_modules/config.py +344 -0
- tinybird/tb_cli_modules/connection.py +803 -0
- tinybird/tb_cli_modules/datasource.py +900 -0
- tinybird/tb_cli_modules/exceptions.py +91 -0
- tinybird/tb_cli_modules/fmt.py +91 -0
- tinybird/tb_cli_modules/job.py +85 -0
- tinybird/tb_cli_modules/pipe.py +858 -0
- tinybird/tb_cli_modules/regions.py +9 -0
- tinybird/tb_cli_modules/tag.py +100 -0
- tinybird/tb_cli_modules/telemetry.py +310 -0
- tinybird/tb_cli_modules/test.py +107 -0
- tinybird/tb_cli_modules/tinyunit/tinyunit.py +340 -0
- tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +71 -0
- tinybird/tb_cli_modules/token.py +349 -0
- tinybird/tb_cli_modules/workspace.py +269 -0
- tinybird/tb_cli_modules/workspace_members.py +212 -0
- tinybird/tornado_template.py +1194 -0
- tinybird-0.0.1.dev0.dist-info/METADATA +2815 -0
- tinybird-0.0.1.dev0.dist-info/RECORD +45 -0
- tinybird-0.0.1.dev0.dist-info/WHEEL +5 -0
- tinybird-0.0.1.dev0.dist-info/entry_points.txt +2 -0
- tinybird-0.0.1.dev0.dist-info/top_level.txt +4 -0
tinybird/datatypes.py
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import decimal
|
|
3
|
+
import re
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
|
|
6
|
+
datetime64_patterns = [
|
|
7
|
+
r"\d\d\d\d.\d\d.\d\d(T|\s)\d\d:\d\d:\d\d.\d\d\d",
|
|
8
|
+
r"\d\d.\d\d.\d\d\d\d.\d{1,2}:\d{1,2}:\d{1,2}.\d{1,3}",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
datetime_patterns = [r"\d\d\d\d.\d\d.\d\d(T|\s)\d\d:\d\d:\d\d", r"\d\d.\d\d.\d\d\d\d.\d{1,2}:\d{1,2}:\d{1,2}"]
|
|
12
|
+
|
|
13
|
+
int_8_max = 2**7
|
|
14
|
+
int16_max = 2**15
|
|
15
|
+
int32_max = 2**31
|
|
16
|
+
int64_max = 2**63
|
|
17
|
+
int128_max = 2**127
|
|
18
|
+
int256_max = 2**255
|
|
19
|
+
uint_8_max = 2**8
|
|
20
|
+
uint16_max = 2**16
|
|
21
|
+
uint32_max = 2**32
|
|
22
|
+
uint64_max = 2**64
|
|
23
|
+
uint128_max = 2**128
|
|
24
|
+
uint256_max = 2**256
|
|
25
|
+
intx_re = r"^[+-]?\d+$"
|
|
26
|
+
uintx_re = r"^\d+$"
|
|
27
|
+
float32_max = 2**23 # 23 bits is the fractional part of float 32 ieee754
|
|
28
|
+
float64_max = 2**52 # 51 bits is the fractional part of float 64 ieee754
|
|
29
|
+
|
|
30
|
+
datetime64_type_pattern = r"^DateTime64(\([1-9](, ?'.+')?\))?$"
|
|
31
|
+
datetime_type_pattern = r"^DateTime(\(('.+')?)?\)?$"
|
|
32
|
+
|
|
33
|
+
# List from https://github.com/tinybirdco/ClickHousePrivate/blob/153473d9c1c871974688a1d72dcff7a13fc2076c/src/DataTypes/Serializations/SerializationBool.cpp#L216
|
|
34
|
+
bool_allowed_values = {
|
|
35
|
+
"true",
|
|
36
|
+
"false",
|
|
37
|
+
"True",
|
|
38
|
+
"False",
|
|
39
|
+
"T",
|
|
40
|
+
"F",
|
|
41
|
+
"Y",
|
|
42
|
+
"N",
|
|
43
|
+
"Yes",
|
|
44
|
+
"No",
|
|
45
|
+
"On",
|
|
46
|
+
"Off",
|
|
47
|
+
"Enable",
|
|
48
|
+
"Disable",
|
|
49
|
+
"Enabled",
|
|
50
|
+
"Disabled",
|
|
51
|
+
"1",
|
|
52
|
+
"0",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def is_type_datetime64(type_to_check):
|
|
57
|
+
"""
|
|
58
|
+
>>> is_type_datetime64('DateTime64')
|
|
59
|
+
True
|
|
60
|
+
>>> is_type_datetime64('DateTime64(1)')
|
|
61
|
+
True
|
|
62
|
+
>>> is_type_datetime64('DateTime64(3)')
|
|
63
|
+
True
|
|
64
|
+
>>> is_type_datetime64("DateTime64(3,'Madrid')")
|
|
65
|
+
True
|
|
66
|
+
>>> is_type_datetime64("DateTime64(3, 'Madrid/Moscow')")
|
|
67
|
+
True
|
|
68
|
+
>>> is_type_datetime64("DateTime64()")
|
|
69
|
+
False
|
|
70
|
+
>>> is_type_datetime64("datetime64")
|
|
71
|
+
False
|
|
72
|
+
"""
|
|
73
|
+
return re.match(datetime64_type_pattern, type_to_check) is not None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def is_type_datetime(type_to_check):
|
|
77
|
+
"""
|
|
78
|
+
>>> is_type_datetime('DateTime')
|
|
79
|
+
True
|
|
80
|
+
>>> is_type_datetime('DateTime()')
|
|
81
|
+
True
|
|
82
|
+
>>> is_type_datetime("DateTime('Madrid')")
|
|
83
|
+
True
|
|
84
|
+
>>> is_type_datetime("DateTime(3)")
|
|
85
|
+
False
|
|
86
|
+
>>> is_type_datetime("datetime")
|
|
87
|
+
False
|
|
88
|
+
"""
|
|
89
|
+
return re.match(datetime_type_pattern, type_to_check) is not None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def string_test(x):
|
|
93
|
+
return True
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def date_test(x):
|
|
97
|
+
return re.match(r"\d\d\d\d-\d\d-\d\d$", x)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def datetime64_test(x):
|
|
101
|
+
return any([re.match(p, x) for p in datetime64_patterns])
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def datetime_test(x):
|
|
105
|
+
return any([re.match(p, x) for p in datetime_patterns])
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def int_8_test(x):
|
|
109
|
+
return re.match(intx_re, x) and -int_8_max <= int(x) < int_8_max
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def int16_test(x):
|
|
113
|
+
return re.match(intx_re, x) and -int16_max <= int(x) < int16_max
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def int32_test(x):
|
|
117
|
+
return re.match(intx_re, x) and -int32_max <= int(x) < int32_max
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def int64_test(x):
|
|
121
|
+
return re.match(intx_re, x) and -int64_max <= int(x) < int64_max
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def int128_test(x):
|
|
125
|
+
return re.match(intx_re, x) and -int128_max <= int(x) < int128_max
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def int256_test(x):
|
|
129
|
+
return re.match(intx_re, x) and -int256_max <= int(x) < int256_max
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def uint_8_test(x):
|
|
133
|
+
return re.match(uintx_re, x) and 0 <= int(x) < uint_8_max
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def uint16_test(x):
|
|
137
|
+
return re.match(uintx_re, x) and 0 <= int(x) < uint16_max
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def uint32_test(x):
|
|
141
|
+
return re.match(uintx_re, x) and 0 <= int(x) < uint32_max
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def uint64_test(x):
|
|
145
|
+
return re.match(uintx_re, x) and 0 <= int(x) < uint64_max
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def uint128_test(x):
|
|
149
|
+
return re.match(intx_re, x) and 0 <= int(x) < uint128_max
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def uint256_test(x):
|
|
153
|
+
return re.match(intx_re, x) and 0 <= int(x) < uint256_max
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def float_test(x):
|
|
157
|
+
return "_" not in x and type_test(x, float)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def float32_test(x):
|
|
161
|
+
return "_" not in x and type_test(x, float) and -float32_max <= float(x) < float32_max
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def float64_test(x):
|
|
165
|
+
return "_" not in x and type_test(x, float) and -float64_max < float(x) < float64_max
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def bool_test(x):
|
|
169
|
+
return x in bool_allowed_values
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_numeric_testers(fn, n):
|
|
173
|
+
"""
|
|
174
|
+
>>> test_numeric_testers(int32_test, (2**31)-1)
|
|
175
|
+
True
|
|
176
|
+
>>> test_numeric_testers(int32_test, -(2**31))
|
|
177
|
+
True
|
|
178
|
+
>>> test_numeric_testers(int32_test, -(2**31)-1)
|
|
179
|
+
False
|
|
180
|
+
>>> test_numeric_testers(int32_test, 2**31)
|
|
181
|
+
False
|
|
182
|
+
|
|
183
|
+
>>> test_numeric_testers(int64_test, (2**63)-1)
|
|
184
|
+
True
|
|
185
|
+
>>> test_numeric_testers(int64_test, -(2**63))
|
|
186
|
+
True
|
|
187
|
+
>>> test_numeric_testers(int64_test, -(2**63)-1)
|
|
188
|
+
False
|
|
189
|
+
>>> test_numeric_testers(int64_test, 2**63)
|
|
190
|
+
False
|
|
191
|
+
"""
|
|
192
|
+
return fn(str(n))
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def array_test(_type_test):
|
|
196
|
+
"""
|
|
197
|
+
>>> array_test(str)("['blabla']")
|
|
198
|
+
True
|
|
199
|
+
>>> array_test(str)('["blabla"]')
|
|
200
|
+
True
|
|
201
|
+
>>> array_test(str)('["blabla","bloblo"]')
|
|
202
|
+
True
|
|
203
|
+
>>> array_test(str)('["blabla, bloblo"]')
|
|
204
|
+
True
|
|
205
|
+
>>> array_test(str)("[ W ]")
|
|
206
|
+
False
|
|
207
|
+
>>> array_test(int)("[1]")
|
|
208
|
+
True
|
|
209
|
+
>>> array_test(int)('[1]')
|
|
210
|
+
True
|
|
211
|
+
>>> array_test(int)('[1,2]')
|
|
212
|
+
True
|
|
213
|
+
>>> array_test(float)("[1.2]")
|
|
214
|
+
True
|
|
215
|
+
>>> array_test(float)('[1.2]')
|
|
216
|
+
True
|
|
217
|
+
>>> array_test(float)('[1.2,2.1]')
|
|
218
|
+
True
|
|
219
|
+
>>> array_test(float)('["1.2","2.1"]')
|
|
220
|
+
False
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
def _test(x):
|
|
224
|
+
if x[0] != "[":
|
|
225
|
+
return False
|
|
226
|
+
try:
|
|
227
|
+
k = ast.literal_eval(x)
|
|
228
|
+
except Exception:
|
|
229
|
+
return False
|
|
230
|
+
if isinstance(k, list):
|
|
231
|
+
return all(isinstance(x, _type_test) for x in k)
|
|
232
|
+
return False
|
|
233
|
+
|
|
234
|
+
return _test
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
numbers_types = (
|
|
238
|
+
"Int8",
|
|
239
|
+
"UInt8",
|
|
240
|
+
"Int16",
|
|
241
|
+
"UInt16",
|
|
242
|
+
"UInt32",
|
|
243
|
+
"Int32",
|
|
244
|
+
"Int64",
|
|
245
|
+
"UInt64",
|
|
246
|
+
"Int128",
|
|
247
|
+
"UInt128",
|
|
248
|
+
"Int256",
|
|
249
|
+
"UInt256",
|
|
250
|
+
"Float32",
|
|
251
|
+
"Float64",
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# Use guessers for discovering types
|
|
255
|
+
# I.e., when you have to take into consideration things like float precision
|
|
256
|
+
guessers = {
|
|
257
|
+
"DateTime64": datetime64_test,
|
|
258
|
+
"DateTime": datetime_test,
|
|
259
|
+
"Date": date_test,
|
|
260
|
+
"Int8": int_8_test,
|
|
261
|
+
"UInt8": uint_8_test,
|
|
262
|
+
"Int16": int16_test,
|
|
263
|
+
"UInt16": uint16_test,
|
|
264
|
+
"Int32": int32_test,
|
|
265
|
+
"UInt32": uint32_test,
|
|
266
|
+
"Int64": int64_test,
|
|
267
|
+
"UInt64": uint64_test,
|
|
268
|
+
"Float32": float32_test,
|
|
269
|
+
"Float64": float64_test,
|
|
270
|
+
"Array(Int32)": array_test(int),
|
|
271
|
+
"Array(Float32)": array_test(float),
|
|
272
|
+
"Array(String)": array_test(str),
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
# Use testers validating a value against a type
|
|
276
|
+
# I.e., you already know the type and you need to check if a value fits there
|
|
277
|
+
testers = {
|
|
278
|
+
"DateTime64": datetime64_test,
|
|
279
|
+
"DateTime": datetime_test,
|
|
280
|
+
"Date": date_test,
|
|
281
|
+
"Int8": int_8_test,
|
|
282
|
+
"UInt8": uint_8_test,
|
|
283
|
+
"Int16": int16_test,
|
|
284
|
+
"UInt16": uint16_test,
|
|
285
|
+
"Int32": int32_test,
|
|
286
|
+
"UInt32": uint32_test,
|
|
287
|
+
"Int64": int64_test,
|
|
288
|
+
"UInt64": uint64_test,
|
|
289
|
+
"Int128": int128_test,
|
|
290
|
+
"UInt128": uint128_test,
|
|
291
|
+
"Int256": int256_test,
|
|
292
|
+
"UInt256": uint256_test,
|
|
293
|
+
"Float32": float_test,
|
|
294
|
+
"Float64": float_test,
|
|
295
|
+
"Bool": bool_test,
|
|
296
|
+
"Array(Int32)": array_test(int),
|
|
297
|
+
"Array(Float32)": array_test(float),
|
|
298
|
+
"Array(String)": array_test(str),
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# Search for `canBeInsideNullable` under CH code and see which ones are true.
|
|
303
|
+
nullable_types = [
|
|
304
|
+
"Date",
|
|
305
|
+
"Date32",
|
|
306
|
+
"DateTime",
|
|
307
|
+
"DateTime32",
|
|
308
|
+
"DateTime64",
|
|
309
|
+
"Decimal",
|
|
310
|
+
"Decimal128",
|
|
311
|
+
"Decimal256",
|
|
312
|
+
"Decimal32",
|
|
313
|
+
"Decimal64",
|
|
314
|
+
"Enum",
|
|
315
|
+
"Enum16",
|
|
316
|
+
"Enum8",
|
|
317
|
+
"FixedString",
|
|
318
|
+
"Float32",
|
|
319
|
+
"Float64",
|
|
320
|
+
"IPv4",
|
|
321
|
+
"IPv6",
|
|
322
|
+
"Int128",
|
|
323
|
+
"Int16",
|
|
324
|
+
"Int256",
|
|
325
|
+
"Int32",
|
|
326
|
+
"Int64",
|
|
327
|
+
"Int8",
|
|
328
|
+
"MultiPolygon",
|
|
329
|
+
"Point",
|
|
330
|
+
"Polygon",
|
|
331
|
+
"Ring",
|
|
332
|
+
"String",
|
|
333
|
+
"UInt128",
|
|
334
|
+
"UInt16",
|
|
335
|
+
"UInt256",
|
|
336
|
+
"UInt32",
|
|
337
|
+
"UInt64",
|
|
338
|
+
"UInt8",
|
|
339
|
+
"UUID",
|
|
340
|
+
]
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def type_test(i, t):
|
|
344
|
+
try:
|
|
345
|
+
t(i)
|
|
346
|
+
return True
|
|
347
|
+
except Exception:
|
|
348
|
+
return False
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def parse_decimal_type(typ):
|
|
352
|
+
"""
|
|
353
|
+
>>> parse_decimal_type("decimal")
|
|
354
|
+
|
|
355
|
+
>>> parse_decimal_type('Decimal')
|
|
356
|
+
(64, 10, 0)
|
|
357
|
+
>>> parse_decimal_type('Decimal()')
|
|
358
|
+
|
|
359
|
+
>>> parse_decimal_type('Decimal(2)')
|
|
360
|
+
(32, 2, 0)
|
|
361
|
+
>>> parse_decimal_type('Decimal(5,2)')
|
|
362
|
+
(32, 5, 2)
|
|
363
|
+
>>> parse_decimal_type('Decimal( 9 , 2 )')
|
|
364
|
+
(32, 9, 2)
|
|
365
|
+
>>> parse_decimal_type('Decimal(10,2)')
|
|
366
|
+
(64, 10, 2)
|
|
367
|
+
>>> parse_decimal_type('Decimal(19,2)')
|
|
368
|
+
(128, 19, 2)
|
|
369
|
+
>>> parse_decimal_type('Decimal(39,2)')
|
|
370
|
+
(256, 39, 2)
|
|
371
|
+
>>> parse_decimal_type('Decimal32(9)')
|
|
372
|
+
(32, 9, 9)
|
|
373
|
+
>>> parse_decimal_type('Decimal32(10)')
|
|
374
|
+
|
|
375
|
+
>>> parse_decimal_type('Decimal(10,10)')
|
|
376
|
+
(64, 10, 10)
|
|
377
|
+
>>> parse_decimal_type('Decimal(10,11)')
|
|
378
|
+
|
|
379
|
+
>>> parse_decimal_type('Decimal32(5, 2)')
|
|
380
|
+
|
|
381
|
+
>>> parse_decimal_type('Decimal64(2)')
|
|
382
|
+
(64, 18, 2)
|
|
383
|
+
>>> parse_decimal_type('Decimal128(2)')
|
|
384
|
+
(128, 38, 2)
|
|
385
|
+
>>> parse_decimal_type('Decimal256(2)')
|
|
386
|
+
(256, 76, 2)
|
|
387
|
+
"""
|
|
388
|
+
# Obtained from https://clickhouse.com/docs/en/sql-reference/data-types/decimal
|
|
389
|
+
max_digits_by_bit_width = {32: 9, 64: 18, 128: 38, 256: 76}
|
|
390
|
+
|
|
391
|
+
# Check if type is Decimal, Decimal(P), or Decimal(P, S) with whitespace support
|
|
392
|
+
# Regex contains two capturing groups. First capturing P. Second capturing S.
|
|
393
|
+
if m := re.match(r"^Decimal\s*(?:\(\s*(\d+)(?:\s*,\s*(\d+))?\s*\))?$", typ):
|
|
394
|
+
p, s = int(m.group(1) or 10), int(m.group(2) or 0)
|
|
395
|
+
b = min(bit_width for bit_width, max_digits in max_digits_by_bit_width.items() if max_digits >= p)
|
|
396
|
+
if p < s or b is None:
|
|
397
|
+
return None
|
|
398
|
+
# Check if type is Decimal32(S), Decimal64(S), Decimal128(S), or Decimal256(S) with whitespace support.
|
|
399
|
+
# Regex contains 2 capturing groups. First capturing 32, 64, 128 or 256. Second capturing S.
|
|
400
|
+
elif m := re.match(r"^Decimal(32|64|128|256)\s*\(\s*(\d+)\s*\)$", typ):
|
|
401
|
+
b, s = int(m.group(1)), int(m.group(2))
|
|
402
|
+
p = max_digits_by_bit_width[int(b)]
|
|
403
|
+
if p < s:
|
|
404
|
+
return None
|
|
405
|
+
else:
|
|
406
|
+
return None
|
|
407
|
+
return b, p, s
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def is_type_decimal(type_to_check):
|
|
411
|
+
"""
|
|
412
|
+
>>> is_type_decimal('Decimal')
|
|
413
|
+
True
|
|
414
|
+
>>> is_type_decimal("Decimal(10, 2)")
|
|
415
|
+
True
|
|
416
|
+
>>> is_type_decimal("decimal")
|
|
417
|
+
False
|
|
418
|
+
"""
|
|
419
|
+
return parse_decimal_type(type_to_check) is not None
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def get_decimal_limits(p, s):
|
|
423
|
+
"""
|
|
424
|
+
>>> get_decimal_limits(1, 0)
|
|
425
|
+
(Decimal('-9'), Decimal('9'))
|
|
426
|
+
>>> get_decimal_limits(5, 5)
|
|
427
|
+
(Decimal('-0.99999'), Decimal('0.99999'))
|
|
428
|
+
>>> get_decimal_limits(76, 38)
|
|
429
|
+
(Decimal('-99999999999999999999999999999999999999.99999999999999999999999999999999999999'), Decimal('99999999999999999999999999999999999999.99999999999999999999999999999999999999'))
|
|
430
|
+
"""
|
|
431
|
+
with decimal.localcontext(prec=p + 2):
|
|
432
|
+
max_value = Decimal((10**p) - 1) / (10**s)
|
|
433
|
+
min_value = -max_value
|
|
434
|
+
return min_value, max_value
|