xlwings-utils 25.1.1__py3-none-any.whl → 25.1.2.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.1.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 enable chunked mode
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
- resp = requests.post(
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
- resp.raise_for_status()
91
+ response.raise_for_status()
95
92
  except requests.exceptions.HTTPError:
96
93
  raise ValueError("invalid dropbox credentials")
97
- _token = resp.json()["access_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
- r = requests.post("https://api.dropboxapi.com/2/files/list_folder", headers=headers, json=payload, timeout=30)
143
- r.raise_for_status()
144
- data = r.json()
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
- r = requests.post(f"{API_RPC}/files/list_folder/continue", headers=headers, json={"cursor": data["cursor"]}, timeout=30)
148
- r.raise_for_status()
149
- data = r.json()
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
- read_dropbox
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 r:
176
+ with requests.post("https://content.dropboxapi.com/2/files/download", headers=headers, stream=True, timeout=60) as response:
185
177
  try:
186
- r.raise_for_status()
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 r.iter_content(chunk_size=1024):
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
- write_dropbox
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
- return response
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
- if response.status_code == 200:
243
- return
244
- raise FileNotFoundError(f"dropbox file {dropbox_path} not found")
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
- list_local
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._highest_used_row_number = None
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._highest_used_row_number = None # invalidate cached value
508
- self._highest_used_column_number = None # invalidate cached value
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, not {value}")
541
- self._highest_used_row_number = None
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, not {value}")
555
- self._highest_used_column_number = None
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
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xlwings_utils
3
- Version: 25.1.1
3
+ Version: 25.1.2.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
@@ -0,0 +1,6 @@
1
+ xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
2
+ xlwings_utils/xlwings_utils.py,sha256=IipMDWYm6HHC0y2Y_Pdu3RV3ppxMtmehWc7uyR4u47s,30753
3
+ xlwings_utils-25.1.2.post0.dist-info/METADATA,sha256=gy_t8BGvlv6EKJ9_pUEa8XahoaHBmWqGrLcLiM2myJM,12271
4
+ xlwings_utils-25.1.2.post0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ xlwings_utils-25.1.2.post0.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
+ xlwings_utils-25.1.2.post0.dist-info/RECORD,,
@@ -1,6 +0,0 @@
1
- xlwings_utils/__init__.py,sha256=FdaRztevSu5akGL7KBUBRzqwLMRTdvVUuS2Kfp2f1Uc,68
2
- xlwings_utils/xlwings_utils.py,sha256=yFbxuyrSkBhsr4OmUUI5ppYyNpb3T9bVi-7qeuDAPKI,30256
3
- xlwings_utils-25.1.1.dist-info/METADATA,sha256=7aBUGylo6jmsoyDbqbOiBZ9T8c9qeXb_KYLwVDdQPzg,12265
4
- xlwings_utils-25.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
- xlwings_utils-25.1.1.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
- xlwings_utils-25.1.1.dist-info/RECORD,,