nc-py-api 0.7.1__py3-none-any.whl → 0.7.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.
nc_py_api/_version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Version of nc_py_api."""
2
2
 
3
- __version__ = "0.7.1"
3
+ __version__ = "0.7.2"
nc_py_api/files/files.py CHANGED
@@ -3,6 +3,7 @@
3
3
  import builtins
4
4
  import os
5
5
  from pathlib import Path
6
+ from urllib.parse import quote
6
7
 
7
8
  from httpx import Headers
8
9
 
@@ -85,7 +86,7 @@ class FilesAPI:
85
86
  def download(self, path: str | FsNode) -> bytes:
86
87
  """Downloads and returns the content of a file."""
87
88
  path = path.user_path if isinstance(path, FsNode) else path
88
- response = self._session.adapter_dav.get(dav_get_obj_path(self._session.user, path))
89
+ response = self._session.adapter_dav.get(quote(dav_get_obj_path(self._session.user, path)))
89
90
  check_error(response, f"download: user={self._session.user}, path={path}")
90
91
  return response.content
91
92
 
@@ -137,7 +138,7 @@ class FilesAPI:
137
138
  """
138
139
  path = path.user_path if isinstance(path, FsNode) else path
139
140
  full_path = dav_get_obj_path(self._session.user, path)
140
- response = self._session.adapter_dav.put(full_path, content=content)
141
+ response = self._session.adapter_dav.put(quote(full_path), content=content)
141
142
  check_error(response, f"upload: user={self._session.user}, path={path}, size={len(content)}")
142
143
  return FsNode(full_path.strip("/"), **etag_fileid_from_response(response))
143
144
 
@@ -166,7 +167,7 @@ class FilesAPI:
166
167
  """
167
168
  path = path.user_path if isinstance(path, FsNode) else path
168
169
  full_path = dav_get_obj_path(self._session.user, path)
169
- response = self._session.adapter_dav.request("MKCOL", full_path)
170
+ response = self._session.adapter_dav.request("MKCOL", quote(full_path))
170
171
  check_error(response)
171
172
  full_path += "/" if not full_path.endswith("/") else ""
172
173
  return FsNode(full_path.lstrip("/"), **etag_fileid_from_response(response))
@@ -201,7 +202,7 @@ class FilesAPI:
201
202
  :param not_fail: if set to ``True`` and the object is not found, it does not raise an exception.
202
203
  """
203
204
  path = path.user_path if isinstance(path, FsNode) else path
204
- response = self._session.adapter_dav.delete(dav_get_obj_path(self._session.user, path))
205
+ response = self._session.adapter_dav.delete(quote(dav_get_obj_path(self._session.user, path)))
205
206
  if response.status_code == 404 and not_fail:
206
207
  return
207
208
  check_error(response)
@@ -218,11 +219,11 @@ class FilesAPI:
218
219
  full_dest_path = dav_get_obj_path(
219
220
  self._session.user, path_dest.user_path if isinstance(path_dest, FsNode) else path_dest
220
221
  )
221
- dest = self._session.cfg.dav_endpoint + full_dest_path
222
+ dest = self._session.cfg.dav_endpoint + quote(full_dest_path)
222
223
  headers = Headers({"Destination": dest, "Overwrite": "T" if overwrite else "F"}, encoding="utf-8")
223
224
  response = self._session.adapter_dav.request(
224
225
  "MOVE",
225
- dav_get_obj_path(self._session.user, path_src),
226
+ quote(dav_get_obj_path(self._session.user, path_src)),
226
227
  headers=headers,
227
228
  )
228
229
  check_error(response, f"move: user={self._session.user}, src={path_src}, dest={dest}, {overwrite}")
@@ -240,11 +241,11 @@ class FilesAPI:
240
241
  full_dest_path = dav_get_obj_path(
241
242
  self._session.user, path_dest.user_path if isinstance(path_dest, FsNode) else path_dest
242
243
  )
