xlwings-utils 0.0.6__py3-none-any.whl → 0.0.7.post0__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.

@@ -5,7 +5,7 @@
5
5
  # /_/\_\|_| \_/\_/ |_||_| |_| \__, ||___/ _____ \__,_| \__||_||_||___/
6
6
  # |___/ |_____|
7
7
 
8
- __version__ = "0.0.6"
8
+ __version__ = "0.0.7"
9
9
 
10
10
 
11
11
  import dropbox
@@ -13,7 +13,8 @@ from pathlib import Path
13
13
  import os
14
14
  import sys
15
15
 
16
- _captured_stdout = []
16
+ _org_stdout = sys.stdout
17
+
17
18
  dbx = None
18
19
  Pythonista = sys.platform == "ios"
19
20
 
@@ -217,7 +218,7 @@ def list_local(path, recursive=False, show_files=True, show_folders=False):
217
218
  show_folders : bool
218
219
  if True, show folder entries
219
220
  if False (default), do not show folder entries
220
-
221
+
221
222
  Returns
222
223
  -------
223
224
  files, relative to path : list
@@ -268,55 +269,48 @@ class block:
268
269
  block
269
270
  """
270
271
 
271
- def __init__(self, number_of_rows, number_of_columns):
272
+ def __init__(self, value=None, *, number_of_rows=None, number_of_columns=None, column_like=False):
272
273
  self.dict = {}
273
- self.number_of_columns = number_of_columns
274
- self.number_of_rows = number_of_rows
275
-
276
- @classmethod
277
- def from_list_of_lists(cls, list_of_lists, column_like=False):
278
- """
279
- from_list_of_lists
280
-
281
- Parameters
282
- ----------
283
- list_of_lists : list of lists
284
- to be used to fill the block
285
-
286
- columns_like : bool
287
- if False (default), one dimenional lists will be treated as a 1 high range (row-like)
288
-
289
- if True, one dimenional lists will be treated as a 1 wide range (column-like)
290
-
291
- This parameter is not used for proper 2 dimensional list of lists or scalars
292
-
293
- Returns
294
- -------
295
- block
296
-
297
- Note
298
- ----
299
- number_of_rows and number_of_columns will be retrieved from list_of_lists dimension
300
- """
301
- if not isinstance(list_of_lists, list):
302
- list_of_lists = [[list_of_lists]]
303
- if not isinstance(list_of_lists[0], list):
304
- if column_like:
305
- list_of_lists = [[value] for value in list_of_lists]
306
- else:
307
- list_of_lists = [list_of_lists]
274
+ self.column_like = column_like
275
+ if value is None:
276
+ if number_of_rows is None:
277
+ number_of_rows = 1
278
+ if number_of_columns is None:
279
+ number_of_columns = 1
280
+ self.number_of_rows = number_of_rows
281
+ self.number_of_columns = number_of_columns
282
+
283
+ else:
284
+ if isinstance(value, block):
285
+ value = value.value
286
+ self.value = value
287
+ if number_of_rows is not None:
288
+ self.number_of_rows = number_of_rows
289
+ if number_of_columns is not None:
290
+ self.number_of_columns = number_of_columns
308
291
 
309
- self = cls(1, 1)
292
+ @property
293
+ def value(self):
294
+ 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)]
295
+
296
+ @value.setter
297
+ def value(self, value):
298
+ if not isinstance(value, list):
299
+ value = [[value]]
300
+ if not isinstance(value[0], list):
301
+ if self.column_like:
302
+ value = [[item] for item in value]
303
+ else:
304
+ value = [value]
310
305
 
311
- self.number_of_rows = len(list_of_lists)
306
+ self.number_of_rows = len(value)
312
307
  self._number_of_columns = 0
313
308
 
314
- for row, row_contents in enumerate(list_of_lists, 1):
315
- for column, value in enumerate(row_contents, 1):
316
- if value is not None:
317
- self.dict[row, column] = value
309
+ for row, row_contents in enumerate(value, 1):
310
+ for column, item in enumerate(row_contents, 1):
311
+ if item is not None:
312
+ self.dict[row, column] = item
318
313
  self._number_of_columns = max(self.number_of_columns, column)
319
- return self
320
314
 
321
315
  def __setitem__(self, row_column, value):
322
316
  row, column = row_column
@@ -334,13 +328,8 @@ class block:
334
328
  raise IndexError(f"column must be between 1 and {self.number_of_columns} not {column}")
335
329
  return self.dict.get((row, column))
336
330
 
337
- @property
338
- def as_list_of_lists(self):
339
- 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)]
340
-
341
- @property
342
- def as_minimal_list_of_lists(self):
343
- return [[self.dict.get((row, column)) for column in range(1, self.maximum_column + 1)] for row in range(1, self.maximum_row + 1)]
331
+ def minimized(self):
332
+ return block(self, number_of_rows=self.highest_used_row_number, number_of_columns=self.highest_used_column_number)
344
333
 
345
334
  @property
346
335
  def number_of_rows(self):
@@ -369,31 +358,39 @@ class block:
369
358
  del self.dict[row, column]
370
359
 
371
360
  @property
372
- def maximum_row(self):
361
+ def highest_used_row_number(self):
373
362
  if self.dict:
374
363
  return max(row for (row, column) in self.dict)
375
364
  else:
376
365
  return 1
377
366
 
378
367
  @property
379
- def maximum_column(self):
368
+ def highest_used_column_number(self):
380
369
  if self.dict:
381
370
  return max(column for (row, column) in self.dict)
382
371
  else:
383
372
  return 1
384
373
 
385
374
  def __repr__(self):
386
- return f"block.from_list_of_lists({self.as_list_of_lists})"
375
+ return f"block({self.value})"
387
376
 
388
377
 
389
- def clear_captured_stdout():
378
+ def clear_captured_stdout(clear=True):
390
379
  """
