xlwings-utils 25.0.5.post0__py3-none-any.whl → 25.0.6.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__ = "25.0.5"
8
+ __version__ = "25.0.6"
9
9
 
10
10
 
11
11
  import dropbox
@@ -155,7 +155,7 @@ def list_dropbox(path="", recursive=False, show_files=True, show_folders=False):
155
155
  return out
156
156
 
157
157
 
158
- def read_dropbox(dropbox_path):
158
+ def read_dropbox(dropbox_path, max_retries=100):
159
159
  """
160
160
  read_dropbox
161
161
 
@@ -166,6 +166,9 @@ def read_dropbox(dropbox_path):
166
166
  dropbox_path : str or Pathlib.Path
167
167
  path to read from
168
168
 
169
+ max_retries : int
170
+ number of retries (default: 100)
171
+
169
172
  Returns
170
173
  -------
171
174
  contents of the dropbox file : bytes
@@ -175,24 +178,25 @@ def read_dropbox(dropbox_path):
175
178
  If REFRESH_TOKEN, APP_KEY and APP_SECRET environment variables are specified,
176
179
  it is not necessary to call dropbox_init() prior to any dropbox function.
177
180
 
178
- If the file size does not match the metadata, as sometimes happens on pyodide, an OSError exception will be raised.
181
+ As reading from dropbox is very unreliable under pyodide, reading will have to be retried (by default maximum 100 times).
182
+ The number of retries can be found with read_dropbox.retries.
179
183
  """
180
184
 
181
185
  _login_dbx()
182
- metadata, response = dbx.files_download(dropbox_path)
183
- file_content = response.content
184
- if len(file_content) != metadata.size:
185
- raise OSError(f"file size ({len(file_content)}) does not match metadata ({metadata.size})")
186
- return file_content
186
+ for read_dropbox.retries in range(max_retries + 1):
187
+ metadata, response = dbx.files_download(dropbox_path)
188
+ file_content = response.content
189
+ if len(file_content) == metadata.size:
190
+ return file_content
191
+ raise OSError(f"after {max_retries} still no valid response")
187
192
 
188
193
 
189
194
  def write_dropbox(dropbox_path, contents):
190
- _login_dbx()
191
195
  """
192
196
  write_dropbox
193
-
197
+
194
198
  write from dopbox at given path
195
-
199
+
196
200
  Parameters
197
201
  ----------
198
202
  dropbox_path : str or Pathlib.Path
@@ -200,12 +204,13 @@ def write_dropbox(dropbox_path, contents):
200
204
 
201
205
  contents : bytes
202
206
  contents to be written
203
-
207
+
204
208
  Note
205
209
  ----
206
210
  If REFRESH_TOKEN, APP_KEY and APP_SECRET environment variables are specified,
207
211
  it is not necessary to call dropbox_init() prior to any dropbox function.
208
212
  """
213
+ _login_dbx()
209
214
  dbx.files_upload(contents, dropbox_path, mode=dropbox.files.WriteMode.overwrite)
210
215
 
211
216
 
@@ -429,6 +434,19 @@ class block:
429
434
  if column > self.number_of_columns:
430
435
  raise ValueError(f"{name}={column} > number_of_columns={self.number_of_columns}")
431
436
 
437
+ def transpose(self):
438
+ """
439
+ transpose block
440
+
441
+ Returns
442
+ -------
443
+ transposed block : block
444
+ """
445
+ bl = block(number_of_rows=self.number_of_columns, number_of_columns=self.number_of_rows)
446
+ for (row, column), value in self.dict.items():
447
+ bl[column, row] = value
448
+ return bl
449
+
432
450
  def vlookup(self, s, *, row_from=1, row_to=missing, column1=1, column2=missing, default=missing):
433
451
  """
434
452
  searches in column1 for row between row_from and row_to for s and returns the value found at (that row, column2)
@@ -680,63 +698,60 @@ class block:
680
698
  return self.vlookup(s, row_from=row_from, row_to=row_to, column1=column1, column2=column2, default=default)
681
699
 
682
700
  def decode_to_files(self):
683
- """
684
- decode the block with encoded file(s) to individual local files
685
701
  """
702
+ decode the block with encoded file(s) to individual pyoidide files
703
+ """
704
+ count = 0
705
+ for column in self.number_of_columns:
706
+ row = 1
707
+ bl = self.mimimized()
708
+ while row <= self.number_of_rows:
709
+ if self[row, column] and self[row, column].startswith("<file=") and self[row, column].endswith(">"):
710
+ filename = self[row, column][6:-1]
711
+ collect = []
712
+ row += 1
713
+ while bl[row, column] != "</file>":
714
+ if bl[row, column]:
715
+ collect.append(bl[row, column])
716
+ row += 1
717
+ decoded = base64.b64decode("".join(collect))
718
+ open(filename, "wb").write(decoded)
719
+ count += 1
720
+ row += 1
721
+ return count
686
722
 
