PKEN6ipfsapi/__init__.py"""Python IPFS API client library""" from __future__ import absolute_import import warnings warnings.warn( "The `ipfsapi` library is deprecated and will stop receiving updates on " "the 31.12.2019! If you are on Python 3.5+ please enable and fix all " "Python deprecation warnings (CPython flag `-Wd`) and switch to the new " "`ipfshttpclient` library name. Python 2.7 and 3.4 will not be supported " "by the new library, so please upgrade.", FutureWarning, stacklevel=2 ) from .version import __version__ ########################### # Import stable API parts # ########################### from . import exceptions from .client import DEFAULT_HOST, DEFAULT_PORT, DEFAULT_BASE from .client import VERSION_MINIMUM, VERSION_MAXIMUM from .client import Client, assert_version, connect PKENS!<.<.ipfsapi/encoding.py# -*- encoding: utf-8 -*- """Defines encoding related classes. .. note:: The XML and ProtoBuf encoders are currently not functional. """ from __future__ import absolute_import import abc import codecs import io import json import pickle import six from . import exceptions class Encoding(object): """Abstract base for a data parser/encoder interface. """ __metaclass__ = abc.ABCMeta @abc.abstractmethod def parse_partial(self, raw): """Parses the given data and yields all complete data sets that can be built from this. Raises ------ ~ipfsapi.exceptions.DecodingError Parameters ---------- raw : bytes Data to be parsed Returns ------- generator """ def parse_finalize(self): """Finalizes parsing based on remaining buffered data and yields the remaining data sets. Raises ------ ~ipfsapi.exceptions.DecodingError Returns ------- generator """ return () def parse(self, raw): """Returns a Python object decoded from the bytes of this encoding. Raises ------ ~ipfsapi.exceptions.DecodingError Parameters ---------- raw : bytes Data to be parsed Returns ------- object """ results = list(self.parse_partial(raw)) results.extend(self.parse_finalize()) return results[0] if len(results) == 1 else results @abc.abstractmethod def encode(self, obj): """Serialize a raw object into corresponding encoding. Raises ------ ~ipfsapi.exceptions.EncodingError Parameters ---------- obj : object Object to be encoded """ class Dummy(Encoding): """Dummy parser/encoder that does nothing. """ name = "none" def parse_partial(self, raw): """Yields the data passed into this method. Parameters ---------- raw : bytes Any kind of data Returns ------- generator """ yield raw def encode(self, obj): """Returns the bytes representation of the data passed into this function. Parameters ---------- obj : object Any Python object Returns ------- bytes """ return six.b(str(obj)) class Json(Encoding): """JSON parser/encoder that handles concatenated JSON. """ name = 'json' def __init__(self): self._buffer = [] self._decoder1 = codecs.getincrementaldecoder('utf-8')() self._decoder2 = json.JSONDecoder() self._lasterror = None def parse_partial(self, data): """Incrementally decodes JSON data sets into Python objects. Raises ------ ~ipfsapi.exceptions.DecodingError Returns ------- generator """ try: # Python 3 requires all JSON data to be a text string lines = self._decoder1.decode(data, False).split("\n") # Add first input line to last buffer line, if applicable, to # handle cases where the JSON string has been chopped in half # at the network level due to streaming if len(self._buffer) > 0 and self._buffer[-1] is not None: self._buffer[-1] += lines[0] self._buffer.extend(lines[1:]) else: self._buffer.extend(lines) except UnicodeDecodeError as error: raise exceptions.DecodingError('json', error) # Process data buffer index = 0 try: # Process each line as separate buffer #PERF: This way the `.lstrip()` call becomes almost always a NOP # even if it does return a different string it will only # have to allocate a new buffer for the currently processed # line. while index < len(self._buffer): while self._buffer[index]: # Make sure buffer does not start with whitespace #PERF: `.lstrip()` does not reallocate if the string does # not actually start with whitespace. self._buffer[index] = self._buffer[index].lstrip() # Handle case where the remainder of the line contained # only whitespace if not self._buffer[index]: self._buffer[index] = None continue # Try decoding the partial data buffer and return results # from this data = self._buffer[index] for index2 in range(index, len(self._buffer)): # If decoding doesn't succeed with the currently # selected buffer (very unlikely with our current # class of input data) then retry with appending # any other pending pieces of input data # This will happen with JSON data that contains # arbitrary new-lines: "{1:\n2,\n3:4}" if index2 > index: data += "\n" + self._buffer[index2] try: (obj, offset) = self._decoder2.raw_decode(data) except ValueError: # Treat error as fatal if we have already added # the final buffer to the input if (index2 + 1) == len(self._buffer): raise else: index = index2 break # Decoding succeeded – yield result and shorten buffer yield obj if offset < len(self._buffer[index]): self._buffer[index] = self._buffer[index][offset:] else: self._buffer[index] = None index += 1 except ValueError as error: # It is unfortunately not possible to reliably detect whether # parsing ended because of an error *within* the JSON string, or # an unexpected *end* of the JSON string. # We therefor have to assume that any error that occurs here # *might* be related to the JSON parser hitting EOF and therefor # have to postpone error reporting until `parse_finalize` is # called. self._lasterror = error finally: # Remove all processed buffers del self._buffer[0:index] def parse_finalize(self): """Raises errors for incomplete buffered data that could not be parsed because the end of the input data has been reached. Raises ------ ~ipfsapi.exceptions.DecodingError Returns ------- tuple : Always empty """ try: try: # Raise exception for remaining bytes in bytes decoder self._decoder1.decode(b'', True) except UnicodeDecodeError as error: raise exceptions.DecodingError('json', error) # Late raise errors that looked like they could have been fixed if # the caller had provided more data if self._buffer: raise exceptions.DecodingError('json', self._lasterror) finally: # Reset state self._buffer = [] self._lasterror = None self._decoder1.reset() return () def encode(self, obj): """Returns ``obj`` serialized as JSON formatted bytes. Raises ------ ~ipfsapi.exceptions.EncodingError Parameters ---------- obj : str | list | dict | int JSON serializable Python object Returns ------- bytes """ try: result = json.dumps(obj, sort_keys=True, indent=None, separators=(',', ':')) if isinstance(result, six.text_type): return result.encode("utf-8") else: return result except (UnicodeEncodeError, TypeError) as error: raise exceptions.EncodingError('json', error) class Pickle(Encoding): """Python object parser/encoder using `pickle`. """ name = 'pickle' def __init__(self): self._buffer = io.BytesIO() def parse_partial(self, raw): """Buffers the given data so that the it can be passed to `pickle` in one go. This does not actually process the data in smaller chunks, but merely buffers it until `parse_finalize` is called! This is mostly because the standard-library module expects the entire data to be available up front, which is currently always the case for our code anyways. Parameters ---------- raw : bytes Data to be buffered Returns ------- tuple : An empty tuple """ self._buffer.write(raw) return () def parse_finalize(self): """Parses the buffered data and yields the result. Raises ------ ~ipfsapi.exceptions.DecodingError Returns ------- generator """ try: self._buffer.seek(0, 0) yield pickle.load(self._buffer) except pickle.UnpicklingError as error: raise exceptions.DecodingError('pickle', error) def parse(self, raw): r"""Returns a Python object decoded from a pickle byte stream. .. code-block:: python >>> p = Pickle() >>> p.parse(b'(lp0\nI1\naI2\naI3\naI01\naF4.5\naNaF6000.0\na.') [1, 2, 3, True, 4.5, None, 6000.0] Raises ------ ~ipfsapi.exceptions.DecodingError Parameters ---------- raw : bytes Pickle data bytes Returns ------- object """ return Encoding.parse(self, raw) def encode(self, obj): """Returns ``obj`` serialized as a pickle binary string. Raises ------ ~ipfsapi.exceptions.EncodingError Parameters ---------- obj : object Serializable Python object Returns ------- bytes """ try: return pickle.dumps(obj) except pickle.PicklingError as error: raise exceptions.EncodingError('pickle', error) class Protobuf(Encoding): """Protobuf parser/encoder that handles protobuf.""" name = 'protobuf' class Xml(Encoding): """XML parser/encoder that handles XML.""" name = 'xml' # encodings supported by the IPFS api (default is JSON) __encodings = { Dummy.name: Dummy, Json.name: Json, Pickle.name: Pickle, Protobuf.name: Protobuf, Xml.name: Xml } def get_encoding(name): """ Returns an Encoder object for the named encoding Raises ------ ~ipfsapi.exceptions.EncoderMissingError Parameters ---------- name : str Encoding name. Supported options: * ``"none"`` * ``"json"`` * ``"pickle"`` * ``"protobuf"`` * ``"xml"`` """ try: return __encodings[name.lower()]() except KeyError: raise exceptions.EncoderMissingError(name) PKENwFipfsapi/exceptions.py# -*- coding: utf-8 -*- """ The class hierachy for exceptions is:: Error +-- VersionMismatch +-- EncoderError | +-- EncoderMissingError | +-- EncodingError | +-- DecodingError +-- CommunicationError +-- ProtocolError +-- StatusError +-- ErrorResponse +-- ConnectionError +-- TimeoutError """ # Delegate list of exceptions to `ipfshttpclient` from ipfshttpclient.exceptions import * __all__ = [ "Error", "VersionMismatch", "EncoderError", "EncoderMissingError", "EncodingError", "DecodingError", "CommunicationError", "ProtocolError", "StatusError", "ErrorResponse", "ConnectionError", "TimeoutError" ]PKmNY$$ipfsapi/version.py# _Versioning scheme:_ # The major and minor version of each release correspond to the supported # IPFS daemon version. The revision number will be updated whenever we make # a new release for the `py-ipfs-api` client for that daemon version. # # Example: The first client version to support the `0.4.x`-series of the IPFS # HTTP API will have version `0.4.0`, the second version will have version # `0.4.1` and so on. When IPFS `0.5.0` is released, the first client version # to support it will also be released as `0.5.0`. __version__ = "0.4.4" PKmNO**ipfsapi/client/__init__.py# -*- coding: utf-8 -*- """IPFS API Bindings for Python. Classes: * Client – a TCP client for interacting with an IPFS daemon """ from __future__ import absolute_import import functools import inspect import os import re import warnings try: #PY3 import urllib.parse except ImportError: #PY2 class urllib: import urlparse as parse import ipfshttpclient import netaddr DEFAULT_HOST = str(os.environ.get("PY_IPFSAPI_DEFAULT_HOST", 'localhost')) DEFAULT_PORT = int(os.environ.get("PY_IPFSAPI_DEFAULT_PORT", 5001)) DEFAULT_BASE = str(os.environ.get("PY_IPFSAPI_DEFAULT_BASE", 'api/v0')) VERSION_MINIMUM = "0.4.3" VERSION_MAXIMUM = "0.5.0" from .. import exceptions, encoding from . import base def assert_version(version, minimum=VERSION_MINIMUM, maximum=VERSION_MAXIMUM): """Make sure that the given daemon version is supported by this client version. Raises ------ ~ipfsapi.exceptions.VersionMismatch Parameters ---------- version : str The version of an IPFS daemon. minimum : str The minimal IPFS version to allow. maximum : str The maximum IPFS version to allow. """ # Convert version strings to integer tuples version = list(map(int, version.split('-', 1)[0].split('.'))) minimum = list(map(int, minimum.split('-', 1)[0].split('.'))) maximum = list(map(int, maximum.split('-', 1)[0].split('.'))) if minimum > version or version >= maximum: raise exceptions.VersionMismatch(version, minimum, maximum) def connect(host=DEFAULT_HOST, port=DEFAULT_PORT, base=DEFAULT_BASE, chunk_size=4096, **defaults): """Create a new :class:`~ipfsapi.Client` instance and connect to the daemon to validate that its version is supported. Raises ------ ~ipfsapi.exceptions.VersionMismatch ~ipfsapi.exceptions.ErrorResponse ~ipfsapi.exceptions.ConnectionError ~ipfsapi.exceptions.ProtocolError ~ipfsapi.exceptions.StatusError ~ipfsapi.exceptions.TimeoutError All parameters are identical to those passed to the constructor of the :class:`~ipfsapi.Client` class. Returns ------- ~ipfsapi.Client """ # Create client instance client = Client(host, port, base, chunk_size, **defaults) # Query version number from daemon and validate it assert_version(client.version()['Version']) return client class Client(ipfshttpclient.Client): # Aliases for previous method names key_gen = base.DeprecatedMethodProperty("key", "gen") key_list = base.DeprecatedMethodProperty("key", "list") key_rename = base.DeprecatedMethodProperty("key", "rename") key_rm = base.DeprecatedMethodProperty("key", "rm") block_get = base.DeprecatedMethodProperty("block", "get") block_put = base.DeprecatedMethodProperty("block", "put") block_stat = base.DeprecatedMethodProperty("block", "stat") files_cp = base.DeprecatedMethodProperty("files", "cp") files_ls = base.DeprecatedMethodProperty("files", "ls") files_mkdir = base.DeprecatedMethodProperty("files", "mkdir") files_stat = base.DeprecatedMethodProperty("files", "stat") files_rm = base.DeprecatedMethodProperty("files", "rm") files_read = base.DeprecatedMethodProperty("files", "read") files_write = base.DeprecatedMethodProperty("files", "write") files_mv = base.DeprecatedMethodProperty("files", "mv") object_data = base.DeprecatedMethodProperty("object", "data") object_get = base.DeprecatedMethodProperty("object", "get") object_links = base.DeprecatedMethodProperty("object", "links") object_new = base.DeprecatedMethodProperty("object", "new") object_put = base.DeprecatedMethodProperty("object", "put") object_stat = base.DeprecatedMethodProperty("object", "stat") object_patch_add_link = base.DeprecatedMethodProperty("object", "patch", "add_link") object_patch_append_data = base.DeprecatedMethodProperty("object", "patch", "append_data") object_patch_rm_link = base.DeprecatedMethodProperty("object", "patch", "rm_link") object_patch_set_data = base.DeprecatedMethodProperty("object", "patch", "set_data") pin_add = base.DeprecatedMethodProperty("pin", "add") pin_ls = base.DeprecatedMethodProperty("pin", "ls") pin_rm = base.DeprecatedMethodProperty("pin", "rm") pin_update = base.DeprecatedMethodProperty("pin", "update") pin_verify = base.DeprecatedMethodProperty("pin", "verify") refs = base.DeprecatedMethodProperty("unstable", "refs") refs_local = base.DeprecatedMethodProperty("unstable", "refs", "local") bootstrap_add = base.DeprecatedMethodProperty("bootstrap", "add") bootstrap_list = base.DeprecatedMethodProperty("bootstrap", "list") bootstrap_rm = base.DeprecatedMethodProperty("bootstrap", "rm") bitswap_stat = base.DeprecatedMethodProperty("bitswap", "stat") bitswap_wantlist = base.DeprecatedMethodProperty("bitswap", "wantlist") dht_findpeer = base.DeprecatedMethodProperty("dht", "findpeer") dht_findprovs = base.DeprecatedMethodProperty("dht", "findproves") dht_get = base.DeprecatedMethodProperty("dht", "get") dht_put = base.DeprecatedMethodProperty("dht", "put") dht_query = base.DeprecatedMethodProperty("dht", "query") pubsub_ls = base.DeprecatedMethodProperty("pubsub", "ls") pubsub_peers = base.DeprecatedMethodProperty("pubsub", "peers") pubsub_pub = base.DeprecatedMethodProperty("pubsub", "publish") pubsub_sub = base.DeprecatedMethodProperty("pubsub", "subscribe") swarm_addrs = base.DeprecatedMethodProperty("swarm", "addrs") swarm_connect = base.DeprecatedMethodProperty("swarm", "connect") swarm_disconnect = base.DeprecatedMethodProperty("swarm", "disconnect") swarm_peers = base.DeprecatedMethodProperty("swarm", "peers") swarm_filters_add = base.DeprecatedMethodProperty("swarm", "filters", "add") swarm_filters_rm = base.DeprecatedMethodProperty("swarm", "filters", "rm") name_publish = base.DeprecatedMethodProperty("name", "publish") name_resolve = base.DeprecatedMethodProperty("name", "resolve") repo_gc = base.DeprecatedMethodProperty("repo", "gc") repo_stat = base.DeprecatedMethodProperty("repo", "stat") config = base.DeprecatedMethodProperty("config", "set") config_show = base.DeprecatedMethodProperty("config", "get") config_replace = base.DeprecatedMethodProperty("config", "replace") log_level = base.DeprecatedMethodProperty("unstable", "log", "level") log_ls = base.DeprecatedMethodProperty("unstable", "log", "ls") log_tail = base.DeprecatedMethodProperty("unstable", "log", "tail") shutdown = base.DeprecatedMethodProperty("stop") def __init__(self, host=DEFAULT_HOST, port=DEFAULT_PORT, base=DEFAULT_BASE, chunk_size=4096, **defaults): # Assemble and parse the URL these parameters are supposed to represent if not re.match('^https?://', host.lower()): host = 'http://' + host url = urllib.parse.urlsplit('%s:%s/%s' % (host, port, base)) # Detect whether `host` is a (DNS) hostname or an IP address host_type = "dns" try: host_type = "ip{0}".format(netaddr.IPAddress(url.hostname).version) except netaddr.AddrFormatError: pass addr = "/{0}/{1}/tcp/{2}/{3}".format(host_type, url.hostname, url.port, url.scheme) super(Client, self).__init__(addr, base, chunk_size, timeout=None, **defaults) def __getattribute__(self, name): value = super(Client, self).__getattribute__(name) if inspect.ismethod(value): @functools.wraps(value) def wrapper(*args, **kwargs): # Rewrite changed named parameter names if "multihash" in kwargs: kwargs["cid"] = kwargs.pop("multihash") if "multihashes" in kwargs: kwargs["cids"] = kwargs.pop("multihashes") try: return value(*args, **kwargs) # Partial error responses used to incorrectly just return # the parts that were successfully received followed by the # (undetected) error frame except exceptions.PartialErrorResponse as error: return error.partial + [{"Type": "error", "Message": str(error)}] return wrapper return value def add(self, files, recursive=False, pattern='**', *args, **kwargs): # Signature changed to: add(self, *files, recursive=False, pattern='**', **kwargs) if not isinstance(files, (list, tuple)): files = (files,) return super(Client, self).add(*files, recursive=recursive, pattern=pattern, **kwargs) # Dropped API methods def bitswap_unwant(self, key, **kwargs): """Deprecated method: Do not use anymore""" warnings.warn( "IPFS API function “bitswap_unwant” support has been dropped " "from go-ipfs", FutureWarning ) args = (key,) return self._client.request('/bitswap/unwant', args, **kwargs) def file_ls(self, multihash, **kwargs): """Deprecated method: Replace usages with the similar “client.ls”""" warnings.warn( "IPFS API function “file_ls” support is highly deprecated and will " "be removed soon from go-ipfs, use plain “ls” instead", FutureWarning ) args = (multihash,) return self._client.request('/file/ls', args, decoder='json', **kwargs) # Dropped utility methods def add_pyobj(self, py_obj, **kwargs): """Adds a picklable Python object as a file to IPFS. .. deprecated:: 0.4.2 The ``*_pyobj`` APIs allow for arbitrary code execution if abused. Either switch to :meth:`~ipfsapi.Client.add_json` or use ``client.add_bytes(pickle.dumps(py_obj))`` instead. Please see :meth:`~ipfsapi.Client.get_pyobj` for the **security risks** of using these methods! .. code-block:: python >>> c.add_pyobj([0, 1.0, 2j, '3', 4e5]) 'QmWgXZSUTNNDD8LdkdJ8UXSn55KfFnNvTP1r7SyaQd74Ji' Parameters ---------- py_obj : object A picklable Python object Returns ------- str : Hash of the added IPFS object """ warnings.warn("Using `*_pyobj` on untrusted data is a security risk", DeprecationWarning) return self.add_bytes(encoding.Pickle().encode(py_obj), **kwargs) def get_pyobj(self, multihash, **kwargs): """Loads a pickled Python object from IPFS. .. deprecated:: 0.4.2 The ``*_pyobj`` APIs allow for arbitrary code execution if abused. Either switch to :meth:`~ipfsapi.Client.get_json` or use ``pickle.loads(client.cat(multihash))`` instead. .. caution:: The pickle module is not intended to be secure against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source. Please **read** `this article `_ to understand the security risks of using this method! .. code-block:: python >>> c.get_pyobj('QmWgXZSUTNNDD8LdkdJ8UXSn55KfFnNvTP1r7SyaQd74Ji') [0, 1.0, 2j, '3', 400000.0] Parameters ---------- multihash : str Multihash of the IPFS object to load Returns ------- object : Deserialized IPFS Python object """ warnings.warn("Using `*_pyobj` on untrusted data is a security risk", DeprecationWarning) return encoding.Pickle().parse(self.cat(multihash, **kwargs)) PKENZipfsapi/client/base.py# -*- coding: utf-8 -*- from __future__ import absolute_import import warnings from . import DEFAULT_HOST, DEFAULT_PORT, DEFAULT_BASE class DeprecatedMethodProperty(object): def __init__(self, *path, **kwargs): #PY2: No support for kw-only parameters after glob parameters prefix = kwargs.pop("prefix", []) strip = kwargs.pop("strip", 0) assert not kwargs self.props = path self.path = tuple(prefix) + (path[:-strip] if strip > 0 else tuple(path)) self.warned = False self.__help__ = "Deprecated method: Please use “client.{0}” instead".format( ".".join(self.path) ) def __get__(self, obj, type=None): if not self.warned: message = "IPFS API function “{0}” has been renamed to “{1}”".format( "_".join(self.path), ".".join(self.path) ) warnings.warn(message, FutureWarning) self.warned = True for name in self.props: print(name, obj) obj = getattr(obj, name) return obj PKNr$IGSTTipfsapi-0.4.4.dist-info/LICENSEThe MIT License (MIT) Copyright (c) 2015 Andrew Stocker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!HMuSaipfsapi-0.4.4.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,szd&Y)r$[)T&UD"PK!HϚ8 ipfsapi-0.4.4.dist-info/METADATAioJE?ͮ6"@ rhhۍ1Jk/٪nLfv":*ZLP ߱{QxBFIkӀ/q{zk3^4YJ\u{ҹ"P߳.1uXqjv܌W:>u)H,䀥u'هvV(qp%@8g%3?UR59!`46tP?!ZR/a\W;!NoƎQ>Kٳ=wZq;b$WPIiyMhjUr<^#=AE %::{(JꀔlG3 GQP)>aGp=[]M+~{.X2!`6녌%^~ ۹3.ώcE ܻ"8N9)mtMh*ҤQMʿz8E_=> q5p2wm?bp(lm7v18e-5 >aoM0N!n2!7Ku 곰︳p:]Ф4&)?6Jm|h{~knګιD675fдߞlIˢ (n)3;+|*t>KiŠh2^b]9LkyWr(,e0rhk4%sAC&0@}'2@"28MO #?N=snf$K[5UpQ~{LT~TUjLdr nGVͩp8nwn^U+n}!9Nl;a4ǫ 8nj 3s!1q I}FtΈ1@$a!$fHaxqc`pp^dRIJP* A>@ZkPX%d 6/E;A:>y9c) Hpx:wGdT٭Wj8\:: !fD Z[%D"ϗ\\f1@SKȃ FVH ȕXC N/J'VR R:pSPCm*IZ[U \ttz+-S l~5BW. 0dȮ@ Df奤xΠ8IY:,@ry/$/d^\uH4" +;)H2<÷KzY =_)/Di \(M|;ļ.saGBKkPg6V.P~!J %Х .e(AWڧO .ayCܶVT>"s(N(#S1 n!~/\R>.M0Xi^}қ7s2>3=TorG5IaR #ԟ8|JG:d􏰠$Wq~ ( mHJss9@g` ;6@y${#Nj5ހ5G~Y^JWy̓@\kYGs#/*v᪦tLDdG wP;U+^^-\AvImU*SPTJ<0SٞEϷ+pjHb~k[{s8]]R^7Z˃l֭BFVFWJ !tl0MMi|JwVOsooП7z`9G&k׿sOԑ(Q 0{LD Q(H}<ј.it2ogVj/ 8 +=:n*~|/d )%/g{8}XL{|Poҵny}wqӃNikmښ &h/Q4;ǽpQ 3ςyGtʾyi?I9Urwy"=М,fc~{d08sNqg䎿D;9cATo%'Gl|ܟ?.E˦ 851u#{M;7~UWtoR<('[1 ި{lYu6{qN+;Nq{iGV\k>xɘX@{q\Y&WNSiB#En-Gۇ׈5kɢv0;Ktxu+w)lbrP9M{IPWIq#LU$ ::Wa~w Z[FӸÛC_j Y.oiFG;,54C{}/dy.Y2#{ Ep+x9>0|,Ÿ*"w)seUlۣz<\XإQ@̾ ӷd+V*JՓr)ޏB|hnɓ66ᗙFA,kU=CSv]YY"a^@ f!5=hdۂի^MuO!O`t  غ bK5lE: ʑ+ē0%N 4GR[O '~wWgEPLu{n 2+^Q֪)fue2\^`AjƐ/jyǕA^dFŠhT )S / bs6jd# 6 =OCt.O&g'tCBI9O>曣\\ 4)K1rv=@6TjgB }0w|0UTiDmy1n= *8Xc =cNsotE )}J<--.>E-^A&92Pr^i,!#,ư2/<}3b% b!g*(2/S , kpKGjj+o#Wկol67csO9RH0[d$:d<K9"ibe7ț& pVB%(@BS/@ЫbmZi,Ut7$(za0Ep^>ˆl?uT ~\ʒ4ȗHX%YT!MK\o'9gz0rPͤz}n"XK-$x-T0ѳj ^{j^U^VRT:rPK!HN'ipfsapi-0.4.4.dist-info/RECORDuK0},D(7oF%DQbZ:N" 2˲OstKΆ+-P57m5N ̇PN{&85GA\%5"`݉wFABokƇ~Ȝ(j 0bƕGJQ(/8"%T2vpmQ,cu,Ӊ&-Ժ0Xr49ocaQ 'tΜ$牜m9]~Oq%Gjc뛥Ss~5 RiU7H=QLpo5'^v8FO#vgyfjzbd^IG8z7ٻwY`f^rEIWNÑ5">PKEN6ipfsapi/__init__.pyPKENS!<.<.Hipfsapi/encoding.pyPKENwF1ipfsapi/exceptions.pyPKmNY$$4ipfsapi/version.pyPKmNO**7ipfsapi/client/__init__.pyPKENZaipfsapi/client/base.pyPKNr$IGSTTeipfsapi-0.4.4.dist-info/LICENSEPK!HMuSasjipfsapi-0.4.4.dist-info/WHEELPK!HϚ8 kipfsapi-0.4.4.dist-info/METADATAPK!HN']yipfsapi-0.4.4.dist-info/RECORDPK y{