matelab-python-sdk 0.1.0a1__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.
- matelab/__init__.py +372 -0
- matelab/_generated/__init__.py +1 -0
- matelab/_generated/models.py +3553 -0
- matelab/_transport.py +481 -0
- matelab/client.py +191 -0
- matelab/cloud_drive.py +789 -0
- matelab/errors.py +38 -0
- matelab/groups.py +78 -0
- matelab/literature.py +1243 -0
- matelab/models.py +27 -0
- matelab/notebooks.py +467 -0
- matelab/py.typed +1 -0
- matelab/records.py +2511 -0
- matelab/streaming.py +82 -0
- matelab/templates.py +737 -0
- matelab/uploads.py +247 -0
- matelab/users.py +77 -0
- matelab_python_sdk-0.1.0a1.dist-info/METADATA +538 -0
- matelab_python_sdk-0.1.0a1.dist-info/RECORD +22 -0
- matelab_python_sdk-0.1.0a1.dist-info/WHEEL +4 -0
- matelab_python_sdk-0.1.0a1.dist-info/licenses/LICENSE +202 -0
- matelab_python_sdk-0.1.0a1.dist-info/licenses/NOTICE +2 -0
matelab/streaming.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from collections.abc import AsyncIterator
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Self
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from matelab.errors import MatelabUsageError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class ByteRange:
|
|
12
|
+
"""One HTTP byte range from the stable subset supported by Matelab."""
|
|
13
|
+
|
|
14
|
+
start: int | None = None
|
|
15
|
+
end: int | None = None
|
|
16
|
+
suffix_length: int | None = None
|
|
17
|
+
|
|
18
|
+
def __post_init__(self) -> None:
|
|
19
|
+
if any(type(value) is not int for value in (self.start, self.end, self.suffix_length) if value is not None):
|
|
20
|
+
raise MatelabUsageError("Byte-range values must be integers.")
|
|
21
|
+
if self.suffix_length is not None:
|
|
22
|
+
if self.start is not None or self.end is not None or self.suffix_length < 1:
|
|
23
|
+
raise MatelabUsageError("A suffix byte range needs a positive length and no start or end.")
|
|
24
|
+
return
|
|
25
|
+
if self.start is None or self.start < 0:
|
|
26
|
+
raise MatelabUsageError("A byte range needs a non-negative start or a positive suffix length.")
|
|
27
|
+
if self.end is not None and (self.end < 1 or self.end < self.start):
|
|
28
|
+
raise MatelabUsageError(
|
|
29
|
+
"An explicit byte-range end must be positive and greater than or equal to the start."
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def from_start(cls, start: int) -> Self:
|
|
34
|
+
return cls(start=start)
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def between(cls, start: int, end: int) -> Self:
|
|
38
|
+
return cls(start=start, end=end)
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def last(cls, length: int) -> Self:
|
|
42
|
+
return cls(suffix_length=length)
|
|
43
|
+
|
|
44
|
+
def to_header(self) -> str:
|
|
45
|
+
if self.suffix_length is not None:
|
|
46
|
+
return f"bytes=-{self.suffix_length}"
|
|
47
|
+
return f"bytes={self.start}-{self.end if self.end is not None else ''}"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DownloadStream:
|
|
51
|
+
"""A Provider response whose bytes remain open until consumed or closed."""
|
|
52
|
+
|
|
53
|
+
def __init__(self, response: httpx.Response, *, content_length: int | None) -> None:
|
|
54
|
+
self._response: httpx.Response = response
|
|
55
|
+
self.status_code: int = response.status_code
|
|
56
|
+
self.content_type: str | None = response.headers.get("Content-Type")
|
|
57
|
+
self.content_length: int | None = content_length
|
|
58
|
+
self.content_range: str | None = response.headers.get("Content-Range")
|
|
59
|
+
self.accept_ranges: str | None = response.headers.get("Accept-Ranges")
|
|
60
|
+
self.content_disposition: str | None = response.headers.get("Content-Disposition")
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def is_closed(self) -> bool:
|
|
64
|
+
return self._response.is_closed
|
|
65
|
+
|
|
66
|
+
async def __aenter__(self) -> Self:
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
async def __aexit__(self, *_: object) -> None:
|
|
70
|
+
await self.aclose()
|
|
71
|
+
|
|
72
|
+
def __aiter__(self) -> AsyncIterator[bytes]:
|
|
73
|
+
return self.aiter_bytes()
|
|
74
|
+
|
|
75
|
+
def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]:
|
|
76
|
+
return self._response.aiter_bytes(chunk_size)
|
|
77
|
+
|
|
78
|
+
async def read(self) -> bytes:
|
|
79
|
+
return await self._response.aread()
|
|
80
|
+
|
|
81
|
+
async def aclose(self) -> None:
|
|
82
|
+
await self._response.aclose()
|