xlwings-utils 0.0.2__tar.gz → 0.0.4__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.
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/PKG-INFO +1 -1
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/pyproject.toml +1 -1
- xlwings_utils-0.0.4/tests/test_xlwings_utils.py +75 -0
- xlwings_utils-0.0.4/xlwings_utils/xlwings_utils.py +382 -0
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/xlwings_utils.egg-info/PKG-INFO +1 -1
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/xlwings_utils.egg-info/SOURCES.txt +1 -0
- xlwings_utils-0.0.2/xlwings_utils/xlwings_utils.py +0 -286
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/README.md +0 -0
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/setup.cfg +0 -0
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/xlwings_utils/__init__.py +0 -0
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/xlwings_utils.egg-info/dependency_links.txt +0 -0
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/xlwings_utils.egg-info/requires.txt +0 -0
- {xlwings_utils-0.0.2 → xlwings_utils-0.0.4}/xlwings_utils.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import itertools
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import re
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
if __name__ == "__main__": # to make the tests run without the pytest cli
|
|
9
|
+
file_folder = os.path.dirname(__file__)
|
|
10
|
+
os.chdir(file_folder)
|
|
11
|
+
sys.path.insert(0, file_folder + "/../xlwings_utils")
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
import xlwings_utils as xwu
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_block():
|
|
19
|
+
this_block = xwu.block(number_of_rows=4, number_of_columns=6)
|
|
20
|
+
this_block[1, 2] = 2
|
|
21
|
+
this_block[2, 5] = 25
|
|
22
|
+
assert this_block.dict == {(1, 2): 2, (2, 5): 25}
|
|
23
|
+
assert this_block.as_list_of_lists == [
|
|
24
|
+
[None, 2, None, None, None, None],
|
|
25
|
+
[None, None, None, None, 25, None],
|
|
26
|
+
[None, None, None, None, None, None],
|
|
27
|
+
[None, None, None, None, None, None],
|
|
28
|
+
]
|
|
29
|
+
assert this_block.as_minimal_list_of_lists == [[None, 2, None, None, None], [None, None, None, None, 25]]
|
|
30
|
+
assert this_block.number_of_rows == 4
|
|
31
|
+
assert this_block.number_of_columns == 6
|
|
32
|
+
assert this_block.maximum_row == 2
|
|
33
|
+
assert this_block.maximum_column == 5
|
|
34
|
+
|
|
35
|
+
this_block.number_of_rows = 99
|
|
36
|
+
this_block.number_of_columns = 99
|
|
37
|
+
assert this_block.maximum_row == 2
|
|
38
|
+
assert this_block.maximum_column == 5
|
|
39
|
+
|
|
40
|
+
this_block.number_of_rows = 1
|
|
41
|
+
this_block.number_of_columns = 3
|
|
42
|
+
assert this_block.as_list_of_lists == [[None, 2, None]]
|
|
43
|
+
assert this_block.as_minimal_list_of_lists == [[None, 2]]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_block_from_list_of_lists():
|
|
47
|
+
this_block = xwu.block.from_list_of_lists([[1, 2, 3], [4, 5, 6]])
|
|
48
|
+
assert this_block.dict == {(1, 1): 1, (1, 2): 2, (1, 3): 3, (2, 1): 4, (2, 2): 5, (2, 3): 6}
|
|
49
|
+
assert this_block.as_list_of_lists == [[1, 2, 3], [4, 5, 6]]
|
|
50
|
+
assert this_block.as_minimal_list_of_lists == [[1, 2, 3], [4, 5, 6]]
|
|
51
|
+
with pytest.raises(ValueError):
|
|
52
|
+
this_block.number_of_rows = 0
|
|
53
|
+
with pytest.raises(ValueError):
|
|
54
|
+
this_block.number_of_columns = 0
|
|
55
|
+
with pytest.raises(ValueError):
|
|
56
|
+
this_block = xwu.block(0, 1)
|
|
57
|
+
with pytest.raises(ValueError):
|
|
58
|
+
this_block = xwu.block(1, 0)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_block_one_dimension():
|
|
62
|
+
this_block = xwu.block.from_list_of_lists([1, 2, 3])
|
|
63
|
+
assert this_block.as_list_of_lists == [[1, 2, 3]]
|
|
64
|
+
|
|
65
|
+
this_block = xwu.block.from_list_of_lists([1, 2, 3], column_like=True)
|
|
66
|
+
assert this_block.as_list_of_lists == [[1], [2], [3]]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_block_scalar():
|
|
70
|
+
this_block = xwu.block.from_list_of_lists(1, column_like=True)
|
|
71
|
+
assert this_block.as_list_of_lists == [[1]]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
pytest.main(["-vv", "-s", "-x", __file__])
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# _ _ _ _ _
|
|
2
|
+
# __ __| |__ __(_) _ __ __ _ ___ _ _ | |_ (_)| | ___
|
|
3
|
+
# \ \/ /| |\ \ /\ / /| || '_ \ / _` |/ __| | | | || __|| || |/ __|
|
|
4
|
+
# > < | | \ V V / | || | | || (_| |\__ \ | |_| || |_ | || |\__ \
|
|
5
|
+
# /_/\_\|_| \_/\_/ |_||_| |_| \__, ||___/ _____ \__,_| \__||_||_||___/
|
|
6
|
+
# |___/ |_____|
|
|
7
|
+
|
|
8
|
+
__version__ = "0.0.4"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
import dropbox
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
import os
|
|
14
|
+
|
|
15
|
+
class BlockRange:
|
|
16
|
+
@property
|
|
17
|
+
def block(self):
|
|
18
|
+
return block(self.value)
|
|
19
|
+
|
|
20
|
+
@block.setter
|
|
21
|
+
def block(self, block):
|
|
22
|
+
self.value=block.as_list_of_lists
|
|
23
|
+
try:
|
|
24
|
+
import xlwings
|
|
25
|
+
xlwings.Range.block=BlockRange
|
|
26
|
+
except ImportError:
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
_captured_stdout = []
|
|
30
|
+
dbx = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def dropbox_init(refresh_token=None, app_key=None, app_secret=None):
|
|
34
|
+
"""
|
|
35
|
+
dropbox initialize
|
|
36
|
+
|
|
37
|
+
This function has to be called prior to using any dropbox function
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
refresh_token : str
|
|
42
|
+
oauth2 refreshntoken
|
|
43
|
+
|
|
44
|
+
if omitted: use the environment variable REFRESH_TOKEN
|
|
45
|
+
|
|
46
|
+
app_key : str
|
|
47
|
+
app key
|
|
48
|
+
|
|
49
|
+
if omitted: use the environment variable APP_KEY
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
app_secret : str
|
|
53
|
+
app secret
|
|
54
|
+
|
|
55
|
+
if omitted: use the environment variable APP_SECRET
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
-
|
|
60
|
+
"""
|
|
61
|
+
if refresh_token is None:
|
|
62
|
+
refresh_token = os.environ["REFRESH_TOKEN"]
|
|
63
|
+
if app_key is None:
|
|
64
|
+
app_key = os.environ["APP_KEY"]
|
|
65
|
+
if app_secret is None:
|
|
66
|
+
app_secret = os.environ["APP_SECRET"]
|
|
67
|
+
|
|
68
|
+
global dbx
|
|
69
|
+
dbx = dropbox.Dropbox(oauth2_refresh_token=refresh_token, app_key=app_key, app_secret=app_secret)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _check_dbx():
|
|
73
|
+
if dbx is None:
|
|
74
|
+
raise ValueError("not initialized. Please call dropbox_init()")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def list_dropbox(path="", recursive=False):
|
|
78
|
+
"""
|
|
79
|
+
list_dropbox
|
|
80
|
+
|
|
81
|
+
returns all dropbox files in path
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
path : str or Pathlib.Path
|
|
86
|
+
path from which to list all files (default: '')
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
recursive : bool
|
|
90
|
+
if True, recursively list files. if False (default) no recursion
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
files, relative to path : list
|
|
95
|
+
"""
|
|
96
|
+
_check_dbx()
|
|
97
|
+
out = []
|
|
98
|
+
result = dbx.files_list_folder(path, recursive=recursive)
|
|
99
|
+
|
|
100
|
+
for entry in result.entries:
|
|
101
|
+
if isinstance(entry, dropbox.files.FileMetadata):
|
|
102
|
+
out.append(entry.path_display)
|
|
103
|
+
|
|
104
|
+
while result.has_more:
|
|
105
|
+
result = dbx.files_list_folder_continue(result.cursor)
|
|
106
|
+
for entry in result.entries:
|
|
107
|
+
if isinstance(entry, dropbox.files.FileMetadata):
|
|
108
|
+
out.append(entry.path_display)
|
|
109
|
+
|
|
110
|
+
return out
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def read_dropbox(dropbox_path):
|
|
114
|
+
"""
|
|
115
|
+
read_dropbox
|
|
116
|
+
|
|
117
|
+
read from dopbox at given path
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
dropbox_path : str or Pathlib.Path
|
|
122
|
+
path to read from
|
|
123
|
+
|
|
124
|
+
Returns
|
|
125
|
+
-------
|
|
126
|
+
contents of the dropbox file : bytes
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
_check_dbx()
|
|
130
|
+
metadata, response = dbx.files_download(dropbox_path)
|
|
131
|
+
file_content = response.content
|
|
132
|
+
return file_content
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def write_dropbox(dropbox_path, contents):
|
|
136
|
+
_check_dbx()
|
|
137
|
+
"""
|
|
138
|
+
write_dropbox
|
|
139
|
+
|
|
140
|
+
write from dopbox at given path
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
dropbox_path : str or Pathlib.Path
|
|
145
|
+
path to write to
|
|
146
|
+
|
|
147
|
+
contents : bytes
|
|
148
|
+
contents to be written
|
|
149
|
+
|
|
150
|
+
"""
|
|
151
|
+
dbx.files_upload(contents, dropbox_path, mode=dropbox.files.WriteMode.overwrite)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def list_local(path):
|
|
155
|
+
path = Path(path)
|
|
156
|
+
return list(path.iterdir())
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def write_local(path, contents):
|
|
160
|
+
path = Path(path)
|
|
161
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
with open(path, "wb") as f:
|
|
163
|
+
f.write(contents)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def read_local(path):
|
|
167
|
+
path = Path(path)
|
|
168
|
+
with open(path, "rb") as f:
|
|
169
|
+
contents = f.read()
|
|
170
|
+
return contents
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class block:
|
|
174
|
+
"""
|
|
175
|
+
block is 2 dimensional with 1 as lowest index (like xlwings range) data structure
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
number_of_rows : int
|
|
180
|
+
number of rows
|
|
181
|
+
|
|
182
|
+
number_of_columns : int
|
|
183
|
+
number of columns
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
-------
|
|
187
|
+
block
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
def __init__(self, number_of_rows, number_of_columns):
|
|
191
|
+
self.dict = {}
|
|
192
|
+
self.number_of_columns = number_of_columns
|
|
193
|
+
self.number_of_rows = number_of_rows
|
|
194
|
+
|
|
195
|
+
@classmethod
|
|
196
|
+
def from_list_of_lists(cls, list_of_lists, column_like=False):
|
|
197
|
+
"""
|
|
198
|
+
from_list_of_lists
|
|
199
|
+
|
|
200
|
+
Parameters
|
|
201
|
+
----------
|
|
202
|
+
list_of_lists : list of lists
|
|
203
|
+
to be used to fill the block
|
|
204
|
+
|
|
205
|
+
columns_like : bool
|
|
206
|
+
if False (default), one dimenional lists will be treated as a 1 high range (row-like)
|
|
207
|
+
|
|
208
|
+
if True, one dimenional lists will be treated as a 1 wide range (column-like)
|
|
209
|
+
|
|
210
|
+
This parameter is not used for proper 2 dimensional list of lists
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
block
|
|
215
|
+
|
|
216
|
+
Note
|
|
217
|
+
----
|
|
218
|
+
number_of_rows and number_of_columns will be retrieved from list_of_lists dimension
|
|
219
|
+
"""
|
|
220
|
+
if not isinstance(list_of_lists, list):
|
|
221
|
+
list_of_lists = [[list_of_lists]]
|
|
222
|
+
if not isinstance(list_of_lists[0], list):
|
|
223
|
+
if column_like:
|
|
224
|
+
list_of_lists = [[value] for value in list_of_lists]
|
|
225
|
+
else:
|
|
226
|
+
list_of_lists = [list_of_lists]
|
|
227
|
+
|
|
228
|
+
self = cls(1, 1)
|
|
229
|
+
|
|
230
|
+
self.number_of_rows = len(list_of_lists)
|
|
231
|
+
self._number_of_columns = 0
|
|
232
|
+
|
|
233
|
+
for row, row_contents in enumerate(list_of_lists, 1):
|
|
234
|
+
for column, value in enumerate(row_contents, 1):
|
|
235
|
+
if value is not None:
|
|
236
|
+
self.dict[row, column] = value
|
|
237
|
+
self._number_of_columns = max(self.number_of_columns, column)
|
|
238
|
+
return self
|
|
239
|
+
|
|
240
|
+
def __setitem__(self, row_column, value):
|
|
241
|
+
row, column = row_column
|
|
242
|
+
if row < 1 or row > self.number_of_rows:
|
|
243
|
+
raise IndexError
|
|
244
|
+
if column < 1 or column > self.number_of_columns:
|
|
245
|
+
raise IndexError
|
|
246
|
+
self.dict[row, column] = value
|
|
247
|
+
|
|
248
|
+
def __getitem__(self, row_column):
|
|
249
|
+
row, column = row_column
|
|
250
|
+
if row < 1 or row > self.number_of_rows:
|
|
251
|
+
raise IndexError
|
|
252
|
+
if column < 1 or column > self.number_of_columns:
|
|
253
|
+
raise IndexError
|
|
254
|
+
return self.dict.get((row, column))
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def as_list_of_lists(self):
|
|
258
|
+
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)]
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def as_minimal_list_of_lists(self):
|
|
262
|
+
return [[self.dict.get((row, column)) for column in range(1, self.maximum_column + 1)] for row in range(1, self.maximum_row + 1)]
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def number_of_rows(self):
|
|
266
|
+
return self._number_of_rows
|
|
267
|
+
|
|
268
|
+
@number_of_rows.setter
|
|
269
|
+
def number_of_rows(self, value):
|
|
270
|
+
if value<1:
|
|
271
|
+
raise ValueError(f"number_of_rows should be >=1, not {value}")
|
|
272
|
+
self._number_of_rows = value
|
|
273
|
+
for row, column in list(self.dict):
|
|
274
|
+
if row > self._number_of_rows:
|
|
275
|
+
del self.dict[row, column]
|
|
276
|
+
|
|
277
|
+
@property
|
|
278
|
+
def number_of_columns(self):
|
|
279
|
+
return self._number_of_columns
|
|
280
|
+
|
|
281
|
+
@number_of_columns.setter
|
|
282
|
+
def number_of_columns(self, value):
|
|
283
|
+
if value<1:
|
|
284
|
+
raise ValueError(f"number_of_columns should be >=1, not {value}")
|
|
285
|
+
self._number_of_columns = value
|
|
286
|
+
for row, column in list(self.dict):
|
|
287
|
+
if column > self._number_of_columns:
|
|
288
|
+
del self.dict[row, column]
|
|
289
|
+
|
|
290
|
+
@property
|
|
291
|
+
def maximum_row(self):
|
|
292
|
+
if self.dict:
|
|
293
|
+
return max(row for (row, column) in self.dict)
|
|
294
|
+
else:
|
|
295
|
+
return 1
|
|
296
|
+
|
|
297
|
+
@property
|
|
298
|
+
def maximum_column(self):
|
|
299
|
+
if self.dict:
|
|
300
|
+
return max(column for (row, column) in self.dict)
|
|
301
|
+
else:
|
|
302
|
+
return 1
|
|
303
|
+
|
|
304
|
+
def __repr__(self):
|
|
305
|
+
return f"block.from_list_of_lists({self.as_list_of_lists})"
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def clear_captured_stdout():
|
|
309
|
+
"""
|
|
310
|
+
empties the captured stdout
|
|
311
|
+
"""
|
|
312
|
+
_captured_stdout.clear()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def captured_stdout_as_str():
|
|
316
|
+
"""
|
|
317
|
+
returns the captured stdout as a list of strings
|
|
318
|
+
|
|
319
|
+
Returns
|
|
320
|
+
-------
|
|
321
|
+
captured stdout : list
|
|
322
|
+
each line is an element of the list
|
|
323
|
+
"""
|
|
324
|
+
|
|
325
|
+
return "".join(_captured_stdout)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def captured_stdout_as_list_of_lists():
|
|
329
|
+
"""
|
|
330
|
+
returns the captured stdout as a list of lists
|
|
331
|
+
|
|
332
|
+
Returns
|
|
333
|
+
-------
|
|
334
|
+
captured stdout : list
|
|
335
|
+
each line is an element of the list
|
|
336
|
+
"""
|
|
337
|
+
|
|
338
|
+
return [[line] for line in captured_stdout_as_str().splitlines()]
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class capture_stdout:
|
|
342
|
+
"""
|
|
343
|
+
specifies how to capture stdout
|
|
344
|
+
|
|
345
|
+
Parameters
|
|
346
|
+
----------
|
|
347
|
+
include_print : bool
|
|
348
|
+
if True (default), the output is also printed out as normal
|
|
349
|
+
|
|
350
|
+
if False, no output is printed
|
|
351
|
+
|
|
352
|
+
Note
|
|
353
|
+
----
|
|
354
|
+
This function is normally used as a context manager, like ::
|
|
355
|
+
|
|
356
|
+
with capture_stdout():
|
|
357
|
+
...
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
def __init__(self, include_print: bool = True):
|
|
361
|
+
self.stdout = sys.stdout
|
|
362
|
+
self.include_print = include_print
|
|
363
|
+
|
|
364
|
+
def __enter__(self):
|
|
365
|
+
sys.stdout = self
|
|
366
|
+
|
|
367
|
+
def __exit__(self, exc_type, exc_value, tb):
|
|
368
|
+
sys.stdout = self.stdout
|
|
369
|
+
|
|
370
|
+
def write(self, data):
|
|
371
|
+
_captured_stdout.append(data)
|
|
372
|
+
if self.include_print:
|
|
373
|
+
self.stdout.write(data)
|
|
374
|
+
|
|
375
|
+
def flush(self):
|
|
376
|
+
if self.include_print:
|
|
377
|
+
self.stdout.flush()
|
|
378
|
+
_captured_stdout.append("\n")
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
if __name__ == "__main__":
|
|
382
|
+
...
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
# _ _ _ _ _
|
|
2
|
-
# __ __| |__ __(_) _ __ __ _ ___ _ _ | |_ (_)| | ___
|
|
3
|
-
# \ \/ /| |\ \ /\ / /| || '_ \ / _` |/ __| | | | || __|| || |/ __|
|
|
4
|
-
# > < | | \ V V / | || | | || (_| |\__ \ | |_| || |_ | || |\__ \
|
|
5
|
-
# /_/\_\|_| \_/\_/ |_||_| |_| \__, ||___/ _____ \__,_| \__||_||_||___/
|
|
6
|
-
# |___/ |_____|
|
|
7
|
-
|
|
8
|
-
__version__ = "0.0.2"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import dropbox
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
import sys
|
|
14
|
-
|
|
15
|
-
_captured_stdout = []
|
|
16
|
-
dbx = None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def dropbox_init(refresh_token, app_key, app_secret):
|
|
20
|
-
'''
|
|
21
|
-
dropbox initialize
|
|
22
|
-
|
|
23
|
-
This function has to be called prior to using any dropbox function
|
|
24
|
-
|
|
25
|
-
Parameters
|
|
26
|
-
----------
|
|
27
|
-
refresh_token : str
|
|
28
|
-
oauth2 refreshntoken
|
|
29
|
-
|
|
30
|
-
app_key : str
|
|
31
|
-
app key
|
|
32
|
-
|
|
33
|
-
app_secret : str
|
|
34
|
-
app secret
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Returns
|
|
38
|
-
-------
|
|
39
|
-
-
|
|
40
|
-
'''
|
|
41
|
-
global dbx
|
|
42
|
-
dbx = dropbox.Dropbox(oauth2_refresh_token=refresh_token, app_key=app_key, app_secret=app_secret)
|
|
43
|
-
|
|
44
|
-
def _check_dbx():
|
|
45
|
-
if dbx is None:
|
|
46
|
-
raise ValueError('not initialized. Please call dropbox_init()')
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def list_dropbox(path="", recursive=False):
|
|
50
|
-
'''
|
|
51
|
-
list_dropbox
|
|
52
|
-
|
|
53
|
-
returns all dropbox files in path
|
|
54
|
-
|
|
55
|
-
Parameters
|
|
56
|
-
----------
|
|
57
|
-
path : str or Pathlib.Path
|
|
58
|
-
path from which to list all files (default: '')
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
recursive : bool
|
|
62
|
-
if True, recursively list files. if False (default) no recursion
|
|
63
|
-
|
|
64
|
-
Returns
|
|
65
|
-
-------
|
|
66
|
-
files, relative to path : list
|
|
67
|
-
'''
|
|
68
|
-
_check_dbx()
|
|
69
|
-
out = []
|
|
70
|
-
result = dbx.files_list_folder(path, recursive=recursive)
|
|
71
|
-
|
|
72
|
-
for entry in result.entries:
|
|
73
|
-
if isinstance(entry, dropbox.files.FileMetadata):
|
|
74
|
-
out.append(entry.path_display)
|
|
75
|
-
|
|
76
|
-
while result.has_more:
|
|
77
|
-
result = dbx.files_list_folder_continue(result.cursor)
|
|
78
|
-
for entry in result.entries:
|
|
79
|
-
if isinstance(entry, dropbox.files.FileMetadata):
|
|
80
|
-
out.append(entry.path_display)
|
|
81
|
-
|
|
82
|
-
return out
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def read_dropbox(dropbox_path):
|
|
86
|
-
'''
|
|
87
|
-
read_dropbox
|
|
88
|
-
|
|
89
|
-
read from dopbox at given path
|
|
90
|
-
|
|
91
|
-
Parameters
|
|
92
|
-
----------
|
|
93
|
-
path : str or Pathlib.Path
|
|
94
|
-
path to read from
|
|
95
|
-
|
|
96
|
-
Returns
|
|
97
|
-
-------
|
|
98
|
-
contents of the dropbox file : bytes
|
|
99
|
-
'''
|
|
100
|
-
|
|
101
|
-
_check_dbx()
|
|
102
|
-
metadata, response = dbx.files_download(dropbox_path)
|
|
103
|
-
file_content = response.content
|
|
104
|
-
return file_content
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def write_dropbox(dropbox_path, contents):
|
|
108
|
-
_check_dbx()
|
|
109
|
-
dbx.files_upload(contents, dropbox_path, mode=dropbox.files.WriteMode.overwrite)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def list_pyodide(path):
|
|
113
|
-
path = Path(path)
|
|
114
|
-
return list(path.iterdir())
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def write_pyodide(path, contents):
|
|
118
|
-
path = Path(path)
|
|
119
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
120
|
-
with open(path, "wb") as f:
|
|
121
|
-
f.write(contents)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def read_pyodide(path):
|
|
125
|
-
path = Path(path)
|
|
126
|
-
with open(path, "rb") as f:
|
|
127
|
-
contents = f.read()
|
|
128
|
-
return contents
|
|
129
|
-
|
|
130
|
-
class block:
|
|
131
|
-
def __init__(self, number_of_rows,number_of_columns):
|
|
132
|
-
self.dict={}
|
|
133
|
-
self.number_of_columns=number_of_columns
|
|
134
|
-
self.number_of_rows=number_of_rows
|
|
135
|
-
|
|
136
|
-
@classmethod
|
|
137
|
-
def from_list_of_lists(cls, list_of_lists, column_like=False):
|
|
138
|
-
if not isinstance(list_of_lists[0],list):
|
|
139
|
-
if column_like:
|
|
140
|
-
list_of_lists=[[value] for value in list_of_lists]
|
|
141
|
-
else:
|
|
142
|
-
list_of_lists=[list_of_lists]
|
|
143
|
-
|
|
144
|
-
self=cls(0,0)
|
|
145
|
-
|
|
146
|
-
self.number_of_rows=len(list_of_lists)
|
|
147
|
-
self.number_of_columns=0
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
for row,row_contents in enumerate(list_of_lists,1):
|
|
151
|
-
for column,value in enumerate(row_contents,1):
|
|
152
|
-
if value is not None:
|
|
153
|
-
self.dict[row,column]=value
|
|
154
|
-
self.number_of_columns=max(self.number_of_columns, column)
|
|
155
|
-
return self
|
|
156
|
-
|
|
157
|
-
def __setitem__(self, row_column, value):
|
|
158
|
-
row,column=row_column
|
|
159
|
-
if row<1 or row>self.number_of_rows:
|
|
160
|
-
raise IndexError
|
|
161
|
-
if column<1 or column>self.number_of_columns:
|
|
162
|
-
raise IndexError
|
|
163
|
-
if value is None:
|
|
164
|
-
del self.dict[row,column]
|
|
165
|
-
else:
|
|
166
|
-
self.dict[row,column]=value
|
|
167
|
-
|
|
168
|
-
def __getitem__(self, row_column):
|
|
169
|
-
row,column=row_column
|
|
170
|
-
if row<1 or row>self.number_of_rows:
|
|
171
|
-
raise IndexError
|
|
172
|
-
if column<1 or column>self.number_of_columns:
|
|
173
|
-
raise IndexError
|
|
174
|
-
return self.dict.get((row,column))
|
|
175
|
-
|
|
176
|
-
@property
|
|
177
|
-
def as_list_of_lists(self):
|
|
178
|
-
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)]
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def __repr__(self):
|
|
182
|
-
return f"block.from_list_of_lists({self.as_list_of_lists})"
|
|
183
|
-
|
|
184
|
-
def clear_captured_stdout():
|
|
185
|
-
"""
|
|
186
|
-
empties the captured stdout
|
|
187
|
-
"""
|
|
188
|
-
_captured_stdout.clear()
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def captured_stdout_as_str():
|
|
192
|
-
"""
|
|
193
|
-
returns the captured stdout as a list of strings
|
|
194
|
-
|
|
195
|
-
Returns
|
|
196
|
-
-------
|
|
197
|
-
captured stdout : list
|
|
198
|
-
each line is an element of the list
|
|
199
|
-
"""
|
|
200
|
-
|
|
201
|
-
return "".join(_captured_stdout)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def captured_stdout_as_range():
|
|
205
|
-
"""
|
|
206
|
-
returns the captured stdout as a list of strings
|
|
207
|
-
|
|
208
|
-
Returns
|
|
209
|
-
-------
|
|
210
|
-
captured stdout : list
|
|
211
|
-
each line is an element of the list
|
|
212
|
-
"""
|
|
213
|
-
|
|
214
|
-
return [[line] for line in captured_stdout_as_str().splitlines()]
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
class capture_stdout:
|
|
218
|
-
"""
|
|
219
|
-
specifies how to capture stdout
|
|
220
|
-
|
|
221
|
-
Parameters
|
|
222
|
-
----------
|
|
223
|
-
include_print : bool
|
|
224
|
-
if True (default), the output is also printed out as normal
|
|
225
|
-
|
|
226
|
-
if False, no output is printed
|
|
227
|
-
|
|
228
|
-
Note
|
|
229
|
-
----
|
|
230
|
-
This function is normally used as a context manager, like ::
|
|
231
|
-
|
|
232
|
-
with capture_stdout():
|
|
233
|
-
...
|
|
234
|
-
"""
|
|
235
|
-
|
|
236
|
-
def __init__(self, include_print: bool = True):
|
|
237
|
-
self.stdout = sys.stdout
|
|
238
|
-
self.include_print = include_print
|
|
239
|
-
|
|
240
|
-
def __enter__(self):
|
|
241
|
-
sys.stdout = self
|
|
242
|
-
|
|
243
|
-
def __exit__(self, exc_type, exc_value, tb):
|
|
244
|
-
sys.stdout = self.stdout
|
|
245
|
-
|
|
246
|
-
def write(self, data):
|
|
247
|
-
_captured_stdout.append(data)
|
|
248
|
-
if self.include_print:
|
|
249
|
-
self.stdout.write(data)
|
|
250
|
-
|
|
251
|
-
def flush(self):
|
|
252
|
-
if self.include_print:
|
|
253
|
-
self.stdout.flush()
|
|
254
|
-
_captured_stdout.append("\n")
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if __name__ == "__main__":
|
|
258
|
-
...
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
# @xw.script
|
|
262
|
-
# def test_stdout(book: xw.Book):
|
|
263
|
-
# with capture_stdout():
|
|
264
|
-
# for i in range(10):
|
|
265
|
-
# print(10 * f'{i}')
|
|
266
|
-
# book.sheets.active.range("A1").value=captured_stdout_as_range()
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
# @xw.script
|
|
270
|
-
# def test(book: xw.Book):
|
|
271
|
-
# dropbox_init(
|
|
272
|
-
# refresh_token=REFRESH_TOKEN,
|
|
273
|
-
# app_key=APP_KEY,
|
|
274
|
-
# app_secret=APP_SECRET,
|
|
275
|
-
# )
|
|
276
|
-
|
|
277
|
-
# files =list_dropbox("/Downloads",recursive=True)
|
|
278
|
-
# peek(files)
|
|
279
|
-
# write_pyodide("Test/x.x", b"abc")
|
|
280
|
-
# files = list_pyodide("Test")
|
|
281
|
-
|
|
282
|
-
# # files=list_dropbox_files("/Downloads")
|
|
283
|
-
# # peek(files)
|
|
284
|
-
# contents = read_dropbox("/Downloads/dropbox setup.py")
|
|
285
|
-
# write_dropbox("/Downloads/dropbox setup1.py", contents)
|
|
286
|
-
# peek("done")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|