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.
- tdrpa/_tdxlwings/__init__.py +193 -0
- tdrpa/_tdxlwings/__pycache__/__init__.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/__init__.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/apps.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/apps.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/base_classes.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/base_classes.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/com_server.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/com_server.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/constants.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/constants.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/expansion.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/expansion.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/main.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/main.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/udfs.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/udfs.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/utils.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/utils.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/_win32patch.py +90 -0
- tdrpa/_tdxlwings/_xlmac.py +2240 -0
- tdrpa/_tdxlwings/_xlwindows.py +2518 -0
- tdrpa/_tdxlwings/addin/Dictionary.cls +474 -0
- tdrpa/_tdxlwings/addin/IWebAuthenticator.cls +71 -0
- tdrpa/_tdxlwings/addin/WebClient.cls +772 -0
- tdrpa/_tdxlwings/addin/WebHelpers.bas +3203 -0
- tdrpa/_tdxlwings/addin/WebRequest.cls +875 -0
- tdrpa/_tdxlwings/addin/WebResponse.cls +453 -0
- tdrpa/_tdxlwings/addin/xlwings.xlam +0 -0
- tdrpa/_tdxlwings/apps.py +35 -0
- tdrpa/_tdxlwings/base_classes.py +1092 -0
- tdrpa/_tdxlwings/cli.py +1306 -0
- tdrpa/_tdxlwings/com_server.py +385 -0
- tdrpa/_tdxlwings/constants.py +3080 -0
- tdrpa/_tdxlwings/conversion/__init__.py +103 -0
- tdrpa/_tdxlwings/conversion/framework.py +147 -0
- tdrpa/_tdxlwings/conversion/numpy_conv.py +34 -0
- tdrpa/_tdxlwings/conversion/pandas_conv.py +184 -0
- tdrpa/_tdxlwings/conversion/standard.py +321 -0
- tdrpa/_tdxlwings/expansion.py +83 -0
- tdrpa/_tdxlwings/ext/__init__.py +3 -0
- tdrpa/_tdxlwings/ext/sql.py +73 -0
- tdrpa/_tdxlwings/html/xlwings-alert.html +71 -0
- tdrpa/_tdxlwings/js/xlwings.js +577 -0
- tdrpa/_tdxlwings/js/xlwings.ts +729 -0
- tdrpa/_tdxlwings/mac_dict.py +6399 -0
- tdrpa/_tdxlwings/main.py +5205 -0
- tdrpa/_tdxlwings/mistune/__init__.py +63 -0
- tdrpa/_tdxlwings/mistune/block_parser.py +366 -0
- tdrpa/_tdxlwings/mistune/inline_parser.py +216 -0
- tdrpa/_tdxlwings/mistune/markdown.py +84 -0
- tdrpa/_tdxlwings/mistune/renderers.py +220 -0
- tdrpa/_tdxlwings/mistune/scanner.py +121 -0
- tdrpa/_tdxlwings/mistune/util.py +41 -0
- tdrpa/_tdxlwings/pro/__init__.py +40 -0
- tdrpa/_tdxlwings/pro/_xlcalamine.py +536 -0
- tdrpa/_tdxlwings/pro/_xlofficejs.py +146 -0
- tdrpa/_tdxlwings/pro/_xlremote.py +1293 -0
- tdrpa/_tdxlwings/pro/custom_functions_code.js +150 -0
- tdrpa/_tdxlwings/pro/embedded_code.py +60 -0
- tdrpa/_tdxlwings/pro/udfs_officejs.py +549 -0
- tdrpa/_tdxlwings/pro/utils.py +199 -0
- tdrpa/_tdxlwings/quickstart.xlsm +0 -0
- tdrpa/_tdxlwings/quickstart_addin.xlam +0 -0
- tdrpa/_tdxlwings/quickstart_addin_ribbon.xlam +0 -0
- tdrpa/_tdxlwings/quickstart_fastapi/main.py +47 -0
- tdrpa/_tdxlwings/quickstart_fastapi/requirements.txt +3 -0
- tdrpa/_tdxlwings/quickstart_standalone.xlsm +0 -0
- tdrpa/_tdxlwings/reports.py +12 -0
- tdrpa/_tdxlwings/rest/__init__.py +1 -0
- tdrpa/_tdxlwings/rest/api.py +368 -0
- tdrpa/_tdxlwings/rest/serializers.py +103 -0
- tdrpa/_tdxlwings/server.py +14 -0
- tdrpa/_tdxlwings/udfs.py +775 -0
- tdrpa/_tdxlwings/utils.py +777 -0
- tdrpa/_tdxlwings/xlwings-0.31.6.applescript +30 -0
- tdrpa/_tdxlwings/xlwings.bas +2061 -0
- tdrpa/_tdxlwings/xlwings_custom_addin.bas +2042 -0
- tdrpa/_tdxlwings/xlwingslib.cp38-win_amd64.pyd +0 -0
- tdrpa/tdworker/__init__.pyi +12 -0
- tdrpa/tdworker/_clip.pyi +50 -0
- tdrpa/tdworker/_excel.pyi +743 -0
- tdrpa/tdworker/_file.pyi +77 -0
- tdrpa/tdworker/_img.pyi +226 -0
- tdrpa/tdworker/_network.pyi +94 -0
- tdrpa/tdworker/_os.pyi +47 -0
- tdrpa/tdworker/_sp.pyi +21 -0
- tdrpa/tdworker/_w.pyi +129 -0
- tdrpa/tdworker/_web.pyi +995 -0
- tdrpa/tdworker/_winE.pyi +228 -0
- tdrpa/tdworker/_winK.pyi +74 -0
- tdrpa/tdworker/_winM.pyi +117 -0
- tdrpa/tdworker.cp312-win_amd64.pyd +0 -0
- tdrpa_tdworker-1.2.13.2.dist-info/METADATA +38 -0
- tdrpa_tdworker-1.2.13.2.dist-info/RECORD +101 -0
- tdrpa_tdworker-1.2.13.2.dist-info/WHEEL +5 -0
- 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()
|