lsst-resources 29.2025.1900__tar.gz → 29.2025.2100__tar.gz

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.
Files changed (53) hide show
  1. {lsst_resources-29.2025.1900/python/lsst_resources.egg-info → lsst_resources-29.2025.2100}/PKG-INFO +1 -1
  2. lsst_resources-29.2025.2100/doc/lsst.resources/dav.rst +251 -0
  3. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/doc/lsst.resources/index.rst +1 -0
  4. lsst_resources-29.2025.2100/python/lsst/resources/_resourceHandles/_davResourceHandle.py +190 -0
  5. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/_resourcePath.py +2 -2
  6. lsst_resources-29.2025.2100/python/lsst/resources/dav.py +896 -0
  7. lsst_resources-29.2025.2100/python/lsst/resources/davutils.py +2584 -0
  8. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/http.py +10 -18
  9. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/s3.py +82 -11
  10. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/tests.py +13 -10
  11. lsst_resources-29.2025.2100/python/lsst/resources/version.py +2 -0
  12. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100/python/lsst_resources.egg-info}/PKG-INFO +1 -1
  13. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst_resources.egg-info/SOURCES.txt +5 -0
  14. lsst_resources-29.2025.2100/tests/test_dav.py +1065 -0
  15. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/tests/test_http.py +0 -6
  16. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/tests/test_s3.py +2 -0
  17. lsst_resources-29.2025.1900/python/lsst/resources/version.py +0 -2
  18. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/COPYRIGHT +0 -0
  19. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/LICENSE +0 -0
  20. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/MANIFEST.in +0 -0
  21. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/README.md +0 -0
  22. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/doc/lsst.resources/CHANGES.rst +0 -0
  23. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/doc/lsst.resources/internal-api.rst +0 -0
  24. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/doc/lsst.resources/s3.rst +0 -0
  25. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/pyproject.toml +0 -0
  26. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/__init__.py +0 -0
  27. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/__init__.py +0 -0
  28. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/_resourceHandles/__init__.py +0 -0
  29. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/_resourceHandles/_baseResourceHandle.py +0 -0
  30. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/_resourceHandles/_fileResourceHandle.py +0 -0
  31. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/_resourceHandles/_httpResourceHandle.py +0 -0
  32. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/_resourceHandles/_s3ResourceHandle.py +0 -0
  33. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/file.py +0 -0
  34. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/gs.py +0 -0
  35. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/location.py +0 -0
  36. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/mem.py +0 -0
  37. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/packageresource.py +0 -0
  38. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/py.typed +0 -0
  39. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/s3utils.py +0 -0
  40. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/schemeless.py +0 -0
  41. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst/resources/utils.py +0 -0
  42. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst_resources.egg-info/dependency_links.txt +0 -0
  43. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst_resources.egg-info/requires.txt +0 -0
  44. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst_resources.egg-info/top_level.txt +0 -0
  45. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/python/lsst_resources.egg-info/zip-safe +0 -0
  46. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/setup.cfg +0 -0
  47. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/tests/test_file.py +0 -0
  48. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/tests/test_gs.py +0 -0
  49. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/tests/test_location.py +0 -0
  50. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/tests/test_mem.py +0 -0
  51. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/tests/test_resource.py +0 -0
  52. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/tests/test_s3utils.py +0 -0
  53. {lsst_resources-29.2025.1900 → lsst_resources-29.2025.2100}/tests/test_schemeless.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lsst-resources
3
- Version: 29.2025.1900
3
+ Version: 29.2025.2100
4
4
  Summary: An abstraction layer for reading and writing from URI file resources.
5
5
  Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
6
  License: BSD 3-Clause License
