xlwings-utils 25.0.6.post4__tar.gz → 25.0.6.post5__tar.gz

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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xlwings_utils
3
- Version: 25.0.6.post4
3
+ Version: 25.0.6.post5
4
4
  Summary: xlwings_utils
5
5
  Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/salabim/xlwings_utils
@@ -10,7 +10,7 @@ authors = [
10
10
  { name = "Ruud van der Ham", email = "rt.van.der.ham@gmail.com" },
11
11
  ]
12
12
  description = "xlwings_utils"
13
- version = "25.0.6.post4"
13
+ version = "25.0.6.post5"
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.9"
16
16
  dependencies = [
@@ -42,7 +42,7 @@ def test_block0():
42
42
 
43
43
 
44
44
  def test_block1():
45
- this_block = xwu.block([[1, 2, 3], [4, 5, 6]])
45
+ this_block = xwu.block.from_value([[1, 2, 3], [4, 5, 6]])
46
46
  assert this_block.dict == {(1, 1): 1, (1, 2): 2, (1, 3): 3, (2, 1): 4, (2, 2): 5, (2, 3): 6}
47
47
  assert this_block.value == [[1, 2, 3], [4, 5, 6]]
48
48
  assert this_block.minimized().value == [[1, 2, 3], [4, 5, 6]]
@@ -57,26 +57,26 @@ def test_block1():
57
57
 
58
58
 
59
59
  def test_block2():
60
- this_block = xwu.block([[1, 2, 3], [4, 5, 6]], number_of_rows=1)
60
+ this_block = xwu.block.from_value([[1, 2, 3], [4, 5, 6]], number_of_rows=1)
61
61
  assert this_block.value == [[1, 2, 3]]
62
62
 
63
- this_block = xwu.block([[1, 2, 3], [4, 5, 6]], number_of_rows=1, number_of_columns=2)
63
+ this_block = xwu.block.from_value([[1, 2, 3], [4, 5, 6]], number_of_rows=1, number_of_columns=2)
64
64
  assert this_block.value == [[1, 2]]
65
65
 
66
- this_block = xwu.block([[1, 2, 3], [4, 5, 6]], number_of_rows=3, number_of_columns=4)
66
+ this_block = xwu.block.from_value([[1, 2, 3], [4, 5, 6]], number_of_rows=3, number_of_columns=4)
67
67
  assert this_block.value == [[1, 2, 3, None], [4, 5, 6, None], [None, None, None, None]]
68
68
 
69
69
 
70
70
  def test_block_one_dimension():
71
- this_block = xwu.block([1, 2, 3])
71
+ this_block = xwu.block.from_value([1, 2, 3])
72
72
  assert this_block.value == [[1, 2, 3]]
73
73
 
74
- this_block = xwu.block([1, 2, 3], column_like=True)
74
+ this_block = xwu.block.from_value([1, 2, 3], column_like=True)
75
75
  assert this_block.value == [[1], [2], [3]]
76
76
 
77
77
 
78
78
  def test_block_scalar():
79
- this_block = xwu.block(1)
79
+ this_block = xwu.block.from_value(1)
80
80
  assert this_block.value == [[1]]
81
81
 
82
82
 
@@ -103,7 +103,7 @@ def test_raise():
103
103
  this_block[1, 7] = 1
104
104
 
105
105
  def test_lookup():
106
- bl=xwu.block([[1,"One", "Un"],[2, "Two", "Deux"],[3,"Three","Trois"]])
106
+ bl=xwu.block.from_value([[1,"One", "Un"],[2, "Two", "Deux"],[3,"Three","Trois"]])
107
107
  assert bl.lookup(1)=="One"
108
108
  assert bl.lookup(3, column2=3)=="Trois"
109
109
  with pytest.raises(ValueError):
@@ -117,7 +117,7 @@ def test_lookup():
117
117
  assert bl.lookup_row(3)==3
118
118
 
119
119
  def test_vookup():
120
- bl=xwu.block([[1,"One", "Un"],[2, "Two", "Deux"],[3,"Three","Trois"]])
120
+ bl=xwu.block.from_value([[1,"One", "Un"],[2, "Two", "Deux"],[3,"Three","Trois"]])
121
121
  assert bl.vlookup(1)=="One"
122
122
  assert bl.vlookup(3, column2=3)=="Trois"
123
123
  with pytest.raises(ValueError):
@@ -129,7 +129,7 @@ def test_vookup():
129
129
  bl.vlookup(1, column1=3)
130
130
 
131
131
  def test_hlookup():
132
- bl=xwu.block([[1,2,3],"One Two Three".split(), "Un Deux Trois".split()])
132
+ bl=xwu.block.from_value([[1,2,3],"One Two Three".split(), "Un Deux Trois".split()])
133
133
 
134
134
  assert bl.hlookup(1)=="One"
135
135
  assert bl.hlookup(3, row2=3)=="Trois"
@@ -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, value=missing, *, number_of_rows=missing, number_of_columns=missing, column_like=False):
288
+ def __init__(self, number_of_rows=1, number_of_columns=1):
290
289
  self.dict = {}
