From a12d66729297b801765d7bb966566e6f723281df Mon Sep 17 00:00:00 2001 From: gmena Date: Sun, 31 Oct 2021 17:16:23 -0600 Subject: [PATCH 1/5] feat: hash function param to `add` file method class --- ipfshttpclient/client/files.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ipfshttpclient/client/files.py b/ipfshttpclient/client/files.py index 30bdb733..9e3b9783 100644 --- a/ipfshttpclient/client/files.py +++ b/ipfshttpclient/client/files.py @@ -244,6 +244,7 @@ def add(self, file: utils.clean_file_t, *files: utils.clean_file_t, wrap_with_directory: bool = False, chunker: ty.Optional[str] = None, pin: bool = True, raw_leaves: bool = None, nocopy: bool = False, cid_version: ty.Optional[int] = None, + hash_function: 'sha2-256', **kwargs: base.CommonArgs): """Adds a file, several files or directory of files to IPFS @@ -350,6 +351,7 @@ def add(self, file: utils.clean_file_t, *files: utils.clean_file_t, } # type: ty.Dict[str, ty.Union[str, bool]] for option_name, option_value in [ ("chunker", chunker), + ("hash", hash_function), ("cid-version", cid_version), ]: if option_value is not None: From da6824f28b9b2a35895dfdf6167319a15bd8cb24 Mon Sep 17 00:00:00 2001 From: gmena Date: Wed, 17 Nov 2021 13:01:20 -0600 Subject: [PATCH 2/5] feat: add get client method --- ipfshttpclient/client/__init__.py | 84 +++++++++++++++---------------- ipfshttpclient/client/files.py | 58 ++++++++++----------- ipfshttpclient/client/pin.py | 31 ++++++------ 3 files changed, 85 insertions(+), 88 deletions(-) diff --git a/ipfshttpclient/client/__init__.py b/ipfshttpclient/client/__init__.py index ec9e525f..dea52b8e 100644 --- a/ipfshttpclient/client/__init__.py +++ b/ipfshttpclient/client/__init__.py @@ -19,9 +19,9 @@ # This range inclusive-exclusive, so the daemon version must match # `VERSION_MINIMUM <= version < VERSION_MAXIMUM` # for it to be considered compatible. -VERSION_MINIMUM = "0.5.0" +VERSION_MINIMUM = "0.5.0" VERSION_BLACKLIST = [] -VERSION_MAXIMUM = "0.9.0" +VERSION_MAXIMUM = "0.9.0" from . import base from . import bitswap @@ -38,7 +38,7 @@ from . import pin from . import pubsub from . import repo -#TODO: `from . import stats` +# TODO: `from . import stats` from . import swarm from . import unstable @@ -46,8 +46,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. @@ -83,16 +83,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, - + username: ty.Optional[str] = DEFAULT_USERNAME, password: ty.Optional[str] = DEFAULT_PASSWORD ): @@ -119,10 +119,10 @@ 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 @@ -172,37 +172,39 @@ def __init__(self): 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) + + 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) - object = base.SectionProperty(object.Section) - pin = base.SectionProperty(pin.Section) - pubsub = base.SectionProperty(pubsub.Section) - repo = base.SectionProperty(repo.Section) - swarm = base.SectionProperty(swarm.Section) - unstable = base.SectionProperty(unstable.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) + object = base.SectionProperty(object.Section) + pin = base.SectionProperty(pin.Section) + pubsub = base.SectionProperty(pubsub.Section) + 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 get_client(self): + return self._client + def close(self): """Close any currently open client session and free any associated resources. @@ -215,12 +217,11 @@ 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 @@ -231,13 +232,13 @@ def apply_workarounds(self): 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() - + return version_info - + @utils.return_field('Hash') @base.returns_single_item(dict) def add_bytes(self, data: bytes, **kwargs): @@ -262,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) @@ -288,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. @@ -309,8 +310,7 @@ 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. @@ -330,4 +330,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/files.py b/ipfshttpclient/client/files.py index 9e3b9783..844e8f6b 100644 --- a/ipfshttpclient/client/files.py +++ b/ipfshttpclient/client/files.py @@ -8,7 +8,7 @@ class Section(base.SectionBase): """Manage files in IPFS's virtual “Mutable File System” (MFS) file storage space""" - + @base.returns_no_item def cp(self, source: str, dest: str, **kwargs: base.CommonArgs): """Creates a copy of a file within the MFS @@ -74,8 +74,8 @@ def ls(self, path: str, **kwargs: base.CommonArgs): """ args = (path,) return self._client.request('/files/ls', args, decoder='json', **kwargs) - - + + @base.returns_no_item def mkdir(self, path: str, parents: bool = False, **kwargs: base.CommonArgs): """Creates a directory within the MFS @@ -93,11 +93,11 @@ def mkdir(self, path: str, parents: bool = False, **kwargs: base.CommonArgs): if the requested directory already exists """ kwargs.setdefault("opts", {})["parents"] = parents - + args = (path,) return self._client.request('/files/mkdir', args, **kwargs) - - + + @base.returns_no_item def mv(self, source: str, dest: str, **kwargs: base.CommonArgs): """Moves files and directories within the MFS @@ -115,8 +115,8 @@ def mv(self, source: str, dest: str, **kwargs: base.CommonArgs): """ args = (source, dest) return self._client.request('/files/mv', args, **kwargs) - - + + def read(self, path: str, offset: int = 0, count: ty.Optional[int] = None, **kwargs: base.CommonArgs): """Reads a file stored in the MFS @@ -143,11 +143,11 @@ def read(self, path: str, offset: int = 0, count: ty.Optional[int] = None, if count is not None: opts["count"] = count kwargs.setdefault("opts", {}).update(opts) - + args = (path,) return self._client.request('/files/read', args, **kwargs) - - + + @base.returns_no_item def rm(self, path: str, recursive: bool = False, **kwargs: base.CommonArgs): """Removes a file from the MFS @@ -169,11 +169,11 @@ def rm(self, path: str, recursive: bool = False, **kwargs: base.CommonArgs): Recursively remove directories? """ kwargs.setdefault("opts", {})["recursive"] = recursive - + args = (path,) return self._client.request('/files/rm', args, **kwargs) - - + + @base.returns_single_item(base.ResponseBase) def stat(self, path: str, **kwargs: base.CommonArgs): """Returns basic ``stat`` information for an MFS file (including its hash) @@ -195,8 +195,8 @@ def stat(self, path: str, **kwargs: base.CommonArgs): """ args = (path,) return self._client.request('/files/stat', args, decoder='json', **kwargs) - - + + @base.returns_no_item def write(self, path: str, file: utils.clean_file_t, offset: int = 0, create: bool = False, truncate: bool = False, @@ -226,7 +226,7 @@ def write(self, path: str, file: utils.clean_file_t, offset: int = 0, if count is not None: opts["count"] = count kwargs.setdefault("opts", {}).update(opts) - + args = (path,) body, headers = multipart.stream_files(file, chunk_size=self.chunk_size) return self._client.request('/files/write', args, data=body, headers=headers, **kwargs) @@ -234,8 +234,8 @@ def write(self, path: str, file: utils.clean_file_t, offset: int = 0, class Base(base.ClientBase): files = base.SectionProperty(Section) - - + + def add(self, file: utils.clean_file_t, *files: utils.clean_file_t, recursive: bool = False, pattern: multipart.match_spec_t[ty.AnyStr] = None, @@ -244,7 +244,7 @@ def add(self, file: utils.clean_file_t, *files: utils.clean_file_t, wrap_with_directory: bool = False, chunker: ty.Optional[str] = None, pin: bool = True, raw_leaves: bool = None, nocopy: bool = False, cid_version: ty.Optional[int] = None, - hash_function: 'sha2-256', + hash_function: "sha2-256", **kwargs: base.CommonArgs): """Adds a file, several files or directory of files to IPFS @@ -357,12 +357,12 @@ def add(self, file: utils.clean_file_t, *files: utils.clean_file_t, if option_value is not None: opts[option_name] = option_value kwargs.setdefault("opts", {}).update(opts) - + # There may be other cases where nocopy will silently fail to work, but # this is by far the most obvious one if isinstance(file, int) and nocopy: raise ValueError("Passing file descriptors is incompatible with *nocopy*") - + assert not isinstance(file, (tuple, list)), \ "Use `client.add(name1, name2, …)` to add several items" multiple = (len(files) > 0) @@ -371,7 +371,7 @@ def add(self, file: utils.clean_file_t, *files: utils.clean_file_t, to_send, chunk_size=self.chunk_size, follow_symlinks=follow_symlinks, period_special=period_special, patterns=pattern, recursive=recursive ) - + resp = self._client.request('/add', decoder='json', data=body, headers=headers, **kwargs) if not multiple and not is_dir and not wrap_with_directory: assert len(resp) == 1 @@ -379,8 +379,8 @@ def add(self, file: utils.clean_file_t, *files: utils.clean_file_t, elif kwargs.get("stream", False): return base.ResponseWrapIterator(resp, base.ResponseBase) return [base.ResponseBase(v) for v in resp] - - + + @base.returns_no_item def get(self, cid: base.cid_t, target: utils.path_t = ".", **kwargs: base.CommonArgs) -> None: @@ -397,8 +397,8 @@ def get(self, cid: base.cid_t, target: utils.path_t = ".", """ args = (str(cid),) return self._client.download('/get', target, args, **kwargs) - - + + def cat(self, cid: base.cid_t, offset: int = 0, length: ty.Optional[int] = None, **kwargs: base.CommonArgs): r"""Retrieves the contents of a file identified by hash @@ -434,8 +434,8 @@ def cat(self, cid: base.cid_t, offset: int = 0, opts['length'] = length kwargs.setdefault('opts', opts) return self._client.request('/cat', args, **kwargs) - - + + @base.returns_single_item(base.ResponseBase) def ls(self, cid: base.cid_t, **kwargs: base.CommonArgs): """Returns a list of objects linked to by the given hash diff --git a/ipfshttpclient/client/pin.py b/ipfshttpclient/client/pin.py index b0b8d1ef..f6ead7e0 100644 --- a/ipfshttpclient/client/pin.py +++ b/ipfshttpclient/client/pin.py @@ -30,11 +30,11 @@ def add(self, path: base.cid_t, *paths: base.cid_t, recursive: bool = True, +------+-----------------------------------------------------------+ """ kwargs.setdefault("opts", {})["recursive"] = recursive - + args = (str(path), *(str(p) for p in paths)) return self._client.request('/pin/add', args, decoder='json', **kwargs) - - + + @base.returns_single_item(base.ResponseBase) def ls(self, *paths: base.cid_t, type: str = "all", **kwargs: base.CommonArgs): """Lists objects pinned in the local repository @@ -108,14 +108,13 @@ def ls(self, *paths: base.cid_t, type: str = "all", **kwargs: base.CommonArgs): +------+--------------------------------------------------------------+ """ kwargs.setdefault("opts", {})["type"] = type - + args = tuple(str(p) for p in paths) return self._client.request('/pin/ls', args, decoder='json', **kwargs) - - + @base.returns_single_item(base.ResponseBase) def rm(self, path: base.cid_t, *paths: base.cid_t, recursive: bool = True, - **kwargs: base.CommonArgs): + **kwargs: base.CommonArgs): """Removes a pinned object from local storage Removes the pin from the given object allowing it to be garbage @@ -149,14 +148,13 @@ def rm(self, path: base.cid_t, *paths: base.cid_t, recursive: bool = True, +------+-------------------------------------------------------------+ """ kwargs.setdefault("opts", {})["recursive"] = recursive - + args = (str(path), *(str(p) for p in paths)) return self._client.request('/pin/rm', args, decoder='json', **kwargs) - - + @base.returns_single_item(base.ResponseBase) def update(self, from_path: base.cid_t, to_path: base.cid_t, *, - unpin: bool = True, **kwargs: base.CommonArgs): + unpin: bool = True, **kwargs: base.CommonArgs): """Replaces one pin with another Updates one pin to another, making sure that all objects in the new pin @@ -190,14 +188,13 @@ def update(self, from_path: base.cid_t, to_path: base.cid_t, *, +------+-------------------------------------------------------------+ """ kwargs.setdefault("opts", {})["unpin"] = unpin - + args = (str(from_path), str(to_path)) return self._client.request('/pin/update', args, decoder='json', **kwargs) - - + @base.returns_multiple_items(base.ResponseBase, stream=True) def verify(self, path: base.cid_t, *paths: base.cid_t, verbose: bool = False, - **kwargs: base.CommonArgs): + **kwargs: base.CommonArgs): """Verifies that all recursive pins are completely available in the local repository @@ -240,6 +237,6 @@ def verify(self, path: base.cid_t, *paths: base.cid_t, verbose: bool = False, +-----+----------------------------------------------------+ """ kwargs.setdefault("opts", {})["verbose"] = verbose - + args = (str(path), *(str(p) for p in paths)) - return self._client.request('/pin/verify', args, decoder='json', stream=True, **kwargs) \ No newline at end of file + return self._client.request('/pin/verify', args, decoder='json', stream=True, **kwargs) From 652a58d3dc664286fc1819dbf903e19c5fa73be1 Mon Sep 17 00:00:00 2001 From: gmena Date: Wed, 17 Nov 2021 13:22:13 -0600 Subject: [PATCH 3/5] chore: bump ipfs version --- ipfshttpclient/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipfshttpclient/client/__init__.py b/ipfshttpclient/client/__init__.py index dea52b8e..f1309a71 100644 --- a/ipfshttpclient/client/__init__.py +++ b/ipfshttpclient/client/__init__.py @@ -21,7 +21,7 @@ # for it to be considered compatible. VERSION_MINIMUM = "0.5.0" VERSION_BLACKLIST = [] -VERSION_MAXIMUM = "0.9.0" +VERSION_MAXIMUM = "0.11.0" from . import base from . import bitswap From ed1aba280107e8b400e5d8b2fb88a75c2e9a423c Mon Sep 17 00:00:00 2001 From: gmena Date: Tue, 21 Dec 2021 17:21:18 -0600 Subject: [PATCH 4/5] chore: bump ipfs version --- ipfshttpclient/http_common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipfshttpclient/http_common.py b/ipfshttpclient/http_common.py index 5d1257cd..c3abb9da 100644 --- a/ipfshttpclient/http_common.py +++ b/ipfshttpclient/http_common.py @@ -317,7 +317,8 @@ def multiaddr_to_url_data(addr: addr_t, base: str # type: ignore[no-any-unimpor query = "", fragment = "" ).geturl() - + + print(base_url) return base_url, uds_path, family, host_numeric From 596e80c6a2113050e81ca4537caff2c6981b2569 Mon Sep 17 00:00:00 2001 From: gmena Date: Tue, 21 Dec 2021 20:21:28 -0600 Subject: [PATCH 5/5] chore: bump ipfs version --- ipfshttpclient/http_common.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ipfshttpclient/http_common.py b/ipfshttpclient/http_common.py index c3abb9da..fe2c45b4 100644 --- a/ipfshttpclient/http_common.py +++ b/ipfshttpclient/http_common.py @@ -318,7 +318,6 @@ def multiaddr_to_url_data(addr: addr_t, base: str # type: ignore[no-any-unimpor fragment = "" ).geturl() - print(base_url) return base_url, uds_path, family, host_numeric @@ -580,7 +579,9 @@ def request( """ method = "POST" parser = encoding.get_encoding(decoder) - + + print(path) + print(headers) closables, res = self._request( method, path, map_args_to_params(args, opts, offline=offline), auth=auth, data=data, headers=headers, timeout=timeout,