From 46644d8a7cf4214e46aa2fe7e56c67a9272f9e62 Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Tue, 18 Aug 2020 23:28:58 -0300 Subject: [PATCH 01/11] Added base interface --- ipfshttpclient/client/dag.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 ipfshttpclient/client/dag.py diff --git a/ipfshttpclient/client/dag.py b/ipfshttpclient/client/dag.py new file mode 100644 index 00000000..3e77fbff --- /dev/null +++ b/ipfshttpclient/client/dag.py @@ -0,0 +1,25 @@ +import typing as ty + +from . import base + +from .. import multipart +from .. import utils + + + +class Section(base.SectionBase): + def export(self, cid: str): + pass + + def get(self, ref: str): + pass + + def import_(self, file: ty.IO, pin: bool = True): + pass + + def put(self, data: ty.IO, format: str = 'cbor', encoding: str = 'json', + pin: bool = True, hash: str = '.'): + pass + + def resolve(self, ref: str): + pass From 2332800e0a4e00e81236dce9eab2d885ca171a7e Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Tue, 18 Aug 2020 23:33:35 -0300 Subject: [PATCH 02/11] Created base tests interface --- test/functional/test_dag.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/functional/test_dag.py diff --git a/test/functional/test_dag.py b/test/functional/test_dag.py new file mode 100644 index 00000000..0de1041d --- /dev/null +++ b/test/functional/test_dag.py @@ -0,0 +1,21 @@ +import pytest + + +def test_export(client): + assert 0 + + +def test_get(client): + assert 0 + + +def test_import_(client): + assert 0 + + +def test_put(client): + assert 0 + + +def test_resolve(client): + assert 0 From 50c646a1d70869e4c741ecb4cb7428c07da61f97 Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Wed, 19 Aug 2020 00:23:57 -0300 Subject: [PATCH 03/11] Added dag.put --- ipfshttpclient/client/__init__.py | 86 ++++++++++++++++--------------- ipfshttpclient/client/dag.py | 8 +-- test/functional/test_dag.py | 7 ++- 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/ipfshttpclient/client/__init__.py b/ipfshttpclient/client/__init__.py index 8b170103..3158ae62 100644 --- a/ipfshttpclient/client/__init__.py +++ b/ipfshttpclient/client/__init__.py @@ -23,6 +23,7 @@ from . import bootstrap from . import config #TODO: `from . import dag` +from . import dag from . import dht from . import files from . import key @@ -40,8 +41,8 @@ def assert_version(version: str, minimum: str = VERSION_MINIMUM, - maximum: str = VERSION_MAXIMUM, - blacklist: ty.Iterable[str] = VERSION_BLACKLIST) -> None: + maximum: str = VERSION_MAXIMUM, + blacklist: ty.Iterable[str] = VERSION_BLACKLIST) -> None: """Make sure that the given daemon version is supported by this client version. @@ -67,7 +68,7 @@ def assert_version(version: str, minimum: str = VERSION_MINIMUM, if minimum > version or version >= maximum: raise exceptions.VersionMismatch(version, minimum, maximum) - + for blacklisted in blacklist: blacklisted = list(map(int, blacklisted.split('-', 1)[0].split('.'))) if version == blacklisted: @@ -77,16 +78,16 @@ def assert_version(version: str, minimum: str = VERSION_MINIMUM, def connect( addr: http.addr_t = DEFAULT_ADDR, base: str = DEFAULT_BASE, *, - + chunk_size: int = multipart.default_chunk_size, offline: bool = False, session: bool = False, - + auth: http.auth_t = None, cookies: http.cookies_t = None, headers: http.headers_t = {}, timeout: http.timeout_t = 120, - + # Backward-compat username: ty.Optional[str] = None, password: ty.Optional[str] = None @@ -94,7 +95,7 @@ def connect( """Create a new :class:`~ipfshttpclient.Client` instance and connect to the daemon to validate that its version is supported as well as applying any known workarounds for the given daemon version - + Raises ------ ~ipfshttpclient.exceptions.VersionMismatch @@ -103,7 +104,7 @@ def connect( ~ipfshttpclient.exceptions.ProtocolError ~ipfshttpclient.exceptions.StatusError ~ipfshttpclient.exceptions.TimeoutError - + All parameters are identical to those passed to the constructor of the :class:`~ipfshttpclient.Client` class. """ @@ -114,39 +115,39 @@ def connect( auth=auth, cookies=cookies, headers=headers, timeout=timeout, username=username, password=password, ) - + # Query version number from daemon and validate it assert_version(client.apply_workarounds()["Version"]) - + return client class Client(files.Base, miscellaneous.Base): """The main IPFS HTTP client class - + Allows access to an IPFS daemon instance using its HTTP API by exposing an `IPFS Interface Core `__ compatible set of methods. - + It is possible to instantiate this class directly, using the same parameters as :func:`connect`, to prevent the client from checking for an active and compatible version of the daemon. In general however, calling :func:`connect` should be preferred. - + In order to reduce latency between individual API calls, this class may keep a pool of TCP connections between this client and the API daemon open between requests. The only caveat of this is that the client object should be closed when it is not used anymore to prevent resource leaks. - + The easiest way of using this “session management” facility is using a context manager:: - + with ipfshttpclient.connect() as client: print(client.version()) # These calls… print(client.version()) # …will reuse their TCP connection - + A client object may be re-opened several times:: - + client = ipfshttpclient.connect() print(client.version()) # Perform API call on separate TCP connection with client: @@ -155,26 +156,27 @@ class Client(files.Base, miscellaneous.Base): with client: print(client.version()) # These calls… print(client.version()) # …will share a different TCP connection - + When storing a long-running :class:`Client` object use it like this:: - + class Consumer: def __init__(self): self._client = ipfshttpclient.connect(session=True) - + # … other code … - + def close(self): # Call this when you're done self._client.close() """ - + # Fix up docstring so that Sphinx doesn't ignore the constructors parameter list __doc__ += "\n\n" + "\n".join(l[1:] for l in base.ClientBase.__init__.__doc__.split("\n")) - + bitswap = base.SectionProperty(bitswap.Section) block = base.SectionProperty(block.Section) bootstrap = base.SectionProperty(bootstrap.Section) config = base.SectionProperty(config.Section) + dag = base.SectionProperty(dag.Section) dht = base.SectionProperty(dht.Section) key = base.SectionProperty(key.Section) name = base.SectionProperty(name.Section) @@ -184,23 +186,23 @@ def close(self): # Call this when you're done repo = base.SectionProperty(repo.Section) swarm = base.SectionProperty(swarm.Section) unstable = base.SectionProperty(unstable.Section) - - + + ###################### # SESSION MANAGEMENT # ###################### - + def __enter__(self): self._client.open_session() return self - + def __exit__(self, exc_type, exc_value, traceback): self.close() - + def close(self): """Close any currently open client session and free any associated resources. - + If there was no session currently open this method does nothing. An open session is not a requirement for using a :class:`~ipfshttpclient.Client` object and as such all method defined on it will continue to work, but @@ -209,34 +211,34 @@ def close(self): in the future. See the class's description for details. """ self._client.close_session() - - + + ########### # HELPERS # ########### - + def apply_workarounds(self): """Query version information of the referenced daemon and enable any workarounds known for the corresponding version - + Returns ------- dict The version information returned by the daemon """ version_info = self.version() - + version = tuple(map(int, version_info["Version"].split('-', 1)[0].split('.'))) - + self._workarounds.clear() if version < (0, 5): # pragma: no cover (workaround) # Not really a workaround, but make use of HEAD requests on versions # that support them to speed things up if we are not interested in the # response anyways self._workarounds.add("use_http_head_for_no_result") - + return version_info - + @utils.return_field('Hash') @base.returns_single_item(dict) def add_bytes(self, data: bytes, **kwargs): @@ -261,7 +263,7 @@ def add_bytes(self, data: bytes, **kwargs): """ body, headers = multipart.stream_bytes(data, chunk_size=self.chunk_size) return self._client.request('/add', decoder='json', - data=body, headers=headers, **kwargs) + data=body, headers=headers, **kwargs) @utils.return_field('Hash') @base.returns_single_item(dict) @@ -287,7 +289,7 @@ def add_str(self, string, **kwargs): """ body, headers = multipart.stream_text(string, chunk_size=self.chunk_size) return self._client.request('/add', decoder='json', - data=body, headers=headers, **kwargs) + data=body, headers=headers, **kwargs) def add_json(self, json_obj, **kwargs): """Adds a json-serializable Python dict as a json file to IPFS. @@ -308,8 +310,8 @@ def add_json(self, json_obj, **kwargs): Hash of the added IPFS object """ return self.add_bytes(encoding.Json().encode(json_obj), **kwargs) - - + + @base.returns_single_item() def get_json(self, cid, **kwargs): """Loads a json object from IPFS. @@ -329,4 +331,4 @@ def get_json(self, cid, **kwargs): object Deserialized IPFS JSON object value """ - return self.cat(cid, decoder='json', **kwargs) \ No newline at end of file + return self.cat(cid, decoder='json', **kwargs) diff --git a/ipfshttpclient/client/dag.py b/ipfshttpclient/client/dag.py index 3e77fbff..ba0d4a72 100644 --- a/ipfshttpclient/client/dag.py +++ b/ipfshttpclient/client/dag.py @@ -17,9 +17,11 @@ def get(self, ref: str): def import_(self, file: ty.IO, pin: bool = True): pass - def put(self, data: ty.IO, format: str = 'cbor', encoding: str = 'json', - pin: bool = True, hash: str = '.'): - pass + @base.returns_single_item(base.ResponseBase) + def put(self, data: ty.IO, **kwargs: base.CommonArgs): + body, headers = multipart .stream_files(data, chunk_size=self.chunk_size) + return self._client .request('/dag/put', decoder='json', data=body, + headers=headers, **kwargs) def resolve(self, ref: str): pass diff --git a/test/functional/test_dag.py b/test/functional/test_dag.py index 0de1041d..14cbf896 100644 --- a/test/functional/test_dag.py +++ b/test/functional/test_dag.py @@ -1,3 +1,4 @@ +import io import pytest @@ -14,7 +15,11 @@ def test_import_(client): def test_put(client): - assert 0 + data = io.BytesIO(br'{"links": []}') + response = client.dag.put(data) + assert 'Cid' in response + assert '/' in response['Cid'] + assert response['Cid']['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' def test_resolve(client): From 84c90c18fffc043e5ef626a0c531c8f7160fe2fc Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Wed, 19 Aug 2020 00:29:24 -0300 Subject: [PATCH 04/11] Implemented get --- ipfshttpclient/client/dag.py | 29 +++++++++++++++++++---------- test/functional/test_dag.py | 12 +++++++----- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/ipfshttpclient/client/dag.py b/ipfshttpclient/client/dag.py index ba0d4a72..96276c29 100644 --- a/ipfshttpclient/client/dag.py +++ b/ipfshttpclient/client/dag.py @@ -8,20 +8,29 @@ class Section(base.SectionBase): - def export(self, cid: str): - pass + @base.returns_single_item(base.ResponseBase) + def export(self, cid: str, **kwargs: base.CommonArgs): + args = (str(cid),) + return self._client.request('/dag/export', args, decoder='json', **kwargs) - def get(self, ref: str): - pass + @base.returns_single_item(base.ResponseBase) + def get(self, cid: str, **kwargs: base.CommonArgs): + args = (str(cid),) + return self._client.request('/dag/get', args, decoder='json', **kwargs) - def import_(self, file: ty.IO, pin: bool = True): - pass + @base.returns_single_item(base.ResponseBase) + def import_(self, data: ty.IO, **kwargs: base.CommonArgs): + body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) + return self._client.request('/dag/import', decoder='json', data=body, + headers=headers, **kwargs) @base.returns_single_item(base.ResponseBase) def put(self, data: ty.IO, **kwargs: base.CommonArgs): - body, headers = multipart .stream_files(data, chunk_size=self.chunk_size) - return self._client .request('/dag/put', decoder='json', data=body, + body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) + return self._client.request('/dag/put', decoder='json', data=body, headers=headers, **kwargs) - def resolve(self, ref: str): - pass + @base.returns_single_item(base.ResponseBase) + def resolve(self, cid: str, **kwargs: base.CommonArgs): + args = (str(cid),) + return self._client.request('/dag/resolve', args, decoder='json', **kwargs) diff --git a/test/functional/test_dag.py b/test/functional/test_dag.py index 14cbf896..6a73b5ca 100644 --- a/test/functional/test_dag.py +++ b/test/functional/test_dag.py @@ -6,21 +6,23 @@ def test_export(client): assert 0 -def test_get(client): - assert 0 - - def test_import_(client): assert 0 -def test_put(client): +def test_put_get(client): data = io.BytesIO(br'{"links": []}') response = client.dag.put(data) + assert 'Cid' in response assert '/' in response['Cid'] assert response['Cid']['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' + response = client.dag.get('bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya') + + assert 'links' in response + assert response['links'] == [] + def test_resolve(client): assert 0 From 4c888e3b081d120d51a51cadcf5cdd6df379c0a7 Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Wed, 19 Aug 2020 00:45:09 -0300 Subject: [PATCH 05/11] Removed import and export --- ipfshttpclient/client/dag.py | 11 ----------- test/functional/test_dag.py | 17 ++++++----------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/ipfshttpclient/client/dag.py b/ipfshttpclient/client/dag.py index 96276c29..dce4c64e 100644 --- a/ipfshttpclient/client/dag.py +++ b/ipfshttpclient/client/dag.py @@ -8,22 +8,11 @@ class Section(base.SectionBase): - @base.returns_single_item(base.ResponseBase) - def export(self, cid: str, **kwargs: base.CommonArgs): - args = (str(cid),) - return self._client.request('/dag/export', args, decoder='json', **kwargs) - @base.returns_single_item(base.ResponseBase) def get(self, cid: str, **kwargs: base.CommonArgs): args = (str(cid),) return self._client.request('/dag/get', args, decoder='json', **kwargs) - @base.returns_single_item(base.ResponseBase) - def import_(self, data: ty.IO, **kwargs: base.CommonArgs): - body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) - return self._client.request('/dag/import', decoder='json', data=body, - headers=headers, **kwargs) - @base.returns_single_item(base.ResponseBase) def put(self, data: ty.IO, **kwargs: base.CommonArgs): body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) diff --git a/test/functional/test_dag.py b/test/functional/test_dag.py index 6a73b5ca..31706ef3 100644 --- a/test/functional/test_dag.py +++ b/test/functional/test_dag.py @@ -1,16 +1,9 @@ import io import pytest +import conftest -def test_export(client): - assert 0 - - -def test_import_(client): - assert 0 - - -def test_put_get(client): +def test_put_get_resolve(client): data = io.BytesIO(br'{"links": []}') response = client.dag.put(data) @@ -23,6 +16,8 @@ def test_put_get(client): assert 'links' in response assert response['links'] == [] + response = client.dag.resolve('bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya') + + assert 'Cid' in response + assert response['Cid']['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' -def test_resolve(client): - assert 0 From 99f46f268d56c0b8937b141428db59c6cb4c9ea9 Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Wed, 19 Aug 2020 00:49:59 -0300 Subject: [PATCH 06/11] Converted to tabs --- ipfshttpclient/client/dag.py | 26 +++++++++++++------------- test/functional/test_dag.py | 22 +++++++++++----------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ipfshttpclient/client/dag.py b/ipfshttpclient/client/dag.py index dce4c64e..e10081c8 100644 --- a/ipfshttpclient/client/dag.py +++ b/ipfshttpclient/client/dag.py @@ -8,18 +8,18 @@ class Section(base.SectionBase): - @base.returns_single_item(base.ResponseBase) - def get(self, cid: str, **kwargs: base.CommonArgs): - args = (str(cid),) - return self._client.request('/dag/get', args, decoder='json', **kwargs) + @base.returns_single_item(base.ResponseBase) + def get(self, cid: str, **kwargs: base.CommonArgs): + args = (str(cid),) + return self._client.request('/dag/get', args, decoder='json', **kwargs) - @base.returns_single_item(base.ResponseBase) - def put(self, data: ty.IO, **kwargs: base.CommonArgs): - body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) - return self._client.request('/dag/put', decoder='json', data=body, - headers=headers, **kwargs) + @base.returns_single_item(base.ResponseBase) + def put(self, data: ty.IO, **kwargs: base.CommonArgs): + body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) + return self._client.request('/dag/put', decoder='json', data=body, + headers=headers, **kwargs) - @base.returns_single_item(base.ResponseBase) - def resolve(self, cid: str, **kwargs: base.CommonArgs): - args = (str(cid),) - return self._client.request('/dag/resolve', args, decoder='json', **kwargs) + @base.returns_single_item(base.ResponseBase) + def resolve(self, cid: str, **kwargs: base.CommonArgs): + args = (str(cid),) + return self._client.request('/dag/resolve', args, decoder='json', **kwargs) diff --git a/test/functional/test_dag.py b/test/functional/test_dag.py index 31706ef3..7d84e1ed 100644 --- a/test/functional/test_dag.py +++ b/test/functional/test_dag.py @@ -4,20 +4,20 @@ import conftest def test_put_get_resolve(client): - data = io.BytesIO(br'{"links": []}') - response = client.dag.put(data) + data = io.BytesIO(br'{"links": []}') + response = client.dag.put(data) - assert 'Cid' in response - assert '/' in response['Cid'] - assert response['Cid']['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' + assert 'Cid' in response + assert '/' in response['Cid'] + assert response['Cid']['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' - response = client.dag.get('bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya') + response = client.dag.get('bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya') - assert 'links' in response - assert response['links'] == [] + assert 'links' in response + assert response['links'] == [] - response = client.dag.resolve('bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya') + response = client.dag.resolve('bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya') - assert 'Cid' in response - assert response['Cid']['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' + assert 'Cid' in response + assert response['Cid']['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' From 46d597ff367aa4ca7df5f15dfe12e353c7e9fd68 Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Wed, 19 Aug 2020 00:57:29 -0300 Subject: [PATCH 07/11] Fixed linting --- ipfshttpclient/client/dag.py | 4 +--- test/functional/test_dag.py | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/ipfshttpclient/client/dag.py b/ipfshttpclient/client/dag.py index e10081c8..2424ae14 100644 --- a/ipfshttpclient/client/dag.py +++ b/ipfshttpclient/client/dag.py @@ -3,8 +3,6 @@ from . import base from .. import multipart -from .. import utils - class Section(base.SectionBase): @@ -17,7 +15,7 @@ def get(self, cid: str, **kwargs: base.CommonArgs): def put(self, data: ty.IO, **kwargs: base.CommonArgs): body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) return self._client.request('/dag/put', decoder='json', data=body, - headers=headers, **kwargs) + headers=headers, **kwargs) @base.returns_single_item(base.ResponseBase) def resolve(self, cid: str, **kwargs: base.CommonArgs): diff --git a/test/functional/test_dag.py b/test/functional/test_dag.py index 7d84e1ed..1cbc90eb 100644 --- a/test/functional/test_dag.py +++ b/test/functional/test_dag.py @@ -1,7 +1,5 @@ import io -import pytest -import conftest def test_put_get_resolve(client): data = io.BytesIO(br'{"links": []}') @@ -20,4 +18,3 @@ def test_put_get_resolve(client): assert 'Cid' in response assert response['Cid']['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' - From 9935821ff06c5497c8f5faeb6c167d8fb883c0ea Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Wed, 19 Aug 2020 20:46:28 -0300 Subject: [PATCH 08/11] Created dag import and export --- ipfshttpclient/client/dag.py | 10 ++++++++++ test/functional/fake_json/data.car | Bin 0 -> 104 bytes test/functional/test_dag.py | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 test/functional/fake_json/data.car diff --git a/ipfshttpclient/client/dag.py b/ipfshttpclient/client/dag.py index 2424ae14..4660ea2e 100644 --- a/ipfshttpclient/client/dag.py +++ b/ipfshttpclient/client/dag.py @@ -21,3 +21,13 @@ def put(self, data: ty.IO, **kwargs: base.CommonArgs): def resolve(self, cid: str, **kwargs: base.CommonArgs): args = (str(cid),) return self._client.request('/dag/resolve', args, decoder='json', **kwargs) + + @base.returns_single_item(base.ResponseBase) + def imprt(self, data: ty.IO, **kwargs: base.CommonArgs): + body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) + return self._client.request('/dag/import', decoder='json', data=body, + headers=headers, **kwargs) + + def export(self, cid: str, **kwargs: base.CommonArgs): + args = (str(cid),) + return self._client.request('/dag/export', args, **kwargs) diff --git a/test/functional/fake_json/data.car b/test/functional/fake_json/data.car new file mode 100644 index 0000000000000000000000000000000000000000..af2b2e419c59ed6b3f396b0bb1cf1246e9eb6bf6 GIT binary patch literal 104 zcmcColv Date: Wed, 19 Aug 2020 21:02:05 -0300 Subject: [PATCH 09/11] Added docs --- ipfshttpclient/client/dag.py | 116 +++++++++++++++++++++++++++++++++++ test/functional/test_dag.py | 3 + 2 files changed, 119 insertions(+) diff --git a/ipfshttpclient/client/dag.py b/ipfshttpclient/client/dag.py index 4660ea2e..043d7944 100644 --- a/ipfshttpclient/client/dag.py +++ b/ipfshttpclient/client/dag.py @@ -8,26 +8,142 @@ class Section(base.SectionBase): @base.returns_single_item(base.ResponseBase) def get(self, cid: str, **kwargs: base.CommonArgs): + """Get and serialize the DAG node named by CID. + + .. code-block:: python + + >>> client.dag.get('QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D') + {'Data': '\x08\x01', + 'Links': [ + {'Hash': 'Qmd2xkBfEwEs9oMTk77A6jrsgurpF3ugXSg7dtPNFkcNMV', + 'Name': 'Makefile', 'Size': 174}, + {'Hash': 'QmeKozNssnkJ4NcyRidYgDY2jfRZqVEoRGfipkgath71bX', + 'Name': 'example', 'Size': 1474}, + {'Hash': 'QmZAL3oHMQYqsV61tGvoAVtQLs1WzRe1zkkamv9qxqnDuK', + 'Name': 'home', 'Size': 3947}, + {'Hash': 'QmZNPyKVriMsZwJSNXeQtVQSNU4v4KEKGUQaMT61LPahso', + 'Name': 'lib', 'Size': 268261}, + {'Hash': 'QmSY8RfVntt3VdxWppv9w5hWgNrE31uctgTiYwKir8eXJY', + 'Name': 'published-version', 'Size': 55} + ]} + + Parameters + ---------- + cid + Key of the object to retrieve, in CID format + + Returns + ------- + dict + Cid with the address of the dag object + """ args = (str(cid),) return self._client.request('/dag/get', args, decoder='json', **kwargs) @base.returns_single_item(base.ResponseBase) def put(self, data: ty.IO, **kwargs: base.CommonArgs): + """Stores input as a DAG object and returns its key. + + .. code-block:: python + + >>> client.dag.put(io.BytesIO(b''' + ... { + ... "Data": "another", + ... "Links": [ { + ... "Name": "some link", + ... "Hash": "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCV … R39V", + ... "Size": 8 + ... } ] + ... }''')) + {'Cid': { + '/': 'bafyreifgjgbmtykld2e3yncey3naek5xad3h4m2pxmo3of376qxh54qk34' + } + } + + Parameters + ---------- + data + IO stream object with data that should be put + + Returns + ------- + dict + Cid with the address of the dag object + """ body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) return self._client.request('/dag/put', decoder='json', data=body, headers=headers, **kwargs) @base.returns_single_item(base.ResponseBase) def resolve(self, cid: str, **kwargs: base.CommonArgs): + """Resolves a DAG node from its Cid + + .. code-block:: python + + >>> client.dag.resolve('QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D') + {'Cid': { + '/': 'QmTkzDwWqPbnAh5YiV5VwcTLnGdwSNsNTn2aDxdXBFca7D' + } + } + + Parameters + ---------- + cid + Key of the object to resolve, in CID format + + Returns + ------- + dict + Cid with the address of the dag object + """ args = (str(cid),) return self._client.request('/dag/resolve', args, decoder='json', **kwargs) @base.returns_single_item(base.ResponseBase) def imprt(self, data: ty.IO, **kwargs: base.CommonArgs): + """Imports a .car file with a DAG to IPFS + + .. code-block:: python + + >>> with open('data.car', 'rb') as file + ... client.dag.imprt(file) + {'Root': { + 'Cid': { + '/': 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' + } + } + } + + Parameters + ---------- + data + IO stream object with data that should be imported + + Returns + ------- + dict + Dictionary with the root CID of the DAG imported + """ body, headers = multipart.stream_files(data, chunk_size=self.chunk_size) return self._client.request('/dag/import', decoder='json', data=body, headers=headers, **kwargs) def export(self, cid: str, **kwargs: base.CommonArgs): + """Exports a DAG into a .car file format + + .. code-block:: python + + >>> bytes = client.dag.export('bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya') + + Parameters + ---------- + cid + Key of the object to export, in CID format + + Returns + ------- + bytes + DAG in a .car format + """ args = (str(cid),) return self._client.request('/dag/export', args, **kwargs) diff --git a/test/functional/test_dag.py b/test/functional/test_dag.py index d084e5b7..e3b5c8bd 100644 --- a/test/functional/test_dag.py +++ b/test/functional/test_dag.py @@ -23,7 +23,10 @@ def test_put_get_resolve(client): def test_import_export(client): + # This file was created by inserting a simple JSON object into IPFS and + # exporting it using `ipfs dag export > file.car` data_car = conftest.TEST_DIR / 'fake_json' / 'data.car' + with open(data_car, 'rb') as file: response = client.dag.imprt(file) From 055c5d6bcfe61fa774f9c05628df2955120e99a3 Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Wed, 19 Aug 2020 21:04:36 -0300 Subject: [PATCH 10/11] Fixed linting --- test/functional/test_dag.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/functional/test_dag.py b/test/functional/test_dag.py index e3b5c8bd..e5eaf9a5 100644 --- a/test/functional/test_dag.py +++ b/test/functional/test_dag.py @@ -33,7 +33,9 @@ def test_import_export(client): assert 'Root' in response assert 'Cid' in response['Root'] assert '/' in response['Root']['Cid'] - assert response['Root']['Cid']['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' + + cid = response['Root']['Cid'] + assert cid['/'] == 'bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya' data = client.dag.export('bafyreidepjmjhvhlvp5eyxqpmyyi7rxwvl7wsglwai3cnvq63komq4tdya') From 45e991ff6b1208d26f9c7ecc59d6d1902b25ba62 Mon Sep 17 00:00:00 2001 From: Joao Meyer <1994meyer@gmail.com> Date: Wed, 19 Aug 2020 21:14:25 -0300 Subject: [PATCH 11/11] Fixed problem with pathlib in some versions of python --- test/functional/test_dag.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/functional/test_dag.py b/test/functional/test_dag.py index e5eaf9a5..6345020c 100644 --- a/test/functional/test_dag.py +++ b/test/functional/test_dag.py @@ -26,6 +26,7 @@ def test_import_export(client): # This file was created by inserting a simple JSON object into IPFS and # exporting it using `ipfs dag export > file.car` data_car = conftest.TEST_DIR / 'fake_json' / 'data.car' + data_car = str(data_car) with open(data_car, 'rb') as file: response = client.dag.imprt(file)