687
- row=1
688
- while row<=self.number_of_rows:
689
- if self[row,1] and self[row,1].startswith('<file=') and self[row,1].endswith('>'):
690
- filename=self[row,1][6:-1]
691
- collect=[]
692
- row+=1
693
- print(f"{filename=}")
694
-
695
- while self[row,1]!='</file>':
696
- print(f"{self[row,1]=}")
697
-
698
- if self[row,1]:
699
- collect.append(self[row,1])
700
- row+=1
701
- print(f"{collect=}")
702
- decoded=base64.b64decode(''.join(collect))
703
- open(filename,'wb').write(decoded)
704
- row+=1
705
-
706
723
  @classmethod
707
- def encode_files(cls, *files):
724
+ def encode_file(cls, file):
708
725
  """
709
- make a block with the given files encoded
726
+ make a block with the given pyodide file encoded
710
727
 
711
728
  Parameters
712
729
  ----------
713
- files : file names (str)
714
- files to be encoded
730
+ file : file name (str)
731
+ file to be encoded
715
732
 
716
733
  Returns
717
734
  -------
718
- block with encoded files : block
719
- not minimized!
735
+ block with encoded file : block (minimized)
720
736
  """
721
737
 
722
- bl =cls(number_of_rows=50000, number_of_columns=1)
723
-
738
+ bl = cls(number_of_rows=100000, number_of_columns=1)
739
+
724
740
  n = 5000 # block size
725
741
  row = 1
726
- for file in files:
727
- bl[row, 1] = f"<file={file}>"
742
+ bl[row, 1] = f"<file={file}>"
743
+ row += 1
744
+ b64 = base64.b64encode(open(file, "rb").read()).decode("utf-8")
745
+ while b64:
746
+ b64_n = b64[:n]
747
+ bl[row, 1] = b64_n
728
748
  row += 1
729
- b64 = base64.b64encode(open(file, "rb").read()).decode("utf-8")
730
- while b64:
731
- b64_n = b64[:n]
732
- bl[row, 1] = b64_n
733
- row += 1
734
- b64 = b64[n:]
735
- bl[row, 1] = f"</file>"
736
- row+=1
737
- return bl
738
-
739
-
749
+ b64 = b64[n:]
750
+ bl[row, 1] = f"</file>"
751
+ row += 1
752
+ return bl.minimized()
753
+
754
+
740
755
  class Capture:
741
756
  """
742
757
  specifies how to capture stdout
@@ -842,9 +857,9 @@ class Capture:
842
857
  @include_print.setter
843
858
  def include_print(self, value):
844
859
  self._include_print = value
845
-
846
860
 
847
- def trigger(sheet):
861
+
862
+ def trigger_macro(sheet):
848
863
  """
849
864
  triggers the macro on sheet
850
865
 
@@ -852,11 +867,12 @@ def trigger(sheet):
852
867
  ----------
853
868
  sheet : sheet
854
869
  sheet to use
