xlwings-utils 25.1.1__py3-none-any.whl → 25.1.2__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,12 +206,7 @@ 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
  }
@@ -230,25 +215,34 @@ def write_dropbox(dropbox_path, contents):
230
215
 
231
216
 
232
217
  def delete_from_dropbox(dropbox_path):
218
+ """
219
+ delete file dropbox
220
+
221
+ Parameters
222
+ ----------
223
+ dropbox_path : str or Pathlib.Path
224
+ path to delete
225
+
226
+ Note
227
+ ----
228
+ If REFRESH_TOKEN, APP_KEY and APP_SECRET environment variables are specified,
229
+ it is not necessary to call dropbox_init() prior to any dropbox function.
230
+ """
233
231
  _login_dropbox()
234
232
 
235
233
  headers = {"Authorization": f"Bearer {_token}", "Content-Type": "application/json"}
236
234
 
237
- data = {
238
- "path": dropbox_path # Path in Dropbox, starting with /
239
- }
235
+ data = {"path": str(dropbox_path)} # Path in Dropbox, starting with /
240
236
 
241
237
  response = requests.post("https://api.dropboxapi.com/2/files/delete_v2", headers=headers, data=json.dumps(data))
242
238
  if response.status_code == 200:
243
- return
239
+ return response
244
240
  raise FileNotFoundError(f"dropbox file {dropbox_path} not found")
245
241
 
246
242
 
247
243
  def list_local(path, recursive=False, show_files=True, show_folders=False):
248
244
  """
249
- list_local
250
-
251
- returns all local files/folders in path
245
+ returns all local files/folders at given path
252
246
 
253
247
  Parameters
254
248
  ----------
@@ -320,8 +314,7 @@ class block:
320
314
  self.dict = {}
321
315
  self.number_of_rows = number_of_rows
322
316
  self.number_of_columns = number_of_columns
323
- self._highest_used_row_number = None
324
- self._highest_used_column_number = None
317
+ self._invalidate_highest_used_cache()
325
318
 
326
319
  def __eq__(self, other):
327
320
  if isinstance(other, block):
@@ -495,17 +488,21 @@ class block:
495
488
  def value(self):
496
489
  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
490
 
491
+ def _invalidate_highest_used_cache(self):
492
+ self._highest_used_row_number = None
493
+ self._highest_used_column_number = None
494
+
498
495
  def __setitem__(self, row_column, value):
499
496
  row, column = row_column
500
497
  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}")
498
+ raise IndexError(f"row must be between 1 and {self.number_of_rows}; not {row}")
502
499
  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}")
500
+ raise IndexError(f"column must be between 1 and {self.number_of_columns}; not {column}")
504
501
  if value is None:
505
502
  if (row, column) in self.dict:
506
503
  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
504
+ self._invalidate_highest_used_cache()
505
+
509
506
  else:
510
507
  self.dict[row, column] = value
511
508
  if self._highest_used_row_number:
@@ -537,8 +534,8 @@ class block:
537
534
  @number_of_rows.setter
538
535
  def number_of_rows(self, value):
539
536
  if value < 1:
540
- raise ValueError(f"number_of_rows should be >=1, not {value}")
541
- self._highest_used_row_number = None
537
+ raise ValueError(f"number_of_rows should be >=1; not {value}")
538
+ self._invalidate_highest_used_cache()
542
539
  self._number_of_rows = value
543
540
  for row, column in list(self.dict):
544
541
  if row > self._number_of_rows:
@@ -551,8 +548,8 @@ class block:
551
548
  @number_of_columns.setter
552
549
  def number_of_columns(self, value):
553
550
  if value < 1:
554
- raise ValueError(f"number_of_columns should be >=1, not {value}")
555
- self._highest_used_column_number = None
551
+ raise ValueError(f"number_of_columns should be >=1; not {value}")
552
+ self._invalidate_highest_used_cache()
556
553
  self._number_of_columns = value
557
554
  for row, column in list(self.dict):
558
555
  if column > self._number_of_columns:
@@ -1042,3 +1039,4 @@ def trigger_macro(sheet):
1042
1039
 
1043
1040
  if __name__ == "__main__":
1044
1041
  ...
1042
+
@@ -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
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=z-N00D0O_0LyhDOhpHuAI2Ddphig40-LpgWS9rOIr_c,30481
3
+ xlwings_utils-25.1.2.dist-info/METADATA,sha256=Kg-GSXueKT9vbNCTaX1EBt-4zB5k7JOHUezbhWBncMY,12265
4
+ xlwings_utils-25.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ xlwings_utils-25.1.2.dist-info/top_level.txt,sha256=kf5SEv0gZiRObPhUoYcc1O_iX_wwTOPeUIYvzyYeAM4,14
6
+ xlwings_utils-25.1.2.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,,