243
- dest = self._session.cfg.dav_endpoint + full_dest_path
244
+ dest = self._session.cfg.dav_endpoint + quote(full_dest_path)
244
245
  headers = Headers({"Destination": dest, "Overwrite": "T" if overwrite else "F"}, encoding="utf-8")
245
246
  response = self._session.adapter_dav.request(
246
247
  "COPY",
247
- dav_get_obj_path(self._session.user, path_src),
248
+ quote(dav_get_obj_path(self._session.user, path_src)),
248
249
  headers=headers,
249
250
  )
250
251
  check_error(response, f"copy: user={self._session.user}, src={path_src}, dest={dest}, {overwrite}")
@@ -276,7 +277,7 @@ class FilesAPI:
276
277
  path = path.user_path if isinstance(path, FsNode) else path
277
278
  root = build_setfav_req(value)
278
279
  webdav_response = self._session.adapter_dav.request(
279
- "PROPPATCH", dav_get_obj_path(self._session.user, path), content=element_tree_as_str(root)
280
+ "PROPPATCH", quote(dav_get_obj_path(self._session.user, path)), content=element_tree_as_str(root)
280
281
  )
281
282
  check_error(webdav_response, f"setfav: path={path}, value={value}")
282
283
 
@@ -300,7 +301,7 @@ class FilesAPI:
300
301
  headers = Headers({"Destination": dest}, encoding="utf-8")
301
302
  response = self._session.adapter_dav.request(
302
303
  "MOVE",
303
- f"/trashbin/{self._session.user}/{path}",
304
+ quote(f"/trashbin/{self._session.user}/{path}"),
304
305
  headers=headers,
305
306
  )
306
307
  check_error(response, f"trashbin_restore: user={self._session.user}, src={path}, dest={dest}")
@@ -312,7 +313,7 @@ class FilesAPI:
312
313
  :param not_fail: if set to ``True`` and the object is not found, it does not raise an exception.
313
314
  """
314
315
  path = path.user_path if isinstance(path, FsNode) else path
315
- response = self._session.adapter_dav.delete(f"/trashbin/{self._session.user}/{path}")
316
+ response = self._session.adapter_dav.delete(quote(f"/trashbin/{self._session.user}/{path}"))
316
317
  if response.status_code == 404 and not_fail:
317
318
  return
318
319
  check_error(response)
@@ -430,7 +431,7 @@ class FilesAPI:
430
431
  root, dav_path = build_listdir_req(user, path, properties, prop_type)
431
432
  webdav_response = self._session.adapter_dav.request(
432
433
  "PROPFIND",
433
- dav_path,
434
+ quote(dav_path),
434
435
  content=element_tree_as_str(root),
435
436
  headers={"Depth": "infinity" if depth == -1 else str(depth)},
436
437
  )
@@ -439,16 +440,17 @@ class FilesAPI:
439
440
  )
440
441
 
441
442
  def __download2stream(self, path: str, fp, **kwargs) -> None:
442
- with self._session.adapter_dav.stream("GET", dav_get_obj_path(self._session.user, path)) as response:
443
+ with self._session.adapter_dav.stream("GET", quote(dav_get_obj_path(self._session.user, path))) as response:
443
444
  check_error(response, f"download_stream: user={self._session.user}, path={path}")
444
445
  for data_chunk in response.iter_raw(chunk_size=kwargs.get("chunk_size", 5 * 1024 * 1024)):
445
446
  fp.write(data_chunk)
446
447
 
447
448
  def __upload_stream(self, path: str, fp, chunk_size: int) -> FsNode:
448
- _dav_path = dav_get_obj_path(self._session.user, "nc-py-api-" + random_string(56), root_path="/uploads")
449
+ _tmp_path = "nc-py-api-" + random_string(56)
450
+ _dav_path = quote(dav_get_obj_path(self._session.user, _tmp_path, root_path="/uploads"))
449
451
  _v2 = bool(self._session.cfg.options.upload_chunk_v2 and chunk_size >= 5 * 1024 * 1024)
450
452
  full_path = dav_get_obj_path(self._session.user, path)
451
- headers = Headers({"Destination": self._session.cfg.dav_endpoint + full_path}, encoding="utf-8")
453
+ headers = Headers({"Destination": self._session.cfg.dav_endpoint + quote(full_path)}, encoding="utf-8")
452
454
  if _v2:
453
455
  response = self._session.adapter_dav.request("MKCOL", _dav_path, headers=headers)
454
456
  else:
@@ -547,7 +549,7 @@ class AsyncFilesAPI:
547
549
  async def download(self, path: str | FsNode) -> bytes:
548
550
  """Downloads and returns the content of a file."""
549
551
  path = path.user_path if isinstance(path, FsNode) else path
550
- response = await self._session.adapter_dav.get(dav_get_obj_path(await self._session.user, path))
552
+ response = await self._session.adapter_dav.get(quote(dav_get_obj_path(await self._session.user, path)))
551
553
  check_error(response, f"download: user={await self._session.user}, path={path}")
552
554
  return response.content
553
555
 
@@ -601,7 +603,7 @@ class AsyncFilesAPI:
601
603
  """
