Skip to content

Commit 6594ffb

Browse files
authored
Merge pull request #15 from fsspec/no_pb_import
No protocol buffer imports
2 parents 1e3c3b1 + 25a5873 commit 6594ffb

File tree

6 files changed

+64
-328
lines changed

6 files changed

+64
-328
lines changed

ipfsspec/async_ipfs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from fsspec.asyn import AsyncFileSystem, sync, sync_wrapper
99
from fsspec.exceptions import FSTimeoutError
1010

11-
from .core import get_default_gateways
11+
from .utils import get_default_gateways
1212

1313
import logging
1414

ipfsspec/core.py

Lines changed: 39 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
from .unixfs_pb2 import Data as UnixFSData
2-
import cid
1+
from .utils import get_default_gateways
32

43
from fsspec.spec import AbstractFileSystem, AbstractBufferedFile
54
import requests
65
from requests.exceptions import HTTPError
76
import hashlib
8-
import base64
97
import functools
108
import time
11-
import json
12-
import os
139

1410
import logging
1511

@@ -59,6 +55,22 @@ def get(self, path):
5955
res.raise_for_status()
6056
return res.content
6157

58+
def head(self, path, headers=None):
59+
logger.debug("head %s via %s", path, self.url, headers=headers or {})
60+
try:
61+
res = self.session.get(self.url + "/ipfs/" + path)
62+
except requests.ConnectionError as e:
63+
logger.debug("Connection Error: %s", e)
64+
self._backoff()
65+
return None
66+
if res.status_code == 429: # too many requests
67+
self._backoff()
68+
return None
69+
elif res.status_code == 200:
70+
self._speedup()
71+
res.raise_for_status()
72+
return res.headers
73+
6274
def apipost(self, call, **kwargs):
6375
logger.debug("post %s via %s", call, self.url)
6476
try:
@@ -108,22 +120,6 @@ def get_state(self):
108120
return (self.state, None)
109121

110122

111-
GATEWAYS = [
112-
"http://127.0.0.1:8080",
113-
"https://ipfs.io",
114-
"https://gateway.pinata.cloud",
115-
"https://cloudflare-ipfs.com",
116-
"https://dweb.link",
117-
]
118-
119-
120-
def get_default_gateways():
121-
try:
122-
return os.environ["IPFSSPEC_GATEWAYS"].split()
123-
except KeyError:
124-
return GATEWAYS
125-
126-
127123
class IPFSFileSystem(AbstractFileSystem):
128124
protocol = "ipfs"
129125

@@ -160,6 +156,9 @@ def _run_on_any_gateway(self, f):
160156
def _gw_get(self, path):
161157
return self._run_on_any_gateway(lambda gw: gw.get(path))
162158

159+
def _gw_head(self, path, headers=None):
160+
return self._run_on_any_gateway(lambda gw: gw.head(path, headers))
161+
163162
def _gw_apipost(self, call, **kwargs):
164163
return self._run_on_any_gateway(lambda gw: gw.apipost(call, **kwargs))
165164

@@ -213,43 +212,28 @@ def _open(
213212
)
214213

215214
def info(self, path, **kwargs):
215+
path = self._strip_protocol(path)
216216
logger.debug("info on %s", path)
217217

218-
def req(endpoint):
219-
try:
220-
return self._gw_apipost(endpoint, arg=path)
221-
except HTTPError as e:
222-
try:
223-
msg = e.response.json()
224-
except json.JSONDecodeError:
225-
raise IOError("unknown error") from e
226-
else:
227-
if "Message" in msg:
228-
raise FileNotFoundError(msg["Message"]) from e
229-
else:
230-
raise IOError(msg) from e
231-
232-
stat = req("object/stat")
233-
c = cid.from_string(stat["Hash"])
234-
if c.codec == "raw":
235-
size = stat["DataSize"]
236-
ftype = "file"
237-
else:
238-
dag = req("dag/get")
239-
data = UnixFSData()
240-
if "data" in dag:
241-
data.ParseFromString(base64.b64decode(dag["data"]))
218+
headers = {"Accept-Encoding": "identity"} # this ensures correct file size
219+
response_headers = self._gw_head(path, headers)
220+
221+
info = {"name": path}
222+
if "Content-Length" in response_headers:
223+
info["size"] = int(response_headers["Content-Length"])
224+
elif "Content-Range" in response_headers:
225+
info["size"] = int(response_headers["Content-Range"].split("/")[1])
226+
227+
if "ETag" in response_headers:
228+
etag = response_headers["ETag"].strip("\"")
229+
info["ETag"] = etag
230+
if etag.startswith("DirIndex"):
231+
info["type"] = "directory"
232+
info["CID"] = etag.split("-")[-1]
242233
else:
243-
rawdata = dag["Data"]["/"]["bytes"]
244-
data.ParseFromString(base64.b64decode(rawdata + "=" * (-len(rawdata) % 4)))
245-
246-
size = data.filesize
247-
if data.Type == data.File:
248-
ftype = "file"
249-
else:
250-
ftype = "directory"
251-
252-
return {"name": path, "size": size, "type": ftype}
234+
info["type"] = "file"
235+
info["CID"] = etag
236+
return info
253237

254238

255239
class IPFSBufferedFile(AbstractBufferedFile):

0 commit comments

Comments
 (0)