xlwings-utils 25.0.0.post2__py3-none-any.whl → 25.0.0.post3__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.

@@ -289,16 +289,30 @@ class block:
289
289
  self.number_of_columns = number_of_columns
290
290
 
291
291
  @classmethod
292
- def from_xlrd_sheet(cls,sheet, number_of_rows=None, number_of_columns=None):
292
+ def from_xlrd_sheet(cls, sheet, number_of_rows=None, number_of_columns=None):
293
293
  v = [sheet.row_values(row_idx)[0 : sheet.ncols] for row_idx in range(0, sheet.nrows)]
294
294
  return cls(v, number_of_rows=number_of_rows, number_of_columns=number_of_columns)
295
295
 
296
+ @classmethod
297
+ def from_openpyxl_sheet(cls, sheet, number_of_rows=None, number_of_columns=None):
298
+ v = [[cell.value for cell in row] for row in sheet.iter_rows()]
299
+ return cls(v, number_of_rows=number_of_rows, number_of_columns=number_of_columns)
300
+
301
+ @classmethod
302
+ def from_file(cls, filename, number_of_rows=None, number_of_columns=None):
303
+ with open(filename, "r") as f:
304
+ v = [[line if line else None] for line in f.read().splitlines()]
305
+ return cls(v, number_of_rows=number_of_rows, number_of_columns=number_of_columns)
296
306
 
297
307
  @classmethod
298
- def from_dataframe(cls,df, number_of_rows=None, number_of_columns=None):
299
- v=df.values.tolist()
308
+ def from_dataframe(cls, df, number_of_rows=None, number_of_columns=None):
309
+ v = df.values.tolist()
300
310
  return cls(v, number_of_rows=number_of_rows, number_of_columns=number_of_columns)
301
311
 
312
+ def to_openpyxl_sheet(self, sheet):
313
+ for row in self.value:
314
+ sheet.append(row)
315
+
302
316
  @property
303
317
  def value(self):
304
318
  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)]
@@ -318,7 +332,7 @@ class block:
318
332
 
319
333
  for row, row_contents in enumerate(value, 1):
320
334
  for column, item in enumerate(row_contents, 1):
321
- if item and not (isinstance(item,float) and math.isnan(item)):
335
+ if item and not (isinstance(item, float) and math.isnan(item)):
322
336
  self.dict[row, column] = item
323
337
  self._number_of_columns = max(self.number_of_columns, column)
324
338
 
@@ -405,36 +419,36 @@ class block:
405
419
  def vlookup(self, s, *, row_from=1, row_to=None, column1=1, column2=None):
406
420
  """
407
421
  searches in column1 for row between row_from and row_to for s and returns the value found at (that row, column2)
408
-
422
+
409
423
  Parameters
410
424
  ----------
411
425
  s : any
412
426
  value to seach for
413
-
427
+
414
428
  row_from : int
415
429
  row to start search (default 1)
416
-
430
+
417
431
  should be between 1 and number_of_rows
418
-
432
+
419
433
  row_to : int
420
434
  row to end search (default number_of_rows)
421
-
422
- should be between 1 and number_of_rows
423
-
435
+
436
+ should be between 1 and number_of_rows
437
+
424
438
  column1 : int
425
439
  column to search in (default 1)
426
-
440
+
427
441
  should be between 1 and number_of_columns
428
-
442
+
429
443
  column2 : int
430
444
  column to return looked up value from (default column1 + 1)
431
-
432
- should be between 1 and number_of_columns
433
-
445
+
446
+ should be between 1 and number_of_columns
447
+
434
448
  Returns
435
449
  -------
436
450
  block[found row number, column2] : any
437
-
451
+
438
452
  Note
439
453
  ----
440
454
  If s is not found, a ValueError is raised
@@ -448,34 +462,34 @@ class block:
448
462
  def lookup_row(self, s, *, row_from=1, row_to=None, column1=1):
449
463
  """
450
464
  searches in column1 for row between row_from and row_to for s and returns that row number
451
-
465
+
452
466
  Parameters
453
467
  ----------
454
468
  s : any
455
469
  value to seach for
456
-
470
+
457
471
  row_from : int
458
472
  row to start search (default 1)
459
-
473
+
460
474
  should be between 1 and number_of_rows
461
-
475
+
462
476
  row_to : int
463
477
  row to end search (default number_of_rows)
464
-
465
- should be between 1 and number_of_rows
466
-
478
+
479
+ should be between 1 and number_of_rows
480
+
467
481
  column1 : int