602
604
  path = path.user_path if isinstance(path, FsNode) else path
603
605
  full_path = dav_get_obj_path(await self._session.user, path)
604
- response = await self._session.adapter_dav.put(full_path, content=content)
606
+ response = await self._session.adapter_dav.put(quote(full_path), content=content)
605
607
  check_error(response, f"upload: user={await self._session.user}, path={path}, size={len(content)}")
606
608
  return FsNode(full_path.strip("/"), **etag_fileid_from_response(response))
607
609
 
@@ -630,7 +632,7 @@ class AsyncFilesAPI:
630
632
  """
631
633
  path = path.user_path if isinstance(path, FsNode) else path
632
634
  full_path = dav_get_obj_path(await self._session.user, path)
633
- response = await self._session.adapter_dav.request("MKCOL", full_path)
635
+ response = await self._session.adapter_dav.request("MKCOL", quote(full_path))
634
636
  check_error(response)
635
637
  full_path += "/" if not full_path.endswith("/") else ""
636
638
  return FsNode(full_path.lstrip("/"), **etag_fileid_from_response(response))
@@ -665,7 +667,7 @@ class AsyncFilesAPI:
665
667
  :param not_fail: if set to ``True`` and the object is not found, it does not raise an exception.
666
668
  """
667
669
  path = path.user_path if isinstance(path, FsNode) else path
668
- response = await self._session.adapter_dav.delete(dav_get_obj_path(await self._session.user, path))
670
+ response = await self._session.adapter_dav.delete(quote(dav_get_obj_path(await self._session.user, path)))
669
671
  if response.status_code == 404 and not_fail:
670
672
  return
671
673
  check_error(response)
@@ -682,11 +684,11 @@ class AsyncFilesAPI:
682
684
  full_dest_path = dav_get_obj_path(
683
685
  await self._session.user, path_dest.user_path if isinstance(path_dest, FsNode) else path_dest
684
686
  )
685
- dest = self._session.cfg.dav_endpoint + full_dest_path
687
+ dest = self._session.cfg.dav_endpoint + quote(full_dest_path)
686
688
  headers = Headers({"Destination": dest, "Overwrite": "T" if overwrite else "F"}, encoding="utf-8")
687
689
  response = await self._session.adapter_dav.request(
688
690
  "MOVE",
689
- dav_get_obj_path(await self._session.user, path_src),
691
+ quote(dav_get_obj_path(await self._session.user, path_src)),
690
692
  headers=headers,
691
693
  )
692
694
  check_error(response, f"move: user={await self._session.user}, src={path_src}, dest={dest}, {overwrite}")
