fsspec-lamindb 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.
- fsspec_lamindb/__init__.py +23 -0
- fsspec_lamindb/_common.py +148 -0
- fsspec_lamindb/_hub.py +797 -0
- fsspec_lamindb/_records.py +178 -0
- fsspec_lamindb/_storage.py +461 -0
- fsspec_lamindb/_version.py +8 -0
- fsspec_lamindb/core.py +1312 -0
- fsspec_lamindb/py.typed +0 -0
- fsspec_lamindb-0.1.0.dist-info/METADATA +371 -0
- fsspec_lamindb-0.1.0.dist-info/RECORD +13 -0
- fsspec_lamindb-0.1.0.dist-info/WHEEL +4 -0
- fsspec_lamindb-0.1.0.dist-info/entry_points.txt +2 -0
- fsspec_lamindb-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Read-only fsspec filesystem for LaminDB instances, with nf-lamin compatible URIs.
|
|
2
|
+
|
|
3
|
+
Installing the package registers the ``lamin`` protocol with fsspec.
|
|
4
|
+
|
|
5
|
+
Examples
|
|
6
|
+
--------
|
|
7
|
+
>>> import fsspec
|
|
8
|
+
>>> fs = fsspec.filesystem("lamin")
|
|
9
|
+
>>> fs.ls("lamin://laminlabs/lamindata", detail=False)
|
|
10
|
+
['laminlabs/lamindata/artifact']
|
|
11
|
+
>>> fs.cat_file("lamin://laminlabs/lamindata/artifact/K415DrXcuPiJmXlX/meta.csv")[:8]
|
|
12
|
+
b',species'
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from ._hub import LaminAPIError
|
|
16
|
+
from ._version import __version__ as __version__
|
|
17
|
+
from .core import LaminDBFileSystem, LaminFile
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"LaminAPIError",
|
|
21
|
+
"LaminDBFileSystem",
|
|
22
|
+
"LaminFile",
|
|
23
|
+
]
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Paths, targets, and other helpers of the filesystem."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from ._hub import Instance
|
|
10
|
+
from ._records import Storage
|
|
11
|
+
|
|
12
|
+
PROTOCOL = "lamin"
|
|
13
|
+
ARTIFACT = "artifact"
|
|
14
|
+
|
|
15
|
+
Listing = dict[str, dict[str, Any]]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class _Target:
|
|
20
|
+
"""What a path refers to: its entry and, for data, the storage that holds it.
|
|
21
|
+
|
|
22
|
+
Attributes
|
|
23
|
+
----------
|
|
24
|
+
info : dict
|
|
25
|
+
The fsspec info of the entry.
|
|
26
|
+
storage : Storage or None
|
|
27
|
+
The storage location that holds the data of the entry, if any.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
info: dict[str, Any]
|
|
31
|
+
storage: Storage | None = None
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def uri(self) -> str | None:
|
|
35
|
+
return self.info.get("uri")
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def is_dir(self) -> bool:
|
|
39
|
+
return self.info["type"] == "directory"
|
|
40
|
+
|
|
41
|
+
def location(self) -> tuple[Storage, str]:
|
|
42
|
+
"""Return the storage and URI that hold the data of the entry.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
storage : Storage
|
|
47
|
+
The storage location.
|
|
48
|
+
uri : str
|
|
49
|
+
The URI of the data.
|
|
50
|
+
|
|
51
|
+
Raises
|
|
52
|
+
------
|
|
53
|
+
FileNotFoundError
|
|
54
|
+
If the storage or URI of the entry is unknown.
|
|
55
|
+
"""
|
|
56
|
+
uri = self.uri
|
|
57
|
+
if self.storage is None or uri is None:
|
|
58
|
+
raise FileNotFoundError(f"the storage of {self.info['name']} is unknown")
|
|
59
|
+
return self.storage, uri
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class _State:
|
|
64
|
+
"""An instance and the branches visible in it.
|
|
65
|
+
|
|
66
|
+
Attributes
|
|
67
|
+
----------
|
|
68
|
+
instance : Instance
|
|
69
|
+
The LaminDB instance.
|
|
70
|
+
branch_ids : list of int
|
|
71
|
+
Ids of the branches whose records are visible.
|
|
72
|
+
branch_id : int or None
|
|
73
|
+
Id of the branch selected in addition to ``main``, if any.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
instance: Instance
|
|
77
|
+
branch_ids: list[int]
|
|
78
|
+
# the branch selected in addition to main
|
|
79
|
+
branch_id: int | None = None
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def slug(self) -> str:
|
|
83
|
+
return self.instance.slug
|
|
84
|
+
|
|
85
|
+
def prefer(self, records: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
86
|
+
"""Order records so that later ones win conflicts.
|
|
87
|
+
|
|
88
|
+
Records of the selected branch win over those of ``main``, newer records win over
|
|
89
|
+
older ones.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
records : list of dict
|
|
94
|
+
The records to order.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
list of dict
|
|
99
|
+
The records, least preferred first.
|
|
100
|
+
"""
|
|
101
|
+
selected = self.branch_id
|
|
102
|
+
|
|
103
|
+
def rank(record: dict[str, Any]) -> tuple[bool, int]:
|
|
104
|
+
return selected is not None and record.get("branch_id") == selected, record["id"]
|
|
105
|
+
|
|
106
|
+
return sorted(records, key=rank)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass
|
|
110
|
+
class _Folder:
|
|
111
|
+
"""A folder artifact, whose content is listed from its storage.
|
|
112
|
+
|
|
113
|
+
Attributes
|
|
114
|
+
----------
|
|
115
|
+
path : str
|
|
116
|
+
Path of the folder artifact.
|
|
117
|
+
info : dict
|
|
118
|
+
The fsspec info of the folder artifact.
|
|
119
|
+
storage : Storage
|
|
120
|
+
The storage location that holds the folder.
|
|
121
|
+
registered : float
|
|
122
|
+
When the folder artifact was registered, as a :func:`time.monotonic` timestamp.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
path: str
|
|
126
|
+
info: dict[str, Any]
|
|
127
|
+
storage: Storage
|
|
128
|
+
registered: float = field(default_factory=time.monotonic)
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def uri(self) -> str:
|
|
132
|
+
return self.info["uri"]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _parts(path: str) -> list[str]:
|
|
136
|
+
return path.split("/") if path else []
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _parent(path: str) -> str:
|
|
140
|
+
return path.rsplit("/", 1)[0] if "/" in path else ""
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _join_uri(uri: str, rel: str) -> str:
|
|
144
|
+
return f"{uri.rstrip('/')}/{rel}" if rel else uri
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _sorted(listing: Listing) -> list[dict[str, Any]]:
|
|
148
|
+
return sorted(listing.values(), key=lambda entry: entry["name"])
|