@@ -0,0 +1,251 @@
1
+ webDAV ResourcePath
2
+ ===================
3
+
4
+ The basic syntax for using a webDAV `~lsst.resources.ResourcePath` is:
5
+
6
+ .. code-block::
7
+
8
+ ResourcePath("davs://host.example.org:1234/path/to/file")
9
+
10
+ Configuration
11
+ -------------
12
+ Interacting with WebDAV servers may require specific client configuration.
13
+ That is for instance the case when the server exposes a host certificate
14
+ which is not issued by a certificate authority trusted by the system where
15
+ the client runs. Another case is when the server requires the client
16
+ presents its identity for authentication purposes.
17
+
18
+ In order to configure `~lsst.resources.ResourcePath` to interact with one or
19
+ more WebDAV servers which require specific configuration, you must initialize
20
+ the environment variable ``LSST_RESOURCES_WEBDAV_CONFIG`` to point to a
21
+ configuration YAML file. This configuration file must contain the settings for
22
+ each of the webDAV servers which host any of the
23
+ `~lsst.resources.ResourcePath` objects with ``davs://`` or ``dav://`` scheme
24
+ you need to instantiate.
25
+
26
+ The general structure of the configuration file is as follows:
27
+
28
+ .. code-block:: yaml
29
+
30
+ # These settings apply to all ResourcePath objects hosted by
31
+ # server 'host.example.org:1234'.
32
+ - base_url: "davs://host.example.org:1234/"
33
+ user_cert: "${X509_USER_PROXY}"
34
+ trusted_authorities: "/etc/grid-security/certificates"
35
+
36
+ # These settings apply to all ResourcePath objects hosted by
37
+ # server 'host.domain.org:5432'.
38
+ - base_url: "davs://host.domain.org:5432/"
39
+ token: "${HOME}/.token"
40
+
41
+ # These settings apply to all ResourcePath objects hosted by
42
+ # server 'webdav.example.org:9876'.
43
+ - base_url: "davs://webdav.example.org:9876/"
44
+ timeout_connect: 30.0
45
+ timeout_read: 120.0
46
+ retries: 5
47
+
48
+ For each WebDAV endpoint you can specify several settings that all instances
49
+ of `~lsst.resources.ResourcePath` hosted at that specific endpoint will use.
50
+ If no settings are found in the configuration for a given endpoint, sensible
51
+ defaults values are used (see below).
52
+
53
+ Please note that if your WebDAV endpoint requires client authentication you
54
+ must have an entry for that endpoint in your configuration file. Currently
55
+ two client authentication mechanisms are supported: either a client
56
+ X.509 certificate (and associated private key) or a token. If both are
57
+ specified, token authentication is preferred.
58
+
59
+ Similarly, if the WebDAV server exposes a host certificate that is not
60
+ issued by a certification authority trusted by the operating system where
61
+ your client runs, you must add an entry for that endpoint to the configuration
62
+ file which includes the local path to the file or directory where the trusted
63
+ authorities can be found.
64
+
65
+ Configuration settings
66
+ ^^^^^^^^^^^^^^^^^^^^^^
67
+ These are the settings you can specify in the configuration file for each
68
+ WebDAV endpoint:
69
+
70
+ ``user_cert``
71
+ Path to the client certificate the WebDAV client must present to the
72
+ server for authentication purposes.
73
+
74
+ If not specified, no client certificate is presented to the server.
75
+
76
+ This setting is ignored if ``token`` is specified (see below).
77
+
78
+ ``user_key``
79
+ Path to the private key associated to the client certificate the WebDAV
80
+ client must present to the server for authentication purposes.
81
+
82
+ If not specified but ``user_cert`` is specified, it is assumed that
83
+ the private key and the user certificate are both in the same file
84
+ specified in the value for ``user_cert``.
85
+
86
+ ``trusted_authorities``
87
+ Path to a local directory or certificate bundle file where the
88
+ certificates of the trusted certificate authorities can be found.
89
+ Those certificates will be used by the client of the WebDAV endpoint
90
+ to verify the server's host certificate.
91
+
92
+ If not specified, the certificates trusted by the system are used. The
93
+ server's host certificate is always verified.
94
+
95
+ ``token``
96
+ Value of the token the WebDAV client must sent to the server for
97
+ authentication purposes. The value of this entry may be the token itself
98
+ or the path to a local file where the value of the token can be found. It
99
+ is however discouraged to specify the token value directly in the
100
+ configuration file. Instead store the value of the token in a protected
101
+ file and specify the path to that file as the value of this entry.
102
+
103
+ If ``token`` is specified that mechanism is preferred for authentication
104
+ of client requests, even if ``user_cert`` is also specified.
105
+
106
+ If you specify a value for ``token`` which is the path to a local file
107
+ which contains the actual token, that file must be protected for
108
+ reading and writing only by its owner. The contents of that file is
109
+ automatically reloaded when every time it is modified.
110
+
111
+ ``timeout_connect``
112
+ Timeout in seconds to establish a network connection with the remote
113
+ server.
114
+
115
+ Default: 10 seconds (``float``).
116
+
117
+ ``timeout_read``
118
+ Timeout in seconds to read the response to a request sent to the server.
119
+ This is total time for reading both the headers and the response body.
120
+ It must be large enough to allow for upload and download of files
121
+ of typical size.
122
+
123
+ Default: 300 seconds (``float``).
124
+
125
+ ``retries``
126
+ Number of times to retry requests before failing. Retry happens only
127
+ under certain conditions.
128
+
129
+ Default: 3 (``int``).
130
+
131
+ ``retry_backoff_min``
132
+ Minimal retry backoff (in seconds) for the client to compute
133
+ the wait time before retrying a request.
134
+ A value in the interval [``retry_backoff_min``, ``retry_backoff_max``]
135
+ is randomly selected as the backoff factor every time a request is
136
+ retried.
137
+
138
+ Default: 1.0 seconds (``float``).
139
+
140
+ ``retry_backoff_max``
141
+ Maximum retry backoff (in seconds) for the client to compute
142
+ the wait time before retrying a request.
143
+ A value in the interval [``retry_backoff_min``, ``retry_backoff_max``]
144
+ is randomly selected as the backoff factor every time a request is
145
+ retried.
146
+
147
+ Default: 3.0 seconds (``float``).
148
+
149
+ ``buffer_size``
150
+ Size of the buffer (in mebibytes, i.e. 1024*1024 bytes) the WebDAV
151
+ client of this endpoint will use when sending requests and receiving
152
+ responses.
153
+
154
+ Default: 5 mebibytes (``int``).
155
+
156
+ ``persistent_connections_frontend``
157
+ Maximum number of network connections to persist against each one of
158
+ the hosts in the server frontend.
159
+
160
+ Default: 50 (``int``).
161
+
162
+ ``persistent_connections_backend``
163
+ Maximum number of network connections to persist against each one of
164
+ the hosts in the server backend.
165
+
166
+ Default: 100 (``int``).
167
+
168
+ ``enable_fsspec``
169
+ If specified, expose a `fsspec <https://filesystem-spec.read>`_-compatible,
170
+ read-only file system for accessing a `~lsst.resources.ResourcePath`
171
+ object.
172
+
173
+ Default: ``true`` (``boolean``).
174
+
175
+ ``request_checksum``
176
+ If specified, the WebDAV client will request the server to compute
177
+ a checksum of the file contents every time a file is uploaded.
178
+
179
+ Note that it is the server decision to compute the checksum. Some
180
+ servers simply ignore that request.
181
+
182
+ Accepted values: ``adler32``, ``md5``, ``sha-256``, ``sha-512``.
183
+
184
+ ``collect_memory_usage``
185
+ If specified, memory usage data is collected when running in debug mode.
186
+ Collecting memory usage data is expensive, so this setting should not
187
+ be used in production.
188
+
189
+ Accepted values: ``true``, ``false``.
190
+
191
+ Default: ``false`` (``boolean``).
192
+
193
+
194
+ Configuration Examples
195
+ ----------------------
196
+
197
+ These are examples of configuration files for
198
+ `dCache <https://www.dcache.org>`_ and `XRootD <https://xrootd.org>`_ WebDAV
199
+ servers.
200
+
201
+ Set the environment variable ``LSST_RESOURCES_WEBDAV_CONFIG`` to point to
202
+ your configuration file:
203
+
204
+ .. code-block:: bash
205
+
206
+ export LSST_RESOURCES_WEBDAV_CONFIG="${HOME}/.lsst/dav_conf.yaml"
207
+
208
+
209
+ dCache
210
+ ^^^^^^
211
+
212
+ Typical configuration settings for a **dCache** endpoint are:
213
+
214
+ .. code-block:: yaml
215
+
216
+ - base_url: "davs://dcache.example.org:2880/"
217
+ trusted_authorities: "/etc/grid-security/certificates"
218
+ user_cert: "${X509_USER_PROXY}"
219
+ request_checksum: "md5"
220
+
221
+ The example above uses X.509 grid proxy client authentication. If you prefer
222
+ to use a token use instead:
223
+
224
+ .. code-block:: yaml
225
+
226
+ - base_url: "davs://dcache.example.org:2880/"
227
+ trusted_authorities: "/etc/grid-security/certificates"
228
+ request_checksum: "md5"
229
+ token: "${HOME}/.lsst/dcache_token"
230
+
231
+ and ensure the file at path ``${HOME}/.lsst/dcache_token`` contains the
232
+ client authentication token and is only readable and writable by you.
233
+
234
+ In this example we configure `~lsst.resources.ResourcePath` to request the
235
+ dCache server to compute and record the MD5 checksum when a file is uploaded,
236
+ in addition to the ADLER32 checksum dCache always computes and records for
237
+ each file.
238
+
239
+ XRootD
240
+ ^^^^^^
241
+
242
+ Typical configuration settings for a **XRootD** endpoint are:
243
+
244
+ .. code-block:: yaml
245
+
246
+ - base_url: "davs://xrootd.example.org:1094/"
247
+ trusted_authorities: "/etc/grid-security/certificates"
248
+ user_cert: "${X509_USER_PROXY}"
249
+
250
+ If you prefer to use a token for client authentication use the ``token``
251
+ setting instead (see dCache example above).
@@ -29,6 +29,7 @@ Configuration
29
29
 
