xlwings-utils 25.0.0.post2__py3-none-any.whl → 25.0.0.post4__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,46 +597,46 @@ 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)
623
637
 
624
638
 
625
- class _capture:
639
+ class Capture:
626
640
  """
627
641
  specifies how to capture stdout
628
642
 
@@ -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
@@ -696,13 +721,6 @@ class _capture:
696
721
  self._include_print = value
697
722
 
698
723
 
699
- def reset():
700
- capture.include_print = False
701
- capture.clear()
702
-
703
-
704
- capture = _capture()
705
-
706
-
707
724
  if __name__ == "__main__":
708
725
  ...
726
+
@@ -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.post4
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
@@ -30,6 +30,16 @@ In the script, add
30
30
  >
31
31
  > The GitHub repository can be found on https://github.com/salabim/xlwings_utils .
32
32
 
33
+ General
34
+
35
+ It is recommended to put
36
+
37
+ ```
38
+ import xlwings_utils as xwu
39
+ ```
40
+
41
+ at the top of a xlwings lite script.
42
+
33
43
  ## Dropbox support
34
44
 
35
45
  The xlwings lite system does not provide access to the local file system. With this module, files can be copied between Dropbox and the local pyodide file system, making it possible to indirectly use the local file system.
@@ -50,15 +60,15 @@ The functions `list_local`, `read_local` and `write_local` offer similar functio
50
60
  So, a way to access a file on the system's drive (mapped to Dropbox) as a local file is:
51
61
 
52
62
  ```
53
- contents = xlwings_utils.read_dropbox('/downloads/file1.xls')
54
- xlwings_utils.write_local('file1.xlsx')
63
+ contents = xwu.read_dropbox('/downloads/file1.xls')
64
+ xwu.write_local('file1.xlsx')
55
65
  df = pandas.read_excel"file1.xlsx")
56
66
  ...
57
67
  ```
58
68
  And the other direction:
59
69
  ```
60
- contents = xlwings_utils.read_local('file1.gif')
61
- xlwings_utils.write_dropbox('/downloads/file1.gif')
70
+ contents = xwu.read_local('file1.gif')
71
+ xwu.write_dropbox('/downloads/file1.gif')
62
72
  ```
63
73
 
64
74
  ## Block support
@@ -89,7 +99,7 @@ It is not allowed t,o read or write outside the block dimensions.
89
99
 
90
100
  It is also possible to define an empty block, like
91
101
  ```
92
- block = xlwings_utils.block(number_of_rows, number_columns)
102
+ block = xwu.block(number_of_rows, number_columns)
93
103
  ```
94
104
  The dimensions can be queried or redefined with `block.number_of_rows` and
95
105
  `block.number_of_columns`.
@@ -119,53 +129,71 @@ sheet.range(10,1).value = this_block.minimized().value
119
129
 
120
130
  In this case, only the really processed rows are copied to the sheet.
121
131
 
122
- With blocks, it is easy to use a sheet as an input for a project / scenario.
132
+ ### Looking up in a block
123
133
 
124
- Like, something like
134
+ With blocks, it is easy to use a sheet as an input for a project / scenario.
125
135
 
136
+ Something like
137
+ <img src="https://www.salabim.org/xlwings_utils/fig01.png">
126
138
 
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.
139
+ 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
140
 
130
- If we read the project sheet (partly) into a block, lookup methods are available to access 'fields' easily and future proof.
141
+ If we read the project sheet (partly) into a block, lookup methods are available to access 'fields' easily and future-proof.
131
142
 
132
- Let's see how this works with the above sheet. The block (bl) looks like
143
+ Let's see how this works with the above sheet. The corresponding block (bl) looks like
133
144
 
134
145
  ```
135
146
  | 1 2 3 4 5
136
147
  --+-------------------------------------------------------------------------------
137
148
  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
-
149
+ 2 | Start date 2025-05-17
150
+ 3 | End date 2026-02-01
151
+ 4 |
152
+ 5 | Parts Width Length Height Weight
153
+ 6 | A 10 5 5 100
154
+ 7 | B 11 5 8 102
155
+ 8 | C 12 2 3 91
156
+ 9 |
157
+ ```
148
158
  Now we can do
149
- project = bl.lookup("Project")
150
- name = bl.lookup("Start date")
159
+ ```project = bl.lookup("Project")
160
+ project = bl.lookup("Project")
151
161
  start_date = bl.lookup("Start date")
152
162
  end_date = bl.lookup("End date")
153
163
  row1 = bl.lookup_row("Parts")
154
164
  parts=[]
155
165
  for row2 in range(row1 + 1, bl.highest_used_row_number + 1):
156
166
  if not (part_name := bl.hlookup("Part",row1=row1, row2=row2)):
157
- # stop when reach a 'blank' part_name
167
+ # stop when a 'blank' part_name is found
158
168
  break
159
169
  width = bl.hlookup("Width",row1=row1, row2=row2)
160
170
  length = bl.hlookup("Length",row1=row1, row2=row2)
161
171
  height = bl.hlookup("HeightL",row1=row1, row2=row2)
162
172
  weight = bl.hlookup("Weight",row1=row1, row2=row2)
163
173
  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.
174
+ ```
175
+ 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.
176
+
177
+ 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.
178
+ And then we just read the following rows (with `hlookup`) and access the required values.
179
+
180
+ ### Filling a block from other sources
181
+
182
+ The advantage of using a block instead of accessing these sources is, that they are one-based, just like in Excel.
183
+
184
+ It is possible to make a block from a xlrd worksheet with `block.from_xlrd_sheet`.
185
+
186
+ 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)`.
187
+
188
+ It is possible to make a block from an openpyxl worksheet with `block.from_openpyxl_sheet`.
189
+
190
+ ### Writing a block to an openpyxl sheet
166
191
 
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.
192
+ In order to write (append) to an openpyxl sheet, use: block.to_openpyxl_sheet.
193
+
194
+ It is possible to make a block from a text file with `block.from_file`.
195
+
196
+ ### Writing a block to an openpyxl sheet
169
197
 
170
198
  ## Capture stdout support
171
199
 
@@ -173,30 +201,53 @@ The module has support for capturing stdout and -later- using showing the captur
173
201
 
174
202
  This is rather important as printing in xlwings lite to the UI pane is rather slow.
175
203
 
176
- In order to capture stdout output, use
204
+ In order to capture stdout output, it is required to first issue
177
205
 
206
+ ```caoture
207
+ capture = xwu.Capture()
208
+ ```
178
209
 
210
+ By this, capture is automatically enabled and print is disabled. Alternatively, it is possible to use
211
+
212
+ ```
213
+ capture = xwu.Capture(enabled=False)
214
+ ```
215
+
216
+ to disable the capture. And with
217
+
218
+ ```
219
+ capture = xwu.Capture(include_print=True)
179
220
  ```
180
- with xwu.capture:
221
+
222
+ the stdout output is captured and printed.
223
+
224
+ Capturing van be enabled and disabled at any time with `capture.enbaled = True` and `capture.enabled = False`.
225
+
226
+ And including print likewise with `capture.include_print`.
227
+
228
+ Alternatively, a context manager is provided:
229
+
230
+
231
+ ```
232
+ with capture:
181
233
  """