@@ -704,11 +706,11 @@ class AsyncFilesAPI:
704
706
  full_dest_path = dav_get_obj_path(
705
707
  await self._session.user, path_dest.user_path if isinstance(path_dest, FsNode) else path_dest
706
708
  )
707
- dest = self._session.cfg.dav_endpoint + full_dest_path
709
+ dest = self._session.cfg.dav_endpoint + quote(full_dest_path)
708
710
  headers = Headers({"Destination": dest, "Overwrite": "T" if overwrite else "F"}, encoding="utf-8")
709
711
  response = await self._session.adapter_dav.request(
710
712
  "COPY",
711
- dav_get_obj_path(await self._session.user, path_src),
713
+ quote(dav_get_obj_path(await self._session.user, path_src)),
712
714
  headers=headers,
713
715
  )
714
716
  check_error(response, f"copy: user={await self._session.user}, src={path_src}, dest={dest}, {overwrite}")
@@ -740,7 +742,7 @@ class AsyncFilesAPI:
740
742
  path = path.user_path if isinstance(path, FsNode) else path
741
743
  root = build_setfav_req(value)
742
744
  webdav_response = await self._session.adapter_dav.request(
743
- "PROPPATCH", dav_get_obj_path(await self._session.user, path), content=element_tree_as_str(root)
745
+ "PROPPATCH", quote(dav_get_obj_path(await self._session.user, path)), content=element_tree_as_str(root)
744
746
  )
745
747
  check_error(webdav_response, f"setfav: path={path}, value={value}")
746
748
 
@@ -769,7 +771,7 @@ class AsyncFilesAPI:
769
771
  headers = Headers({"Destination": dest}, encoding="utf-8")
770
772
  response = await self._session.adapter_dav.request(
771
773
  "MOVE",
772
- f"/trashbin/{await self._session.user}/{path}",
774
+ quote(f"/trashbin/{await self._session.user}/{path}"),
773
775
  headers=headers,
774
776
  )
775
777
  check_error(response, f"trashbin_restore: user={await self._session.user}, src={path}, dest={dest}")
@@ -781,7 +783,7 @@ class AsyncFilesAPI:
781
783
  :param not_fail: if set to ``True`` and the object is not found, it does not raise an exception.
