Skip to content

Commit 78cbeb8

Browse files
committed
Implement write_to_disk for ipfs backends
1 parent 0dfc6da commit 78cbeb8

6 files changed

Lines changed: 74 additions & 4 deletions

File tree

ethpm/backends/base.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from abc import ABC, abstractmethod
2+
from pathlib import Path
23
from typing import Union
34

45
from eth_typing import URI
@@ -34,3 +35,11 @@ def fetch_uri_contents(self, uri: URI) -> Union[bytes, URI]:
3435
Fetch the contents stored at a URI.
3536
"""
3637
pass
38+
39+
@abstractmethod
40+
def write_to_disk(self, uri: URI, target_path: Path) -> None:
41+
"""
42+
Writes the contents of target URI to target path.
43+
Raises exception if target path exists.
44+
"""
45+
pass

ethpm/backends/http.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import base64
22
import json
3+
from pathlib import Path
4+
import shutil
5+
import tempfile
36

47
from eth_typing import URI
58
import requests
@@ -18,6 +21,10 @@ class GithubOverHTTPSBackend(BaseURIBackend):
1821
Base class for all URIs pointing to a content-addressed Github URI.
1922
"""
2023

24+
@property
25+
def base_uri(self) -> str:
26+
return GITHUB_API_AUTHORITY
27+
2128
def can_resolve_uri(self, uri: URI) -> bool:
2229
return is_valid_content_addressed_github_uri(uri)
2330

@@ -44,6 +51,14 @@ def fetch_uri_contents(self, uri: URI) -> bytes:
4451
validate_blob_uri_contents(decoded_contents, uri)
4552
return decoded_contents
4653

47-
@property
48-
def base_uri(self) -> str:
49-
return GITHUB_API_AUTHORITY
54+
def write_to_disk(self, uri: URI, target_path: Path) -> None:
55+
contents = self.fetch_uri_contents(uri)
56+
if target_path.exists():
57+
raise CannotHandleURI(
58+
f"Github blob: {uri} cannot be written to disk since target path ({target_path}) "
59+
"already exists. Please provide a target_path that does not exist."
60+
)
61+
with tempfile.NamedTemporaryFile() as temp:
62+
temp.write(contents)
63+
temp.seek(0)
64+
shutil.copyfile(temp.name, target_path)

ethpm/backends/ipfs.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from abc import abstractmethod
22
import os
33
from pathlib import Path
4+
import shutil
5+
import tempfile
46
from typing import Dict, List, Type
57

8+
from eth_typing import URI
69
from eth_utils import import_string, to_bytes
710
import ipfshttpclient
811

@@ -49,6 +52,18 @@ def pin_assets(self, file_or_dir_path: Path) -> List[Dict[str, str]]:
4952
"""
5053
pass
5154

55+
def write_to_disk(self, uri: URI, target_path: Path) -> None:
56+
contents = self.fetch_uri_contents(uri)
57+
if target_path.exists():
58+
raise CannotHandleURI(
59+
f"IPFS uri: {uri} cannot be written to disk since target path ({target_path}) "
60+
"already exists. Please provide a target_path that does not exist."
61+
)
62+
with tempfile.NamedTemporaryFile() as temp:
63+
temp.write(contents)
64+
temp.seek(0)
65+
shutil.copyfile(temp.name, target_path)
66+
5267

5368
class IPFSOverHTTPBackend(BaseIPFSBackend):
5469
"""
@@ -59,7 +74,7 @@ class IPFSOverHTTPBackend(BaseIPFSBackend):
5974
def __init__(self) -> None:
6075
self.client = ipfshttpclient.connect(self.base_uri)
6176

62-
def fetch_uri_contents(self, uri: str) -> bytes:
77+
def fetch_uri_contents(self, uri: URI) -> bytes:
6378
ipfs_hash = extract_ipfs_path_from_uri(uri)
6479
contents = self.client.cat(ipfs_hash)
6580
validation_hash = generate_file_hash(contents)
@@ -108,6 +123,11 @@ def fetch_uri_contents(self, uri: str) -> bytes:
108123
"IPFS gateway is currently disabled, please use a different IPFS backend."
109124
)
110125

126+
def write_to_disk(self, uri: URI, target_path: Path) -> None:
127+
raise CannotHandleURI(
128+
"IPFS gateway is currently disabled, please use a different IPFS backend."
129+
)
130+
111131

112132
class InfuraIPFSBackend(IPFSOverHTTPBackend):
113133
"""

ethpm/backends/registry.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import os
2+
from pathlib import Path
23

34
from eth_typing import URI
45
from web3 import Web3
56
from web3.providers.auto import load_provider_from_uri
67

78
from ethpm.backends.base import BaseURIBackend
89
from ethpm.constants import INFURA_API_KEY
10+
from ethpm.exceptions import CannotHandleURI
911
from ethpm.utils.registry import fetch_standard_registry_abi
1012
from ethpm.utils.uri import parse_registry_uri
1113
from ethpm.validation import is_valid_registry_uri
@@ -41,3 +43,9 @@ def fetch_uri_contents(self, uri: str) -> URI:
4143
self.w3.pm.set_registry(address)
4244
_, _, manifest_uri = self.w3.pm.get_release_data(pkg_name, pkg_version)
4345
return manifest_uri
46+
47+
def write_to_disk(self, uri: str, target_path: Path) -> None:
48+
raise CannotHandleURI(
49+
"Registry backends are not allowed to write resolved uris to disk. "
50+
"Please use a different backend."
51+
)

tests/ethpm/backends/test_http_backends.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,12 @@ def test_github_over_https_backend_raises_error_with_invalid_content_hash(w3):
2626
invalid_uri = "https://api.github.com/repos/ethpm/py-ethpm/git/blobs/a7232a93f1e9e75d606f6c1da18aa16037e03123"
2727
with pytest.raises(HTTPError):
2828
Package.from_uri(invalid_uri, w3)
29+
30+
31+
def test_github_backend_writes_to_disk(tmp_path):
32+
uri = "https://api.github.com/repos/ethpm/py-ethpm/git/blobs/a7232a93f1e9e75d606f6c1da18aa16037e03480"
33+
backend = GithubOverHTTPSBackend()
34+
contents = backend.fetch_uri_contents(uri)
35+
target_path = tmp_path / "github_uri.txt"
36+
backend.write_to_disk(uri, target_path)
37+
assert target_path.read_bytes() == contents

tests/ethpm/backends/test_ipfs_backends.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,12 @@ def test_pin_assets_to_dummy_backend(dummy_ipfs_backend):
116116
assert "StandardToken.sol" in dir_names
117117
assert "QmRJHLmPVct2rbBpdGjP3xkXbF7romQigtmcs8TRfV1yC7" in dir_hashes
118118
assert "2865" in dir_sizes
119+
120+
121+
def test_ipfs_backend_write_to_disk(tmp_path):
122+
ipfs_uri = "ipfs://QmeD2s7KaBUoGYTP1eutHBmBkMMMoycdfiyGMx2DKrWXyV"
123+
backend = LocalIPFSBackend()
124+
contents = backend.fetch_uri_contents(ipfs_uri)
125+
target_path = tmp_path / "ipfs_uri.txt"
126+
backend.write_to_disk(ipfs_uri, target_path)
127+
assert target_path.read_bytes() == contents

0 commit comments

Comments
 (0)