xlwings-utils 25.0.6.post3__py3-none-any.whl → 25.0.6.post5__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 xlwings-utils might be problematic. Click here for more details.
- xlwings_utils/xlwings_utils.py +254 -63
- {xlwings_utils-25.0.6.post3.dist-info → xlwings_utils-25.0.6.post5.dist-info}/METADATA +1 -1
- xlwings_utils-25.0.6.post5.dist-info/RECORD +6 -0
- xlwings_utils-25.0.6.post3.dist-info/RECORD +0 -6
- {xlwings_utils-25.0.6.post3.dist-info → xlwings_utils-25.0.6.post5.dist-info}/WHEEL +0 -0
- {xlwings_utils-25.0.6.post3.dist-info → xlwings_utils-25.0.6.post5.dist-info}/top_level.txt +0 -0
xlwings_utils/xlwings_utils.py
CHANGED
|
@@ -268,7 +268,6 @@ def read_local(path):
|
|
|
268
268
|
contents = f.read()
|
|
269
269
|
return contents
|
|
270
270
|
|
|
271
|
-
|
|
272
271
|
class block:
|
|
273
272
|
"""
|
|
274
273
|
block is 2 dimensional data structure with 1 as lowest index (like xlwings range)
|
|
@@ -276,98 +275,239 @@ class block:
|
|
|
276
275
|
Parameters
|
|
277
276
|
----------
|
|
278
277
|
number_of_rows : int
|
|
279
|
-
number of rows
|
|
278
|
+
number of rows (dedault 1)
|
|
280
279
|
|
|
281
280
|
number_of_columns : int
|
|
282
|
-
number of columns
|
|
281
|
+
number of columns (default 1)
|
|
283
282
|
|
|
284
283
|
Returns
|
|
285
284
|
-------
|
|
286
285
|
block
|
|
287
286
|
"""
|
|
288
287
|
|
|
289
|
-
def __init__(self,
|
|
288
|
+
def __init__(self, number_of_rows=1, number_of_columns=1):
|
|
290
289
|
self.dict = {}
|
|
291
|
-
self.
|
|
292
|
-
|
|
293
|
-
if number_of_rows is missing:
|
|
294
|
-
number_of_rows = 1
|
|
295
|
-
if number_of_columns is missing:
|
|
296
|
-
number_of_columns = 1
|
|
297
|
-
self.number_of_rows = number_of_rows
|
|
298
|
-
self.number_of_columns = number_of_columns
|
|
290
|
+
self.number_of_rows = number_of_rows
|
|
291
|
+
self.number_of_columns = number_of_columns
|
|
299
292
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
293
|
+
@classmethod
|
|
294
|
+
def from_value(
|
|
295
|
+
cls, value, column_like=False, number_of_rows=missing, number_of_columns=missing
|
|
296
|
+
):
|
|
297
|
+
"""
|
|
298
|
+
makes a block from a given value
|
|
299
|
+
|
|
300
|
+
Parameters
|
|
301
|
+
----------
|
|
302
|
+
value : scaler, list of scalars, list of lists of scalars or block
|
|
303
|
+
value to be used in block, possibly expanded to a list of lists of scalars
|
|
304
|
+
|
|
305
|
+
column_like : boolean
|
|
306
|
+
if value is a list of scalars, values is interpreted as a column if True, as a row otherwise
|
|
307
|
+
|
|
308
|
+
number_of_rows : int
|
|
309
|
+
if given, expand or shrink to the given number of rows
|
|
310
|
+
|
|
311
|
+
number_of_columns : int
|
|
312
|
+
if given, expand or shrink to the given number of columns
|
|
313
|
+
|
|
314
|
+
Returns
|
|
315
|
+
-------
|
|
316
|
+
block : block
|
|
317
|
+
"""
|
|
318
|
+
if isinstance(value, block):
|
|
319
|
+
value = value.value
|
|
320
|
+
if not isinstance(value, list):
|
|
321
|
+
value = [[value]]
|
|
322
|
+
if not isinstance(value[0], list):
|
|
323
|
+
if column_like:
|
|
324
|
+
value = [[item] for item in value]
|
|
325
|
+
else:
|
|
326
|
+
value = [value]
|
|
327
|
+
bl = cls(len(value), 1)
|
|
328
|
+
|
|
329
|
+
for row, row_contents in enumerate(value, 1):
|
|
330
|
+
for column, item in enumerate(row_contents, 1):
|
|
331
|
+
if item and not (isinstance(item, float) and math.isnan(item)):
|
|
332
|
+
bl.dict[row, column] = item
|
|
333
|
+
bl._number_of_columns = max(bl.number_of_columns, column)
|
|
334
|
+
if number_of_rows is not missing:
|
|
335
|
+
bl.number_of_rows = number_of_rows
|
|
336
|
+
if number_of_columns is not missing:
|
|
337
|
+
bl.number_of_columns = number_of_columns
|
|
338
|
+
return bl
|
|
339
|
+
|
|
340
|
+
@classmethod
|
|
341
|
+
def from_range(cls, rng, number_of_rows=missing, number_of_columns=missing):
|
|
342
|
+
"""
|
|
343
|
+
makes a block from a given range
|
|
344
|
+
|
|
345
|
+
Parameters
|
|
346
|
+
----------
|
|
347
|
+
rng : xlwings.Range
|
|
348
|
+
range to be used be used in block
|
|
349
|
+
|
|
350
|
+
number_of_rows : int
|
|
351
|
+
if given, expand or shrink to the given number of rows
|
|
352
|
+
|
|
353
|
+
number_of_columns : int
|
|
354
|
+
if given, expand or shrink to the given number of columns
|
|
355
|
+
|
|
356
|
+
Returns
|
|
357
|
+
-------
|
|
358
|
+
block : block
|
|
359
|
+
"""
|
|
360
|
+
number_of_rows, number_of_columns = rng.shape
|
|
361
|
+
bl = cls.from_value(
|
|
362
|
+
rng.value,
|
|
363
|
+
column_like=(number_of_columns == 1),
|
|
364
|
+
number_of_rows=number_of_rows,
|
|
365
|
+
number_of_columns=number_of_columns,
|
|
366
|
+
)
|
|
367
|
+
return bl
|
|
308
368
|
|
|
309
369
|
@classmethod
|
|
310
370
|
def from_xlrd_sheet(cls, sheet, number_of_rows=missing, number_of_columns=missing):
|
|
311
|
-
|
|
312
|
-
|
|
371
|
+
"""
|
|
372
|
+
makes a block from a xlrd sheet
|
|
373
|
+
|
|
374
|
+
Parameters
|
|
375
|
+
----------
|
|
376
|
+
sheet : xlrd sheet
|
|
377
|
+
sheet to be used be used in block
|
|
378
|
+
|
|
379
|
+
number_of_rows : int
|
|
380
|
+
if given, expand or shrink to the given number of rows
|
|
381
|
+
|
|
382
|
+
number_of_columns : int
|
|
383
|
+
if given, expand or shrink to the given number of columns
|
|
384
|
+
|
|
385
|
+
Returns
|
|
386
|
+
-------
|
|
387
|
+
block : block
|
|
388
|
+
"""
|
|
389
|
+
v = [
|
|
390
|
+
sheet.row_values(row_idx)[0 : sheet.ncols]
|
|
391
|
+
for row_idx in range(0, sheet.nrows)
|
|
392
|
+
]
|
|
393
|
+
return cls.from_value(
|
|
394
|
+
v, number_of_rows=number_of_rows, number_of_columns=number_of_columns
|
|
395
|
+
)
|
|
313
396
|
|
|
314
397
|
@classmethod
|
|
315
|
-
def from_openpyxl_sheet(
|
|
398
|
+
def from_openpyxl_sheet(
|
|
399
|
+
cls, sheet, number_of_rows=missing, number_of_columns=missing
|
|
400
|
+
):
|
|
316
401
|
v = [[cell.value for cell in row] for row in sheet.iter_rows()]
|
|
317
|
-
return cls(
|
|
402
|
+
return cls.from_value(
|
|
403
|
+
v, number_of_rows=number_of_rows, number_of_columns=number_of_columns
|
|
404
|
+
)
|
|
318
405
|
|
|
319
406
|
@classmethod
|
|
320
407
|
def from_file(cls, filename, number_of_rows=missing, number_of_columns=missing):
|
|
408
|
+
"""
|
|
409
|
+
makes a block from a file
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
filename : str
|
|
414
|
+
file to be used be used in block
|
|
415
|
+
|
|
416
|
+
number_of_rows : int
|
|
417
|
+
if given, expand or shrink to the given number of rows
|
|
418
|
+
|
|
419
|
+
number_of_columns : int
|
|
420
|
+
if given, expand or shrink to the given number of columns
|
|
421
|
+
|
|
422
|
+
Returns
|
|
423
|
+
-------
|
|
424
|
+
block : block
|
|
425
|
+
"""
|
|
321
426
|
with open(filename, "r") as f:
|
|
322
427
|
v = [[line if line else missing] for line in f.read().splitlines()]
|
|
323
|
-
return cls(
|
|
428
|
+
return cls.from_value(
|
|
429
|
+
v, number_of_rows=number_of_rows, number_of_columns=number_of_columns
|
|
430
|
+
)
|
|
324
431
|
|
|
325
432
|
@classmethod
|
|
326
433
|
def from_dataframe(cls, df, number_of_rows=missing, number_of_columns=missing):
|
|
434
|
+
"""
|
|
435
|
+
makes a block from a given dataframe
|
|
436
|
+
|
|
437
|
+
Parameters
|
|
438
|
+
----------
|
|
439
|
+
df : pandas dataframe
|
|
440
|
+
dataframe to be used be used in block
|
|
441
|
+
|
|
442
|
+
number_of_rows : int
|
|
443
|
+
if given, expand or shrink to the given number of rows
|
|
444
|
+
|
|
445
|
+
number_of_columns : int
|
|
446
|
+
if given, expand or shrink to the given number of columns
|
|
447
|
+
|
|
448
|
+
Returns
|
|
449
|
+
-------
|
|
450
|
+
block : block
|
|
451
|
+
"""
|
|
327
452
|
v = df.values.tolist()
|
|
328
|
-
return cls(
|
|
453
|
+
return cls.from_value(
|
|
454
|
+
v, number_of_rows=number_of_rows, number_of_columns=number_of_columns
|
|
455
|
+
)
|
|
329
456
|
|
|
330
457
|
def to_openpyxl_sheet(self, sheet):
|
|
458
|
+
"""
|
|
459
|
+
makes a block from a given openpyxl sheet
|
|
460
|
+
|
|
461
|
+
Parameters
|
|
462
|
+
----------
|
|
463
|
+
sheet: openpyxl sheet
|
|
464
|
+
sheet to be used be used in block
|
|
465
|
+
|
|
466
|
+
number_of_rows : int
|
|
467
|
+
if given, expand or shrink to the given number of rows
|
|
468
|
+
|
|
469
|
+
number_of_columns : int
|
|
470
|
+
if given, expand or shrink to the given number of columns
|
|
471
|
+
|
|
472
|
+
Returns
|
|
473
|
+
-------
|
|
474
|
+
block : block
|
|
475
|
+
"""
|
|
331
476
|
for row in self.value:
|
|
332
477
|
sheet.append(row)
|
|
333
478
|
|
|
334
479
|
@property
|
|
335
480
|
def value(self):
|
|
336
|
-
return [
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
if self.column_like:
|
|
344
|
-
value = [[item] for item in value]
|
|
345
|
-
else:
|
|
346
|
-
value = [value]
|
|
347
|
-
|
|
348
|
-
self.number_of_rows = len(value)
|
|
349
|
-
self._number_of_columns = 0
|
|
350
|
-
|
|
351
|
-
for row, row_contents in enumerate(value, 1):
|
|
352
|
-
for column, item in enumerate(row_contents, 1):
|
|
353
|
-
if item and not (isinstance(item, float) and math.isnan(item)):
|
|
354
|
-
self.dict[row, column] = item
|
|
355
|
-
self._number_of_columns = max(self.number_of_columns, column)
|
|
481
|
+
return [
|
|
482
|
+
[
|
|
483
|
+
self.dict.get((row, column))
|
|
484
|
+
for column in range(1, self.number_of_columns + 1)
|
|
485
|
+
]
|
|
486
|
+
for row in range(1, self.number_of_rows + 1)
|
|
487
|
+
]
|
|
356
488
|
|
|
357
489
|
def __setitem__(self, row_column, value):
|
|
358
490
|
row, column = row_column
|
|
359
491
|
if row < 1 or row > self.number_of_rows:
|
|
360
|
-
raise IndexError(
|
|
492
|
+
raise IndexError(
|
|
493
|
+
f"row must be between 1 and {self.number_of_rows} not {row}"
|
|
494
|
+
)
|
|
361
495
|
if column < 1 or column > self.number_of_columns:
|
|
362
|
-
raise IndexError(
|
|
496
|
+
raise IndexError(
|
|
497
|
+
f"column must be between 1 and {self.number_of_columns} not {column}"
|
|
498
|
+
)
|
|
363
499
|
self.dict[row, column] = value
|
|
364
500
|
|
|
365
501
|
def __getitem__(self, row_column):
|
|
366
502
|
row, column = row_column
|
|
367
503
|
if row < 1 or row > self.number_of_rows:
|
|
368
|
-
raise IndexError(
|
|
504
|
+
raise IndexError(
|
|
505
|
+
f"row must be between 1 and {self.number_of_rows} not {row}"
|
|
506
|
+
)
|
|
369
507
|
if column < 1 or column > self.number_of_columns:
|
|
370
|
-
raise IndexError(
|
|
508
|
+
raise IndexError(
|
|
509
|
+
f"column must be between 1 and {self.number_of_columns} not {column}"
|
|
510
|
+
)
|
|
371
511
|
return self.dict.get((row, column))
|
|
372
512
|
|
|
373
513
|
def minimized(self):
|
|
@@ -377,7 +517,11 @@ class block:
|
|
|
377
517
|
minimized block : block
|
|
378
518
|
uses highest_used_row_number and highest_used_column_number to minimize the block
|
|
379
519
|
"""
|
|
380
|
-
return block(
|
|
520
|
+
return block.from_value(
|
|
521
|
+
self,
|
|
522
|
+
number_of_rows=self.highest_used_row_number,
|
|
523
|
+
number_of_columns=self.highest_used_column_number,
|
|
524
|
+
)
|
|
381
525
|
|
|
382
526
|
@property
|
|
383
527
|
def number_of_rows(self):
|
|
@@ -432,7 +576,9 @@ class block:
|
|
|
432
576
|
if column < 1:
|
|
433
577
|
raise ValueError(f"{name}={column} < 1")
|
|
434
578
|
if column > self.number_of_columns:
|
|
435
|
-
raise ValueError(
|
|
579
|
+
raise ValueError(
|
|
580
|
+
f"{name}={column} > number_of_columns={self.number_of_columns}"
|
|
581
|
+
)
|
|
436
582
|
|
|
437
583
|
def transpose(self):
|
|
438
584
|
"""
|
|
@@ -442,12 +588,23 @@ class block:
|
|
|
442
588
|
-------
|
|
443
589
|
transposed block : block
|
|
444
590
|
"""
|
|
445
|
-
bl = block(
|
|
591
|
+
bl = block(
|
|
592
|
+
number_of_rows=self.number_of_columns, number_of_columns=self.number_of_rows
|
|
593
|
+
)
|
|
446
594
|
for (row, column), value in self.dict.items():
|
|
447
595
|
bl[column, row] = value
|
|
448
596
|
return bl
|
|
449
597
|
|
|
450
|
-
def vlookup(
|
|
598
|
+
def vlookup(
|
|
599
|
+
self,
|
|
600
|
+
s,
|
|
601
|
+
*,
|
|
602
|
+
row_from=1,
|
|
603
|
+
row_to=missing,
|
|
604
|
+
column1=1,
|
|
605
|
+
column2=missing,
|
|
606
|
+
default=missing,
|
|
607
|
+
):
|
|
451
608
|
"""
|
|
452
609
|
searches in column1 for row between row_from and row_to for s and returns the value found at (that row, column2)
|
|
453
610
|
|
|
@@ -488,7 +645,9 @@ class block:
|
|
|
488
645
|
if column2 is missing:
|
|
489
646
|
column2 = column1 + 1
|
|
490
647
|
self._check_column(column2, "column2")
|
|
491
|
-
row = self.lookup_row(
|
|
648
|
+
row = self.lookup_row(
|
|
649
|
+
s, row_from=row_from, row_to=row_to, column1=column1, default=-1
|
|
650
|
+
)
|
|
492
651
|
if row == -1:
|
|
493
652
|
if default is missing:
|
|
494
653
|
raise ValueError(f"{s} not found]")
|
|
@@ -553,7 +712,16 @@ class block:
|
|
|
553
712
|
else:
|
|
554
713
|
return default
|
|
555
714
|
|
|
556
|
-
def hlookup(
|
|
715
|
+
def hlookup(
|
|
716
|
+
self,
|
|
717
|
+
s,
|
|
718
|
+
*,
|
|
719
|
+
column_from=1,
|
|
720
|
+
column_to=missing,
|
|
721
|
+
row1=1,
|
|
722
|
+
row2=missing,
|
|
723
|
+
default=missing,
|
|
724
|
+
):
|
|
557
725
|
"""
|
|
558
726
|
searches in row1 for column between column_from and column_to for s and returns the value found at (that column, row2)
|
|
559
727
|
|
|
@@ -594,7 +762,9 @@ class block:
|
|
|
594
762
|
if row2 is missing:
|
|
595
763
|
row2 = row1 + 1
|
|
596
764
|
self._check_row(row2, "row2")
|
|
597
|
-
column = self.lookup_column(
|
|
765
|
+
column = self.lookup_column(
|
|
766
|
+
s, column_from=column_from, column_to=column_to, row1=row1, default=-1
|
|
767
|
+
)
|
|
598
768
|
if column == -1:
|
|
599
769
|
if default is missing:
|
|
600
770
|
raise ValueError(f"{s} not found")
|
|
@@ -603,7 +773,9 @@ class block:
|
|
|
603
773
|
else:
|
|
604
774
|
return self[row2, column]
|
|
605
775
|
|
|
606
|
-
def lookup_column(
|
|
776
|
+
def lookup_column(
|
|
777
|
+
self, s, *, column_from=1, column_to=missing, row1=1, default=missing
|
|
778
|
+
):
|
|
607
779
|
"""
|
|
608
780
|
searches in row1 for column between column_from and column_to for s and returns that column number
|
|
609
781
|
|
|
@@ -653,7 +825,16 @@ class block:
|
|
|
653
825
|
else:
|
|
654
826
|
return default
|
|
655
827
|
|
|
656
|
-
def lookup(
|
|
828
|
+
def lookup(
|
|
829
|
+
self,
|
|
830
|
+
s,
|
|
831
|
+
*,
|
|
832
|
+
row_from=1,
|
|
833
|
+
row_to=missing,
|
|
834
|
+
column1=1,
|
|
835
|
+
column2=missing,
|
|
836
|
+
default=missing,
|
|
837
|
+
):
|
|
657
838
|
"""
|
|
658
839
|
searches in column1 for row between row_from and row_to for s and returns the value found at (that row, column2)
|
|
659
840
|
|
|
@@ -695,7 +876,14 @@ class block:
|
|
|
695
876
|
----
|
|
696
877
|
This is exactly the same as vlookup.
|
|
697
878
|
"""
|
|
698
|
-
return self.vlookup(
|
|
879
|
+
return self.vlookup(
|
|
880
|
+
s,
|
|
881
|
+
row_from=row_from,
|
|
882
|
+
row_to=row_to,
|
|
883
|
+
column1=column1,
|
|
884
|
+
column2=column2,
|
|
885
|
+
default=default,
|
|
886
|
+
)
|
|
699
887
|
|
|
700
888
|
def decode_to_files(self):
|
|
701
889
|
"""
|
|
@@ -704,9 +892,13 @@ class block:
|
|
|
704
892
|
count = 0
|
|
705
893
|
for column in range(1, self.number_of_columns + 1):
|
|
706
894
|
row = 1
|
|
707
|
-
bl = self.
|
|
895
|
+
bl = self.minimized()
|
|
708
896
|
while row <= self.number_of_rows:
|
|
709
|
-
if
|
|
897
|
+
if (
|
|
898
|
+
self[row, column]
|
|
899
|
+
and self[row, column].startswith("<file=")
|
|
900
|
+
and self[row, column].endswith(">")
|
|
901
|
+
):
|
|
710
902
|
filename = self[row, column][6:-1]
|
|
711
903
|
collect = []
|
|
712
904
|
row += 1
|
|
@@ -751,7 +943,6 @@ class block:
|
|
|
751
943
|
row += 1
|
|
752
944
|
return bl.minimized()
|
|
753
945
|
|
|
754
|
-
|
|
755
946
|
class Capture:
|
|
756
947
|
"""
|
|
757
948
|
specifies how to capture stdout
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
|
|
2
|
+
xlwings_utils/xlwings_utils.py,sha256=2OxAeSEvIxSrj5aMMIxAsFYthYKk9yaYVvo8phtaf1c,29626
|
|
3
|
+
xlwings_utils-25.0.6.post5.dist-info/METADATA,sha256=8Ub-c1wDWkoZcW5qXie2w2gqItVa3RMPLjpKaZN7pt8,12255
|
|
4
|
+
xlwings_utils-25.0.6.post5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
5
|
+
xlwings_utils-25.0.6.post5.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
|
|
6
|
+
xlwings_utils-25.0.6.post5.dist-info/RECORD,,
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
|
|
2
|
-
xlwings_utils/xlwings_utils.py,sha256=P5GMfrG94ICyNTgHDT-jZI3a0oRSsnzR-25UKGcyulo,25788
|
|
3
|
-
xlwings_utils-25.0.6.post3.dist-info/METADATA,sha256=1YBjkDhO_X0ahbfgPhtG74yhkL5mfRyZM7nen0xgOH0,12255
|
|
4
|
-
xlwings_utils-25.0.6.post3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
5
|
-
xlwings_utils-25.0.6.post3.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
|
|
6
|
-
xlwings_utils-25.0.6.post3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|