468
482
  column to search in (default 1)
469
-
483
+
470
484
  should be between 1 and number_of_columns
471
-
485
+
472
486
  column2 : int
473
487
  column to return looked up value from (default column1 + 1)
474
-
488
+
475
489
  Returns
476
490
  -------
477
- row number where block[row nunber, column1] == s : int
478
-
491
+ row number where block[row nunber, column1] == s : int
492
+
479
493
  Note
480
494
  ----
481
495
  If s is not found, a ValueError is raised
@@ -494,41 +508,41 @@ class block:
494
508
  def hlookup(self, s, *, column_from=1, column_to=None, row1=1, row2=None):
495
509
  """
496
510
  searches in row1 for column between column_from and column_to for s and returns the value found at (that column, row2)
497
-
511
+
498
512
  Parameters
499
513
  ----------
500
514
  s : any
501
515
  value to seach for
502
-
516
+
503
517
  column_from : int
504
518
  column to start search (default 1)
505
-
519
+
506
520
  should be between 1 and number_of_columns
507
-
521
+
508
522
  column_to : int
509
523
  column to end search (default number_of_columns)
510
-
511
- should be between 1 and number_of_columns
512
-
524
+
525
+ should be between 1 and number_of_columns
526
+
513
527
  row1 : int
514
528
  row to search in (default 1)
515
-
529
+
516
530
  should be between 1 and number_of_rows
517
-
531
+
518
532
  row2 : int
519
533
  row to return looked up value from (default row1 + 1)
520
-
521
- should be between 1 and number_of_rows
522
-
534
+
535
+ should be between 1 and number_of_rows
536
+
523
537
  Returns
524
538
  -------
525
539
  block[row, found column, row2] : any
526
-
540
+
527
541
  Note
528
542
  ----
529
543
  If s is not found, a ValueError is raised
530
544
  """
531
- if column2 is None:
545
+ if row2 is None:
532
546
  row2 = row1 + 1
533
547
  self._check_row(row2, "row2")
534
548
  column = self.lookup_column(s, column_from=column_from, column_to=column_to, row1=row1)
@@ -537,34 +551,34 @@ class block:
537
551
  def lookup_column(self, s, *, column_from=1, column_to=None, row1=1):
538
552
  """
539
553
  searches in row1 for column between column_from and column_to for s and returns that column number
540
-
554
+
541
555
  Parameters
542
556
  ----------
543
557
  s : any
544
558
  value to seach for
545
-
559
+
546
560
  column_from : int
547
561
  column to start search (default 1)
548
-
562
+
549
563
  should be between 1 and number_of_columns
550
-
564
+
551
565
  column_to : int
552
566
  column to end search (default number_of_columns)
553
-
554
- should be between 1 and number_of_columns
555
-
567
+
568
+ should be between 1 and number_of_columns
569
+
556
570
  row1 : int
557
571
  row to search in (default 1)
558
-
572
+
559
573
  should be between 1 and number_of_rows
560
-
574
+
561
575
  row2 : int
562
576
  row to return looked up value from (default row1 + 1)
563
-
577
+
564
578
  Returns
565
579
  -------
566
- column number where block[row1, column number] == s : int
567
-
580
+ column number where block[row1, column number] == s : int
581
+
568
582
  Note
569
583
  ----
570
584
  If s is not found, a ValueError is raised
@@ -583,40 +597,40 @@ class block:
583
597
  def lookup(self, s, *, row_from=1, row_to=None, column1=1, column2=None):
584
598
  """
585
599
  searches in column1 for row between row_from and row_to for s and returns the value found at (that row, column2)
586
-
600
+
587
601
  Parameters
588
602
  ----------
589
603
  s : any
590
604
  value to seach for
591
-
605
+
592
606
  row_from : int
593
607
  row to start search (default 1)
594
-
608
+
595
609
  should be between 1 and number_of_rows
596
-
610
+
597
611
  row_to : int
598
612
  row to end search (default number_of_rows)
599
-
600
- should be between 1 and number_of_rows
601
-
613
+
614
+ should be between 1 and number_of_rows
615
+
602
616
  column1 : int
603
617
  column to search in (default 1)
604
-
618
+
605
619
  should be between 1 and number_of_columns
606
-
620
+
607
621
  column2 : int
608
622
  column to return looked up value from (default column1 + 1)
