xlwings-utils 25.1.1__tar.gz → 25.1.2.post0__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-25.1.1 → xlwings_utils-25.1.2.post0}/PKG-INFO +1 -1
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/pyproject.toml +1 -1
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/xlwings_utils/xlwings_utils.py +59 -57
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/xlwings_utils.egg-info/PKG-INFO +1 -1
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/README.md +0 -0
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/setup.cfg +0 -0
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/tests/test_xlwings_utils.py +0 -0
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/xlwings_utils/__init__.py +0 -0
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/xlwings_utils.egg-info/SOURCES.txt +0 -0
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/xlwings_utils.egg-info/dependency_links.txt +0 -0
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/xlwings_utils.egg-info/requires.txt +0 -0
- {xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/xlwings_utils.egg-info/top_level.txt +0 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# /_/\_\|_| \_/\_/ |_||_| |_| \__, ||___/ _____ \__,_| \__||_||_||___/
|
|
6
6
|
# |___/ |_____|
|
|
7
7
|
|
|
8
|
-
__version__ = "25.1.
|
|
8
|
+
__version__ = "25.1.2"
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
from pathlib import Path
|
|
@@ -30,13 +30,10 @@ except ImportError:
|
|
|
30
30
|
missing = object()
|
|
31
31
|
|
|
32
32
|
_token = None
|
|
33
|
-
missing = object()
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
def dropbox_init(refresh_token=missing, app_key=missing, app_secret=missing, **kwargs):
|
|
37
36
|
"""
|
|
38
|
-
dropbox initialize
|
|
39
|
-
|
|
40
37
|
This function may to be called prior to using any dropbox function
|
|
41
38
|
to specify the request token, app key and app secret.
|
|
42
39
|
If these are specified as DROPBOX.REFRESH_TOKEN, DROPBOX.APP_KEY and DROPBOX.APP_SECRET
|
|
@@ -47,18 +44,18 @@ def dropbox_init(refresh_token=missing, app_key=missing, app_secret=missing, **k
|
|
|
47
44
|
refresh_token : str
|
|
48
45
|
oauth2 refreshntoken
|
|
49
46
|
|
|
50
|
-
if omitted: use the environment variable REFRESH_TOKEN
|
|
47
|
+
if omitted: use the environment variable DROPBOX.REFRESH_TOKEN
|
|
51
48
|
|
|
52
49
|
app_key : str
|
|
53
50
|
app key
|
|
54
51
|
|
|
55
|
-
if omitted: use the environment variable APP_KEY
|
|
52
|
+
if omitted: use the environment variable DROPBOX.APP_KEY
|
|
56
53
|
|
|
57
54
|
|
|
58
55
|
app_secret : str
|
|
59
56
|
app secret
|
|
60
57
|
|
|
61
|
-
if omitted: use the environment variable APP_SECRET
|
|
58
|
+
if omitted: use the environment variable DROPBOX.APP_SECRET
|
|
62
59
|
|
|
63
60
|
Returns
|
|
64
61
|
-------
|
|
@@ -67,7 +64,7 @@ def dropbox_init(refresh_token=missing, app_key=missing, app_secret=missing, **k
|
|
|
67
64
|
|
|
68
65
|
global _token
|
|
69
66
|
if xlwings:
|
|
70
|
-
pyodide_http.patch_all() # to
|
|
67
|
+
pyodide_http.patch_all() # required to reliably use requests on pyodide platforms
|
|
71
68
|
|
|
72
69
|
if refresh_token is missing:
|
|
73
70
|
if "DROPBOX.REFRESH_TOKEN" in os.environ:
|
|
@@ -85,28 +82,25 @@ def dropbox_init(refresh_token=missing, app_key=missing, app_secret=missing, **k
|
|
|
85
82
|
else:
|
|
86
83
|
raise ValueError("no DROPBOX.APP_SECRET found in environment.")
|
|
87
84
|
|
|
88
|
-
|
|
85
|
+
response = requests.post(
|
|
89
86
|
"https://api.dropbox.com/oauth2/token",
|
|
90
87
|
data={"grant_type": "refresh_token", "refresh_token": refresh_token, "client_id": app_key, "client_secret": app_secret},
|
|
91
88
|
timeout=30,
|
|
92
89
|
)
|
|
93
90
|
try:
|
|
94
|
-
|
|
91
|
+
response.raise_for_status()
|
|
95
92
|
except requests.exceptions.HTTPError:
|
|
96
93
|
raise ValueError("invalid dropbox credentials")
|
|
97
|
-
_token =
|
|
94
|
+
_token = response.json()["access_token"]
|
|
98
95
|
|
|
99
96
|
|
|
100
97
|
def _login_dropbox():
|
|
101
|
-
global _token
|
|
102
98
|
if _token is None:
|
|
103
99
|
dropbox_init() # use environment
|
|
104
100
|
|
|
105
101
|
|
|
106
102
|
def list_dropbox(path="", recursive=False, show_files=True, show_folders=False):
|
|
107
103
|
"""
|
|
108
|
-
list_dropbox
|
|
109
|
-
|
|
110
104
|
returns all dropbox files/folders in path
|
|
111
105
|
|
|
112
106
|
Parameters
|
|
@@ -138,15 +132,15 @@ def list_dropbox(path="", recursive=False, show_files=True, show_folders=False):
|
|
|
138
132
|
|
|
139
133
|
API_RPC = "https://api.dropboxapi.com/2"
|
|
140
134
|
headers = {"Authorization": f"Bearer {_token}", "Content-Type": "application/json"}
|
|
141
|
-
payload = {"path": path, "recursive": recursive, "include_deleted": False}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
data =
|
|
135
|
+
payload = {"path": str(path), "recursive": recursive, "include_deleted": False}
|
|
136
|
+
response = requests.post("https://api.dropboxapi.com/2/files/list_folder", headers=headers, json=payload, timeout=30)
|
|
137
|
+
response.raise_for_status()
|
|
138
|
+
data = response.json()
|
|
145
139
|
entries = data["entries"]
|
|
146
140
|
while data.get("has_more"):
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
data =
|
|
141
|
+
response = requests.post(f"{API_RPC}/files/list_folder/continue", headers=headers, json={"cursor": data["cursor"]}, timeout=30)
|
|
142
|
+
response.raise_for_status()
|
|
143
|
+
data = response.json()
|
|
150
144
|
entries.extend(data["entries"])
|
|
151
145
|
|
|
152
146
|
result = []
|
|
@@ -160,9 +154,7 @@ def list_dropbox(path="", recursive=False, show_files=True, show_folders=False):
|
|
|
160
154
|
|
|
161
155
|
def read_dropbox(dropbox_path):
|
|
162
156
|
"""
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
read from dopbox at given path
|
|
157
|
+
read file from dropbox
|
|
166
158
|
|
|
167
159
|
Parameters
|
|
168
160
|
----------
|
|
@@ -181,13 +173,13 @@ def read_dropbox(dropbox_path):
|
|
|
181
173
|
|
|
182
174
|
_login_dropbox()
|
|
183
175
|
headers = {"Authorization": f"Bearer {_token}", "Dropbox-API-Arg": json.dumps({"path": dropbox_path})}
|
|
184
|
-
with requests.post("https://content.dropboxapi.com/2/files/download", headers=headers, stream=True, timeout=60) as
|
|
176
|
+
with requests.post("https://content.dropboxapi.com/2/files/download", headers=headers, stream=True, timeout=60) as response:
|
|
185
177
|
try:
|
|
186
|
-
|
|
178
|
+
response.raise_for_status()
|
|
187
179
|
except requests.exceptions.HTTPError as e:
|
|
188
|
-
raise FileNotFoundError(f"file {dropbox_path} not found. Original message is {e}") from None
|
|
180
|
+
raise FileNotFoundError(f"file {str(dropbox_path)} not found. Original message is {e}") from None
|
|
189
181
|
chunks = []
|
|
190
|
-
for chunk in
|
|
182
|
+
for chunk in response.iter_content(chunk_size=1024):
|
|
191
183
|
if chunk:
|
|
192
184
|
chunks.append(chunk)
|
|
193
185
|
return b"".join(chunks)
|
|
@@ -195,9 +187,7 @@ def read_dropbox(dropbox_path):
|
|
|
195
187
|
|
|
196
188
|
def write_dropbox(dropbox_path, contents):
|
|
197
189
|
"""
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
write from dopbox at given path
|
|
190
|
+
write to file on dropbox
|
|
201
191
|
|
|
202
192
|
Parameters
|
|
203
193
|
----------
|
|
@@ -216,39 +206,47 @@ def write_dropbox(dropbox_path, contents):
|
|
|
216
206
|
headers = {
|
|
217
207
|
"Authorization": f"Bearer {_token}",
|
|
218
208
|
"Dropbox-API-Arg": json.dumps(
|
|
219
|
-
{
|
|
220
|
-
"path": dropbox_path, # Where it will be saved in Dropbox
|
|
221
|
-
"mode": "overwrite", # "add" or "overwrite"
|
|
222
|
-
"autorename": False,
|
|
223
|
-
"mute": False,
|
|
224
|
-
}
|
|
209
|
+
{"path": str(dropbox_path), "mode": "overwrite", "autorename": False, "mute": False} # Where it will be saved in Dropbox # "add" or "overwrite"
|
|
225
210
|
),
|
|
226
211
|
"Content-Type": "application/octet-stream",
|
|
227
212
|
}
|
|
228
213
|
response = requests.post("https://content.dropboxapi.com/2/files/upload", headers=headers, data=contents)
|
|
229
|
-
|
|
214
|
+
try:
|
|
215
|
+
response.raise_for_status()
|
|
216
|
+
except requests.exceptions.HTTPError as e:
|
|
217
|
+
raise FileNotFoundError(f"file {str(dropbox_path)} could not be written. Original message is {e}") from None
|
|
230
218
|
|
|
231
219
|
|
|
232
220
|
def delete_from_dropbox(dropbox_path):
|
|
221
|
+
"""
|
|
222
|
+
delete file dropbox
|
|
223
|
+
|
|
224
|
+
Parameters
|
|
225
|
+
----------
|
|
226
|
+
dropbox_path : str or Pathlib.Path
|
|
227
|
+
path to delete
|
|
228
|
+
|
|
229
|
+
Note
|
|
230
|
+
----
|
|
231
|
+
If REFRESH_TOKEN, APP_KEY and APP_SECRET environment variables are specified,
|
|
232
|
+
it is not necessary to call dropbox_init() prior to any dropbox function.
|
|
233
|
+
"""
|
|
233
234
|
_login_dropbox()
|
|
234
235
|
|
|
235
236
|
headers = {"Authorization": f"Bearer {_token}", "Content-Type": "application/json"}
|
|
236
237
|
|
|
237
|
-
data = {
|
|
238
|
-
"path": dropbox_path # Path in Dropbox, starting with /
|
|
239
|
-
}
|
|
238
|
+
data = {"path": str(dropbox_path)} # Path in Dropbox, starting with /
|
|
240
239
|
|
|
241
240
|
response = requests.post("https://api.dropboxapi.com/2/files/delete_v2", headers=headers, data=json.dumps(data))
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
try:
|
|
242
|
+
response.raise_for_status()
|
|
243
|
+
except requests.exceptions.HTTPError as e:
|
|
244
|
+
raise FileNotFoundError(f"file {str(dropbox_path)} could not be deleted. Original message is {e}") from None
|
|
245
245
|
|
|
246
246
|
|
|
247
247
|
def list_local(path, recursive=False, show_files=True, show_folders=False):
|
|
248
248
|
"""
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
returns all local files/folders in path
|
|
249
|
+
returns all local files/folders at given path
|
|
252
250
|
|
|
253
251
|
Parameters
|
|
254
252
|
----------
|
|
@@ -320,8 +318,7 @@ class block:
|
|
|
320
318
|
self.dict = {}
|
|
321
319
|
self.number_of_rows = number_of_rows
|
|
322
320
|
self.number_of_columns = number_of_columns
|
|
323
|
-
self.
|
|
324
|
-
self._highest_used_column_number = None
|
|
321
|
+
self._invalidate_highest_used_cache()
|
|
325
322
|
|
|
326
323
|
def __eq__(self, other):
|
|
327
324
|
if isinstance(other, block):
|
|
@@ -495,17 +492,21 @@ class block:
|
|
|
495
492
|
def value(self):
|
|
496
493
|
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)]
|
|
497
494
|
|
|
495
|
+
def _invalidate_highest_used_cache(self):
|
|
496
|
+
self._highest_used_row_number = None
|
|
497
|
+
self._highest_used_column_number = None
|
|
498
|
+
|
|
498
499
|
def __setitem__(self, row_column, value):
|
|
499
500
|
row, column = row_column
|
|
500
501
|
if row < 1 or row > self.number_of_rows:
|
|
501
|
-
raise IndexError(f"row must be between 1 and {self.number_of_rows} not {row}")
|
|
502
|
+
raise IndexError(f"row must be between 1 and {self.number_of_rows}; not {row}")
|
|
502
503
|
if column < 1 or column > self.number_of_columns:
|
|
503
|
-
raise IndexError(f"column must be between 1 and {self.number_of_columns} not {column}")
|
|
504
|
+
raise IndexError(f"column must be between 1 and {self.number_of_columns}; not {column}")
|
|
504
505
|
if value is None:
|
|
505
506
|
if (row, column) in self.dict:
|
|
506
507
|
del self.dict[row, column]
|
|
507
|
-
self.
|
|
508
|
-
|
|
508
|
+
self._invalidate_highest_used_cache()
|
|
509
|
+
|
|
509
510
|
else:
|
|
510
511
|
self.dict[row, column] = value
|
|
511
512
|
if self._highest_used_row_number:
|
|
@@ -537,8 +538,8 @@ class block:
|
|
|
537
538
|
@number_of_rows.setter
|
|
538
539
|
def number_of_rows(self, value):
|
|
539
540
|
if value < 1:
|
|
540
|
-
raise ValueError(f"number_of_rows should be >=1
|
|
541
|
-
self.
|
|
541
|
+
raise ValueError(f"number_of_rows should be >=1; not {value}")
|
|
542
|
+
self._invalidate_highest_used_cache()
|
|
542
543
|
self._number_of_rows = value
|
|
543
544
|
for row, column in list(self.dict):
|
|
544
545
|
if row > self._number_of_rows:
|
|
@@ -551,8 +552,8 @@ class block:
|
|
|
551
552
|
@number_of_columns.setter
|
|
552
553
|
def number_of_columns(self, value):
|
|
553
554
|
if value < 1:
|
|
554
|
-
raise ValueError(f"number_of_columns should be >=1
|
|
555
|
-
self.
|
|
555
|
+
raise ValueError(f"number_of_columns should be >=1; not {value}")
|
|
556
|
+
self._invalidate_highest_used_cache()
|
|
556
557
|
self._number_of_columns = value
|
|
557
558
|
for row, column in list(self.dict):
|
|
558
559
|
if column > self._number_of_columns:
|
|
@@ -1042,3 +1043,4 @@ def trigger_macro(sheet):
|
|
|
1042
1043
|
|
|
1043
1044
|
if __name__ == "__main__":
|
|
1044
1045
|
...
|
|
1046
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xlwings_utils-25.1.1 → xlwings_utils-25.1.2.post0}/xlwings_utils.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|