Skip to content

Commit 9190a40

Browse files
authored
added Nextcloud.response_headers property (#81)
This will allow to implement Talk API, as we need `X-Nextcloud-Talk-Hash` and `X-Nextcloud-Talk-Modified-Before` headers. --------- Signed-off-by: Alexander Piskun <bigcat88@icloud.com>
1 parent b1c4fcc commit 9190a40

File tree

8 files changed

+44
-7
lines changed

8 files changed

+44
-7
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.0.30 - 2023-08-17]
6+
7+
### Added
8+
9+
- `Nextcloud.response_headers` property, to get headers from last response.
10+
511
## [0.0.29 - 2023-08-13]
612

713
### Added

docs/conf.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"sphinx_copybutton",
2121
"sphinx_inline_tabs",
2222
"sphinx_issues",
23-
"sphinx_rtd_theme"]
23+
"sphinx_rtd_theme",
24+
]
2425

2526
intersphinx_mapping = {
2627
"python": ("https://docs.python.org/3", None),
@@ -57,7 +58,10 @@
5758
# Default is False. You can activate this mode temporarily using the -n command-line
5859
# switch.
5960
nitpicky = True
60-
nitpick_ignore_regex = [(r"py:class", r"starlette\.requests\.Request")]
61+
nitpick_ignore_regex = [
62+
(r"py:class", r"starlette\.requests\.Request"),
63+
(r"py:.*", r"httpx.*"),
64+
]
6165

6266
autodoc_member_order = "bysource"
6367

nc_py_api/_session.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
from urllib.parse import quote, urlencode
1515

1616
from fastapi import Request
17-
from httpx import Client, Limits, ReadTimeout, Response
17+
from httpx import Client
18+
from httpx import Headers as HttpxHeaders
19+
from httpx import Limits, ReadTimeout, Response
1820

1921
try:
2022
from xxhash import xxh64
@@ -137,6 +139,7 @@ class NcSessionBasic(ABC):
137139
user: str
138140
custom_headers: dict
139141
_capabilities: dict
142+
response_headers: HttpxHeaders
140143

141144
@abstractmethod
142145
def __init__(self, **kwargs):
@@ -145,6 +148,7 @@ def __init__(self, **kwargs):
145148
self.custom_headers = kwargs.get("headers", {})
146149
self.limits = Limits(max_keepalive_connections=20, max_connections=20, keepalive_expiry=60.0)
147150
self.init_adapter()
151+
self.response_headers = HttpxHeaders()
148152

149153
def __del__(self):
150154
if hasattr(self, "adapter") and self.adapter:
@@ -200,6 +204,7 @@ def _ocs(self, method: str, path_params: str, headers: dict, data: Optional[byte
200204
except ReadTimeout:
201205
raise NextcloudException(408, info=info) from None
202206

207+
self.response_headers = response.headers
203208
check_error(response.status_code, info)
204209
response_data = loads(response.text)
205210
ocs_meta = response_data["ocs"]["meta"]
@@ -235,11 +240,12 @@ def dav_stream(
235240

236241
def _dav(self, method: str, path: str, headers: dict, data: Optional[bytes], **kwargs) -> Response:
237242
self.init_adapter()
238-
# self.cfg.
239243
timeout = kwargs.pop("timeout", self.cfg.options.timeout_dav)
240-
return self.adapter.request(
244+
result = self.adapter.request(
241245
method, self.cfg.endpoint + path, headers=headers, content=data, timeout=timeout, **kwargs
242246
)
247+
self.response_headers = result.headers
248+
return result
243249

244250
def _dav_stream(self, method: str, path: str, headers: dict, data: Optional[bytes], **kwargs) -> Iterator[Response]:
245251
self.init_adapter()

nc_py_api/files/files.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def download_directory_as_zip(
162162
with self._session.get_stream(
163163
"/index.php/apps/files/ajax/download.php", params={"dir": path}
164164
) as response: # type: ignore
165+
self._session.response_headers = response.headers
165166
check_error(response.status_code, f"download_directory_as_zip: user={self._session.user}, path={path}")
166167
result_path = local_path if local_path else os.path.basename(path)
167168
with open(
@@ -446,6 +447,7 @@ def __download2stream(self, path: str, fp, **kwargs) -> None:
446447
with self._session.dav_stream(
447448
"GET", self._dav_get_obj_path(self._session.user, path)
448449
) as response: # type: ignore
450+
self._session.response_headers = response.headers
449451
check_error(response.status_code, f"download_stream: user={self._session.user}, path={path}")
450452
for data_chunk in response.iter_raw(chunk_size=kwargs.get("chunk_size", 4 * 1024 * 1024)):
451453
fp.write(data_chunk)

nc_py_api/nextcloud.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Optional, Union
44

55
from fastapi import Request
6+
from httpx import Headers as HttpxHeaders
67

78
from ._misc import check_capabilities
89
from ._session import AppConfig, NcSession, NcSessionApp, NcSessionBasic, ServerVersion
@@ -57,6 +58,11 @@ def update_server_info(self) -> None:
5758
"""
5859
self._session.update_server_info()
5960

61+
@property
62+
def response_headers(self) -> HttpxHeaders:
63+
"""Returns the `HTTPX headers <https://www.python-httpx.org/api/#headers>`_ from the last response."""
64+
return self._session.response_headers
65+
6066
@property
6167
def theme(self) -> Optional[ThemingInfo]:
6268
"""Returns Theme information."""

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ preview = true
105105
line-length = 120
106106
target-version = "py39"
107107
select = ["A", "B", "C", "D", "E", "F", "G", "I", "UP", "SIM", "Q", "W"]
108-
extend-ignore = ["D107", "D105", "D203", "D213", "D401"]
108+
extend-ignore = ["D107", "D105", "D203", "D213", "D401", "I001"]
109109

110110
[tool.ruff.per-file-ignores]
111111
"nc_py_api/__init__.py" = ["F401"]

tests/files_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,12 @@ def test_file_download2stream(nc, data_type, chunk_size):
115115
srv_admin_manual_buf = MyBytesIO()
116116
content = "".join(choice(ascii_lowercase) for _ in range(64)) if data_type == "str" else randbytes(64)
117117
nc.files.upload("test_file.txt", content=content)
118+
old_headers = nc.response_headers
118119
if chunk_size is not None:
119120
nc.files.download2stream("/test_file.txt", srv_admin_manual_buf, chunk_size=chunk_size)
120121
else:
121122
nc.files.download2stream("/test_file.txt", srv_admin_manual_buf)
123+
assert nc.response_headers != old_headers
122124
assert nc.files.download("test_file.txt") == srv_admin_manual_buf.getbuffer()
123125
if chunk_size is None:
124126
assert srv_admin_manual_buf.n_write_calls == 1
@@ -542,7 +544,9 @@ def test_download_as_zip(nc):
542544
nc.files.upload("test_root_folder/0.txt", content="")
543545
nc.files.upload("test_root_folder/1.txt", content="123")
544546
nc.files.upload("test_root_folder/test_subfolder/0.txt", content="")
547+
old_headers = nc.response_headers
545548
result = nc.files.download_directory_as_zip("test_root_folder")
549+
assert nc.response_headers != old_headers
546550
try:
547551
with zipfile.ZipFile(result, "r") as zip_ref:
548552
assert zip_ref.filelist[0].filename == "test_root_folder/"
@@ -560,7 +564,9 @@ def test_download_as_zip(nc):
560564
assert len(zip_ref.filelist) == 6
561565
finally:
562566
os.remove(result)
567+
old_headers = nc.response_headers
563568
result = nc.files.download_directory_as_zip("test_root_folder/test_subfolder", "2.zip")
569+
assert nc.response_headers != old_headers
564570
try:
565571
assert str(result) == "2.zip"
566572
with zipfile.ZipFile(result, "r") as zip_ref:

tests/misc_test.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pytest
2-
from gfixture import NC_APP
2+
from gfixture import NC_APP, NC_TO_TEST
33

44
from nc_py_api import NextcloudException
55
from nc_py_api._deffered_error import DeferredError # noqa
@@ -53,3 +53,10 @@ def test_deffered_error():
5353

5454
with pytest.raises(ModuleNotFoundError):
5555
unknown_non_exist_module.some_class_or_func()
56+
57+
58+
@pytest.mark.parametrize("nc", NC_TO_TEST)
59+
def test_ocs_response_headers(nc):
60+
old_headers = nc.response_headers
61+
nc.users.get_details()
62+
assert old_headers != nc.response_headers

0 commit comments

Comments
 (0)