609
-
610
- should be between 1 and number_of_columns
611
-
623
+
624
+ should be between 1 and number_of_columns
625
+
612
626
  Returns
613
627
  -------
614
628
  block[found row number, column2] : any
615
-
629
+
616
630
  Note
617
631
  ----
618
632
  If s is not found, a ValueError is raised
619
-
633
+
620
634
  This is exactly the same as vlookup.
621
635
  """
622
636
  return self.vlookup(s, row_from=row_from, row_to=row_to, column1=column1, column2=column2)
@@ -647,10 +661,10 @@ class _capture:
647
661
  self._buffer = []
648
662
 
649
663
  def __enter__(self):
650
- sys.stdout = self
664
+ self.enabled = True
651
665
 
652
666
  def __exit__(self, exc_type, exc_value, tb):
653
- sys.stdout = self.stdout
667
+ self.enabled = False
654
668
 
655
669
  def write(self, data):
656
670
  self._buffer.append(data)
@@ -662,6 +676,17 @@ class _capture:
662
676
  self.stdout.flush()
663
677
  self._buffer.append("\n")
664
678
 
679
+ @property
680
+ def enabled(self):
681
+ return sys.out == self
682
+
683
+ @enabled.setter
684
+ def enabled(self, value):
685
+ if value:
686
+ sys.stdout = self
687
+ else:
688
+ sys.stdout = self.stdout
689
+
665
690
  @property
666
691
  def value(self):
667
692
  result = self.value_keep
@@ -697,12 +722,14 @@ class _capture:
697
722
 
698
723
 
699
724
  def reset():
725
+ capture.enabled = False
700
726
  capture.include_print = False
701
727
  capture.clear()
702
-
728
+ print('reset',sys.stdout)
703
729
 
704
730
  capture = _capture()
705
731
 
706
732
 
707
733
  if __name__ == "__main__":
708
734
  ...
735
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xlwings_utils
3
- Version: 25.0.0.post2
3
+ Version: 25.0.0.post3
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
@@ -119,53 +119,71 @@ sheet.range(10,1).value = this_block.minimized().value
119
119
 
120
120
  In this case, only the really processed rows are copied to the sheet.
121
121
 
122
- With blocks, it is easy to use a sheet as an input for a project / scenario.
122
+ ### Looking up in a block
123
123
 
124
- Like, something like
124
+ With blocks, it is easy to use a sheet as an input for a project / scenario.
125
125
 
126
+ Something like
127
+ <img src="https://www.salabim.org/xlwings_utils/fig01.png">
126
128
 
127
- Of course we could access the various input fields with absolute ranges, but if something
128
- changes later (like adding a row), all references have to be updated.
129
+ Of course, we could access the various input fields with absolute ranges, but if something changes later (like adding a row), all references would have to be updated.
129
130
 
130
- If we read the project sheet (partly) into a block, lookup methods are available to access 'fields' easily and future proof.
131
+ If we read the project sheet (partly) into a block, lookup methods are available to access 'fields' easily and future-proof.
131
132
 
132
- Let's see how this works with the above sheet. The block (bl) looks like
133
+ Let's see how this works with the above sheet. The corresponding block (bl) looks like
133
134
 
134
135
  ```
135
136
  | 1 2 3 4 5
136
137
  --+-------------------------------------------------------------------------------
137
138
  1 | Project Factory1
138
- 2 | Name Mega1
139
- 3 | Start date 2025-05-17
140
- 4 | End date 2026-02-01
141
- 5 |
142
- 6 | Parts Width Length Height Weight
143
- 7 | A 10 5 5 100
144
- 8 | B 11 5 8 102
145
- 9 | C 12 2 3 91
146
- 10|
147
-
139
+ 2 | Start date 2025-05-17
140
+ 3 | End date 2026-02-01
141
+ 4 |
142
+ 5 | Parts Width Length Height Weight
143
+ 6 | A 10 5 5 100
144
+ 7 | B 11 5 8 102
145
+ 8 | C 12 2 3 91
146
+ 9 |
147
+ ```
148
148
  Now we can do
149
- project = bl.lookup("Project")
150
- name = bl.lookup("Start date")
149
+ ```project = bl.lookup("Project")
150
+ project = bl.lookup("Project")
151
151
  start_date = bl.lookup("Start date")
152
152
  end_date = bl.lookup("End date")
153
153
  row1 = bl.lookup_row("Parts")
154
154
  parts=[]
155
155
  for row2 in range(row1 + 1, bl.highest_used_row_number + 1):
156
156
  if not (part_name := bl.hlookup("Part",row1=row1, row2=row2)):
157
- # stop when reach a 'blank' part_name
157
+ # stop when a 'blank' part_name is found
158
158
  break
159
159
  width = bl.hlookup("Width",row1=row1, row2=row2)
160
160
  length = bl.hlookup("Length",row1=row1, row2=row2)
161
161
  height = bl.hlookup("HeightL",row1=row1, row2=row2)
162
162
  weight = bl.hlookup("Weight",row1=row1, row2=row2)
163
163
  parts.append(Part(part_name, width, length, height, weight))
164
-
165
- You see lookup, which is vertical lookup, which just scans column 1 for the given label and then returns the corresponding value from column 2.
164
+ ```
165
+ First we do a couple of `lookup`s, which are vertical lookups, to scan column 1 for the given labels and return the corresponding values from column 2.
166
+
167
+ Then, there's `lookup_row`, which also scans column1 for the given label (Parts), but returns the corresponding row (5). It is then stored in row1.
168
+ And then we just read the following rows (with `hlookup`) and access the required values.
169
+
170
+ ### Filling a block from other sources
171
+
172
+ The advantage of using a block instead of accessing these sources is, that they are one-based, just like in Excel.
173
+
174
+ It is possible to make a block from a xlrd worksheet with `block.from_xlrd_sheet`.
175
+
176
+ It is possible to make a block from a pandas dataframe with `block.from_dataframe`. Make sure that, if the dataframe is created by reading from an Excel sheet, headers=None should be specified, e.g. `df = pd.read_excel(filename, header=None)`.
177
+
178
+ It is possible to make a block from an openpyxl worksheet with `block.from_openpyxl_sheet`.
179
+
180
+ ### Writing a block to an openpyxl sheet
181
+
182
+ In order to write (append) to an openpyxl sheet, use: block.to_openpyxl_sheet.
183
+
184
+ It is possible to make a block from a text file with `block.from_file`.
166
185
 
167
- Then, there's lookup_row, which also scans column1 for the given label (Parts), but returns the corresponding row (6). Store it in row1.
168
- And then we just read the following rows (with hlookup) and access the required values.
186
+ ### Writing a block to an openpyxl sheet
169
187
 
170
188
  ## Capture stdout support
171
189
 
@@ -173,7 +191,8 @@ The module has support for capturing stdout and -later- using showing the captur
173
191
 
174
192
  This is rather important as printing in xlwings lite to the UI pane is rather slow.
175
193
 
176
- In order to capture stdout output, use
194
+ In order to capture stdout output, use `xwu.capture.enabled = True`. And to stop capturing, use `xwu.capture.enabled = False`.
195
+ Alternatively, a context manager is provided:
177
196
 
178
197
 
179
198
  ```
@@ -182,8 +201,9 @@ with xwu.capture:
182
201
  code with print statements
183
202
  """