391
380
  empties the captured stdout
381
+
382
+ Parameters
383
+ ----------
384
+ clear : bool
385
+ if True (default), the capture buffer is emptied
386
+
387
+ if False: no action
392
388
  """
393
- _captured_stdout.clear()
389
+ if clear:
390
+ _buffer.clear()
394
391
 
395
392
 
396
- def captured_stdout_as_str():
393
+ def captured_stdout_as_str(clear=True):
397
394
  """
398
395
  returns the captured stdout as a string
399
396
 
@@ -402,64 +399,84 @@ def captured_stdout_as_str():
402
399
  captured stdout : list
403
400
  each line is an element of the list
404
401
  """
405
-
406
- return "".join(_captured_stdout)
402
+ result = "".join(_buffer)
403
+ clear_captured_stdout(clear)
404
+ return result
407
405
 
408
406
 
409
- def captured_stdout_as_list_of_lists():
407
+ def captured_stdout_as_value(clear=True):
410
408
  """
411
409
  returns the captured stdout as a list of lists
412
410
 
413
411
  Returns
414
412
  -------
415
- captured stdout : list
413
+ captured stdout : list of lists
416
414
  each line is an element of the list
417
415
 
418
416
  Note
419
417
  ----
420
418
  This can be used directly to fill a xlwings range
421
419
  """
422
- return [[line] for line in captured_stdout_as_str().splitlines()]
420
+ return [[line] for line in captured_stdout_as_str(clear).splitlines()]
421
+
423
422
 
423
+ class handle_capture_stdout:
424
+ def __init__(self):
425
+ pass
424
426
 
425
- class capture_stdout:
427
+ def isatty(self):
428
+ return False
429
+
430
+ def write(self, data):
431
+ if _do_capture:
432
+ _buffer.append(data)
433
+ if _do_print:
434
+ _org_stdout.write(data)
435
+
436
+ def flush(self):
437
+ if _do_print:
438
+ _org_stdout.flush()
439
+ if _do_capture:
440
+ _buffer.append("\n")
441
+
442
+
443
+ def reset():
444
+ global _buffer
445
+ global _do_print
446
+ global _do_capture
447
+ _buffer = []
448
+ _do_print = True
449
+ _do_capture = False
450
+ sys.stdout = handle_capture_stdout()
451
+
452
+ reset()
453
+
454
+ def capture_stdout(do_print=True, do_capture=True, clear=True):
455
+ global _do_print
456
+ global _do_capture
426
457
  """
427
458
  start capture stdout
428
459
 
429
460
  Parameters
430
461
  ----------
431
- include_print : bool
432
- if True (default), the output is also printed out as normal
462
+ do_print : bool
463
+ if True (default), the output is (also) printed out as normal
433
464
 
434
465
  if False, no output is printed
435
466
 
436
- Note
437
- ----
438
- This function is normally used as a context manager, like ::
467
+ do_capture : bool
468
+ if True (default), the output is (also) captured to a buffer
439
469
 
440
- with capture_stdout():
441
- ...
442
- """
443
-
444
- def __init__(self, include_print: bool = True):
445
- self.stdout = sys.stdout
446
- self.include_print = include_print
447
-
448
- def __enter__(self):
449
- sys.stdout = self
450
-
451
- def __exit__(self, exc_type, exc_value, tb):
452
- sys.stdout = self.stdout
470
+ if False, no output is captured
453
471
 
454
- def write(self, data):
455
- _captured_stdout.append(data)
456
- if self.include_print:
457
- self.stdout.write(data)
458
-
459
- def flush(self):
460
- if self.include_print:
461
- self.stdout.flush()
462
- _captured_stdout.append("\n")
472
+ clear : bool
473
+ if True (default), the capture buffer is emptied
474
+ """
475
+ if not (do_print or do_capture):
476
+ raise ValueError("at least one of do_print and do_capture should be True")
477
+ clear_captured_stdout(clear)
478
+ _do_print = do_print
479
+ _do_capture = do_capture
463
480
 
