daft-sources 0.1.0__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.
@@ -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,10 @@
1
+ daft_sources/tar/schema.py,sha256=gAzNjLPuf550wlQHSSfBCFRq40X4OR923jhGdN8MYRM,481
2
+ daft_sources/tar/source.py,sha256=eYNOLSmKZ7kDWAR48NG6M-UzBqEamL6UszChJVQhC4g,1322
3
+ daft_sources/tar/task.py,sha256=CB8h8tCqlMLICzVliSyd7JDWvlKnmFu-C_yFSNvkD1o,2476
4
+ daft_sources/wds/schema.py,sha256=lEjflgZ-5NO_Uw62YlcW5ABunImsdjRZXyX2Ckmivcs,281
5
+ daft_sources/wds/source.py,sha256=Bp0NzKHemnlXjlHaLNjXMf2nD7_BiS9fkwa2Crtd0aU,1172
6
+ daft_sources/wds/task.py,sha256=uq20LswXVZASqEVCLK2BOPno6xt1m5W33AznmWnBjHs,1574
7
+ daft_sources-0.1.0.dist-info/METADATA,sha256=tcsR1CKiuEv5CiSzEcjKay6jUMOBG43m0nX6y-6cg7U,320
8
+ daft_sources-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
9
+ daft_sources-0.1.0.dist-info/top_level.txt,sha256=Jk7UhqTe3ZAHu1s0oAZL78J82zSa5TbDkBpLiRAaBr0,13
10
+ daft_sources-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ daft_sources