30
30
  .. toctree::
31
31
  s3.rst
32
+ dav.rst
32
33
 
33
34
  .. _lsst.resources-pyapi:
34
35
 
@@ -0,0 +1,190 @@
1
+ # This file is part of lsst-resources.
2
+ #
3
+ # Developed for the LSST Data Management System.
4
+ # This product includes software developed by the LSST Project
5
+ # (https://www.lsst.org).
6
+ # See the COPYRIGHT file at the top-level directory of this distribution
7
+ # for details of code ownership.
8
+ #
9
+ # Use of this source code is governed by a 3-clause BSD-style
10
+ # license that can be found in the LICENSE file.
11
+
12
+ from __future__ import annotations
13
+
14
+ __all__ = ("DavReadResourceHandle",)
15
+
16
+ import io
17
+ import logging
18
+ from collections.abc import Callable, Iterable
19
+ from typing import TYPE_CHECKING, AnyStr
20
+
21
+ from ..davutils import DavFileMetadata
22
+ from ._baseResourceHandle import BaseResourceHandle, CloseStatus
23
+
24
+ if TYPE_CHECKING:
25
+ from ..dav import DavResourcePath
26
+
27
+
28
+ class DavReadResourceHandle(BaseResourceHandle[bytes]):
29
+ """WebDAV-based specialization of `.BaseResourceHandle`.
30
+
31
+ Parameters
32
+ ----------
33
+ mode : `str`
34
+ Handle modes as described in the python `io` module.
35
+ log : `~logging.Logger`
36
+ Logger to used when writing messages.
37
+ uri : `lsst.resources.dav.DavResourcePath`
38
+ URI of remote resource.
39
+ newline : `str` or `None`, optional
40
+ When doing multiline operations, break the stream on given character.
41
+ Defaults to newline. If a file is opened in binary mode, this argument
42
+ is not used, as binary files will only split lines on the binary
43
+ newline representation.
44
+ """
45
+
46
+ def __init__(
47
+ self,
48
+ mode: str,
49
+ log: logging.Logger,
50
+ uri: DavResourcePath,
51
+ stat: DavFileMetadata,
52
+ *,
53
+ newline: AnyStr | None = None,
54
+ ) -> None:
55
+ super().__init__(mode, log, uri, newline=newline)
56
+ self._uri: DavResourcePath = uri
57
+ self._stat: DavFileMetadata = stat
58
+ self._current_position = 0
59
+ self._cache: io.BytesIO | None = None
60
+ self._buffer: io.BytesIO | None = None
61
+ self._closed = CloseStatus.OPEN
62
+
63
+ def close(self) -> None:
64
+ self._closed = CloseStatus.CLOSED
65
+ self._cache = None
66
+
67
+ @property
68
+ def closed(self) -> bool:
69
+ return self._closed == CloseStatus.CLOSED
70
+
71
+ def fileno(self) -> int:
72
+ raise io.UnsupportedOperation("DavReadResourceHandle does not have a file number")
73
+
74
+ def flush(self) -> None:
75
+ modes = set(self._mode)
76
+ if {"w", "x", "a", "+"} & modes:
77
+ raise io.UnsupportedOperation("DavReadResourceHandles are read only")
78
+
79
+ @property
80
+ def isatty(self) -> bool | Callable[[], bool]:
81
+ return False
82
+
83
+ def readable(self) -> bool:
84
+ return True
85
+
86
+ def readline(self, size: int = -1) -> bytes:
87
+ raise io.UnsupportedOperation("DavReadResourceHandles Do not support line by line reading")
88
+
89
+ def readlines(self, size: int = -1) -> Iterable[bytes]:
90
+ raise io.UnsupportedOperation("DavReadResourceHandles Do not support line by line reading")
91
+
92
+ def seek(self, offset: int, whence: int = io.SEEK_SET) -> int:
93
+ match whence:
94
+ case io.SEEK_SET:
95
+ if offset < 0:
96
+ raise ValueError(f"negative seek value {offset}")
97
+ self._current_position = offset
98
+ case io.SEEK_CUR:
99
+ self._current_position += offset
100
+ case io.SEEK_END:
101
+ self._current_position = self._stat.size + offset
102
+ case _:
103
+ raise ValueError(f"unexpected value {whence} for whence in seek()")
104
+
105
+ if self._current_position < 0:
106
+ self._current_position = 0
107
+
108
+ return self._current_position
109
+
110
+ def seekable(self) -> bool:
111
+ return True
112
+
113
+ def tell(self) -> int:
114
+ return self._current_position
115
+
116
+ def truncate(self, size: int | None = None) -> int:
117
+ raise io.UnsupportedOperation("DavReadResourceHandles Do not support truncation")
118
+
119
+ def writable(self) -> bool:
120
+ return False
121
+
122
+ def write(self, b: bytes, /) -> int:
123
+ raise io.UnsupportedOperation("DavReadResourceHandles are read only")
124
+
125
+ def writelines(self, b: Iterable[bytes], /) -> None:
126
+ raise io.UnsupportedOperation("DavReadResourceHandles are read only")
127
+
128
+ @property
129
+ def _eof(self) -> bool:
130
+ return self._current_position >= self._stat.size
131
+
132
+ def _download_to_cache(self) -> io.BytesIO:
133
+ """Download the entire content of the remote resource to an internal
134
+ memory buffer.
135
+ """
136
+ if self._cache is None:
137
+ self._cache = io.BytesIO()
138
+ self._cache.write(self._uri.read())
139
+
140
+ return self._cache
141
+
142
+ def read(self, size: int = -1) -> bytes:
143
+ if self._eof or size == 0:
144
+ return b""
145
+
146
+ # If this file's size is small than the buffer size configured for
147
+ # this URI's client, download the entire file in one request and cache
148
+ # its content. This avoids multiple roundtrips to the server
149
+ # for retrieving small chunks.
150
+ if self._stat.size <= self._uri._client._config.buffer_size:
151
+ self._download_to_cache()
152
+
153
+ # If we are asked to read the whole file content, cache the entire
154
+ # file content and return a copy-on-write memory view of our internal
155
+ # cache.
156
+ if self._current_position == 0 and size == -1:
157
+ cache = self._download_to_cache()
158
+ self._current_position = self._stat.size
159
+ return cache.getvalue()
160
+
161
+ # This is a partial read. If we have already cached the whole file
162
+ # content use the cache to build the return value.
163
+ if self._cache is not None:
164
+ start = self._current_position
165
+ end = self._current_position = self._stat.size if size < 0 else start + size
166
+ return self._cache.getvalue()[start:end]
167
+
168
+ # We need to make a partial read from the server. Reuse our internal
169
+ # I/O buffer to reduce memory allocations.
170
+ if self._buffer is None:
171
+ self._buffer = io.BytesIO()
172
+
173
+ start = self._current_position
174
+ end = self._stat.size if size < 0 else min(start + size, self._stat.size)
175
+ self._buffer.seek(0)
176
+ self._buffer.write(self._uri.read_range(start=start, end=end - 1))
177
+ count = self._buffer.tell()
178
+ self._current_position += count
179
+ return self._buffer.getvalue()[0:count]
180
+
181
+ def readinto(self, output: bytearray) -> int:
182
+ """Read up to `len(output)` bytes into `output` and return the number
183
+ of bytes read.
184
+ """
185
+ if self._eof or len(output) == 0:
186
+ return 0
187
+
188
+ data = self.read(len(output))
189
+ output[:] = data
190
+ return len(data)
@@ -368,9 +368,9 @@ class ResourcePath: # numpydoc ignore=PR02
368
368
 
369
369
  subclass = HttpResourcePath
370
370
  elif parsed.scheme in {"dav", "davs"}:
371
- from .http import HttpResourcePath
371
+ from .dav import DavResourcePath
372
372
 
373
- subclass = HttpResourcePath
373
+ subclass = DavResourcePath
374
374
  elif parsed.scheme == "gs":
375
375
  from .gs import GSResourcePath
376
376