tdrpa.tdworker 1.2.13.2__py312-none-win_amd64.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.
Files changed (101) hide show
  1. tdrpa/_tdxlwings/__init__.py +193 -0
  2. tdrpa/_tdxlwings/__pycache__/__init__.cpython-311.pyc +0 -0
  3. tdrpa/_tdxlwings/__pycache__/__init__.cpython-38.pyc +0 -0
  4. tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-311.pyc +0 -0
  5. tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-38.pyc +0 -0
  6. tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-311.pyc +0 -0
  7. tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-38.pyc +0 -0
  8. tdrpa/_tdxlwings/__pycache__/apps.cpython-311.pyc +0 -0
  9. tdrpa/_tdxlwings/__pycache__/apps.cpython-38.pyc +0 -0
  10. tdrpa/_tdxlwings/__pycache__/base_classes.cpython-311.pyc +0 -0
  11. tdrpa/_tdxlwings/__pycache__/base_classes.cpython-38.pyc +0 -0
  12. tdrpa/_tdxlwings/__pycache__/com_server.cpython-311.pyc +0 -0
  13. tdrpa/_tdxlwings/__pycache__/com_server.cpython-38.pyc +0 -0
  14. tdrpa/_tdxlwings/__pycache__/constants.cpython-311.pyc +0 -0
  15. tdrpa/_tdxlwings/__pycache__/constants.cpython-38.pyc +0 -0
  16. tdrpa/_tdxlwings/__pycache__/expansion.cpython-311.pyc +0 -0
  17. tdrpa/_tdxlwings/__pycache__/expansion.cpython-38.pyc +0 -0
  18. tdrpa/_tdxlwings/__pycache__/main.cpython-311.pyc +0 -0
  19. tdrpa/_tdxlwings/__pycache__/main.cpython-38.pyc +0 -0
  20. tdrpa/_tdxlwings/__pycache__/udfs.cpython-311.pyc +0 -0
  21. tdrpa/_tdxlwings/__pycache__/udfs.cpython-38.pyc +0 -0
  22. tdrpa/_tdxlwings/__pycache__/utils.cpython-311.pyc +0 -0
  23. tdrpa/_tdxlwings/__pycache__/utils.cpython-38.pyc +0 -0
  24. tdrpa/_tdxlwings/_win32patch.py +90 -0
  25. tdrpa/_tdxlwings/_xlmac.py +2240 -0
  26. tdrpa/_tdxlwings/_xlwindows.py +2518 -0
  27. tdrpa/_tdxlwings/addin/Dictionary.cls +474 -0
  28. tdrpa/_tdxlwings/addin/IWebAuthenticator.cls +71 -0
  29. tdrpa/_tdxlwings/addin/WebClient.cls +772 -0
  30. tdrpa/_tdxlwings/addin/WebHelpers.bas +3203 -0
  31. tdrpa/_tdxlwings/addin/WebRequest.cls +875 -0
  32. tdrpa/_tdxlwings/addin/WebResponse.cls +453 -0
  33. tdrpa/_tdxlwings/addin/xlwings.xlam +0 -0
  34. tdrpa/_tdxlwings/apps.py +35 -0
  35. tdrpa/_tdxlwings/base_classes.py +1092 -0
  36. tdrpa/_tdxlwings/cli.py +1306 -0
  37. tdrpa/_tdxlwings/com_server.py +385 -0
  38. tdrpa/_tdxlwings/constants.py +3080 -0
  39. tdrpa/_tdxlwings/conversion/__init__.py +103 -0
  40. tdrpa/_tdxlwings/conversion/framework.py +147 -0
  41. tdrpa/_tdxlwings/conversion/numpy_conv.py +34 -0
  42. tdrpa/_tdxlwings/conversion/pandas_conv.py +184 -0
  43. tdrpa/_tdxlwings/conversion/standard.py +321 -0
  44. tdrpa/_tdxlwings/expansion.py +83 -0
  45. tdrpa/_tdxlwings/ext/__init__.py +3 -0
  46. tdrpa/_tdxlwings/ext/sql.py +73 -0
  47. tdrpa/_tdxlwings/html/xlwings-alert.html +71 -0
  48. tdrpa/_tdxlwings/js/xlwings.js +577 -0
  49. tdrpa/_tdxlwings/js/xlwings.ts +729 -0
  50. tdrpa/_tdxlwings/mac_dict.py +6399 -0
  51. tdrpa/_tdxlwings/main.py +5205 -0
  52. tdrpa/_tdxlwings/mistune/__init__.py +63 -0
  53. tdrpa/_tdxlwings/mistune/block_parser.py +366 -0
  54. tdrpa/_tdxlwings/mistune/inline_parser.py +216 -0
  55. tdrpa/_tdxlwings/mistune/markdown.py +84 -0
  56. tdrpa/_tdxlwings/mistune/renderers.py +220 -0
  57. tdrpa/_tdxlwings/mistune/scanner.py +121 -0
  58. tdrpa/_tdxlwings/mistune/util.py +41 -0
  59. tdrpa/_tdxlwings/pro/__init__.py +40 -0
  60. tdrpa/_tdxlwings/pro/_xlcalamine.py +536 -0
  61. tdrpa/_tdxlwings/pro/_xlofficejs.py +146 -0
  62. tdrpa/_tdxlwings/pro/_xlremote.py +1293 -0
  63. tdrpa/_tdxlwings/pro/custom_functions_code.js +150 -0
  64. tdrpa/_tdxlwings/pro/embedded_code.py +60 -0
  65. tdrpa/_tdxlwings/pro/udfs_officejs.py +549 -0
  66. tdrpa/_tdxlwings/pro/utils.py +199 -0
  67. tdrpa/_tdxlwings/quickstart.xlsm +0 -0
  68. tdrpa/_tdxlwings/quickstart_addin.xlam +0 -0
  69. tdrpa/_tdxlwings/quickstart_addin_ribbon.xlam +0 -0
  70. tdrpa/_tdxlwings/quickstart_fastapi/main.py +47 -0
  71. tdrpa/_tdxlwings/quickstart_fastapi/requirements.txt +3 -0
  72. tdrpa/_tdxlwings/quickstart_standalone.xlsm +0 -0
  73. tdrpa/_tdxlwings/reports.py +12 -0
  74. tdrpa/_tdxlwings/rest/__init__.py +1 -0
  75. tdrpa/_tdxlwings/rest/api.py +368 -0
  76. tdrpa/_tdxlwings/rest/serializers.py +103 -0
  77. tdrpa/_tdxlwings/server.py +14 -0
  78. tdrpa/_tdxlwings/udfs.py +775 -0
  79. tdrpa/_tdxlwings/utils.py +777 -0
  80. tdrpa/_tdxlwings/xlwings-0.31.6.applescript +30 -0
  81. tdrpa/_tdxlwings/xlwings.bas +2061 -0
  82. tdrpa/_tdxlwings/xlwings_custom_addin.bas +2042 -0
  83. tdrpa/_tdxlwings/xlwingslib.cp38-win_amd64.pyd +0 -0
  84. tdrpa/tdworker/__init__.pyi +12 -0
  85. tdrpa/tdworker/_clip.pyi +50 -0
  86. tdrpa/tdworker/_excel.pyi +743 -0
  87. tdrpa/tdworker/_file.pyi +77 -0
  88. tdrpa/tdworker/_img.pyi +226 -0
  89. tdrpa/tdworker/_network.pyi +94 -0
  90. tdrpa/tdworker/_os.pyi +47 -0
  91. tdrpa/tdworker/_sp.pyi +21 -0
  92. tdrpa/tdworker/_w.pyi +129 -0
  93. tdrpa/tdworker/_web.pyi +995 -0
  94. tdrpa/tdworker/_winE.pyi +228 -0
  95. tdrpa/tdworker/_winK.pyi +74 -0
  96. tdrpa/tdworker/_winM.pyi +117 -0
  97. tdrpa/tdworker.cp312-win_amd64.pyd +0 -0
  98. tdrpa_tdworker-1.2.13.2.dist-info/METADATA +38 -0
  99. tdrpa_tdworker-1.2.13.2.dist-info/RECORD +101 -0
  100. tdrpa_tdworker-1.2.13.2.dist-info/WHEEL +5 -0
  101. tdrpa_tdworker-1.2.13.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,536 @@
