xlwings-utils 0.0.3__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.3 → xlwings_utils-0.0.4}/PKG-INFO +1 -1
- {xlwings_utils-0.0.3 → 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.3 → xlwings_utils-0.0.4}/xlwings_utils/xlwings_utils.py +164 -86
- {xlwings_utils-0.0.3 → xlwings_utils-0.0.4}/xlwings_utils.egg-info/PKG-INFO +1 -1
- {xlwings_utils-0.0.3 → xlwings_utils-0.0.4}/xlwings_utils.egg-info/SOURCES.txt +1 -0
- {xlwings_utils-0.0.3 → xlwings_utils-0.0.4}/README.md +0 -0
- {xlwings_utils-0.0.3 → xlwings_utils-0.0.4}/setup.cfg +0 -0
- {xlwings_utils-0.0.3 → xlwings_utils-0.0.4}/xlwings_utils/__init__.py +0 -0
- {xlwings_utils-0.0.3 → xlwings_utils-0.0.4}/xlwings_utils.egg-info/dependency_links.txt +0 -0
- {xlwings_utils-0.0.3 → xlwings_utils-0.0.4}/xlwings_utils.egg-info/requires.txt +0 -0
- {xlwings_utils-0.0.3 → 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__])
|
|
@@ -5,20 +5,33 @@
|
|
|
5
5
|
# /_/\_\|_| \_/\_/ |_||_| |_| \__, ||___/ _____ \__,_| \__||_||_||___/
|
|
6
6
|
# |___/ |_____|
|
|
7
7
|
|
|
8
|
-
__version__ = "0.0.
|
|
8
|
+
__version__ = "0.0.4"
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
import dropbox
|
|
12
12
|
from pathlib import Path
|
|
13
|
-
import sys
|
|
14
13
|
import os
|
|
15
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
|
+
|
|
16
29
|
_captured_stdout = []
|
|
17
30
|
dbx = None
|
|
18
31
|
|
|
19
32
|
|
|
20
33
|
def dropbox_init(refresh_token=None, app_key=None, app_secret=None):
|
|
21
|
-
|
|
34
|
+
"""
|
|
22
35
|
dropbox initialize
|
|
23
36
|
|
|
24
37
|
This function has to be called prior to using any dropbox function
|
|
@@ -29,13 +42,13 @@ def dropbox_init(refresh_token=None, app_key=None, app_secret=None):
|
|
|
29
42
|
oauth2 refreshntoken
|
|
30
43
|
|
|
31
44
|
if omitted: use the environment variable REFRESH_TOKEN
|
|
32
|
-
|
|
45
|
+
|
|
33
46
|
app_key : str
|
|
34
47
|
app key
|
|
35
48
|
|
|
36
49
|
if omitted: use the environment variable APP_KEY
|
|
37
50
|
|
|
38
|
-
|
|
51
|
+
|
|
39
52
|
app_secret : str
|
|
40
53
|
app secret
|
|
41
54
|
|
|
@@ -44,41 +57,42 @@ def dropbox_init(refresh_token=None, app_key=None, app_secret=None):
|
|
|
44
57
|
Returns
|
|
45
58
|
-------
|
|
46
59
|
-
|
|
47
|
-
|
|
60
|
+
"""
|
|
48
61
|
if refresh_token is None:
|
|
49
|
-
refresh_token =os.environ["REFRESH_TOKEN"]
|
|
62
|
+
refresh_token = os.environ["REFRESH_TOKEN"]
|
|
50
63
|
if app_key is None:
|
|
51
|
-
app_key=os.environ["APP_KEY"]
|
|
64
|
+
app_key = os.environ["APP_KEY"]
|
|
52
65
|
if app_secret is None:
|
|
53
|
-
app_secret =os.environ["APP_SECRET"]
|
|
66
|
+
app_secret = os.environ["APP_SECRET"]
|
|
54
67
|
|
|
55
68
|
global dbx
|
|
56
69
|
dbx = dropbox.Dropbox(oauth2_refresh_token=refresh_token, app_key=app_key, app_secret=app_secret)
|
|
57
|
-
|
|
70
|
+
|
|
71
|
+
|
|
58
72
|
def _check_dbx():
|
|
59
73
|
if dbx is None:
|
|
60
|
-
raise ValueError(
|
|
74
|
+
raise ValueError("not initialized. Please call dropbox_init()")
|
|
61
75
|
|
|
62
76
|
|
|
63
77
|
def list_dropbox(path="", recursive=False):
|
|
64
|
-
|
|
78
|
+
"""
|
|
65
79
|
list_dropbox
|
|
66
|
-
|
|
80
|
+
|
|
67
81
|
returns all dropbox files in path
|
|
68
|
-
|
|
82
|
+
|
|
69
83
|
Parameters
|
|
70
84
|
----------
|
|
71
85
|
path : str or Pathlib.Path
|
|
72
86
|
path from which to list all files (default: '')
|
|
73
|
-
|
|
74
|
-
|
|
87
|
+
|
|
88
|
+
|
|
75
89
|
recursive : bool
|
|
76
90
|
if True, recursively list files. if False (default) no recursion
|
|
77
|
-
|
|
91
|
+
|
|
78
92
|
Returns
|
|
79
93
|
-------
|
|
80
94
|
files, relative to path : list
|
|
81
|
-
|
|
95
|
+
"""
|
|
82
96
|
_check_dbx()
|
|
83
97
|
out = []
|
|
84
98
|
result = dbx.files_list_folder(path, recursive=recursive)
|
|
@@ -97,20 +111,20 @@ def list_dropbox(path="", recursive=False):
|
|
|
97
111
|
|
|
98
112
|
|
|
99
113
|
def read_dropbox(dropbox_path):
|
|
100
|
-
|
|
114
|
+
"""
|
|
101
115
|
read_dropbox
|
|
102
|
-
|
|
116
|
+
|
|
103
117
|
read from dopbox at given path
|
|
104
|
-
|
|
118
|
+
|
|
105
119
|
Parameters
|
|
106
120
|
----------
|
|
107
|
-
|
|
121
|
+
dropbox_path : str or Pathlib.Path
|
|
108
122
|
path to read from
|
|
109
|
-
|
|
123
|
+
|
|
110
124
|
Returns
|
|
111
125
|
-------
|
|
112
126
|
contents of the dropbox file : bytes
|
|
113
|
-
|
|
127
|
+
"""
|
|
114
128
|
|
|
115
129
|
_check_dbx()
|
|
116
130
|
metadata, response = dbx.files_download(dropbox_path)
|
|
@@ -120,85 +134,177 @@ def read_dropbox(dropbox_path):
|
|
|
120
134
|
|
|
121
135
|
def write_dropbox(dropbox_path, contents):
|
|
122
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
|
+
"""
|
|
123
151
|
dbx.files_upload(contents, dropbox_path, mode=dropbox.files.WriteMode.overwrite)
|
|
124
152
|
|
|
125
153
|
|
|
126
|
-
def
|
|
154
|
+
def list_local(path):
|
|
127
155
|
path = Path(path)
|
|
128
156
|
return list(path.iterdir())
|
|
129
157
|
|
|
130
158
|
|
|
131
|
-
def
|
|
159
|
+
def write_local(path, contents):
|
|
132
160
|
path = Path(path)
|
|
133
161
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
134
162
|
with open(path, "wb") as f:
|
|
135
163
|
f.write(contents)
|
|
136
164
|
|
|
137
165
|
|
|
138
|
-
def
|
|
166
|
+
def read_local(path):
|
|
139
167
|
path = Path(path)
|
|
140
168
|
with open(path, "rb") as f:
|
|
141
169
|
contents = f.read()
|
|
142
170
|
return contents
|
|
143
171
|
|
|
172
|
+
|
|
144
173
|
class block:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
149
194
|
|
|
150
195
|
@classmethod
|
|
151
196
|
def from_list_of_lists(cls, list_of_lists, column_like=False):
|
|
152
|
-
|
|
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):
|
|
153
223
|
if column_like:
|
|
154
|
-
list_of_lists=[[value] for value in list_of_lists]
|
|
224
|
+
list_of_lists = [[value] for value in list_of_lists]
|
|
155
225
|
else:
|
|
156
|
-
list_of_lists=[list_of_lists]
|
|
226
|
+
list_of_lists = [list_of_lists]
|
|
157
227
|
|
|
158
|
-
self=cls(
|
|
228
|
+
self = cls(1, 1)
|
|
159
229
|
|
|
160
|
-
self.number_of_rows=len(list_of_lists)
|
|
161
|
-
self.
|
|
162
|
-
|
|
230
|
+
self.number_of_rows = len(list_of_lists)
|
|
231
|
+
self._number_of_columns = 0
|
|
163
232
|
|
|
164
|
-
for row,row_contents in enumerate(list_of_lists,1):
|
|
165
|
-
for column,value in enumerate(row_contents,1):
|
|
233
|
+
for row, row_contents in enumerate(list_of_lists, 1):
|
|
234
|
+
for column, value in enumerate(row_contents, 1):
|
|
166
235
|
if value is not None:
|
|
167
|
-
self.dict[row,column]=value
|
|
168
|
-
self.
|
|
236
|
+
self.dict[row, column] = value
|
|
237
|
+
self._number_of_columns = max(self.number_of_columns, column)
|
|
169
238
|
return self
|
|
170
|
-
|
|
239
|
+
|
|
171
240
|
def __setitem__(self, row_column, value):
|
|
172
|
-
row,column=row_column
|
|
173
|
-
if row<1 or row>self.number_of_rows:
|
|
241
|
+
row, column = row_column
|
|
242
|
+
if row < 1 or row > self.number_of_rows:
|
|
174
243
|
raise IndexError
|
|
175
|
-
if column<1 or column>self.number_of_columns:
|
|
244
|
+
if column < 1 or column > self.number_of_columns:
|
|
176
245
|
raise IndexError
|
|
177
|
-
self.dict[row,column]=value
|
|
246
|
+
self.dict[row, column] = value
|
|
178
247
|
|
|
179
248
|
def __getitem__(self, row_column):
|
|
180
|
-
row,column=row_column
|
|
181
|
-
if row<1 or row>self.number_of_rows:
|
|
249
|
+
row, column = row_column
|
|
250
|
+
if row < 1 or row > self.number_of_rows:
|
|
182
251
|
raise IndexError
|
|
183
|
-
if column<1 or column>self.number_of_columns:
|
|
252
|
+
if column < 1 or column > self.number_of_columns:
|
|
184
253
|
raise IndexError
|
|
185
|
-
return self.dict.get((row,column))
|
|
254
|
+
return self.dict.get((row, column))
|
|
186
255
|
|
|
187
256
|
@property
|
|
188
257
|
def as_list_of_lists(self):
|
|
189
|
-
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)]
|
|
190
|
-
|
|
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)]
|
|
191
259
|
|
|
192
260
|
@property
|
|
193
261
|
def as_minimal_list_of_lists(self):
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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]
|
|
197
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
|
|
198
303
|
|
|
199
304
|
def __repr__(self):
|
|
200
305
|
return f"block.from_list_of_lists({self.as_list_of_lists})"
|
|
201
306
|
|
|
307
|
+
|
|
202
308
|
def clear_captured_stdout():
|
|
203
309
|
"""
|
|
204
310
|
empties the captured stdout
|
|
@@ -219,9 +325,9 @@ def captured_stdout_as_str():
|
|
|
219
325
|
return "".join(_captured_stdout)
|
|
220
326
|
|
|
221
327
|
|
|
222
|
-
def
|
|
328
|
+
def captured_stdout_as_list_of_lists():
|
|
223
329
|
"""
|
|
224
|
-
returns the captured stdout as a list of
|
|
330
|
+
returns the captured stdout as a list of lists
|
|
225
331
|
|
|
226
332
|
Returns
|
|
227
333
|
-------
|
|
@@ -274,31 +380,3 @@ class capture_stdout:
|
|
|
274
380
|
|
|
275
381
|
if __name__ == "__main__":
|
|
276
382
|
...
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
# @xw.script
|
|
280
|
-
# def test_stdout(book: xw.Book):
|
|
281
|
-
# with capture_stdout():
|
|
282
|
-
# for i in range(10):
|
|
283
|
-
# print(10 * f'{i}')
|
|
284
|
-
# book.sheets.active.range("A1").value=captured_stdout_as_range()
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
# @xw.script
|
|
288
|
-
# def test(book: xw.Book):
|
|
289
|
-
# dropbox_init(
|
|
290
|
-
# refresh_token=REFRESH_TOKEN,
|
|
291
|
-
# app_key=APP_KEY,
|
|
292
|
-
# app_secret=APP_SECRET,
|
|
293
|
-
# )
|
|
294
|
-
|
|
295
|
-
# files =list_dropbox("/Downloads",recursive=True)
|
|
296
|
-
# peek(files)
|
|
297
|
-
# write_pyodide("Test/x.x", b"abc")
|
|
298
|
-
# files = list_pyodide("Test")
|
|
299
|
-
|
|
300
|
-
# # files=list_dropbox_files("/Downloads")
|
|
301
|
-
# # peek(files)
|
|
302
|
-
# contents = read_dropbox("/Downloads/dropbox setup.py")
|
|
303
|
-
# write_dropbox("/Downloads/dropbox setup1.py", contents)
|
|
304
|
-
# peek("done")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|