xlwings-utils 25.2.1__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.
- xlwings_utils/__init__.py +5 -0
- xlwings_utils/dropbox.py +258 -0
- xlwings_utils/local.py +53 -0
- xlwings_utils/nextcloud.py +236 -0
- xlwings_utils/xlwings_utils.py +794 -0
- xlwings_utils-25.2.1.dist-info/METADATA +391 -0
- xlwings_utils-25.2.1.dist-info/RECORD +9 -0
- xlwings_utils-25.2.1.dist-info/WHEEL +5 -0
- xlwings_utils-25.2.1.dist-info/top_level.txt +1 -0
xlwings_utils/dropbox.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
_token = None
|
|
6
|
+
missing = object()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def normalize_path(path):
|
|
10
|
+
path = str(path).strip()
|
|
11
|
+
if path == "/":
|
|
12
|
+
path = ""
|
|
13
|
+
if path and not path.startswith("/"):
|
|
14
|
+
path = "/" + path
|
|
15
|
+
return path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def init(refresh_token=missing, app_key=missing, app_secret=missing, **kwargs):
|
|
19
|
+
"""
|
|
20
|
+
This function may to be called prior to using any dropbox function
|
|
21
|
+
to specify the request token, app key and app secret.
|
|
22
|
+
If these are specified as DROPBOX.REFRESH_TOKEN, DROPBOX.APP_KEY and DROPBOX.APP_SECRET
|
|
23
|
+
environment variables, it is not necessary to call dropbox_init().
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
refresh_token : str
|
|
28
|
+
oauth2 refreshntoken
|
|
29
|
+
|
|
30
|
+
if omitted: use the environment variable DROPBOX.REFRESH_TOKEN
|
|
31
|
+
|
|
32
|
+
app_key : str
|
|
33
|
+
app key
|
|
34
|
+
|
|
35
|
+
if omitted: use the environment variable DROPBOX.APP_KEY
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
app_secret : str
|
|
39
|
+
app secret
|
|
40
|
+
|
|
41
|
+
if omitted: use the environment variable DROPBOX.APP_SECRET
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
dropbox object
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
global _token
|
|
49
|
+
try:
|
|
50
|
+
import pyodide_http
|
|
51
|
+
|
|
52
|
+
pyodide_http.patch_all() # required to reliably use requests on pyodide platforms
|
|
53
|
+
|
|
54
|
+
except ImportError:
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
if refresh_token is missing:
|
|
58
|
+
if "DROPBOX.REFRESH_TOKEN" in os.environ:
|
|
59
|
+
refresh_token = os.environ["DROPBOX.REFRESH_TOKEN"]
|
|
60
|
+
else:
|
|
61
|
+
raise ValueError("no DROPBOX.REFRESH_TOKEN found in environment.")
|
|
62
|
+
if app_key is missing:
|
|
63
|
+
if "DROPBOX.APP_KEY" in os.environ:
|
|
64
|
+
app_key = os.environ["DROPBOX.APP_KEY"]
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError("no DROPBOX.APP_KEY found in environment.")
|
|
67
|
+
if app_secret is missing:
|
|
68
|
+
if "DROPBOX.APP_SECRET" in os.environ:
|
|
69
|
+
app_secret = os.environ["DROPBOX.APP_SECRET"]
|
|
70
|
+
else:
|
|
71
|
+
raise ValueError("no DROPBOX.APP_SECRET found in environment.")
|
|
72
|
+
|
|
73
|
+
response = requests.post(
|
|
74
|
+
"https://api.dropbox.com/oauth2/token",
|
|
75
|
+
data={"grant_type": "refresh_token", "refresh_token": refresh_token, "client_id": app_key, "client_secret": app_secret},
|
|
76
|
+
timeout=30,
|
|
77
|
+
)
|
|
78
|
+
try:
|
|
79
|
+
response.raise_for_status()
|
|
80
|
+
except requests.exceptions.HTTPError:
|
|
81
|
+
raise ValueError("invalid dropbox credentials")
|
|
82
|
+
_token = response.json()["access_token"]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _login():
|
|
86
|
+
if _token is None:
|
|
87
|
+
init() # use environment
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def dir(path="", recursive=False, show_files=True, show_folders=False):
|
|
91
|
+
"""
|
|
92
|
+
returns all dropbox files/folders in path
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
path : str or Pathlib.Path
|
|
97
|
+
path from which to list all files (default: '')
|
|
98
|
+
|
|
99
|
+
recursive : bool
|
|
100
|
+
if True, recursively list files and folders. if False (default) no recursion
|
|
101
|
+
|
|
102
|
+
show_files : bool
|
|
103
|
+
if True (default), show file entries
|
|
104
|
+
if False, do not show file entries
|
|
105
|
+
|
|
106
|
+
show_folders : bool
|
|
107
|
+
if True, show folder entries
|
|
108
|
+
if False (default), do not show folder entries
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
files : list
|
|
113
|
+
|
|
114
|
+
Note
|
|
115
|
+
----
|
|
116
|
+
If DROPBOX.REFRESH_TOKEN, DROPBOX.APP_KEY and DROPBOX.APP_SECRET environment variables are specified,
|
|
117
|
+
it is not necessary to call dropbox_init() prior to any dropbox function.
|
|
118
|
+
"""
|
|
119
|
+
_login()
|
|
120
|
+
|
|
121
|
+
path = normalize_path(path)
|
|
122
|
+
|
|
123
|
+
API_RPC = "https://api.dropboxapi.com/2"
|
|
124
|
+
headers = {"Authorization": f"Bearer {_token}", "Content-Type": "application/json"}
|
|
125
|
+
payload = {"path": path, "recursive": recursive, "include_deleted": False}
|
|
126
|
+
response = requests.post("https://api.dropboxapi.com/2/files/list_folder", headers=headers, json=payload, timeout=30)
|
|
127
|
+
try:
|
|
128
|
+
response.raise_for_status()
|
|
129
|
+
except requests.exceptions.HTTPError as e:
|
|
130
|
+
raise OSError(f"error listing dropbox. Original message is {e}") from None
|
|
131
|
+
data = response.json()
|
|
132
|
+
entries = data["entries"]
|
|
133
|
+
while data.get("has_more"):
|
|
134
|
+
response = requests.post(f"{API_RPC}/files/list_folder/continue", headers=headers, json={"cursor": data["cursor"]}, timeout=30)
|
|
135
|
+
try:
|
|
136
|
+
response.raise_for_status()
|
|
137
|
+
except requests.exceptions.HTTPError as e:
|
|
138
|
+
raise OSError(f"error listing dropbox. Original message is {e}") from None
|
|
139
|
+
data = response.json()
|
|
140
|
+
entries.extend(data["entries"])
|
|
141
|
+
|
|
142
|
+
result = []
|
|
143
|
+
for entry in entries:
|
|
144
|
+
if show_files and entry[".tag"] == "file":
|
|
145
|
+
result.append(entry["path_display"])
|
|
146
|
+
if show_folders and entry[".tag"] == "folder":
|
|
147
|
+
result.append(entry["path_display"] + "/")
|
|
148
|
+
return result
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def read(path):
|
|
152
|
+
"""
|
|
153
|
+
read file from dropbox
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
path : str or Pathlib.Path
|
|
158
|
+
path to read from
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
contents of the dropbox file : bytes
|
|
163
|
+
|
|
164
|
+
Note
|
|
165
|
+
----
|
|
166
|
+
If the file could not be read, an OSError will be raised.
|
|
167
|
+
|
|
168
|
+
Note
|
|
169
|
+
----
|
|
170
|
+
If DROPBOX.REFRESH_TOKEN, DROPBOX.APP_KEY and DROPBOX.APP_SECRET environment variables are specified,
|
|
171
|
+
it is not necessary to call dropbox_init() prior to any dropbox function.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
_login()
|
|
175
|
+
|
|
176
|
+
path = normalize_path(path)
|
|
177
|
+
|
|
178
|
+
headers = {"Authorization": f"Bearer {_token}", "Dropbox-API-Arg": json.dumps({"path": path})}
|
|
179
|
+
with requests.post("https://content.dropboxapi.com/2/files/download", headers=headers, stream=True, timeout=60) as response:
|
|
180
|
+
try:
|
|
181
|
+
response.raise_for_status()
|
|
182
|
+
except requests.exceptions.HTTPError as e:
|
|
183
|
+
raise OSError(f"file {str(path)} not found. Original message is {e}") from None
|
|
184
|
+
chunks = []
|
|
185
|
+
for chunk in response.iter_content(chunk_size=1024):
|
|
186
|
+
if chunk:
|
|
187
|
+
chunks.append(chunk)
|
|
188
|
+
return b"".join(chunks)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def write(path, contents):
|
|
192
|
+
"""
|
|
193
|
+
write to file on dropbox
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
path : str or Pathlib.Path
|
|
198
|
+
path to write to
|
|
199
|
+
|
|
200
|
+
contents : bytes
|
|
201
|
+
contents to be written
|
|
202
|
+
|
|
203
|
+
Note
|
|
204
|
+
----
|
|
205
|
+
If the file could not be written, an OSError will be raised.
|
|
206
|
+
|
|
207
|
+
Note
|
|
208
|
+
----
|
|
209
|
+
If DROPBOX.REFRESH_TOKEN, DROPBOX.APP_KEY and DROPBOX.APP_SECRET environment variables are specified,
|
|
210
|
+
it is not necessary to call dropbox_init() prior to any dropbox function.
|
|
211
|
+
"""
|
|
212
|
+
_login()
|
|
213
|
+
path = normalize_path(path)
|
|
214
|
+
|
|
215
|
+
headers = {
|
|
216
|
+
"Authorization": f"Bearer {_token}",
|
|
217
|
+
"Dropbox-API-Arg": json.dumps(
|
|
218
|
+
{"path": str(path), "mode": "overwrite", "autorename": False, "mute": False} # Where it will be saved in Dropbox # "add" or "overwrite"
|
|
219
|
+
),
|
|
220
|
+
"Content-Type": "application/octet-stream",
|
|
221
|
+
}
|
|
222
|
+
response = requests.post("https://content.dropboxapi.com/2/files/upload", headers=headers, data=contents)
|
|
223
|
+
try:
|
|
224
|
+
response.raise_for_status()
|
|
225
|
+
except requests.exceptions.HTTPError as e:
|
|
226
|
+
raise OSError(f"file {str(path)} could not be written. Original message is {e}") from None
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def delete(path):
|
|
230
|
+
"""
|
|
231
|
+
delete file dropbox
|
|
232
|
+
|
|
233
|
+
Parameters
|
|
234
|
+
----------
|
|
235
|
+
path : str or Pathlib.Path
|
|
236
|
+
path to delete
|
|
237
|
+
|
|
238
|
+
Note
|
|
239
|
+
----
|
|
240
|
+
If the file could not be deleted, an OSError will be raised.
|
|
241
|
+
|
|
242
|
+
Note
|
|
243
|
+
----
|
|
244
|
+
If DROPBOX.REFRESH_TOKEN, DROPBOX.APP_KEY and DROPBOX.APP_SECRET environment variables are specified,
|
|
245
|
+
it is not necessary to call dropbox_init() prior to any dropbox function.
|
|
246
|
+
"""
|
|
247
|
+
_login()
|
|
248
|
+
path = normalize_path(path)
|
|
249
|
+
|
|
250
|
+
headers = {"Authorization": f"Bearer {_token}", "Content-Type": "application/json"}
|
|
251
|
+
|
|
252
|
+
data = {"path": str(path)} # Path in Dropbox, starting with /
|
|
253
|
+
|
|
254
|
+
response = requests.post("https://api.dropboxapi.com/2/files/delete_v2", headers=headers, data=json.dumps(data))
|
|
255
|
+
try:
|
|
256
|
+
response.raise_for_status()
|
|
257
|
+
except requests.exceptions.HTTPError as e:
|
|
258
|
+
raise OSError(f"file {str(path)} could not be deleted. Original message is {e}") from None
|
xlwings_utils/local.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
def dir(path, recursive=False, show_files=True, show_folders=False):
|
|
4
|
+
"""
|
|
5
|
+
returns all local files/folders at given path
|
|
6
|
+
|
|
7
|
+
Parameters
|
|
8
|
+
----------
|
|
9
|
+
path : str or Pathlib.Path
|
|
10
|
+
path from which to list all files (default: '')
|
|
11
|
+
|
|
12
|
+
recursive : bool
|
|
13
|
+
if True, recursively list files. if False (default) no recursion
|
|
14
|
+
|
|
15
|
+
show_files : bool
|
|
16
|
+
if True (default), show file entries
|
|
17
|
+
if False, do not show file entries
|
|
18
|
+
|
|
19
|
+
show_folders : bool
|
|
20
|
+
if True, show folder entries
|
|
21
|
+
if False (default), do not show folder entries
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
files, relative to path : list
|
|
26
|
+
"""
|
|
27
|
+
path = Path(path)
|
|
28
|
+
|
|
29
|
+
result = []
|
|
30
|
+
for entry in path.iterdir():
|
|
31
|
+
if entry.is_file():
|
|
32
|
+
if show_files:
|
|
33
|
+
result.append(str(entry))
|
|
34
|
+
elif entry.is_dir():
|
|
35
|
+
if show_folders:
|
|
36
|
+
result.append(str(entry) + "/")
|
|
37
|
+
if recursive:
|
|
38
|
+
result.extend(list_local(entry, recursive=recursive, show_files=show_files, show_folders=show_folders))
|
|
39
|
+
return result
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def write(path, contents):
|
|
43
|
+
path = Path(path)
|
|
44
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
with open(path, "wb") as f:
|
|
46
|
+
f.write(contents)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def read(path):
|
|
50
|
+
path = Path(path)
|
|
51
|
+
with open(path, "rb") as f:
|
|
52
|
+
contents = f.read()
|
|
53
|
+
return contents
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import xml.etree.ElementTree
|
|
3
|
+
import urllib.parse
|
|
4
|
+
import xlwings_utils
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import pyodide_http
|
|
9
|
+
except ImportError:
|
|
10
|
+
...
|
|
11
|
+
|
|
12
|
+
missing = object()
|
|
13
|
+
|
|
14
|
+
_url = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def make_base_path(webdav_url: str) -> str:
|
|
18
|
+
"""
|
|
19
|
+
Turn 'https://host/remote.php/dav/files/user%40mail.com/'
|
|
20
|
+
into '/remote.php/dav/files/user@mail.com/' (decoded, with trailing slash).
|
|
21
|
+
"""
|
|
22
|
+
p = urllib.parse.urlparse(webdav_url)
|
|
23
|
+
path = urllib.parse.unquote(p.path)
|
|
24
|
+
if not path.endswith("/"):
|
|
25
|
+
path += "/"
|
|
26
|
+
return path
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def clean_href(href: str, base_path: str) -> str:
|
|
30
|
+
"""
|
|
31
|
+
Turn href from PROPFIND into a clean relative path.
|
|
32
|
+
Works whether href is absolute URL or just a server path,
|
|
33
|
+
and whether it's encoded or not.
|
|
34
|
+
"""
|
|
35
|
+
# href may be a full URL or just a path
|
|
36
|
+
if "://" in href:
|
|
37
|
+
href_path = urllib.parse.unquote(urllib.parse.urlparse(href).path)
|
|
38
|
+
else:
|
|
39
|
+
href_path = urllib.parse.unquote(href)
|
|
40
|
+
|
|
41
|
+
# Ensure base_path is decoded and slash-normalized
|
|
42
|
+
base_path = urllib.parse.unquote(base_path)
|
|
43
|
+
if not base_path.endswith("/"):
|
|
44
|
+
base_path += "/"
|
|
45
|
+
|
|
46
|
+
if href_path.startswith(base_path):
|
|
47
|
+
rel = href_path[len(base_path) :]
|
|
48
|
+
else:
|
|
49
|
+
# fallback: just strip leading slash to avoid weird output
|
|
50
|
+
rel = href_path.lstrip("/")
|
|
51
|
+
|
|
52
|
+
return rel.lstrip("/")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def init(url=missing, username=missing, password=missing, **kwargs):
|
|
56
|
+
global _auth
|
|
57
|
+
global _url
|
|
58
|
+
try:
|
|
59
|
+
import pyodide_http
|
|
60
|
+
|
|
61
|
+
pyodide_http.patch_all() # required to reliably use requests on pyodide platforms
|
|
62
|
+
|
|
63
|
+
except ImportError:
|
|
64
|
+
...
|
|
65
|
+
|
|
66
|
+
if url is missing:
|
|
67
|
+
if "NEXTCLOUD.URL" in os.environ:
|
|
68
|
+
url = os.environ["NEXTCLOUD.URL"]
|
|
69
|
+
else:
|
|
70
|
+
raise ValueError("no NEXTCLOUD.URL found in environment.")
|
|
71
|
+
if username is missing:
|
|
72
|
+
if "NEXTCLOUD.USERNAME" in os.environ:
|
|
73
|
+
username = os.environ["NEXTCLOUD.USERNAME"]
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError("no NEXTCLOUD.USERNAMEfound in environment.")
|
|
76
|
+
if password is missing:
|
|
77
|
+
if "NEXTCLOUD.PASSWORD" in os.environ:
|
|
78
|
+
password = os.environ["NEXTCLOUD.PASSWORD"]
|
|
79
|
+
else:
|
|
80
|
+
raise ValueError("no NEXTCLOUD.PASSWORD found in environment.")
|
|
81
|
+
_url = url
|
|
82
|
+
_auth = (username, password)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _login():
|
|
86
|
+
global _url
|
|
87
|
+
if _url is None:
|
|
88
|
+
init() # use environment
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def dir(path="", recursive=False, show_files=True, show_folders=False):
|
|
92
|
+
"""
|
|
93
|
+
returns all nextcloud files/folders in path
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
path : str or Pathlib.Path
|
|
98
|
+
path from which to list all files (default: '')
|
|
99
|
+
|
|
100
|
+
recursive : bool
|
|
101
|
+
if True, recursively list files and folders. if False (default) no recursion
|
|
102
|
+
|
|
103
|
+
show_files : bool
|
|
104
|
+
if True (default), show file entries
|
|
105
|
+
if False, do not show file entries
|
|
106
|
+
|
|
107
|
+
show_folders : bool
|
|
108
|
+
if True, show folder entries
|
|
109
|
+
if False (default), do not show folder entries
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
files : list
|
|
114
|
+
|
|
115
|
+
Note
|
|
116
|
+
----
|
|
117
|
+
If NEXTCLOUD.URL, NEXTCLOUD.USERNAME and NEXTCLOUD.PASSWORD environment variables are specified,
|
|
118
|
+
it is not necessary to call nextcloud_init() prior to any nextcloud function.
|
|
119
|
+
"""
|
|
120
|
+
_login()
|
|
121
|
+
|
|
122
|
+
headers = {"Depth": "1000" if recursive else "1"} # 1 = directory + its immediate children
|
|
123
|
+
|
|
124
|
+
response = requests.request("PROPFIND", _url + path, auth=_auth, headers=headers)
|
|
125
|
+
|
|
126
|
+
response.raise_for_status()
|
|
127
|
+
root = xml.etree.ElementTree.fromstring(response.text)
|
|
128
|
+
namespaces = {"d": "DAV:"}
|
|
129
|
+
|
|
130
|
+
items = []
|
|
131
|
+
|
|
132
|
+
base_path = make_base_path(_url)
|
|
133
|
+
|
|
134
|
+
for response_el in root.findall("d:response", namespaces):
|
|
135
|
+
href = response_el.find("d:href", namespaces).text
|
|
136
|
+
href = clean_href(href, base_path)
|
|
137
|
+
if not href.startswith("/"):
|
|
138
|
+
href = "/" + href
|
|
139
|
+
|
|
140
|
+
prop = response_el.find("d:propstat/d:prop", namespaces)
|
|
141
|
+
res_type = prop.find("d:resourcetype", namespaces)
|
|
142
|
+
is_dir = res_type.find("d:collection", namespaces) is not None
|
|
143
|
+
if is_dir and show_folders:
|
|
144
|
+
items.append(href)
|
|
145
|
+
if not is_dir and show_files:
|
|
146
|
+
items.append(href)
|
|
147
|
+
return items
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def read(path):
|
|
151
|
+
"""
|
|
152
|
+
read file from nextcloud
|
|
153
|
+
|
|
154
|
+
Parameters
|
|
155
|
+
----------
|
|
156
|
+
path : str or Pathlib.Path
|
|
157
|
+
path to read from
|
|
158
|
+
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
contents of the nextcloud file : bytes
|
|
162
|
+
|
|
163
|
+
Note
|
|
164
|
+
----
|
|
165
|
+
If the file could not be read, an OSError will be raised.
|
|
166
|
+
|
|
167
|
+
Note
|
|
168
|
+
----
|
|
169
|
+
If NEXTCLOUD.URL, NEXTCLOUD.USERNAME and NEXTCLOUD.PASSWORD environment variables are specified,
|
|
170
|
+
it is not necessary to call nextcloud_init() prior to any nextcloud function.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
_login()
|
|
174
|
+
response = requests.get(_url + path, auth=_auth)
|
|
175
|
+
try:
|
|
176
|
+
response.raise_for_status()
|
|
177
|
+
except requests.exceptions.HTTPError as e:
|
|
178
|
+
raise OSError(f"file {str(path)} not found. Original message is {e}") from None
|
|
179
|
+
file_content = response.content
|
|
180
|
+
return file_content
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def write(path, contents):
|
|
184
|
+
"""
|
|
185
|
+
write to file on nextcloud
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
path : str or Pathlib.Path
|
|
190
|
+
path to write to
|
|
191
|
+
|
|
192
|
+
contents : bytes
|
|
193
|
+
contents to be written
|
|
194
|
+
|
|
195
|
+
Note
|
|
196
|
+
----
|
|
197
|
+
If the file could not be written, an OSError will be raised.
|
|
198
|
+
|
|
199
|
+
Note
|
|
200
|
+
----
|
|
201
|
+
If NEXTCLOUD.URL, NEXTCLOUD.USERNAME and NEXTCLOUD.PASSWORD environment variables are specified,
|
|
202
|
+
it is not necessary to call nextcloud_init() prior to any nextcloud function.
|
|
203
|
+
"""
|
|
204
|
+
_login()
|
|
205
|
+
response = requests.put(_url + path, auth=_auth, data=contents, timeout=60)
|
|
206
|
+
try:
|
|
207
|
+
response.raise_for_status()
|
|
208
|
+
except requests.exceptions.HTTPError as e:
|
|
209
|
+
raise OSError(f"file {str(path)} could not be written. Original message is {e}") from None
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def delete(path):
|
|
213
|
+
"""
|
|
214
|
+
delete file nextcloud
|
|
215
|
+
|
|
216
|
+
Parameters
|
|
217
|
+
----------
|
|
218
|
+
path : str or Pathlib.Path
|
|
219
|
+
path to delete
|
|
220
|
+
|
|
221
|
+
Note
|
|
222
|
+
----
|
|
223
|
+
If the file could not be deleted, an OSError will be raised.
|
|
224
|
+
|
|
225
|
+
Note
|
|
226
|
+
----
|
|
227
|
+
If NEXTCLOUD.URL, NEXTCLOUD.USERNAME and NEXTCLOUD.PASSWORD environment variables are specified,
|
|
228
|
+
it is not necessary to call nextcloud_init() prior to any nextcloud function.
|
|
229
|
+
"""
|
|
230
|
+
_login()
|
|
231
|
+
|
|
232
|
+
response = requests.delete(_url + path, auth=_auth, timeout=30)
|
|
233
|
+
try:
|
|
234
|
+
response.raise_for_status()
|
|
235
|
+
except requests.exceptions.HTTPError as e:
|
|
236
|
+
raise OSError(f"file {str(path)} could not be deleted. Original message is {e}") from None
|