182
234
  code with print statements
183
235
  """
184
236
  ```
237
+ Note that stopping the capture, leaves the captured output in place, so it can be extended later.
185
238
 
186
- and then the captured output can be copied to a sheet, like
239
+ In either case, the captured output can be then copied to a sheet, like
187
240
 
188
241
  ```
189
- sheet.range(4,5).value = xwu.capture.value
242
+ sheet.range(4,5).value = capture.value
190
243
  ```
191
244
  Upon reading the value, the capture buffer will be emptied.
192
245
 
193
- If you don't want the buffer to be emptied after accessing the value, use `xwu.capture.value_keep`.
194
-
195
- The capture buffer can also be retrieved as a string with `xwu.capture.str` and `xwu.capture.str_keep`.
246
+ If you don't want the buffer to be emptied after accessing the value, use `capture.value_keep`.
196
247
 
197
- Clearing the captured stdout buffer can be done at any time with `xwu.capture.clear()`.
248
+ The capture buffer can also be retrieved as a string with `capture.str` and `capture.str_keep`.
198
249
 
199
- Normally, stdout will not be sent to the xlwings lite UI panel when captured with the `xwu.capture` context manager. However, if you specify `xwu.capture.include_print = True`, the output will be sent to the UI panel as well. Note that this setting remains active until a `xwu.capture.include_print = False` is issued.
250
+ Clearing the captured stdout buffer can be done at any time with `capture.clear()`.
200
251
 
201
252
 
202
253
  ## Contact info
@@ -0,0 +1,6 @@
1
+ xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
2
+ xlwings_utils/xlwings_utils.py,sha256=j0FNzIJ1eu56uk1m3T739pslbl-gL8duZY0oofXzaC4,20889
3
+ xlwings_utils-25.0.0.post4.dist-info/METADATA,sha256=IhzzvU8yc2eO58cjjgM-nngwqCFXl77TabmB2Mmttxc,9713
4
+ xlwings_utils-25.0.0.post4.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
5
+ xlwings_utils-25.0.0.post4.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
+ xlwings_utils-25.0.0.post4.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,,