aspose-cells-foss 25.12.1__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.
- aspose/__init__.py +14 -0
- aspose/cells/__init__.py +31 -0
- aspose/cells/cell.py +350 -0
- aspose/cells/constants.py +44 -0
- aspose/cells/converters/__init__.py +13 -0
- aspose/cells/converters/csv_converter.py +55 -0
- aspose/cells/converters/json_converter.py +46 -0
- aspose/cells/converters/markdown_converter.py +453 -0
- aspose/cells/drawing/__init__.py +17 -0
- aspose/cells/drawing/anchor.py +172 -0
- aspose/cells/drawing/collection.py +233 -0
- aspose/cells/drawing/image.py +338 -0
- aspose/cells/formats.py +80 -0
- aspose/cells/formula/__init__.py +10 -0
- aspose/cells/formula/evaluator.py +360 -0
- aspose/cells/formula/functions.py +433 -0
- aspose/cells/formula/tokenizer.py +340 -0
- aspose/cells/io/__init__.py +27 -0
- aspose/cells/io/csv/__init__.py +8 -0
- aspose/cells/io/csv/reader.py +88 -0
- aspose/cells/io/csv/writer.py +98 -0
- aspose/cells/io/factory.py +138 -0
- aspose/cells/io/interfaces.py +48 -0
- aspose/cells/io/json/__init__.py +8 -0
- aspose/cells/io/json/reader.py +126 -0
- aspose/cells/io/json/writer.py +119 -0
- aspose/cells/io/md/__init__.py +8 -0
- aspose/cells/io/md/reader.py +161 -0
- aspose/cells/io/md/writer.py +334 -0
- aspose/cells/io/models.py +64 -0
- aspose/cells/io/xlsx/__init__.py +9 -0
- aspose/cells/io/xlsx/constants.py +312 -0
- aspose/cells/io/xlsx/image_writer.py +311 -0
- aspose/cells/io/xlsx/reader.py +284 -0
- aspose/cells/io/xlsx/writer.py +931 -0
- aspose/cells/plugins/__init__.py +6 -0
- aspose/cells/plugins/docling_backend/__init__.py +7 -0
- aspose/cells/plugins/docling_backend/backend.py +535 -0
- aspose/cells/plugins/markitdown_plugin/__init__.py +15 -0
- aspose/cells/plugins/markitdown_plugin/plugin.py +128 -0
- aspose/cells/range.py +210 -0
- aspose/cells/style.py +287 -0
- aspose/cells/utils/__init__.py +54 -0
- aspose/cells/utils/coordinates.py +68 -0
- aspose/cells/utils/exceptions.py +43 -0
- aspose/cells/utils/validation.py +102 -0
- aspose/cells/workbook.py +352 -0
- aspose/cells/worksheet.py +670 -0
- aspose_cells_foss-25.12.1.dist-info/METADATA +189 -0
- aspose_cells_foss-25.12.1.dist-info/RECORD +53 -0
- aspose_cells_foss-25.12.1.dist-info/WHEEL +5 -0
- aspose_cells_foss-25.12.1.dist-info/entry_points.txt +2 -0
- aspose_cells_foss-25.12.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Excel Function Library - Built-in functions for formula evaluation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
import datetime
|
|
7
|
+
from typing import Union, Any, List, Callable
|
|
8
|
+
from decimal import Decimal
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Type aliases
|
|
12
|
+
Number = Union[int, float, Decimal]
|
|
13
|
+
Value = Union[Number, str, bool, datetime.datetime, None]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ExcelError(Exception):
|
|
17
|
+
"""Base class for Excel errors."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DivisionByZeroError(ExcelError):
|
|
22
|
+
"""#DIV/0! error."""
|
|
23
|
+
def __str__(self):
|
|
24
|
+
return "#DIV/0!"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ValueErrorExcel(ExcelError):
|
|
28
|
+
"""#VALUE! error."""
|
|
29
|
+
def __str__(self):
|
|
30
|
+
return "#VALUE!"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class NumError(ExcelError):
|
|
34
|
+
"""#NUM! error."""
|
|
35
|
+
def __str__(self):
|
|
36
|
+
return "#NUM!"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class NameError(ExcelError):
|
|
40
|
+
"""#NAME? error."""
|
|
41
|
+
def __str__(self):
|
|
42
|
+
return "#NAME?"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def to_number(value: Value) -> Number:
|
|
46
|
+
"""Convert value to number, raising #VALUE! if not possible."""
|
|
47
|
+
if isinstance(value, (int, float, Decimal)):
|
|
48
|
+
return value
|
|
49
|
+
elif isinstance(value, bool):
|
|
50
|
+
return 1 if value else 0
|
|
51
|
+
elif isinstance(value, str):
|
|
52
|
+
try:
|
|
53
|
+
return float(value) if '.' in value else int(value)
|
|
54
|
+
except ValueError:
|
|
55
|
+
raise ValueErrorExcel()
|
|
56
|
+
else:
|
|
57
|
+
raise ValueErrorExcel()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def to_text(value: Value) -> str:
|
|
61
|
+
"""Convert value to text."""
|
|
62
|
+
if value is None:
|
|
63
|
+
return ""
|
|
64
|
+
elif isinstance(value, bool):
|
|
65
|
+
return "TRUE" if value else "FALSE"
|
|
66
|
+
else:
|
|
67
|
+
return str(value)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def to_boolean(value: Value) -> bool:
|
|
71
|
+
"""Convert value to boolean."""
|
|
72
|
+
if isinstance(value, bool):
|
|
73
|
+
return value
|
|
74
|
+
elif isinstance(value, (int, float, Decimal)):
|
|
75
|
+
return value != 0
|
|
76
|
+
elif isinstance(value, str):
|
|
77
|
+
upper = value.upper()
|
|
78
|
+
if upper == "TRUE":
|
|
79
|
+
return True
|
|
80
|
+
elif upper == "FALSE":
|
|
81
|
+
return False
|
|
82
|
+
else:
|
|
83
|
+
raise ValueErrorExcel()
|
|
84
|
+
else:
|
|
85
|
+
raise ValueErrorExcel()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# Mathematical Functions
|
|
89
|
+
def func_abs(value: Value) -> Number:
|
|
90
|
+
"""ABS function - absolute value."""
|
|
91
|
+
return abs(to_number(value))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def func_sum(*args: Value) -> Number:
|
|
95
|
+
"""SUM function - sum of values."""
|
|
96
|
+
total = 0
|
|
97
|
+
for arg in args:
|
|
98
|
+
if isinstance(arg, (list, tuple)):
|
|
99
|
+
total += func_sum(*arg)
|
|
100
|
+
else:
|
|
101
|
+
try:
|
|
102
|
+
total += to_number(arg)
|
|
103
|
+
except (ValueErrorExcel, TypeError):
|
|
104
|
+
continue # Skip non-numeric values
|
|
105
|
+
return total
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def func_average(*args: Value) -> Number:
|
|
109
|
+
"""AVERAGE function - average of values."""
|
|
110
|
+
values = []
|
|
111
|
+
for arg in args:
|
|
112
|
+
if isinstance(arg, (list, tuple)):
|
|
113
|
+
values.extend([v for v in arg if isinstance(v, (int, float, Decimal))])
|
|
114
|
+
else:
|
|
115
|
+
try:
|
|
116
|
+
values.append(to_number(arg))
|
|
117
|
+
except (ValueErrorExcel, TypeError):
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
if not values:
|
|
121
|
+
raise DivisionByZeroError()
|
|
122
|
+
|
|
123
|
+
return sum(values) / len(values)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def func_count(*args: Value) -> int:
|
|
127
|
+
"""COUNT function - count of numeric values."""
|
|
128
|
+
count = 0
|
|
129
|
+
for arg in args:
|
|
130
|
+
if isinstance(arg, (list, tuple)):
|
|
131
|
+
count += func_count(*arg)
|
|
132
|
+
else:
|
|
133
|
+
try:
|
|
134
|
+
to_number(arg)
|
|
135
|
+
count += 1
|
|
136
|
+
except (ValueErrorExcel, TypeError):
|
|
137
|
+
continue
|
|
138
|
+
return count
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def func_counta(*args: Value) -> int:
|
|
142
|
+
"""COUNTA function - count of non-empty values."""
|
|
143
|
+
count = 0
|
|
144
|
+
for arg in args:
|
|
145
|
+
if isinstance(arg, (list, tuple)):
|
|
146
|
+
count += func_counta(*arg)
|
|
147
|
+
else:
|
|
148
|
+
if arg is not None and arg != "":
|
|
149
|
+
count += 1
|
|
150
|
+
return count
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def func_max(*args: Value) -> Number:
|
|
154
|
+
"""MAX function - maximum value."""
|
|
155
|
+
values = []
|
|
156
|
+
for arg in args:
|
|
157
|
+
if isinstance(arg, (list, tuple)):
|
|
158
|
+
values.extend([v for v in arg if isinstance(v, (int, float, Decimal))])
|
|
159
|
+
else:
|
|
160
|
+
try:
|
|
161
|
+
values.append(to_number(arg))
|
|
162
|
+
except (ValueErrorExcel, TypeError):
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
if not values:
|
|
166
|
+
return 0
|
|
167
|
+
|
|
168
|
+
return max(values)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def func_min(*args: Value) -> Number:
|
|
172
|
+
"""MIN function - minimum value."""
|
|
173
|
+
values = []
|
|
174
|
+
for arg in args:
|
|
175
|
+
if isinstance(arg, (list, tuple)):
|
|
176
|
+
values.extend([v for v in arg if isinstance(v, (int, float, Decimal))])
|
|
177
|
+
else:
|
|
178
|
+
try:
|
|
179
|
+
values.append(to_number(arg))
|
|
180
|
+
except (ValueErrorExcel, TypeError):
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
if not values:
|
|
184
|
+
return 0
|
|
185
|
+
|
|
186
|
+
return min(values)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def func_round(number: Value, digits: Value = 0) -> Number:
|
|
190
|
+
"""ROUND function - round to specified digits."""
|
|
191
|
+
num = to_number(number)
|
|
192
|
+
dig = int(to_number(digits))
|
|
193
|
+
return round(num, dig)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def func_power(number: Value, power: Value) -> Number:
|
|
197
|
+
"""POWER function - raise to power."""
|
|
198
|
+
num = to_number(number)
|
|
199
|
+
pow_val = to_number(power)
|
|
200
|
+
return num ** pow_val
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def func_sqrt(number: Value) -> Number:
|
|
204
|
+
"""SQRT function - square root."""
|
|
205
|
+
num = to_number(number)
|
|
206
|
+
if num < 0:
|
|
207
|
+
raise NumError()
|
|
208
|
+
return math.sqrt(num)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def func_exp(number: Value) -> Number:
|
|
212
|
+
"""EXP function - e raised to power."""
|
|
213
|
+
num = to_number(number)
|
|
214
|
+
return math.exp(num)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def func_ln(number: Value) -> Number:
|
|
218
|
+
"""LN function - natural logarithm."""
|
|
219
|
+
num = to_number(number)
|
|
220
|
+
if num <= 0:
|
|
221
|
+
raise NumError()
|
|
222
|
+
return math.log(num)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def func_log10(number: Value) -> Number:
|
|
226
|
+
"""LOG10 function - base-10 logarithm."""
|
|
227
|
+
num = to_number(number)
|
|
228
|
+
if num <= 0:
|
|
229
|
+
raise NumError()
|
|
230
|
+
return math.log10(num)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
# Trigonometric Functions
|
|
234
|
+
def func_sin(number: Value) -> Number:
|
|
235
|
+
"""SIN function."""
|
|
236
|
+
return math.sin(to_number(number))
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def func_cos(number: Value) -> Number:
|
|
240
|
+
"""COS function."""
|
|
241
|
+
return math.cos(to_number(number))
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def func_tan(number: Value) -> Number:
|
|
245
|
+
"""TAN function."""
|
|
246
|
+
return math.tan(to_number(number))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def func_pi() -> Number:
|
|
250
|
+
"""PI function."""
|
|
251
|
+
return math.pi
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# Logical Functions
|
|
255
|
+
def func_if(condition: Value, true_value: Value, false_value: Value = False) -> Value:
|
|
256
|
+
"""IF function."""
|
|
257
|
+
try:
|
|
258
|
+
if to_boolean(condition):
|
|
259
|
+
return true_value
|
|
260
|
+
else:
|
|
261
|
+
return false_value
|
|
262
|
+
except ValueErrorExcel:
|
|
263
|
+
return false_value
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def func_and(*args: Value) -> bool:
|
|
267
|
+
"""AND function."""
|
|
268
|
+
for arg in args:
|
|
269
|
+
if not to_boolean(arg):
|
|
270
|
+
return False
|
|
271
|
+
return True
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def func_or(*args: Value) -> bool:
|
|
275
|
+
"""OR function."""
|
|
276
|
+
for arg in args:
|
|
277
|
+
if to_boolean(arg):
|
|
278
|
+
return True
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def func_not(value: Value) -> bool:
|
|
283
|
+
"""NOT function."""
|
|
284
|
+
return not to_boolean(value)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def func_true() -> bool:
|
|
288
|
+
"""TRUE function."""
|
|
289
|
+
return True
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def func_false() -> bool:
|
|
293
|
+
"""FALSE function."""
|
|
294
|
+
return False
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# Text Functions
|
|
298
|
+
def func_concatenate(*args: Value) -> str:
|
|
299
|
+
"""CONCATENATE function."""
|
|
300
|
+
return "".join(to_text(arg) for arg in args)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def func_len(text: Value) -> int:
|
|
304
|
+
"""LEN function - length of text."""
|
|
305
|
+
return len(to_text(text))
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def func_left(text: Value, num_chars: Value = 1) -> str:
|
|
309
|
+
"""LEFT function - leftmost characters."""
|
|
310
|
+
text_str = to_text(text)
|
|
311
|
+
num = int(to_number(num_chars))
|
|
312
|
+
return text_str[:num]
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def func_right(text: Value, num_chars: Value = 1) -> str:
|
|
316
|
+
"""RIGHT function - rightmost characters."""
|
|
317
|
+
text_str = to_text(text)
|
|
318
|
+
num = int(to_number(num_chars))
|
|
319
|
+
return text_str[-num:] if num > 0 else ""
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def func_mid(text: Value, start_pos: Value, num_chars: Value) -> str:
|
|
323
|
+
"""MID function - substring."""
|
|
324
|
+
text_str = to_text(text)
|
|
325
|
+
start = int(to_number(start_pos)) - 1 # Excel is 1-based
|
|
326
|
+
num = int(to_number(num_chars))
|
|
327
|
+
return text_str[start:start+num]
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def func_upper(text: Value) -> str:
|
|
331
|
+
"""UPPER function - convert to uppercase."""
|
|
332
|
+
return to_text(text).upper()
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def func_lower(text: Value) -> str:
|
|
336
|
+
"""LOWER function - convert to lowercase."""
|
|
337
|
+
return to_text(text).lower()
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def func_trim(text: Value) -> str:
|
|
341
|
+
"""TRIM function - remove extra spaces."""
|
|
342
|
+
return " ".join(to_text(text).split())
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# Date Functions
|
|
346
|
+
def func_today() -> datetime.date:
|
|
347
|
+
"""TODAY function - current date."""
|
|
348
|
+
return datetime.date.today()
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def func_now() -> datetime.datetime:
|
|
352
|
+
"""NOW function - current date and time."""
|
|
353
|
+
return datetime.datetime.now()
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def func_year(date_value: Value) -> int:
|
|
357
|
+
"""YEAR function - year from date."""
|
|
358
|
+
if isinstance(date_value, datetime.datetime):
|
|
359
|
+
return date_value.year
|
|
360
|
+
elif isinstance(date_value, datetime.date):
|
|
361
|
+
return date_value.year
|
|
362
|
+
else:
|
|
363
|
+
raise ValueErrorExcel()
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def func_month(date_value: Value) -> int:
|
|
367
|
+
"""MONTH function - month from date."""
|
|
368
|
+
if isinstance(date_value, datetime.datetime):
|
|
369
|
+
return date_value.month
|
|
370
|
+
elif isinstance(date_value, datetime.date):
|
|
371
|
+
return date_value.month
|
|
372
|
+
else:
|
|
373
|
+
raise ValueErrorExcel()
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def func_day(date_value: Value) -> int:
|
|
377
|
+
"""DAY function - day from date."""
|
|
378
|
+
if isinstance(date_value, datetime.datetime):
|
|
379
|
+
return date_value.day
|
|
380
|
+
elif isinstance(date_value, datetime.date):
|
|
381
|
+
return date_value.day
|
|
382
|
+
else:
|
|
383
|
+
raise ValueErrorExcel()
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
# Registry of all built-in functions
|
|
387
|
+
BUILTIN_FUNCTIONS: dict[str, Callable] = {
|
|
388
|
+
# Math functions
|
|
389
|
+
'ABS': func_abs,
|
|
390
|
+
'SUM': func_sum,
|
|
391
|
+
'AVERAGE': func_average,
|
|
392
|
+
'COUNT': func_count,
|
|
393
|
+
'COUNTA': func_counta,
|
|
394
|
+
'MAX': func_max,
|
|
395
|
+
'MIN': func_min,
|
|
396
|
+
'ROUND': func_round,
|
|
397
|
+
'POWER': func_power,
|
|
398
|
+
'SQRT': func_sqrt,
|
|
399
|
+
'EXP': func_exp,
|
|
400
|
+
'LN': func_ln,
|
|
401
|
+
'LOG10': func_log10,
|
|
402
|
+
|
|
403
|
+
# Trigonometric
|
|
404
|
+
'SIN': func_sin,
|
|
405
|
+
'COS': func_cos,
|
|
406
|
+
'TAN': func_tan,
|
|
407
|
+
'PI': func_pi,
|
|
408
|
+
|
|
409
|
+
# Logical
|
|
410
|
+
'IF': func_if,
|
|
411
|
+
'AND': func_and,
|
|
412
|
+
'OR': func_or,
|
|
413
|
+
'NOT': func_not,
|
|
414
|
+
'TRUE': func_true,
|
|
415
|
+
'FALSE': func_false,
|
|
416
|
+
|
|
417
|
+
# Text
|
|
418
|
+
'CONCATENATE': func_concatenate,
|
|
419
|
+
'LEN': func_len,
|
|
420
|
+
'LEFT': func_left,
|
|
421
|
+
'RIGHT': func_right,
|
|
422
|
+
'MID': func_mid,
|
|
423
|
+
'UPPER': func_upper,
|
|
424
|
+
'LOWER': func_lower,
|
|
425
|
+
'TRIM': func_trim,
|
|
426
|
+
|
|
427
|
+
# Date
|
|
428
|
+
'TODAY': func_today,
|
|
429
|
+
'NOW': func_now,
|
|
430
|
+
'YEAR': func_year,
|
|
431
|
+
'MONTH': func_month,
|
|
432
|
+
'DAY': func_day,
|
|
433
|
+
}
|