184
203
  ```
204
+ Note that stopping the capture, leaves the captured output in place, so it can be extended later.
185
205
 
186
- and then the captured output can be copied to a sheet, like
206
+ In either case, the captured output can be then copied to a sheet, like
187
207
 
188
208
  ```
189
209
  sheet.range(4,5).value = xwu.capture.value
@@ -0,0 +1,6 @@
1
+ xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
2
+ xlwings_utils/xlwings_utils.py,sha256=o1iCbb5BmXDeVelksKNTJrSkGZ7e0y-iwnYlITKr3dQ,21039
3
+ xlwings_utils-25.0.0.post3.dist-info/METADATA,sha256=Mwx61halKRiHZAvXI5eoSrLiV5lqUCwKVkZu-tZ3P5E,9537
4
+ xlwings_utils-25.0.0.post3.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
5
+ xlwings_utils-25.0.0.post3.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
+ xlwings_utils-25.0.0.post3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,6 +0,0 @@
1
- xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
2
- xlwings_utils/xlwings_utils.py,sha256=bqy2SkFBf2GvHPLcXu1YpyLESiIHkT3rpSemruy5twc,20792
3
- xlwings_utils-25.0.0.post2.dist-info/METADATA,sha256=PJyv0aEsxLn5v3V8kVtriKjl_2tjwjXXNogHjKNEMt8,8358
4
- xlwings_utils-25.0.0.post2.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
5
- xlwings_utils-25.0.0.post2.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
- xlwings_utils-25.0.0.post2.dist-info/RECORD,,