Skip to content

Commit 0263936

Browse files
committed
feat: support v0 pin meta in pin_search
1 parent 359e058 commit 0263936

File tree

3 files changed

+72
-26
lines changed

3 files changed

+72
-26
lines changed

pins/boards.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ def pin_search(self, search=None, as_df=True):
416416
import pandas as pd
417417

418418
# TODO(question): was the pulling of specific fields out a v0 thing?
419-
extracted = list(map(self._extract_meta_results, res))
419+
extracted = list(map(self._extract_search_meta, res))
420420
return pd.DataFrame(extracted)
421421

422422
# TODO(compat): double check on the as_df=True convention
@@ -540,7 +540,7 @@ def prepare_pin_version(
540540
def _extract_search_meta(self, meta):
541541
keep_fields = ["name", "type", "title", "created", "file_size"]
542542

543-
d = {k: getattr(meta, k) for k in keep_fields}
543+
d = {k: getattr(meta, k, None) for k in keep_fields}
544544
d["meta"] = meta
545545
return d
546546

@@ -593,7 +593,7 @@ def pin_meta(self, name, version=None):
593593
path_to_pin = self.construct_path([pin_name])
594594
if self.fs.protocol == "http" and not path_to_pin.rstrip().endswith("/"):
595595
# create metadata, rather than read from a file
596-
return self.meta_factory.create_raw(path_to_pin, type="file",)
596+
return self.meta_factory.create_raw(path_to_pin, type="file", name=pin_name)
597597

598598
path_meta = self.construct_path([pin_name, meta_name])
599599
f = self.fs.open(path_meta)
@@ -661,10 +661,8 @@ def pin_search(self, search=None, as_df=True):
661661
# verify code is for inadequate permission to access
662662
if e.args[0]["code"] != 19:
663663
raise e
664-
# TODO(question): should this be a MetaRaw class or something?
665-
# that fixes our isinstance Meta below.
666664
# TODO(compatibility): R pins errors instead, see #27
667-
res.append({"name": pin_name, "meta": None})
665+
res.append(self.meta_factory.create_raw(None, type=None, name=pin_name))
668666

669667
# extract specific fields out ----
670668

@@ -674,11 +672,7 @@ def pin_search(self, search=None, as_df=True):
674672

675673
extract = []
676674
for entry in res:
677-
extract.append(
678-
self._extract_search_meta(entry)
679-
if isinstance(entry, Meta)
680-
else entry
681-
)
675+
extract.append(self._extract_search_meta(entry))
682676

683677
return pd.DataFrame(extract)
684678

pins/meta.py

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from typing import ClassVar
12
from dataclasses import dataclass, asdict, field
23

34
import yaml
@@ -24,8 +25,9 @@ class MetaRaw:
2425
The type of pin data stored. This is used to determine how to read / write it.
2526
"""
2627

27-
file: Union[str, Sequence[str]]
28+
file: "str | Sequence[str] | None"
2829
type: str
30+
name: str
2931

3032

3133
@dataclass
@@ -57,7 +59,7 @@ class Meta:
5759
5860
"""
5961

60-
title: str
62+
title: Optional[str]
6163
description: Optional[str]
6264

6365
# TODO(defer): different from R pins, which has a local field
@@ -93,19 +95,58 @@ def to_pin_dict(self):
9395
@classmethod
9496
def from_pin_dict(cls, data, pin_name, version) -> "Meta":
9597

96-
# version_fields = {"created", "pin_hash"}
97-
98-
# #get items necessary for re-creating meta data
99-
# meta_data = {k: v for k, v in data.items() if k not in version_fields}
100-
# version = version_cls.from_meta_fields(data["created"], data["pin_hash"])
101-
return cls(**data, name=pin_name, version=version)
98+
# TODO: re-arrange Meta argument positions to reflect what's been
99+
# learned about default arguments. e.g. title was not used at some
100+
# point in api_version 1
101+
extra = {"title": None} if "title" not in data else {}
102+
return cls(**data, **extra, name=pin_name, version=version)
102103

103104
def to_pin_yaml(self, f: Optional[IOBase] = None) -> "str | None":
104105
data = self.to_pin_dict()
105106

106107
return yaml.dump(data, f)
107108

108109

110+
@dataclass
111+
class MetaV0:
112+
file: Union[str, Sequence[str]]
113+
type: str
114+
115+
description: str
116+
name: str
117+
118+
version: VersionRaw
119+
120+
# holds raw data.txt contents
121+
original_fields: dict = field(default_factory=dict)
122+
user: dict = field(default_factory=dict, init=False)
123+
124+
title: ClassVar[None] = None
125+
created: ClassVar[None] = None
126+
pin_hash: ClassVar[None] = None
127+
file_size: ClassVar[None] = None
128+
api_version: ClassVar[None] = None
129+
130+
def to_dict(self):
131+
return asdict(self)
132+
133+
@classmethod
134+
def from_pin_dict(cls, data, pin_name, version) -> "MetaV0":
135+
# could infer from dataclasses.fields(), but seems excessive.
136+
req_fields = {"type", "description", "name"}
137+
138+
req_inputs = {k: v for k, v in data.items() if k in req_fields}
139+
req_inputs["file"] = data["path"]
140+
141+
return cls(**req_inputs, name=pin_name, original_fields=data, version=version)
142+
143+
def to_pin_dict(self):
144+
raise NotImplementedError("v0 pins metadata are read only.")
145+
146+
def to_pin_yaml(self, *args, **kwargs):
147+
self.to_pin_dict()
148+
149+
109150
class MetaFactory:
110151
"""Responsible for creating and loading (e.g. from yaml) of meta objects.
111152
@@ -168,8 +209,8 @@ def create(
168209
version=version,
169210
)
170211

171-
def create_raw(self, files: Sequence[StrOrFile], type: str = "file",) -> MetaRaw:
172-
return MetaRaw(files, type)
212+
def create_raw(self, files: Sequence[StrOrFile], type: str, name: str) -> MetaRaw:
213+
return MetaRaw(files, type, name)
173214

174215
def read_pin_yaml(
175216
self, f: IOBase, pin_name: str, version: "str | VersionRaw"
@@ -181,4 +222,14 @@ def read_pin_yaml(
181222

182223
data = yaml.safe_load(f)
183224

184-
return Meta.from_pin_dict(data, pin_name, version=version_obj)
225+
api_version = data.get("api_version", 0)
226+
if api_version >= 2:
227+
raise NotImplementedError(
228+
f"api_version {api_version} by this version of the pins library"
229+
)
230+
elif api_version == 0:
231+
cls_meta = MetaV0
232+
else:
233+
cls_meta = Meta
234+
235+
return cls_meta.from_pin_dict(data, pin_name, version=version_obj)

pins/tests/test_boards.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from pins.tests.helpers import DEFAULT_CREATION_DATE
77
from pins.errors import PinsError
8+
from pins.meta import MetaRaw
89

910
from datetime import datetime, timedelta
1011
from time import sleep
@@ -283,13 +284,13 @@ def test_board_pin_search_admin_user(df, fs_short, fs_admin): # noqa
283284
search_res = board_admin.pin_search("susan", as_df=False)
284285

285286
assert len(search_res) == 1
286-
assert search_res[0]["name"] == "susan/some_df"
287-
assert search_res[0]["meta"] is None
287+
assert search_res[0].name == "susan/some_df"
288+
assert isinstance(search_res[0], MetaRaw)
288289

289290
search_res2 = board_admin.pin_search("susan", as_df=True)
290-
assert search_res2.shape == (1, 2)
291+
assert search_res2.shape == (1, 6)
291292
assert search_res2.loc[0, "name"] == "susan/some_df"
292-
assert search_res2.loc[0, "meta"] is None
293+
assert isinstance(search_res2.loc[0, "meta"], MetaRaw)
293294

294295

295296
# Manual Board Specific =======================================================

0 commit comments

Comments
 (0)