291
- self.column_like = column_like
292
- if value is missing:
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
- else:
301
- if isinstance(value, block):
302
- value = value.value
303
- self.value = value
304
- if number_of_rows is not missing:
305
- self.number_of_rows = number_of_rows
306
- if number_of_columns is not missing:
307
- self.number_of_columns = number_of_columns
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
- v = [sheet.row_values(row_idx)[0 : sheet.ncols] for row_idx in range(0, sheet.nrows)]
312
- return cls(v, number_of_rows=number_of_rows, number_of_columns=number_of_columns)
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(cls, sheet, number_of_rows=missing, number_of_columns=missing):
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(v, number_of_rows=number_of_rows, number_of_columns=number_of_columns)
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(v, number_of_rows=number_of_rows, number_of_columns=number_of_columns)
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(v, number_of_rows=number_of_rows, number_of_columns=number_of_columns)
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 [[self.dict.get((row, column)) for column in range(1, self.number_of_columns + 1)] for row in range(1, self.number_of_rows + 1)]
337
-
338
- @value.setter
339
- def value(self, value):
340
- if not isinstance(value, list):
341
- value = [[value]]
342
- if not isinstance(value[0], list):
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(f"row must be between 1 and {self.number_of_rows} not {row}")
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(f"column must be between 1 and {self.number_of_columns} not {column}")
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(f"row must be between 1 and {self.number_of_rows} not {row}")
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(f"column must be between 1 and {self.number_of_columns} not {column}")
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(self, number_of_rows=self.highest_used_row_number, number_of_columns=self.highest_used_column_number)
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(f"{name}={column} > number_of_columns={self.number_of_columns}")
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(number_of_rows=self.number_of_columns, number_of_columns=self.number_of_rows)
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(self, s, *, row_from=1, row_to=missing, column1=1, column2=missing, default=missing):
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(s, row_from=row_from, row_to=row_to, column1=column1, default=-1)
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(self, s, *, column_from=1, column_to=missing, row1=1, row2=missing, default=missing):
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(s, column_from=column_from, column_to=column_to, row1=row1, default=-1)
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(self, s, *, column_from=1, column_to=missing, row1=1, default=missing):
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(self, s, *, row_from=1, row_to=missing, column1=1, column2=missing, default=missing):
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(s, row_from=row_from, row_to=row_to, column1=column1, column2=column2, default=default)
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
  """
@@ -706,7 +894,11 @@ class block:
706
894
  row = 1
707
895
  bl = self.minimized()
708
896
  while row <= self.number_of_rows:
709
- if self[row, column] and self[row, column].startswith("<file=") and self[row, column].endswith(">"):
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xlwings_utils
3
- Version: 25.0.6.post4
3
+ Version: 25.0.6.post5
4
4
  Summary: xlwings_utils
5
5
  Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/salabim/xlwings_utils