1
+ """
2
+ Required Notice: Copyright (C) Zoomer Analytics GmbH.
3
+
4
+ xlwings PRO is dual-licensed under one of the following licenses:
5
+
6
+ * PolyForm Noncommercial License 1.0.0 (for noncommercial use):
7
+ https://polyformproject.org/licenses/noncommercial/1.0.0
8
+ * xlwings PRO License (for commercial use):
9
+ https://github.com/xlwings/xlwings/blob/main/LICENSE_PRO.txt
10
+
11
+ Commercial licenses can be purchased at https://www.xlwings.org
12
+ """
13
+
14
+ import datetime as dt
15
+ import numbers
16
+ from pathlib import Path
17
+
18
+ try:
19
+ import numpy as np
20
+ except ImportError:
21
+ np = None
22
+ try:
23
+ import pandas as pd
24
+ except ImportError:
25
+ pd = None
26
+
27
+ from tdrpa._tdxlwings import xlwingslib
28
+
29
+ from .. import NoSuchObjectError, base_classes, utils
30
+
31
+ MAX_ROWS = 1_048_576
32
+ MAX_COLUMNS = 16_384
33
+
34
+
35
+ def _clean_value_data_element(value, datetime_builder, empty_as, number_builder):
36
+ if value is None:
37
+ return empty_as
38
+ elif isinstance(value, dt.datetime) and datetime_builder is not dt.datetime:
39
+ value = datetime_builder(
40
+ month=value.month,
41
+ day=value.day,
42
+ year=value.year,
43
+ hour=value.hour,
44
+ minute=value.minute,
45
+ second=value.second,
46
+ microsecond=value.microsecond,
47
+ tzinfo=None,
48
+ )
49
+ elif number_builder is not None and isinstance(value, float):
50
+ value = number_builder(value)
51
+ return value
52
+
53
+
54
+ class Engine:
55
+ def __init__(self):
56
+ self.apps = Apps()
57
+
58
+ @staticmethod
59
+ def clean_value_data(data, datetime_builder, empty_as, number_builder, err_to_str):
60
+ # err_to_str is handled in raw_values for efficiency
61
+ if empty_as or number_builder or datetime_builder is not dt.datetime:
62
+ return [
63
+ [
64
+ _clean_value_data_element(
65
+ c, datetime_builder, empty_as, number_builder
66
+ )
67
+ for c in row
68
+ ]
69
+ for row in data
70
+ ]
71
+ else:
72
+ return data
73
+
74
+ @staticmethod
75
+ def prepare_xl_data_element(x, options):
76
+ return x
77
+
78
+ @property
79
+ def name(self):
80
+ return "calamine"
81
+
82
+ @property
83
+ def type(self):
84
+ return "reader"
85
+
86
+
87
+ class Apps(base_classes.Apps):
88
+ def __init__(self):
89
+ self._apps = [App(self)]
90
+
91
+ def __iter__(self):
92
+ return iter(self._apps)
93
+
94
+ def __len__(self):
95
+ return len(self._apps)
96
+
97
+ def __getitem__(self, index):
98
+ return self._apps[index]
99
+
100
+ def add(self, **kwargs):
101
+ self._apps.insert(0, App(self, **kwargs))
102
+ return self._apps[0]
103
+
104
+
105
+ class App(base_classes.App):
106
+ _next_pid = -1
107
+
108
+ def __init__(self, apps, add_book=True, **kwargs):
109
+ self.apps = apps
110
+ self._pid = App._next_pid
111
+ App._next_pid -= 1
112
+ self._books = Books(self)
113
+ if add_book:
114
+ self._books.add()
115
+
116
+ def kill(self):
117
+ self.apps._apps.remove(self)
118
+ self.apps = None
119
+
120
+ @property
121
+ def engine(self):
122
+ return engine
123
+
124
+ @property
125
+ def books(self):
126
+ return self._books
127
+
128
+ @property
129
+ def pid(self):
130
+ return self._pid
131
+
132
+ @property
133
+ def visible(self):
134
+ return True
135
+
136
+ def activate(self, steal_focus=None):
137
+ pass
138
+
139
+
140
+ class Books(base_classes.Books):
141
+ def __init__(self, app):
142
+ self.app = app
143
+ self.books = []
144
+ self._active = None
145
+
146
+ @property
147
+ def active(self):
148
+ return self._active
149
+
150
+ def open(self, filename):
151
+ filename = str(Path(filename).resolve())
152
+ sheet_names = xlwingslib.get_sheet_names(filename)
153
+ names = []
154
+ for name, ref in xlwingslib.get_defined_names(filename):
155
+ if ref.split("!")[0].strip("'") in sheet_names:
156
+ names.append(
157
+ {
158
+ "name": name,
159
+ "sheet_index": sheet_names.index(ref.split("!")[0].strip("'")),
160
+ "address": ref.split("!")[1],
161
+ "book_scope": True, # TODO: not provided by calamine
162
+ }
163
+ )
164
+ book = Book(
165
+ api={
166
+ "sheet_names": sheet_names,
167
+ "names": names,
168
+ },
169
+ books=self,
170
+ path=filename,
171
+ )
172
+ self.books.append(book)
173
+ self._active = book
174
+ return book
175
+
176
+ def add(self):
177
+ book = Book(api={"sheet_names": ["Sheet1"]}, books=self, path="dummy")
178
+ self.books.append(book)
179
+ self._active = book
180
+ return book
181
+
182
+ def _try_find_book_by_name(self, name):
183
+ for book in self.books:
184
+ if book.name == name or book.fullname == name:
185
+ return book
186
+ return None
187
+
188
+ def __len__(self):
189
+ return len(self.books)
190
+
191
+ def __iter__(self):
192
+ for book in self.books:
193
+ yield book
194
+
195
+ def __call__(self, name_or_index):
196
+ if isinstance(name_or_index, numbers.Number):
197
+ return self.books[name_or_index - 1]
198
+ else:
199
+ book = self._try_find_book_by_name(name_or_index)
200
+ if book is None:
201
+ raise KeyError(name_or_index)
202
+ return book
203
+
204
+
205
+ class Book(base_classes.Book):
206
+ def __init__(self, api, books, path):
207
+ self._api = api
208
+ self.books = books
209
+ self.path = path
210
+
211
+ @property
212
+ def api(self):
213
+ return self._api
214
+
215
+ @property
216
+ def name(self):
217
+ return Path(self.fullname).name
218
+
219
+ @property
220
+ def fullname(self):
221
+ return self.path
222
+
223
+ @property
224
+ def names(self):
225
+ return Names(parent=self, api=self.api["names"])
226
+
227
+ @property
228
+ def sheets(self):
229
+ return Sheets(book=self)
230
+
231
+ @property
232
+ def app(self):
233
+ return self.books.app
234
+
235
+ def close(self):
236
+ assert self.api is not None, "Seems this book was already closed."
237
+ self.books.books.remove(self)
238
+ self.books = None
239
+ self._api = None
240
+
241
+ def activate(self):
242
+ pass
243
+
244
+
245
+ class Sheets(base_classes.Sheets):
246
+ def __init__(self, book):
247
+ self.book = book
248
+
249
+ @property
250
+ def api(self):
251
+ return None
252
+
253
+ def __call__(self, name_or_index):
254
+ if isinstance(name_or_index, str):
255
+ sheet_names = self.book.api["sheet_names"]
256
+ if name_or_index not in sheet_names:
257
+ raise NoSuchObjectError(f"Sheet {name_or_index} doesn't exist.")
258
+ else:
259
+ ix = self.book.api["sheet_names"].index(name_or_index) + 1
260
+ else:
261
+ ix = name_or_index
262
+
263
+ return Sheet(book=self.book, sheet_index=ix)
264
+
265
+ def __len__(self):
266
+ return len(self.book.api["sheet_names"])
267
+
268
+ def __iter__(self):
269
+ for ix, sheet in enumerate(self.book.api["sheet_names"]):
270
+ yield Sheet(book=self.book, sheet_index=ix + 1)
271
+
272
+
273
+ class Sheet(base_classes.Sheet):
274
+ def __init__(self, book, sheet_index):
275
+ self._api = {} # used by e.g., Range.end()
276
+ self._book = book
277
+ self.sheet_index = sheet_index
278
+
279
+ @property
280
+ def api(self):
281
+ return self._api
282
+
283
+ @property
284
+ def name(self):
285
+ return self.book.api["sheet_names"][self.index - 1]
286
+
287
+ @property
288
+ def index(self):
289
+ return self.sheet_index
290
+
291
+ @property
292
+ def book(self):
293
+ return self._book
294
+
295
+ def range(self, arg1, arg2=None):
296
+ return Range(sheet=self, book=self.book, arg1=arg1, arg2=arg2)
297
+
298
+ @property
299
+ def cells(self):
300
+ return Range(
301
+ sheet=self,
302
+ book=self.book,
303
+ arg1=(1, 1),
304
+ arg2=(MAX_ROWS, MAX_COLUMNS),
305
+ )
306
+
307
+
308
+ class Range(base_classes.Range):
309
+ def __init__(self, sheet, book, arg1, arg2=None):
310
+ self.sheet = sheet
311
+ self.book = book
312
+ self.options = None # Assigned by main.Range to keep API of sheet.range clean
313
+
314
+ # Range
315
+ if isinstance(arg1, Range) and isinstance(arg2, Range):
316
+ cell1 = arg1.coords[1], arg1.coords[2]
317
+ cell2 = arg2.coords[1], arg2.coords[2]
318
+ arg1 = min(cell1[0], cell2[0]), min(cell1[1], cell2[1])
319
+ arg2 = max(cell1[0], cell2[0]), max(cell1[1], cell2[1])
320
+ # A1 notation
321
+ if isinstance(arg1, str):
322
+ # A1 notation
323
+ tuple1, tuple2 = utils.a1_to_tuples(arg1)
324
+ if not tuple1:
325
+ # Named range
326
+ for api_name in sheet.book.api["names"]:
327
+ if (
328
+ api_name["name"].split("!")[-1] == arg1
329
+ and api_name["sheet_index"] == sheet.index - 1
330
+ ):
331
+ tuple1, tuple2 = utils.a1_to_tuples(api_name["address"])
332
+ if not tuple1:
333
+ raise NoSuchObjectError(
334
+ f"The address/named range '{arg1}' doesn't exist."
335
+ )
336
+ arg1, arg2 = tuple1, tuple2
337
+
338
+ # Coordinates
339
+ if len(arg1) == 4:
340
+ row, col, nrows, ncols = arg1
341
+ arg1 = (row, col)
342
+ if nrows > 1 or ncols > 1:
343
+ arg2 = (row + nrows - 1, col + ncols - 1)
344
+
345
+ self.arg1 = arg1 # 1-based tuple
346
+ self.arg2 = arg2 # 1-based tuple
347
+
348
+ @property
349
+ def api(self):
350
+ return self.raw_value
351
+
352
+ @property
353
+ def coords(self):
354
+ return self.sheet.name, self.row, self.column, len(self.api), len(self.api[0])
355
+
356
+ @property
357
+ def row(self):
358
+ return self.arg1[0]
359
+
360
+ @property
361
+ def column(self):
362
+ return self.arg1[1]
363
+
364
+ @property
365
+ def shape(self):
366
+ if self.arg2:
367
+ return self.arg2[0] - self.arg1[0] + 1, self.arg2[1] - self.arg1[1] + 1
368
+ else:
369
+ return 1, 1
370
+
371
+ @property
372
+ def raw_value(self):
373
+ err_to_str = self.options.get("err_to_str", False)
374
+ if self.arg2 is None:
375
+ self.arg2 = self.arg1
376
+ if self.arg2[0] == MAX_ROWS and self.arg2[1] == MAX_COLUMNS:
377
+ # Whole sheet via sheet.cells
378
+ if not self.sheet.api.get(f"values_err_to_str_{err_to_str}"):
379
+ values = xlwingslib.get_sheet_values(
380
+ self.book.fullname, self.sheet.index - 1, err_to_str
381
+ )
382
+ self.sheet._api[f"values_err_to_str_{err_to_str}"] = values
383
+ return values
384
+ else:
385
+ # Specific range
386
+ return xlwingslib.get_range_values(
387
+ self.book.fullname,
388
+ self.sheet.index - 1,
389
+ (self.arg1[0] - 1, self.arg1[1] - 1),
390
+ (self.arg2[0] - 1, self.arg2[1] - 1),
391
+ err_to_str,
392
+ )
393
+
394
+ @property
395
+ def address(self):
396
+ nrows, ncols = self.shape
397
+ address = f"${utils.col_name(self.column)}${self.row}"
398
+ if nrows == 1 and ncols == 1:
399
+ return address
400
+ else:
401
+ return (
402
+ f"{address}"
403
+ f":${utils.col_name(self.column + ncols - 1)}${self.row + nrows - 1}"
404
+ )
405
+
406
+ @property
407
+ def has_array(self):
408
+ # Not supported, but since this is only used for legacy CSE arrays, probably
409
+ # not much of an issue. Here as there's currently a dependency in expansion.py.
410
+ return None
411
+
412
+ def end(self, direction):
413
+ err_to_str = self.options.get("err_to_str", False)
414
+ if not self.sheet.api.get(f"values_err_to_str_{err_to_str}"):
415
+ values = xlwingslib.get_sheet_values(
416
+ self.book.fullname, self.sheet.index - 1, err_to_str
417
+ )
418
+ self.sheet._api[f"values_err_to_str_{err_to_str}"] = values
419
+ else:
420
+ values = self.sheet.api[f"values_err_to_str_{err_to_str}"]
421
+ if direction == "down":
422
+ i = 1
423
+ while True:
424
+ try:
425
+ if values[self.row - 1 + i][self.column - 1]:
426
+ i += 1
427
+ else:
428
+ break
429
+ except IndexError:
430
+ break # outside used range
431
+ nrows = i - 1
432
+ return self.sheet.range((self.row + nrows, self.column))
433
+ if direction == "up":
434
+ i = -1
435
+ while True:
436
+ row_ix = self.row - 1 + i
437
+ if row_ix >= 0 and values[row_ix][self.column - 1]:
438
+ i -= 1
439
+ else:
440
+ break
441
+ nrows = i + 1
442
+ return self.sheet.range((self.row + nrows, self.column))
443
+ if direction == "right":
444
+ i = 1
445
+ while True:
446
+ try:
447
+ if values[self.row - 1][self.column - 1 + i]:
448
+ i += 1
449
+ else:
450
+ break
451
+ except IndexError:
452
+ break # outside used range
453
+ ncols = i - 1
454
+ return self.sheet.range((self.row, self.column + ncols))
455
+ if direction == "left":
456
+ i = -1
457
+ while True:
458
+ col_ix = self.column - 1 + i
459
+ if col_ix >= 0 and values[self.row - 1][col_ix]:
460
+ i -= 1
461
+ else:
462
+ break
463
+ ncols = i + 1
464
+ return self.sheet.range((self.row, self.column + ncols))
465
+
466
+ def __len__(self):
467
+ nrows, ncols = self.shape
468
+ return nrows * ncols
469
+
470
+ def __call__(self, arg1, arg2=None):
471
+ if arg2 is None:
472
+ col = (arg1 - 1) % self.shape[1]
473
+ row = int((arg1 - 1 - col) / self.shape[1])
474
+ return self(row + 1, col + 1)
475
+ else:
476
+ return Range(
477
+ sheet=self.sheet,
478
+ book=self.book,
479
+ arg1=(self.row + arg1 - 1, self.column + arg2 - 1),
480
+ )
481
+
482
+
483
+ class Name(base_classes.Name):
484
+ def __init__(self, parent, api):
485
+ self.parent = parent # only implemented for Book, not Sheet
486
+ self.api = api
487
+
488
+ @property
489
+ def name(self):
490
+ return self.api["name"]
491
+
492
+ @property
493
+ def refers_to(self):
494
+ sheet_name = self.parent.sheets(self.api["sheet_index"] + 1).name
495
+ sheet_name = f"'{sheet_name}'" if " " in sheet_name else sheet_name
496
+ return f"={sheet_name}!{self.api['address']}"
497
+
498
+ @property
499
+ def refers_to_range(self):
500
+ return self.parent.sheets(self.api["sheet_index"] + 1).range(
501
+ self.api["address"]
502
+ )
503
+
504
+
505
+ class Names(base_classes.Names):
506
+ def __init__(self, parent, api):
507
+ self.parent = parent # only implemented for Book, not Sheet
508
+ self.api = api
509
+
510
+ def __call__(self, name_or_index):
511
+ if isinstance(name_or_index, numbers.Number):
512
+ name_or_index -= 1
513
+ if name_or_index > len(self):
514
+ raise KeyError(name_or_index)
515
+ else:
516
+ return Name(self.parent, api=self.api[name_or_index])
517
+ else:
518
+ for ix, i in enumerate(self.api):
519
+ if i["name"] == name_or_index:
520
+ return Name(self.parent, api=self.api[ix])
521
+ raise KeyError(name_or_index)
522
+
523
+ def contains(self, name_or_index):
524
+ if isinstance(name_or_index, numbers.Number):
525
+ return 1 <= name_or_index <= len(self)
526
+ else:
527
+ for i in self.api:
528
+ if i["name"] == name_or_index:
529
+ return True
530
+ return False
531
+
532
+ def __len__(self):
533
+ return len(self.api)
534
+
535
+
536
+ engine = Engine()
@@ -0,0 +1,146 @@
1
+ """
2
+ Required Notice: Copyright (C) Zoomer Analytics GmbH.
3
+
4
+ xlwings PRO is dual-licensed under one of the following licenses:
5
+
6
+ * PolyForm Noncommercial License 1.0.0 (for noncommercial use):
7
+ https://polyformproject.org/licenses/noncommercial/1.0.0
8
+ * xlwings PRO License (for commercial use):
9
+ https://github.com/xlwings/xlwings/blob/main/LICENSE_PRO.txt
10
+
11
+ Commercial licenses can be purchased at https://www.xlwings.org
12
+ """
13
+
14
+ """
15
+ This engine is only used in connection with Office.js UDFs, not with runPython.
16
+ """
17
+
18
+ import datetime as dt
19
+
20
+ try:
21
+ import numpy as np
22
+ except ImportError:
23
+ np = None
24
+ try:
25
+ import pandas as pd
26
+ except ImportError:
27
+ pd = None
28
+
29
+ from .. import utils
30
+
31
+
32
+ def datetime_to_formatted_number(datetime_object, date_format, runtime):
33
+ # https://learn.microsoft.com/en-us/javascript/api/requirement-sets/excel/custom-functions-requirement-sets
34
+ serial = utils.datetime_to_xlserial(datetime_object)
35
+ if float(runtime) < 1.4 or date_format is None:
36
+ return serial
37
+ else:
38
+ return {
39
+ "type": "FormattedNumber",
40
+ "basicValue": serial,
41
+ "numberFormat": date_format,
42
+ }
43
+
44
+
45
+ def errorstr_to_errortype(error, runtime):
46
+ if float(runtime) < 1.4:
47
+ return error
48
+ error_to_type = {
49
+ "#DIV/0!": "Div0",
50
+ "#N/A": "NotAvailable",
51
+ "#NAME?": "Name",
52
+ "#NULL!": "Null",
53
+ "#NUM!": "Num",
54
+ "#REF!": "Ref",
55
+ "#VALUE!": "Value",
56
+ }
57
+ error_type = error_to_type.get(error)
58
+ if not error_type:
59
+ return error
60
+ else:
61
+ return {
62
+ "type": "Error",
63
+ "errorType": error_type,
64
+ }
65
+
66
+
67
+ def _clean_value_data_element(
68
+ value,
69
+ datetime_builder,
70
+ empty_as,
71
+ number_builder,
72
+ err_to_str,
73
+ ):
74
+ # datetime_builder is not supported as normal date-formatted cells aren't recognized
75
+ if value == "":
76
+ return empty_as
77
+ elif isinstance(value, dict):
78
+ # https://learn.microsoft.com/en-us/office/dev/add-ins/excel/custom-functions-data-types-concepts
79
+ if value["type"] == "Error":
80
+ if err_to_str:
81
+ return value["basicValue"]
82
+ else:
83
+ return None
84
+ else:
85
+ value = value["basicValue"] # e.g., datetime (only via data types)
86
+ elif number_builder is not None and isinstance(value, float):
87
+ value = number_builder(value)
88
+ return value
89
+
90
+
91
+ class Engine:
92
+ @staticmethod
93
+ def clean_value_data(data, datetime_builder, empty_as, number_builder, err_to_str):
94
+ return [
95
+ [
96
+ _clean_value_data_element(
97
+ c, datetime_builder, empty_as, number_builder, err_to_str
98
+ )
99
+ for c in row
100
+ ]
101
+ for row in data
102
+ ]
103
+
104
+ @staticmethod
105
+ def prepare_xl_data_element(x, options):
106
+ if x is None:
107
+ return ""
108
+ elif pd and pd.isna(x):
109
+ return errorstr_to_errortype("#NUM!", options["runtime"])
110
+ elif np and isinstance(x, (np.floating, float)) and np.isnan(x):
111
+ return errorstr_to_errortype("#NUM!", options["runtime"])
112
+ elif np and isinstance(x, np.number):
113
+ return float(x)
114
+ elif np and isinstance(x, np.datetime64):
115
+ return datetime_to_formatted_number(
116
+ utils.np_datetime_to_datetime(x),
117
+ options["date_format"],
118
+ options["runtime"],
119
+ )
120
+ elif pd and isinstance(x, pd.Timestamp):
121
+ return datetime_to_formatted_number(
122
+ x.to_pydatetime(),
123
+ options["date_format"],
124
+ options["runtime"],
125
+ )
126
+ elif pd and isinstance(x, type(pd.NaT)):
127
+ # This seems to be caught by pd.isna() nowadays?
128
+ return ""
129
+ elif isinstance(x, (dt.date, dt.datetime)):
130
+ return datetime_to_formatted_number(
131
+ x, options["date_format"], options["runtime"]
132
+ )
133
+ elif isinstance(x, str) and x.startswith("#"):
134
+ return errorstr_to_errortype(x, options["runtime"])
135
+ return x
136
+
137
+ @property
138
+ def name(self):
139
+ return "officejs"
140
+
141
+ @property
142
+ def type(self):
143
+ return "remote"
144
+
145
+
146
+ engine = Engine()