782
784
  """
783
785
  path = path.user_path if isinstance(path, FsNode) else path
784
- response = await self._session.adapter_dav.delete(f"/trashbin/{await self._session.user}/{path}")
786
+ response = await self._session.adapter_dav.delete(quote(f"/trashbin/{await self._session.user}/{path}"))
785
787
  if response.status_code == 404 and not_fail:
786
788
  return
787
789
  check_error(response)
@@ -899,7 +901,7 @@ class AsyncFilesAPI:
899
901
  root, dav_path = build_listdir_req(user, path, properties, prop_type)
900
902
  webdav_response = await self._session.adapter_dav.request(
901
903
  "PROPFIND",
902
- dav_path,
904
+ quote(dav_path),
903
905
  content=element_tree_as_str(root),
904
906
  headers={"Depth": "infinity" if depth == -1 else str(depth)},
905
907
  )
@@ -909,17 +911,18 @@ class AsyncFilesAPI:
909
911
 
910
912
  async def __download2stream(self, path: str, fp, **kwargs) -> None:
911
913
  async with self._session.adapter_dav.stream(
912
- "GET", dav_get_obj_path(await self._session.user, path)
914
+ "GET", quote(dav_get_obj_path(await self._session.user, path))
913
915
  ) as response:
914
916
  check_error(response, f"download_stream: user={await self._session.user}, path={path}")
915
917
  async for data_chunk in response.aiter_raw(chunk_size=kwargs.get("chunk_size", 5 * 1024 * 1024)):
916
918
  fp.write(data_chunk)
917
919
 
918
920
  async def __upload_stream(self, path: str, fp, chunk_size: int) -> FsNode:
919
- _dav_path = dav_get_obj_path(await self._session.user, "nc-py-api-" + random_string(56), root_path="/uploads")
921
+ _tmp_path = "nc-py-api-" + random_string(56)
922
+ _dav_path = quote(dav_get_obj_path(await self._session.user, _tmp_path, root_path="/uploads"))
920
923
  _v2 = bool(self._session.cfg.options.upload_chunk_v2 and chunk_size >= 5 * 1024 * 1024)
921
924
  full_path = dav_get_obj_path(await self._session.user, path)
922
- headers = Headers({"Destination": self._session.cfg.dav_endpoint + full_path}, encoding="utf-8")
925
+ headers = Headers({"Destination": self._session.cfg.dav_endpoint + quote(full_path)}, encoding="utf-8")
923
926
  if _v2:
924
927
  response = await self._session.adapter_dav.request("MKCOL", _dav_path, headers=headers)
925
928
  else:
nc_py_api/nextcloud.py CHANGED
@@ -113,6 +113,14 @@ class _NextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
113
113
  """Returns Theme information."""
114
114
  return get_parsed_theme(self.capabilities["theming"]) if "theming" in self.capabilities else None
115
115
 
116
+ def perform_login(self) -> bool:
117
+ """Performs login into Nextcloud if not already logged in; manual invocation of this method is unnecessary."""
118
+ try:
119
+ self.update_server_info()
120
+ except Exception: # noqa pylint: disable=broad-exception-caught
121
+ return False
122
+ return True
123
+
116
124
  def ocs(
117
125
  self,
118
126
  method: str,
@@ -199,6 +207,14 @@ class _AsyncNextcloudBasic(ABC): # pylint: disable=too-many-instance-attributes
199
207
  """Returns Theme information."""
200
208
  return get_parsed_theme((await self.capabilities)["theming"]) if "theming" in await self.capabilities else None
201
209
 
210
+ async def perform_login(self) -> bool:
211
+ """Performs login into Nextcloud if not already logged in; manual invocation of this method is unnecessary."""
212
+ try:
213
+ await self.update_server_info()
214
+ except Exception: # noqa pylint: disable=broad-exception-caught
215
+ return False
216
+ return True
217
+
202
218
  async def ocs(
203
219
  self,
204
220
  method: str,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nc-py-api
3
- Version: 0.7.1
3
+ Version: 0.7.2
4
4
  Summary: Nextcloud Python Framework
5
5
  Project-URL: Changelog, https://github.com/cloud-py-api/nc_py_api/blob/main/CHANGELOG.md
6
6
  Project-URL: Documentation, https://cloud-py-api.github.io/nc_py_api/
@@ -7,11 +7,11 @@ nc_py_api/_preferences_ex.py,sha256=gW9bWVHayE6TifoA5hY_RWYUkdFgWrd0OabPvuNSAe0,
7
7
  nc_py_api/_session.py,sha256=y1tKqootIRI7c2jS70fsnmcm1imEjviJ2GzFWYLB7dg,17895
8
8
  nc_py_api/_talk_api.py,sha256=sIEBATbTVuLtLvcyOPwpUSURMyGl8ZpGB7hnEWKQbpM,51033
9
9
  nc_py_api/_theming.py,sha256=hTr3nuOemSuRFZaPy9iXNmBM7rDgQHECH43tHMWGqEY,1870
10
- nc_py_api/_version.py,sha256=wKplGHRmG8XcnEoJl1Yx2Zhykyx1f7YBUfMrnHri8DI,51
10
+ nc_py_api/_version.py,sha256=cuGgTOEQlTm_RtlgmNF0Q5vGJv2Lb5zI0xfefSRw6EM,51
11
11
  nc_py_api/activity.py,sha256=t9VDSnnaXRNOvALqOSGCeXSQZ-426pCOMSfQ96JHys4,9574
12
12
  nc_py_api/apps.py,sha256=6vOFFs6vNHCCvZ_SwXxPq5-X1xfgyLjW8uZSfJKduC8,9774
13
13
  nc_py_api/calendar.py,sha256=-T6CJ8cRbJZTLtxSEDWuuYpD29DMJGCTfLONmtxZV9w,1445
14
- nc_py_api/nextcloud.py,sha256=xz5nnE-SJitvj5HQ2HY-n85KYbOs3jI39GnDXYA2QcA,20908
14
+ nc_py_api/nextcloud.py,sha256=gATnAWslcbv1Lko3hJDnupSQwvL7r--ovqdWRxwwro4,21572
15
15
  nc_py_api/notes.py,sha256=ljO3TOe-Qg0bJ0mlFQwjg--Pxmj-XFknoLbcbJmII0A,15106
16
16
  nc_py_api/notifications.py,sha256=WgzV21TuLOhLk-UEjhBSbMsIi2isa5MmAx6cbe0pc2Y,9187
17
17
  nc_py_api/options.py,sha256=K5co-fIfFVbwF6r3sqWsJF3cKgAbS2CjLAXdyTOkP9s,1717
@@ -34,10 +34,10 @@ nc_py_api/ex_app/ui/top_menu.py,sha256=6BvNtm03rF-fqmOiGM2mVrn0sJRLOWLfhrvzXj7ND
34
34
  nc_py_api/ex_app/ui/ui.py,sha256=dplr1ZIqjtHvOH3lIHBLWmP9vr-m7EIk_oJU5K62low,1284
35
35
  nc_py_api/files/__init__.py,sha256=sVidF669ocg9vSoo4TBKAwGRRVUJHxtyENNUko11qTs,11798
36
36
  nc_py_api/files/_files.py,sha256=IJibj0_y3UQyyVhVI2RgQapPFPsOAV6tyH2sFlLy09I,12226
37
- nc_py_api/files/files.py,sha256=n6SsuYJVO3BA1mofBuO6mOl0tflkMdgzgY3uxYlSGeo,46557
37
+ nc_py_api/files/files.py,sha256=y2syg5JPancawJDFqdu-12OnF6b_p9aytAsSWrYFhRk,46858
38
38
  nc_py_api/files/sharing.py,sha256=bZRSsFdlaXJKheXtyTjPJwpQSpYDnRcyDosMwrvRlRc,14395
39
- nc_py_api-0.7.1.dist-info/METADATA,sha256=khc0FpYq3frTqngrbfcHaIlkuoR5gRQ8ACuXTOrkRno,8571
40
- nc_py_api-0.7.1.dist-info/WHEEL,sha256=mRYSEL3Ih6g5a_CVMIcwiF__0Ae4_gLYh01YFNwiq1k,87
41
- nc_py_api-0.7.1.dist-info/licenses/AUTHORS,sha256=Y1omFHyI8ned9k4jJXs2ATgmgi1GmQ7EZ6S1gxqnX2k,572
42
- nc_py_api-0.7.1.dist-info/licenses/LICENSE.txt,sha256=OLEMh401fAumGHfRSna365MLIfnjdTcdOHZ6QOzMjkg,1551
43
- nc_py_api-0.7.1.dist-info/RECORD,,
39
+ nc_py_api-0.7.2.dist-info/METADATA,sha256=nqjE4PEDCmWE5mPd8rYg9_Ppw9vRRCug097ZFy1QUic,8571
40
+ nc_py_api-0.7.2.dist-info/WHEEL,sha256=mRYSEL3Ih6g5a_CVMIcwiF__0Ae4_gLYh01YFNwiq1k,87
41
+ nc_py_api-0.7.2.dist-info/licenses/AUTHORS,sha256=Y1omFHyI8ned9k4jJXs2ATgmgi1GmQ7EZ6S1gxqnX2k,572
42
+ nc_py_api-0.7.2.dist-info/licenses/LICENSE.txt,sha256=OLEMh401fAumGHfRSna365MLIfnjdTcdOHZ6QOzMjkg,1551
43
+ nc_py_api-0.7.2.dist-info/RECORD,,