daft-sources 0.1.0__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.
- daft_sources-0.1.0/PKG-INFO +15 -0
- daft_sources-0.1.0/README.md +5 -0
- daft_sources-0.1.0/pyproject.toml +11 -0
- daft_sources-0.1.0/setup.cfg +4 -0
- daft_sources-0.1.0/src/daft_sources/tar/schema.py +16 -0
- daft_sources-0.1.0/src/daft_sources/tar/source.py +46 -0
- daft_sources-0.1.0/src/daft_sources/tar/task.py +81 -0
- daft_sources-0.1.0/src/daft_sources/wds/schema.py +11 -0
- daft_sources-0.1.0/src/daft_sources/wds/source.py +42 -0
- daft_sources-0.1.0/src/daft_sources/wds/task.py +60 -0
- daft_sources-0.1.0/src/daft_sources.egg-info/PKG-INFO +15 -0
- daft_sources-0.1.0/src/daft_sources.egg-info/SOURCES.txt +13 -0
- daft_sources-0.1.0/src/daft_sources.egg-info/dependency_links.txt +1 -0
- daft_sources-0.1.0/src/daft_sources.egg-info/requires.txt +3 -0
- daft_sources-0.1.0/src/daft_sources.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: daft-sources
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: daft>=0.7.21
|
|
8
|
+
Requires-Dist: google-re2>=1.1.20251105
|
|
9
|
+
Requires-Dist: webdataset>=1.0.2
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uvx ruff format
|
|
14
|
+
uvx ruff check --select I --fix .
|
|
15
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from daft import DataType, Schema
|
|
2
|
+
|
|
3
|
+
TAR_SCHEMA = Schema._from_field_name_and_types(
|
|
4
|
+
[
|
|
5
|
+
("archive_path", DataType.string()),
|
|
6
|
+
("member_path", DataType.string()),
|
|
7
|
+
("size", DataType.int64()),
|
|
8
|
+
("mtime", DataType.float64()),
|
|
9
|
+
("mode", DataType.int64()),
|
|
10
|
+
("uid", DataType.int64()),
|
|
11
|
+
("gid", DataType.int64()),
|
|
12
|
+
("uname", DataType.string()),
|
|
13
|
+
("gname", DataType.string()),
|
|
14
|
+
("bytes", DataType.binary()),
|
|
15
|
+
]
|
|
16
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from collections.abc import AsyncIterator
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from daft.io import DataSource
|
|
5
|
+
from daft.io.pushdowns import Pushdowns
|
|
6
|
+
from daft.schema import Schema
|
|
7
|
+
|
|
8
|
+
from daft_sources.tar.schema import TAR_SCHEMA
|
|
9
|
+
from daft_sources.tar.task import TarDataSourceTask
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TarDataSource(DataSource):
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
archive_paths: list[str],
|
|
16
|
+
*,
|
|
17
|
+
batch_size: int = 256,
|
|
18
|
+
max_member_bytes: int = 100 * 1024 * 1024,
|
|
19
|
+
):
|
|
20
|
+
assert batch_size > 0, "Batch_size must be greater than zero"
|
|
21
|
+
assert max_member_bytes is not None and max_member_bytes > 0, (
|
|
22
|
+
"max_member_bytes must be non-negative"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
self.archive_paths = [Path(path) for path in archive_paths]
|
|
26
|
+
self.batch_size = batch_size
|
|
27
|
+
self.max_member_bytes = max_member_bytes
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def name(self) -> str:
|
|
31
|
+
return "TAR Data Source"
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def schema(self) -> Schema:
|
|
35
|
+
return TAR_SCHEMA
|
|
36
|
+
|
|
37
|
+
async def get_tasks(
|
|
38
|
+
self,
|
|
39
|
+
pushdowns: Pushdowns,
|
|
40
|
+
) -> AsyncIterator["TarDataSourceTask"]:
|
|
41
|
+
for archive_path in self.archive_paths:
|
|
42
|
+
yield TarDataSourceTask(
|
|
43
|
+
archive_path,
|
|
44
|
+
batch_size=self.batch_size,
|
|
45
|
+
max_member_bytes=self.max_member_bytes,
|
|
46
|
+
)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import tarfile
|
|
2
|
+
from collections.abc import AsyncIterator
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from daft.io import DataSourceTask
|
|
6
|
+
from daft.recordbatch import RecordBatch
|
|
7
|
+
from daft.schema import Schema
|
|
8
|
+
|
|
9
|
+
from daft_sources.tar.schema import TAR_SCHEMA
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TarDataSourceTask(DataSourceTask):
|
|
13
|
+
"""Read one TAR archive."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
archive_path: Path,
|
|
18
|
+
*,
|
|
19
|
+
batch_size: int,
|
|
20
|
+
max_member_bytes: int | None,
|
|
21
|
+
) -> None:
|
|
22
|
+
self.archive_path = archive_path
|
|
23
|
+
self.batch_size = batch_size
|
|
24
|
+
self.max_member_bytes = max_member_bytes
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def schema(self) -> Schema:
|
|
28
|
+
return TAR_SCHEMA
|
|
29
|
+
|
|
30
|
+
async def read(self) -> AsyncIterator[RecordBatch]:
|
|
31
|
+
columns: dict[str, list[object]] = {
|
|
32
|
+
"archive_path": [],
|
|
33
|
+
"member_path": [],
|
|
34
|
+
"size": [],
|
|
35
|
+
"mtime": [],
|
|
36
|
+
"mode": [],
|
|
37
|
+
"uid": [],
|
|
38
|
+
"gid": [],
|
|
39
|
+
"uname": [],
|
|
40
|
+
"gname": [],
|
|
41
|
+
"bytes": [],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
with tarfile.open(self.archive_path, mode="r:*") as archive:
|
|
45
|
+
for member in archive:
|
|
46
|
+
if not member.isfile():
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
if (
|
|
50
|
+
self.max_member_bytes is not None
|
|
51
|
+
and member.size > self.max_member_bytes
|
|
52
|
+
):
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
data: bytes | None = None
|
|
56
|
+
|
|
57
|
+
extracted = archive.extractfile(member)
|
|
58
|
+
|
|
59
|
+
if extracted is None:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
with extracted:
|
|
63
|
+
data = extracted.read()
|
|
64
|
+
|
|
65
|
+
columns["archive_path"].append(str(self.archive_path))
|
|
66
|
+
columns["member_path"].append(member.name)
|
|
67
|
+
columns["size"].append(member.size)
|
|
68
|
+
columns["mtime"].append(float(member.mtime))
|
|
69
|
+
columns["mode"].append(member.mode)
|
|
70
|
+
columns["uid"].append(member.uid)
|
|
71
|
+
columns["gid"].append(member.gid)
|
|
72
|
+
columns["uname"].append(member.uname or None)
|
|
73
|
+
columns["gname"].append(member.gname or None)
|
|
74
|
+
columns["bytes"].append(data)
|
|
75
|
+
|
|
76
|
+
if len(columns["member_path"]) >= self.batch_size:
|
|
77
|
+
yield RecordBatch.from_pydict(columns)
|
|
78
|
+
columns = {name: [] for name in columns}
|
|
79
|
+
|
|
80
|
+
if columns["member_path"]:
|
|
81
|
+
yield RecordBatch.from_pydict(columns)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from daft.datatype import DataType
|
|
2
|
+
from daft.schema import Schema
|
|
3
|
+
|
|
4
|
+
WDS_SCHEMA = Schema._from_field_name_and_types(
|
|
5
|
+
[
|
|
6
|
+
("__key__", DataType.string()),
|
|
7
|
+
("__url__", DataType.string()),
|
|
8
|
+
("jpg", DataType.binary()),
|
|
9
|
+
("txt", DataType.string()),
|
|
10
|
+
]
|
|
11
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from collections.abc import AsyncIterator
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from daft.io import DataSource
|
|
5
|
+
from daft.io.pushdowns import Pushdowns
|
|
6
|
+
from daft.schema import Schema
|
|
7
|
+
|
|
8
|
+
from daft_sources.wds.schema import WDS_SCHEMA
|
|
9
|
+
from daft_sources.wds.task import WebDatasetDataSourceTask
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class WebDatasetDataSource(DataSource):
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
shard_paths: list[str],
|
|
16
|
+
*,
|
|
17
|
+
batch_size: int = 256,
|
|
18
|
+
):
|
|
19
|
+
assert shard_paths, "At least one WebDataset shard is required"
|
|
20
|
+
assert batch_size > 0, "batch_size must be greater than zero"
|
|
21
|
+
|
|
22
|
+
self.shard_paths = [Path(path) for path in shard_paths]
|
|
23
|
+
self.batch_size = batch_size
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def name(self) -> str:
|
|
27
|
+
return "WebDataset Data Source"
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def schema(self) -> Schema:
|
|
31
|
+
return WDS_SCHEMA
|
|
32
|
+
|
|
33
|
+
async def get_tasks(
|
|
34
|
+
self,
|
|
35
|
+
pushdowns: Pushdowns,
|
|
36
|
+
) -> AsyncIterator[WebDatasetDataSourceTask]:
|
|
37
|
+
for shard_path in self.shard_paths:
|
|
38
|
+
yield WebDatasetDataSourceTask(
|
|
39
|
+
shard_path=shard_path,
|
|
40
|
+
batch_size=self.batch_size,
|
|
41
|
+
schema=self.schema,
|
|
42
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import webdataset as wds
|
|
4
|
+
from daft.io import DataSourceTask
|
|
5
|
+
from daft.recordbatch import RecordBatch
|
|
6
|
+
from daft.schema import Schema
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WebDatasetDataSourceTask(DataSourceTask):
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
*,
|
|
13
|
+
shard_path: Path,
|
|
14
|
+
batch_size: int,
|
|
15
|
+
schema: Schema,
|
|
16
|
+
):
|
|
17
|
+
self.shard_path = shard_path
|
|
18
|
+
self.batch_size = batch_size
|
|
19
|
+
self._schema = schema
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def schema(self) -> Schema:
|
|
23
|
+
return self._schema
|
|
24
|
+
|
|
25
|
+
async def read(self):
|
|
26
|
+
dataset = wds.WebDataset(
|
|
27
|
+
str(self.shard_path),
|
|
28
|
+
shardshuffle=False,
|
|
29
|
+
nodesplitter=None,
|
|
30
|
+
workersplitter=None,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
columns = self._empty_columns()
|
|
34
|
+
|
|
35
|
+
for sample in dataset:
|
|
36
|
+
txt = sample.get("txt")
|
|
37
|
+
|
|
38
|
+
if isinstance(txt, bytes):
|
|
39
|
+
txt = txt.decode("utf-8")
|
|
40
|
+
|
|
41
|
+
columns["__key__"].append(sample["__key__"])
|
|
42
|
+
columns["__url__"].append(sample.get("__url__", str(self.shard_path)))
|
|
43
|
+
columns["jpg"].append(sample.get("jpg"))
|
|
44
|
+
columns["txt"].append(txt)
|
|
45
|
+
|
|
46
|
+
if len(columns["__key__"]) >= self.batch_size:
|
|
47
|
+
yield RecordBatch.from_pydict(columns)
|
|
48
|
+
columns = self._empty_columns()
|
|
49
|
+
|
|
50
|
+
if columns["__key__"]:
|
|
51
|
+
yield RecordBatch.from_pydict(columns)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def _empty_columns() -> dict[str, list]:
|
|
55
|
+
return {
|
|
56
|
+
"__key__": [],
|
|
57
|
+
"__url__": [],
|
|
58
|
+
"jpg": [],
|
|
59
|
+
"txt": [],
|
|
60
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: daft-sources
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: daft>=0.7.21
|
|
8
|
+
Requires-Dist: google-re2>=1.1.20251105
|
|
9
|
+
Requires-Dist: webdataset>=1.0.2
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uvx ruff format
|
|
14
|
+
uvx ruff check --select I --fix .
|
|
15
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/daft_sources.egg-info/PKG-INFO
|
|
4
|
+
src/daft_sources.egg-info/SOURCES.txt
|
|
5
|
+
src/daft_sources.egg-info/dependency_links.txt
|
|
6
|
+
src/daft_sources.egg-info/requires.txt
|
|
7
|
+
src/daft_sources.egg-info/top_level.txt
|
|
8
|
+
src/daft_sources/tar/schema.py
|
|
9
|
+
src/daft_sources/tar/source.py
|
|
10
|
+
src/daft_sources/tar/task.py
|
|
11
|
+
src/daft_sources/wds/schema.py
|
|
12
|
+
src/daft_sources/wds/source.py
|
|
13
|
+
src/daft_sources/wds/task.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
daft_sources
|