xlwings-utils 25.0.0.post2__tar.gz → 25.0.0.post3__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.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
@@ -1,211 +1,217 @@
1
- Metadata-Version: 2.4
2
- Name: xlwings_utils
3
- Version: 25.0.0.post2
4
- Summary: xlwings_utils
5
- Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
- Project-URL: Homepage, https://github.com/salabim/xlwings_utils
7
- Project-URL: Repository, https://github.com/salabim/xlwings_utils
8
- Classifier: Development Status :: 5 - Production/Stable
9
- Classifier: Programming Language :: Python :: 3 :: Only
10
- Requires-Python: >=3.9
11
- Description-Content-Type: text/markdown
12
- Requires-Dist: dropbox
13
- Requires-Dist: ssl
14
-
15
- <img src="https://www.salabim.org/xlwings_utils_logo2.png">
16
-
17
- ## Introduction
18
-
19
- This module provides some useful functions to be used in xlwings (lite).
20
-
21
- ## Installation
22
-
23
- Just add xlwings-utils to the *requirements.txt* tab.
24
-
25
- In the script, add
26
-
27
- ```ìmport xlwings_utils as xwu```
28
-
29
- > [!NOTE]
30
- >
31
- > The GitHub repository can be found on https://github.com/salabim/xlwings_utils .
32
-
33
- ## Dropbox support
34
-
35
- 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.
36
-
37
- It is only possible, as of now, to use full-access Dropbox apps.
38
-
39
- The easiest way to use the Dropbox functionality is to add the credentials to the environment variables. Add REFRESH_TOKEN, APP_KEY and APP_SECRET with their corresponding values to the environment variables.
40
-
41
- Then, it is possible to list all files in a specified folder with the function `list_dropbox`.
42
- It is also possible to get the folders and to access all underlying folders.
43
-
44
- The function `read_dropbox` can be used to read a Dropbox file's contents (bytes).
45
-
46
- The function `write_dropbox` can be used to write contents (bytes) to a Dropbox file.
47
-
48
- The functions `list_local`, `read_local` and `write_local` offer similar functionality for the local file system (on pyodide).
49
-
50
- So, a way to access a file on the system's drive (mapped to Dropbox) as a local file is:
51
-
52
- ```
53
- contents = xlwings_utils.read_dropbox('/downloads/file1.xls')
54
- xlwings_utils.write_local('file1.xlsx')
55
- df = pandas.read_excel"file1.xlsx")
56
- ...
57
- ```
58
- And the other direction:
59
- ```
60
- contents = xlwings_utils.read_local('file1.gif')
61
- xlwings_utils.write_dropbox('/downloads/file1.gif')
62
- ```
63
-
64
- ## Block support
65
-
66
- The module contains a useful 2-dimensional data structure: *block*.
67
- This can be useful to manipulate a range without accessing the range directly, which is expensive in terms of memory and execution time.
68
- The advantage over an ordinary list of lists is that a block is index one-based, in line with range and addressing is done with a row, column tuple.
69
- So, `my_block(lol)[row, col]` is roughly equivalent to `lol[row-1][col-1]`
70
-
71
- A block stores the values internally as a dictionary and will only convert these to a list of lists when using `block.value`.
72
-
73
- Converting the value of a range (usually a list of lists, but can also be a list or scalar) to a block can be done with
74
-
75
- ```
76
- my_block = xwu.block.from_value(range.value)
77
- ```
78
- The dimensions (number of rows and number of columns) are automatically set.
79
-
80
- Setting of an individual item (one-based, like range) can be done like
81
- ```
82
- my_block[row, column] = x
83
- ```
84
- And, likewise, reading an individual item can be done like
85
- ```
86
- x = my_block[row, column]
87
- ```
88
- It is not allowed t,o read or write outside the block dimensions.
89
-
90
- It is also possible to define an empty block, like
91
- ```
92
- block = xlwings_utils.block(number_of_rows, number_columns)
93
- ```
94
- The dimensions can be queried or redefined with `block.number_of_rows` and
95
- `block.number_of_columns`.
96
-
97
- To assign a block to range, use
98
- ```
99
- range.value = block.value
100
- ```
101
-
102
- The property `block.highest_used_row_number` returns the row number of the highest non-None cell.
103
-
104
- The property `block.highest_used_column_number` returns the column_number of the highest non-None cell.
105
-
106
- The method `block.minimized()` returns a block that has the dimensions of (highest_used_row_number, highest_used_column_number).
107
-
108
- Particularly if we process an unknown number of lines, we can do something like:
109
-
110
- ```
111
- this_block = xwu.block(number_of_rows=10000, number_of_columns=2)
112
- for row in range(1, 10001):
113
- this_block[row,1]= ...
114
- this_block[row,2]= ...
115
- if ...: # end condition
116
- break
117
- sheet.range(10,1).value = this_block.minimized().value
118
- ```
119
-
120
- In this case, only the really processed rows are copied to the sheet.
121
-
122
- With blocks, it is easy to use a sheet as an input for a project / scenario.
123
-
124
- Like, something like
125
-
126
-
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
-
130
- If we read the project sheet (partly) into a block, lookup methods are available to access 'fields' easily and future proof.
131
-
132
- Let's see how this works with the above sheet. The block (bl) looks like
133
-
134
- ```
135
- | 1 2 3 4 5
136
- --+-------------------------------------------------------------------------------
137
- 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
-
148
- Now we can do
149
- project = bl.lookup("Project")
150
- name = bl.lookup("Start date")
151
- start_date = bl.lookup("Start date")
152
- end_date = bl.lookup("End date")
153
- row1 = bl.lookup_row("Parts")
154
- parts=[]
155
- for row2 in range(row1 + 1, bl.highest_used_row_number + 1):
156
- if not (part_name := bl.hlookup("Part",row1=row1, row2=row2)):
157
- # stop when reach a 'blank' part_name
158
- break
159
- width = bl.hlookup("Width",row1=row1, row2=row2)
160
- length = bl.hlookup("Length",row1=row1, row2=row2)
161
- height = bl.hlookup("HeightL",row1=row1, row2=row2)
162
- weight = bl.hlookup("Weight",row1=row1, row2=row2)
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.
166
-
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.
169
-
170
- ## Capture stdout support
171
-
172
- The module has support for capturing stdout and -later- using showing the captured output on a sheet.
173
-
174
- This is rather important as printing in xlwings lite to the UI pane is rather slow.
175
-
176
- In order to capture stdout output, use
177
-
178
-
179
- ```
180
- with xwu.capture:
181
- """
182
- code with print statements
183
- """
184
- ```
185
-
186
- and then the captured output can be copied to a sheet, like
187
-
188
- ```
189
- sheet.range(4,5).value = xwu.capture.value
190
- ```
191
- Upon reading the value, the capture buffer will be emptied.
192
-
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`.
196
-
197
- Clearing the captured stdout buffer can be done at any time with `xwu.capture.clear()`.
198
-
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.
200
-
201
-
202
- ## Contact info
203
-
204
- You can contact Ruud van der Ham, the core developer, via ruud@salabim.org .
205
-
206
- ## Badges
207
-
208
- ![PyPI](https://img.shields.io/pypi/v/xlwings-utils) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/xlwings-utils) ![PyPI - Implementation](https://img.shields.io/pypi/implementation/xlwings-utils)
209
- ![PyPI - License](https://img.shields.io/pypi/l/xlwings-utils) ![ruff](https://img.shields.io/badge/style-ruff-41B5BE?style=flat)
210
- ![GitHub last commit](https://img.shields.io/github/last-commit/salabim/peek)
211
-
1
+ <img src="https://www.salabim.org/xlwings_utils_logo2.png">
2
+
3
+ ## Introduction
4
+
5
+ This module provides some useful functions to be used in xlwings (lite).
6
+
7
+ ## Installation
8
+
9
+ Just add xlwings-utils to the *requirements.txt* tab.
10
+
11
+ In the script, add
12
+
13
+ ```ìmport xlwings_utils as xwu```
14
+
15
+ > [!NOTE]
16
+ >
17
+ > The GitHub repository can be found on https://github.com/salabim/xlwings_utils .
18
+
19
+ ## Dropbox support
20
+
21
+ 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.
22
+
23
+ It is only possible, as of now, to use full-access Dropbox apps.
24
+
25
+ The easiest way to use the Dropbox functionality is to add the credentials to the environment variables. Add REFRESH_TOKEN, APP_KEY and APP_SECRET with their corresponding values to the environment variables.
26
+
27
+ Then, it is possible to list all files in a specified folder with the function `list_dropbox`.
28
+ It is also possible to get the folders and to access all underlying folders.
29
+
30
+ The function `read_dropbox` can be used to read a Dropbox file's contents (bytes).
31
+
32
+ The function `write_dropbox` can be used to write contents (bytes) to a Dropbox file.
33
+
34
+ The functions `list_local`, `read_local` and `write_local` offer similar functionality for the local file system (on pyodide).
35
+
36
+ So, a way to access a file on the system's drive (mapped to Dropbox) as a local file is:
37
+
38
+ ```
39
+ contents = xlwings_utils.read_dropbox('/downloads/file1.xls')
40
+ xlwings_utils.write_local('file1.xlsx')
41
+ df = pandas.read_excel"file1.xlsx")
42
+ ...
43
+ ```
44
+ And the other direction:
45
+ ```
46
+ contents = xlwings_utils.read_local('file1.gif')
47
+ xlwings_utils.write_dropbox('/downloads/file1.gif')
48
+ ```
49
+
50
+ ## Block support
51
+
52
+ The module contains a useful 2-dimensional data structure: *block*.
53
+ This can be useful to manipulate a range without accessing the range directly, which is expensive in terms of memory and execution time.
54
+ The advantage over an ordinary list of lists is that a block is index one-based, in line with range and addressing is done with a row, column tuple.
55
+ So, `my_block(lol)[row, col]` is roughly equivalent to `lol[row-1][col-1]`
56
+
57
+ A block stores the values internally as a dictionary and will only convert these to a list of lists when using `block.value`.
58
+
59
+ Converting the value of a range (usually a list of lists, but can also be a list or scalar) to a block can be done with
60
+
61
+ ```
62
+ my_block = xwu.block.from_value(range.value)
63
+ ```
64
+ The dimensions (number of rows and number of columns) are automatically set.
65
+
66
+ Setting of an individual item (one-based, like range) can be done like
67
+ ```
68
+ my_block[row, column] = x
69
+ ```
70
+ And, likewise, reading an individual item can be done like
71
+ ```
72
+ x = my_block[row, column]
73
+ ```
74
+ It is not allowed t,o read or write outside the block dimensions.
75
+
76
+ It is also possible to define an empty block, like
77
+ ```
78
+ block = xlwings_utils.block(number_of_rows, number_columns)
79
+ ```
80
+ The dimensions can be queried or redefined with `block.number_of_rows` and
81
+ `block.number_of_columns`.
82
+
83
+ To assign a block to range, use
84
+ ```
85
+ range.value = block.value
86
+ ```
87
+
88
+ The property `block.highest_used_row_number` returns the row number of the highest non-None cell.
89
+
90
+ The property `block.highest_used_column_number` returns the column_number of the highest non-None cell.
91
+
92
+ The method `block.minimized()` returns a block that has the dimensions of (highest_used_row_number, highest_used_column_number).
93
+
94
+ Particularly if we process an unknown number of lines, we can do something like:
95
+
96
+ ```
97
+ this_block = xwu.block(number_of_rows=10000, number_of_columns=2)
98
+ for row in range(1, 10001):
99
+ this_block[row,1]= ...
100
+ this_block[row,2]= ...
101
+ if ...: # end condition
102
+ break
103
+ sheet.range(10,1).value = this_block.minimized().value
104
+ ```
105
+
106
+ In this case, only the really processed rows are copied to the sheet.
107
+
108
+ ### Looking up in a block
109
+
110
+ With blocks, it is easy to use a sheet as an input for a project / scenario.
111
+
112
+ Something like
113
+ <img src="https://www.salabim.org/xlwings_utils/fig01.png">
114
+
115
+ 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.
116
+
117
+ If we read the project sheet (partly) into a block, lookup methods are available to access 'fields' easily and future-proof.
118
+
119
+ Let's see how this works with the above sheet. The corresponding block (bl) looks like
120
+
121
+ ```
122
+ | 1 2 3 4 5
123
+ --+-------------------------------------------------------------------------------
124
+ 1 | Project Factory1
125
+ 2 | Start date 2025-05-17
126
+ 3 | End date 2026-02-01
127
+ 4 |
128
+ 5 | Parts Width Length Height Weight
129
+ 6 | A 10 5 5 100
130
+ 7 | B 11 5 8 102
131
+ 8 | C 12 2 3 91
132
+ 9 |
133
+ ```
134
+ Now we can do
135
+ ```project = bl.lookup("Project")
136
+ project = bl.lookup("Project")
137
+ start_date = bl.lookup("Start date")
138
+ end_date = bl.lookup("End date")
139
+ row1 = bl.lookup_row("Parts")
140
+ parts=[]
141
+ for row2 in range(row1 + 1, bl.highest_used_row_number + 1):
142
+ if not (part_name := bl.hlookup("Part",row1=row1, row2=row2)):
143
+ # stop when a 'blank' part_name is found
144
+ break
145
+ width = bl.hlookup("Width",row1=row1, row2=row2)
146
+ length = bl.hlookup("Length",row1=row1, row2=row2)
147
+ height = bl.hlookup("HeightL",row1=row1, row2=row2)
148
+ weight = bl.hlookup("Weight",row1=row1, row2=row2)
149
+ parts.append(Part(part_name, width, length, height, weight))
150
+ ```
151
+ 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.
152
+
153
+ 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.
154
+ And then we just read the following rows (with `hlookup`) and access the required values.
155
+
156
+ ### Filling a block from other sources
157
+
158
+ The advantage of using a block instead of accessing these sources is, that they are one-based, just like in Excel.
159
+
160
+ It is possible to make a block from a xlrd worksheet with `block.from_xlrd_sheet`.
161
+
162
+ 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)`.
163
+
164
+ It is possible to make a block from an openpyxl worksheet with `block.from_openpyxl_sheet`.
165
+
166
+ ### Writing a block to an openpyxl sheet
167
+
168
+ In order to write (append) to an openpyxl sheet, use: block.to_openpyxl_sheet.
169
+
170
+ It is possible to make a block from a text file with `block.from_file`.
171
+
172
+ ### Writing a block to an openpyxl sheet
173
+
174
+ ## Capture stdout support
175
+
176
+ The module has support for capturing stdout and -later- using showing the captured output on a sheet.
177
+
178
+ This is rather important as printing in xlwings lite to the UI pane is rather slow.
179
+
180
+ In order to capture stdout output, use `xwu.capture.enabled = True`. And to stop capturing, use `xwu.capture.enabled = False`.
181
+ Alternatively, a context manager is provided:
182
+
183
+
184
+ ```
185
+ with xwu.capture:
186
+ """
187
+ code with print statements
188
+ """
189
+ ```
190
+ Note that stopping the capture, leaves the captured output in place, so it can be extended later.
191
+
192
+ In either case, the captured output can be then copied to a sheet, like
193
+
194
+ ```
195
+ sheet.range(4,5).value = xwu.capture.value
196
+ ```
197
+ Upon reading the value, the capture buffer will be emptied.
198
+
199
+ If you don't want the buffer to be emptied after accessing the value, use `xwu.capture.value_keep`.
200
+
201
+ The capture buffer can also be retrieved as a string with `xwu.capture.str` and `xwu.capture.str_keep`.
202
+
203
+ Clearing the captured stdout buffer can be done at any time with `xwu.capture.clear()`.
204
+
205
+ 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.
206
+
207
+
208
+ ## Contact info
209
+
210
+ You can contact Ruud van der Ham, the core developer, via ruud@salabim.org .
211
+
212
+ ## Badges
213
+
214
+ ![PyPI](https://img.shields.io/pypi/v/xlwings-utils) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/xlwings-utils) ![PyPI - Implementation](https://img.shields.io/pypi/implementation/xlwings-utils)
215
+ ![PyPI - License](https://img.shields.io/pypi/l/xlwings-utils) ![ruff](https://img.shields.io/badge/style-ruff-41B5BE?style=flat)
216
+ ![GitHub last commit](https://img.shields.io/github/last-commit/salabim/peek)
217
+
@@ -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.0.post2"
13
+ version = "25.0.0.post3"
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.9"
16
16
  dependencies = [
@@ -178,8 +178,20 @@ def test_capture(capsys):
178
178
  assert xwu.capture.value_keep == [['abc'], ['def'], ['ghi'], ['jkl']]
179
179
  assert xwu.capture.value == [['abc'], ['def'], ['ghi'], ['jkl']]
180
180
  assert xwu.capture.value == []
181
-
182
- # include_print is not testable with pytest
181
+
182
+ xwu.capture.enabled=True
183
+ print("abc")
184
+ print("def")
185
+ xwu.capture.enabled=False
186
+ print("xxx")
187
+ print("yyy")
188
+ xwu.capture.enabled=True
189
+ print("ghi")
190
+ print("jkl")
191
+ assert xwu.capture.str_keep == "abc\ndef\nghi\njkl\n"
192
+
193
+
194
+ # include_print is not testable with pytest
183
195
 
184
196
  if __name__ == "__main__":
185
197
  pytest.main(["-vv", "-s", "-x", __file__])