464
481
 
465
482
  if __name__ == "__main__":
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: xlwings_utils
3
+ Version: 0.0.7.post0
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
+ ## Capture stdout support
123
+
124
+ The module has support for capturing stdout and -later- using showing the captured output on a sheet.
125
+
126
+ To do that:
127
+
128
+ ```makes it possible to capture stdout writes, which
129
+ with xwu.capture_stdout():
130
+ ...
131
+ ```
132
+ and then the captured output can be copied to the screen, like
133
+
134
+ ```can then be copied to a worksheet in a later stage.
135
+ sheet.range(4,5).value = xwu.captured_stdout_as_value()
136
+ ```
137
+
138
+ Clearing the captured stdout buffer can be done with `xwu.clear_captured_std_out`.
139
+
140
+ Normally, stdout will also be sent to the xlwings lite UI panel. This can be suppressed with
141
+
142
+ ```
143
+ with xwu.capture_stdout(include_print=False):
144
+ ...
145
+ ```
146
+
147
+ ## Contact info
148
+
149
+ You can contact Ruud van der Ham, the core developer, via ruud@salabim.org .
150
+
151
+ ## Badges
152
+
153
+ ![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)
154
+ ![PyPI - License](https://img.shields.io/pypi/l/xlwings-utils) ![ruff](https://img.shields.io/badge/style-ruff-41B5BE?style=flat)
155
+ ![GitHub last commit](https://img.shields.io/github/last-commit/salabim/peek)
156
+
@@ -0,0 +1,6 @@
1
+ xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
2
+ xlwings_utils/xlwings_utils.py,sha256=J-OBksRT1nBZZLmo4qYcFa1iNLQvH0wGjnBSEIuz-Y0,13237
3
+ xlwings_utils-0.0.7.post0.dist-info/METADATA,sha256=mdTOcI24b_DCtg_FnumaXzAcmamKTx6nDH0cuBWyoAo,5695
4
+ xlwings_utils-0.0.7.post0.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
5
+ xlwings_utils-0.0.7.post0.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
+ xlwings_utils-0.0.7.post0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,52 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: xlwings_utils
3
- Version: 0.0.6
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_logo1.png">
16
-
17
- ## Introduction
18
-
19
- This module provides some useful functions to be used in xlwings lite.
20
-
21
- The xlwings lite system does not provide access to the local file system. With this
22
- module, files can be copied between dropbox and the local pyodide file systen. And
23
- therefore, it is possible to indirectly use the local file system.
24
-
25
- The module contains support for a useful 2 dimensional data structure: block.
26
- Thjs can be useful to manipulate a range without accessing the range directly,
27
- which is expensive in terms of memory and execution time.
28
-
29
- On top of that, this module makes it possible to capture stdout writes, which
30
- can then be copied to a worksheet in a later stage.
31
-
32
- ## Installation
33
-
34
- Just add xlwings-utils to the requirement tab. It might be required to add ssl.
35
-
36
- ## Dropbox support
37
-
38
- xlwings_lite only works with full access dropbox apps.
39
-
40
- In order to use dropbox functionality, is is necessary to initialize the module with credentials.
41
-
42
- ```xwu.dropbox_init()```
43
- If called without parameters, the refresh_token is
44
-
45
- ## Capture stdout support
46
-
47
- Badges
48
-
49
- ![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)
50
- ![PyPI - License](https://img.shields.io/pypi/l/xlwings-utils) ![ruff](https://img.shields.io/badge/style-ruff-41B5BE?style=flat)
51
- ![GitHub last commit](https://img.shields.io/github/last-commit/salabim/peek)
52
-
@@ -1,6 +0,0 @@
1
- xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
2
- xlwings_utils/xlwings_utils.py,sha256=yvWW2yRjkHvF5814ptlbGxT5tpRRg-wH1yCDO-5qIe8,12866
3
- xlwings_utils-0.0.6.dist-info/METADATA,sha256=0fJEsPdOJ1uoHQnAeGzB5dPm-qKD9F_1CGfP9h1Oc4s,2083
4
- xlwings_utils-0.0.6.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
5
- xlwings_utils-0.0.6.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
- xlwings_utils-0.0.6.dist-info/RECORD,,