Skip to content

Commit e6e55ab

Browse files
authored
feat: Reader.list_readables, process_next_samples join_method (#39)
* feat: Reader.list_readables, process_next_samples join_method * feat: preview deserialize * fix: test
1 parent b861581 commit e6e55ab

67 files changed

Lines changed: 236 additions & 168 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lavender_data/server/background_worker/process_pool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def _worker_process(
9292
result = _tasks[work_item.func](**work_item.kwargs)
9393
result_item = ResultItem(work_id=work_item.work_id, result=result)
9494
except Exception as e:
95-
logger.exception(f"Error processing work {work_item.work_id}: {e}")
95+
# logger.exception(f"Error processing work {work_item.work_id}: {e}")
9696
result_item = ResultItem(
9797
work_id=work_item.work_id,
9898
exception="".join(

lavender_data/server/cache/abc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def lrem(self, name: str, count: int, value: str) -> int: ...
7676

7777

7878
class CacheInterface(CacheOperations):
79+
@contextmanager
7980
@abstractmethod
8081
def lock(self, key: str, timeout: Optional[int] = None) -> Iterator[None]: ...
8182

lavender_data/server/dataset/preview.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
)
3232
from lavender_data.server.shardset import get_main_shardset, span
3333
from lavender_data.storage import get_url
34-
from lavender_data.serialize import serialize_list
34+
from lavender_data.serialize import serialize_list, deserialize_item
3535
from lavender_data.logging import get_logger
3636

3737
try:
@@ -150,14 +150,20 @@ def _set_file(content: bytes):
150150

151151
def refine_value_previewable(value: Any):
152152
if type(value) == bytes:
153-
if len(value) > 0:
154-
try:
155-
local_path = _set_file(value)
156-
return f"file://{local_path}"
157-
except ValueError:
158-
return f"<bytes>"
159-
else:
153+
if len(value) == 0:
160154
return ""
155+
156+
try:
157+
return f"file://{_set_file(value)}"
158+
except ValueError:
159+
pass
160+
161+
try:
162+
return refine_value_previewable(deserialize_item(value))
163+
except Exception:
164+
pass
165+
166+
return f"<bytes>"
161167
elif type(value) == dict:
162168
if value.get("bytes"):
163169
try:

lavender_data/server/iteration/process.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
from lavender_data.server.reader import (
2222
get_reader_instance,
2323
GlobalSampleIndex,
24+
JoinMethod,
25+
InnerJoinSampleInsufficient,
2426
)
2527
from lavender_data.server.registries import (
2628
PreprocessorRegistry,
@@ -107,7 +109,13 @@ def _decollate(batch: dict) -> dict:
107109
return _batch
108110

109111

110-
def _process_next_samples(params: ProcessNextSamplesParams) -> dict:
112+
class NoSamplesFound(Exception):
113+
pass
114+
115+
116+
def _process_next_samples(
117+
params: ProcessNextSamplesParams, join_method: JoinMethod = "left"
118+
) -> dict:
111119
reader = get_reader_instance()
112120

113121
current = params.current
@@ -118,7 +126,15 @@ def _process_next_samples(params: ProcessNextSamplesParams) -> dict:
118126
batch_size = params.batch_size
119127

120128
if samples is None:
121-
samples = [reader.get_sample(i, join="left") for i in global_sample_indices]
129+
samples = []
130+
for i in global_sample_indices:
131+
try:
132+
samples.append(reader.get_sample(i, join_method))
133+
except InnerJoinSampleInsufficient:
134+
pass
135+
136+
if len(samples) == 0:
137+
raise NoSamplesFound()
122138

123139
batch = (
124140
CollaterRegistry.get(collater["name"]).collate(samples)
@@ -146,18 +162,24 @@ def _process_next_samples(params: ProcessNextSamplesParams) -> dict:
146162
def process_next_samples(
147163
params: ProcessNextSamplesParams,
148164
max_retry_count: int,
165+
join_method: JoinMethod = "left",
149166
) -> dict:
150-
logger = get_logger(__name__)
151-
152167
for i in range(max_retry_count + 1):
153168
try:
154-
return _process_next_samples(params)
169+
return _process_next_samples(params, join_method)
170+
except NoSamplesFound as e:
171+
raise ProcessNextSamplesException(
172+
e=e,
173+
current=params.current,
174+
global_sample_indices=params.global_sample_indices,
175+
)
155176
except Exception as e:
156177
error = ProcessNextSamplesException(
157178
e=e,
158179
current=params.current,
159180
global_sample_indices=params.global_sample_indices,
160181
)
182+
logger = get_logger(__name__)
161183
if i < max_retry_count:
162184
logger.warning(f"{str(error)}, retrying... ({i+1}/{max_retry_count})")
163185
else:

lavender_data/server/routes/datasets.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pydantic import BaseModel
99

1010
from lavender_data.logging import get_logger
11+
from lavender_data.shard.readers import Reader
1112
from lavender_data.server.db import DbSession
1213
from lavender_data.server.db.models import (
1314
Dataset,
@@ -42,7 +43,6 @@
4243
preprocess_shardset,
4344
)
4445
from lavender_data.server.auth import AppAuth
45-
from lavender_data.storage import list_files
4646
from lavender_data.shard import inspect_shard
4747
from lavender_data.serialize import deserialize_list
4848

@@ -139,7 +139,11 @@ def get_dataset_preview(
139139
raise HTTPException(status_code=404, detail="Dataset not found")
140140

141141
if cache.exists(f"preview:{dataset_id}:{preview_id}:error"):
142-
error = cache.get(f"preview:{dataset_id}:{preview_id}:error").decode()
142+
error = cache.get(f"preview:{dataset_id}:{preview_id}:error")
143+
if error is None:
144+
error = "Unknown error"
145+
else:
146+
error = error.decode()
143147
cache.delete(f"preview:{dataset_id}:{preview_id}:error")
144148
raise HTTPException(
145149
status_code=500,
@@ -235,8 +239,7 @@ def create_dataset(
235239
cluster=cluster,
236240
)
237241
except:
238-
if cluster:
239-
cluster.sync_changes([dataset], delete=True)
242+
delete_dataset(dataset.id, session, cluster)
240243
raise
241244

242245
return dataset
@@ -390,7 +393,7 @@ def create_shardset(
390393
uid_column = None
391394

392395
try:
393-
shard_basenames = sorted(list_files(params.location, limit=1))
396+
shard_basenames = Reader.list_readables(params.location)
394397
except Exception as e:
395398
shard_basenames = []
396399
logger.warning(f"Failed to list shardset location: {e}")

lavender_data/server/shardset/preprocess.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
from sqlmodel import select
99
from sqlalchemy.orm import selectinload
1010

11+
from lavender_data.shard.readers import Reader
1112
from lavender_data.logging import get_logger
12-
from lavender_data.storage import list_files, upload_file
13+
from lavender_data.storage import upload_file
1314
from lavender_data.server.reader import GlobalSampleIndex, MainShardInfo, ShardInfo
1415
from lavender_data.server.iteration import (
1516
process_next_samples,
@@ -102,17 +103,16 @@ def preprocess_shardset(
102103
existing_shard_basenames = []
103104
if not overwrite:
104105
try:
105-
existing_shard_basenames = [
106-
basename
107-
for basename in sorted(list_files(shardset_location))
108-
if basename.endswith(".parquet") or basename.endswith(".csv")
109-
]
106+
existing_shard_basenames = Reader.list_readables(shardset_location)
110107
except Exception as e:
111108
pass
112109

113-
for main_shard in main_shardset.shards:
114-
shard_basename = f"shard.{main_shard.index:05d}.parquet"
110+
for main_shard in sorted(main_shardset.shards, key=lambda x: x.index):
111+
shard_basename = os.path.basename(main_shard.location)
112+
filename, extension = os.path.splitext(shard_basename)
113+
shard_basename = f"{filename}.parquet"
115114
location = os.path.join(shardset_location, shard_basename)
115+
116116

117117
if shard_basename in existing_shard_basenames:
118118
logger.info(
@@ -187,6 +187,7 @@ def preprocess_shardset(
187187
process_next_samples,
188188
params=params,
189189
max_retry_count=max_retry_count,
190+
join_method="inner",
190191
)
191192
)
192193

@@ -196,9 +197,14 @@ def preprocess_shardset(
196197
try:
197198
batch = process_pool.result(work_id)
198199
except Exception as e:
200+
if "NoSamplesFound" in str(e):
201+
continue
199202
logger.error(e)
200203
continue
201204

205+
if batch is None:
206+
continue
207+
202208
keys = list(batch.keys())
203209
for key in keys:
204210
if key not in _export_columns:

lavender_data/server/shardset/sync.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from sqlmodel import update, insert, select, delete
66

77
from lavender_data.logging import get_logger
8-
from lavender_data.storage import list_files
98
from lavender_data.shard.inspect import OrphanShardInfo, inspect_shard
9+
from lavender_data.shard.readers import Reader
1010
from lavender_data.shard.readers.exceptions import ReaderException
1111
from lavender_data.server.background_worker import (
1212
TaskStatus,
@@ -43,7 +43,7 @@ def inspect_shardset_location(
4343
try:
4444
shard_index = 0
4545

46-
shard_basenames = sorted(list_files(shardset_location))
46+
shard_basenames = Reader.list_readables(os.path.join(shardset_location))
4747

4848
shard_locations: list[str] = []
4949
for shard_basename in shard_basenames:

lavender_data/shard/readers/abc.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import Any, Iterator, Optional, Union
44
from typing_extensions import Self
55

6-
from lavender_data.storage import download_file
6+
from lavender_data.storage import download_file, list_files
77
from lavender_data.logging import get_logger
88

99
from .exceptions import (
@@ -19,6 +19,22 @@
1919
class Reader(ABC):
2020
format: str = ""
2121

22+
@classmethod
23+
def is_readable(cls, location: str) -> bool:
24+
shard_format = os.path.splitext(location)[1].lstrip(".")
25+
for subcls in cls._reader_classes():
26+
if shard_format == subcls.format:
27+
return True
28+
return False
29+
30+
@classmethod
31+
def list_readables(cls, location: str) -> list[str]:
32+
return [
33+
basename
34+
for basename in sorted(list_files(location))
35+
if cls.is_readable(os.path.join(location, basename))
36+
]
37+
2238
@classmethod
2339
def get(
2440
cls,

lavender_data/shard/readers/csv.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,37 @@ class CsvReader(UntypedReader):
1515
format = "csv"
1616
typed_columns = False
1717

18-
def resolve_type(self, value: Any, typestr: str) -> type:
18+
def resolve_type(self, value: Any, typestr: str) -> Any:
1919
if typestr in ["int", "int32", "int64"]:
20-
if value == "":
20+
if value == "" or value is None:
2121
return np.nan
2222
return int(value)
2323
elif typestr in ["float", "double"]:
24-
if value == "":
24+
if value == "" or value is None:
2525
return np.nan
2626
return float(value)
2727
elif typestr in ["string", "text", "str"]:
2828
return str(value)
2929
elif typestr in ["bool", "boolean"]:
3030
return value.lower() in ["true", "t", "yes", "y", "1"]
3131
elif typestr in ["list"]:
32-
if value == "":
32+
if value == "" or value is None:
3333
return []
3434
return ast.literal_eval(value)
3535
elif typestr in ["map"]:
36-
if value == "":
36+
if value == "" or value is None:
3737
return {}
3838
return ast.literal_eval(value)
3939
elif typestr in ["binary"]:
40-
if value == "":
40+
if value == "" or value is None:
4141
return b""
4242
return ast.literal_eval(value)
4343
return value
4444

4545
def read_columns(self) -> dict[str, str]:
4646
with open(self.filepath, "r") as f:
4747
reader = csv.DictReader(f)
48-
return {name: "string" for name in reader.fieldnames}
48+
return {name: "string" for name in reader.fieldnames or []}
4949

5050
def read_samples(self) -> list[dict[str, Any]]:
5151
samples = []

lavender_data/storage/hf.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ def __init__(self):
3131
def _parse_remote_path(self, remote_path: str) -> tuple[str, str]:
3232
parsed = urllib.parse.urlparse(remote_path)
3333
org = parsed.netloc
34-
repo, path = parsed.path.lstrip("/").split("/", 1)
34+
splitted = parsed.path.lstrip("/").split("/", 1)
35+
repo = splitted[0]
36+
if len(splitted) > 1:
37+
path = splitted[1]
38+
else:
39+
path = ""
3540
repo_id = f"{org}/{repo}"
3641
return repo_id, path
3742

0 commit comments

Comments
 (0)