855
-
870
+
856
871
  """
857
872
 
858
873
  sheet["A1"].value = "=NOW()"
859
874
 
860
-
875
+
861
876
  if __name__ == "__main__":
862
877
  ...
878
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xlwings_utils
3
- Version: 25.0.5.post0
3
+ Version: 25.0.6.post0
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
@@ -62,8 +62,7 @@ python -c "exec(__import__('requests').get('https://salabim.org/dropbox setup.py
62
62
  Then, it is possible to list all files in a specified folder using the list_dropbox function.
63
63
  It is also possible to get the folders and to access all underlying folders.
64
64
 
65
- The `read_dropbox` function can be used to read the contents (bytes) of a Dropbox file. If the file is not read correctly, which seems
66
- to happen rather frequently, an OSError exception is raised.
65
+ The `read_dropbox` function can be used to read the contents (bytes) of a Dropbox file. As reading from Dropbox under pyodide is rather unreliable, xlwings_utils automatically retries several times (by default 100 times). The actual number of retries can be found with `read_dropbox.retries`.
67
66
 
68
67
  The function `write_dropbox` can be used to write contents (bytes) to a Dropbox file.
69
68
 
@@ -267,84 +266,52 @@ Clearing the captured stdout buffer can be done at any time with `capture.clear(
267
266
 
268
267
  Currently, *xlwings Lite* does not provide access to the local file system. Therefore, xlwings_utils offers some functionality to trigger a VBA script as well as functionality to encode a file in the pyodide file system to a VBA sheet and to trigger writing the encoded file(s) to the local file system.
269
268
 
270
- Note that the sheet must have a worksheet named `VBA`, and the VBA code will normally reside there.
269
+ Files can be encoded into a block, like:
271
270
 
272
- There are three Python functions defined:
271
+ ```
272
+ bl = xwu.block.encode_file("film1.mp4")
273
+ book.sheets["VBA"].range((10,1)).value=bl.value
274
+ ```
273
275
 
274
- * `trigger_VBA()`
275
- fires a VBA script, provided a VBA function is defined on the sheet, like:
276
-
277
- ```
278
- Sub Worksheet_Calculate()
279
- If Me.Range("A1").Formula = "=NOW()" Then
280
- Me.Range("A1").Value = Null
281
- REM Code to run
282
- End If
283
- End Sub
284
- ```
285
- * `init_transfer_files()`
286
- This should be called prior to any `transfer_file` call. It just removes all encoded files from the VBA sheet (if any)
276
+ With this code, column A will be filled with an encoded copy of the files *film1.mp4* . This can then be used with a suitable VBA macro to decode to the real file system. A VBA macro can be triggered with `xwu.trigger_macro()`. This requires an Excel worksheet where cell A1 is reserved for communication with xlwings lite. This worksheet needs to contain a macro, like
277
+
278
+ ```
279
+ Private Sub Worksheet_Calculate()
280
+ If Me.Range("A1").Formula = "=NOW()" Then
281
+ Me.Range("A1").Value = Null
282
+ Call MacroToExecute
283
+ End If
284
+ End Sub
285
+ ```
287
286
 
288
- * `transfer_file()`
289
- Can be used to encode a file on the pyodide file system to the VBA sheet. Multiple files are allowed. The file name will be encoded on the sheet as well.
287
+ , where `MacroToExecute` should contain the user code, most likely code to decode file(s) encoded.
290
288
 
291
- The VBA code below can be used to decode encoded file(s) and write to the local file system.
289
+ The repo contains a VBA module called xlwings_utils.bas with code to decode encoded files.
290
+ Just add the .bas file to a worksheet and call like
292
291
 
293
292
  ```
294
- Sub WriteFiles()
295
-
296
- Dim vArr() As Byte
297
- Dim S As String
298
- Dim Column As Integer
299
- Dim Row As Integer
300
- Dim ws As Worksheet
301
- Dim Count As Integer
302
- Dim FileNames As String
303
-
304
- Column = 1
305
- Row = 1
306
-
307
- ThisDir = ThisWorkbook.Path
308
- Set ws = Me
309
- Count = 0
310
- While Row < 30000
311
- Line = ws.Cells(Row, Column)
312
- If InStr(Line, "<file=") = 1 And Right(Line, 1) = ">" Then
313
- If Count <> 0 Then
314
- FileNames = FileNames & ", "
315
- End If
316
- Count = Count + 1
317
- FileNameOnly = Mid(Line, 7, Len(Line) - 7)
318
- Filename = ThisDir & "/" & FileNameOnly
319
- FileNames = FileNames & FileNameOnly
320
-
321
- Row = Row + 1
322
- S = ""
323
- While ws.Cells(Row, Column) <> "</file>"
324
- S = S & ws.Cells(Row, Column)
325
- Row = Row + 1
326
- Wend
327
-
328
- vArr = Base64ToArray(S)
329
-
330
- Open Filename For Binary Access Write As #1
331
- WritePos = 1
332
- Put #1, WritePos, vArr
333
- Close #1
334
- End If
335
-
336
- Row = Row + 1
337
-
338
- Wend
339
- If Cells(4, 2) = "y" Then
340
- ws.Range("A10:A1000000").Clear
341
- End If
293
+ Sub MacroToExecute()
294
+ DecodeFile(Me, 10, 1)
295
+ ```
342
296
 
343
- If Count = 0 Then
344
- MsgBox "No files written"
345
- Else
346
- MsgBox "Successfully written " & Str(Count) & " file(s): " & FileNames
347
- End If
297
+ In this example, the file *film1.mp4* will be downloaded into the current directory.
298
+
299
+ The module xlwings_utils also contains code to encode a local file to a sheet:
300
+
301
+ ```
302
+ Sub MacroToExecute()
303
+ EncodeFile(Me, "data.json", 10, 1)
304
+ ```
305
+
306
+ This will place an encoded version of *data.json* on the sheet.
307
+
308
+ Then, the file can be copied to the pyodide file system with
309
+
310
+ ```
311
+ bl = block(xw.range((10,1),(50000,1)).decode_to_files())
312
+ ```
313
+ ```
314
+
348
315
  ```
349
316
 
350
317
  ## Contact info
@@ -0,0 +1,6 @@
1
+ xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
2
+ xlwings_utils/xlwings_utils.py,sha256=arj8LGanSJk3pKER9_cIMNuCB77hBVtiPpiKUaXqqKY,25775
3
+ xlwings_utils-25.0.6.post0.dist-info/METADATA,sha256=uUqweJdGbgthjjuACxO2JwfgsBojtsyIrWeBRahVQIo,12255
4
+ xlwings_utils-25.0.6.post0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ xlwings_utils-25.0.6.post0.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
+ xlwings_utils-25.0.6.post0.dist-info/RECORD,,
@@ -1,6 +0,0 @@
1
- xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
2
- xlwings_utils/xlwings_utils.py,sha256=KP4RPmEyajLF2pn9IS3QEQyYC_xBn1yNoWzdh3cqqSo,25238
3
- xlwings_utils-25.0.5.post0.dist-info/METADATA,sha256=1amSYUkgWF76__slfIRyOpR-J-VbHjeoqmZxipWK21Q,13036
4
- xlwings_utils-25.0.5.post0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
- xlwings_utils-25.0.5.post0.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
- xlwings_utils-25.0.5.post0.dist-info/RECORD,,