{
"info": {
"author": "Selim Naji, Adam Radomski and Marko Ristin",
"author_email": "selim.naji@parquery.com, adam.radomski@parquery.com, marko.ristin@gmail.com",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.5"
],
"description": "gs-wrap\n=======\n\n.. image:: https://badges.frapsoft.com/os/mit/mit.png?v=103\n :target: https://opensource.org/licenses/mit-license.php\n :alt: MIT License\n\n.. image:: https://badge.fury.io/py/gs-wrap.svg\n :target: https://badge.fury.io/py/gs-wrap\n :alt: PyPI - version\n\n.. image:: https://img.shields.io/pypi/pyversions/gs-wrap.svg\n :alt: PyPI - Python Version\n\n.. image:: https://readthedocs.org/projects/gs-wrap/badge/?version=latest\n :target: https://gs-wrap.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n``gs-wrap`` wraps `Google Cloud Storage API `_\nfor multi-threaded data manipulation including copying, reading, writing and\nhashing.\n\nOriginally, we used our `gsutilwrap `_,\na thin wrapper around ``gsutil`` command-line interface, to simplify\nthe deployment and backup tasks related to Google Cloud Storage.\nHowever, ``gsutilwrap`` was prohibitively slow at copying many objects into\ndifferent destinations.\n\nTherefore we developed ``gs-wrap`` to accelerate these operations while keeping\nit equally fast or faster than ``gsutilwrap`` at other operations.\n\nWhile the `google-cloud-storage\n`_\nlibrary provided by Google offers sophisticated features and good performance,\nits use cases and behavior differ from ``gsutil``. \nSince we wanted the simplicity and usage patterns of ``gsutil``, we created\n``gs-wrap``, which wraps ``google-cloud-storage`` in its core and with its\ninterface set to behave like ``gsutil``.\n\n``gs-wrap`` is not the first Python library wrapping Google Cloud Storage API.\n`cloud-storage-client `_\ntakes a similar approach and aims to manage both Amazon's S3 and Google Cloud\nStorage. Parts of it are also based on ``google-cloud-storage``, however the\nlibrary's behaviour differs from ``gsutil`` which made it hard to use as an\nin-place replacement for ``gsutilwrap``. Additionally, the library did not\noffer all needed operations, for example copying to many destinations, reading,\nwriting and hashing.\n\nThe main strength of ``gs-wrap`` is the ability to copy many objects from many\ndifferent paths to multiple destinations, while still mimicking ``gsutil``\ninterface. A direct comparison of performance between ``gs-wrap`` and\n``gsutilwrap`` can be found in the `section Benchmarks\n`_.\n\nUsage\n=====\nYou need to create a Google Cloud Storage bucket to use this client library.\nFollow along with the `official Google Cloud Storage documentation\n`_ to\nlearn how to create a bucket.\n\nConnect to your Google Cloud Storage bucket\n-------------------------------------------\n\nFirst a client for interacting with the Google Cloud Storage API needs to be\ncreated. This one uses internally the `Storage Client\n`_\nfrom ``google-cloud-storage``.\n\nOne parameter can be passed to the client:\n\nThe Google Cloud Storage **project** which the client acts on behalf of. It will\nbe passed when creating the internal client. If not passed, falls back to the\ndefault inferred from the locally authenticated `Google Cloud SDK\n`_ environment. Each project needs a separate\nclient. Operations between two different projects are not supported.\n\n.. code-block:: python\n\n import gswrap\n\n client = gswrap.Client() # project is optional\n\nList objects in your bucket\n---------------------------\n\n.. warning::\n\n Wildcards (\\*, \\*\\*, \\?, \\[chars\\], \\[char range\\]) are not supported by\n Google Cloud Storage API and neither by ``gs-wrap`` at the moment\n [2019-01-16]. Reasons are that the ``gsutil`` with wildcards can hardly be\n equivalently reconstructed and that the toplevel search is extremely\n inefficient. More information about ``gsutil`` wildcards can be found here:\n ``_\n\n.. code-block:: python\n\n client.ls(gcs_url=\"gs://your-bucket/your-dir\", recursive=False)\n # gs://your-bucket/your-dir/your-subdir1/\n # gs://your-bucket/your-dir/your-subdir2/\n # gs://your-bucket/your-dir/file1\n\n client.ls(gcs_url=\"gs://your-bucket/your-dir\", recursive=True)\n # gs://your-bucket/your-dir/your-subdir1/file1\n # gs://your-bucket/your-dir/your-subdir1/file2\n # gs://your-bucket/your-dir/your-subdir2/file1\n # gs://your-bucket/your-dir/file1\n\nCopy objects within Google Cloud Storage\n----------------------------------------\n\nIf both the source and destination URL are cloud URLs from the same provider,\n``gsutil`` copies data \"in the cloud\" (i.e. without downloading to and\nuploading from the machine where you run ``gs-wrap``).\n\n.. note::\n client.cp() runs single-threaded by default. When multi-threading is\n activated, the maximum number of workers is the number of processors on the\n machine, multiplied by 5. This is the multi-threading default of the\n `ThreadPoolExecuter from the concurrent.futures library\n `_.\n\nCopy file within Google Cloud Storage\n-------------------------------------\n\n.. code-block:: python\n\n # your-bucket before:\n # gs://your-bucket/file1\n client.cp(src=\"gs://your-bucket/file1\",\n dst=\"gs://your-bucket/your-dir/\",\n recursive=True)\n # your-bucket after:\n # gs://your-bucket/file1\n # gs://your-bucket/your-dir/file1\n\n # your-backup-bucket before:\n # \"empty\"\n client.cp(src=\"gs://your-bucket/file1\",\n dst=\"gs://your-backup-bucket/backup-file1\",\n recursive=False)\n # your-backup-bucket after:\n # gs://your-backup-bucket/backup-file1\n\nCopy directory within Google Cloud Storage\n------------------------------------------\n\n.. code-block:: python\n\n # your-bucket before:\n # \"empty\"\n client.cp(src=\"gs://your-bucket/some-dir/\",\n dst=\"gs://your-bucket/another-dir/\", recursive=False)\n # google.api_core.exceptions.GoogleAPIError: No URLs matched\n\n # your-bucket before:\n # gs://your-bucket/some-dir/file1\n # gs://your-bucket/some-dir/dir1/file11\n\n # Destination URL without slash\n client.cp(src=\"gs://your-bucket/some-dir/\",\n dst=\"gs://your-bucket/another-dir\", recursive=True)\n # your-bucket after:\n # gs://your-bucket/another-dir/file1\n # gs://your-bucket/another-dir/dir1/file11\n\n # Destination URL with slash\n client.cp(src=\"gs://your-bucket/some-dir/\",\n dst=\"gs://your-bucket/another-dir/\", recursive=True)\n # your-bucket after:\n # gs://your-bucket/another-dir/some-dir/file1\n # gs://your-bucket/another-dir/some-dir/dir1/file11\n\n # Choose to copy multi-threaded. (default=False)\n client.cp(src=\"gs://your-bucket/some-dir/\",\n dst=\"gs://your-bucket/another-dir\", recursive=True, multithreaded=True)\n # your-bucket after:\n # gs://your-bucket/another-dir/file1\n # gs://your-bucket/another-dir/dir1/file11\n\nUpload objects to Google Cloud Storage\n--------------------------------------\n\n.. note::\n\n **recursive** causes directories, buckets, and bucket subdirectories to be\n copied recursively. If you upload from local disk to Google Cloud Storage\n and set recursive to ``False``, ``gs-wrap``\n will raise an exception and inform you that no URL matched.\n This mimicks the behaviour of ``gsutil`` when no wildcards are used.\n\n.. code-block:: python\n\n # Your local directory:\n # /home/user/storage/file1\n # /home/user/storage/file2\n # your-bucket before:\n # \"empty\"\n\n client.cp(src=\"/home/user/storage/\",\n dst=\"gs://your-bucket/local/\",\n recursive=True)\n # your-bucket after:\n # gs://your-bucket/local/storage/file1\n # gs://your-bucket/local/storage/file2\n\nDownload objects from Google Cloud Storage\n------------------------------------------\n\n.. note::\n\n **recursive** causes directories, buckets, and bucket subdirectories to be\n copied recursively. If you upload from local disk to Google Cloud Storage\n and set recursive to ``False``, ``gs-wrap``\n will raise an exception and inform you that no URL matched.\n This mimicks the behaviour of ``gsutil`` when no wildcards are used.\n\n.. code-block:: python\n\n import os\n # Current your-bucket:\n # gs://your-bucket/file1\n\n client.cp(\n src=\"gs://your-bucket/file1\", \n dst=\"/home/user/storage/file1\")\n\n # Your local directory:\n # /home/user/storage/file1\n\nCopy, download and upload with parameters\n-----------------------------------------\n\n.. note::\n\n All parameters can be used for any kind of ``cp`` operation.\n\n.. code-block:: python\n\n # Parameter: no_clobber example:\n import os\n\n # File content before: \"hello\"\n os.stat(\"/home/user/storage/file1\").st_mtime # 1537947563\n\n client.cp(\n src=\"gs://your-bucket/file1\",\n dst=\"/home/user/storage/file1\",\n no_clobber=True)\n\n # no_clobber option stops from overwriting.\n # File content after: \"hello\"\n os.stat(\"/home/user/storage/file1\").st_mtime # 1537947563\n\n client.cp(\n src=\"gs://your-bucket/file1\",\n dst=\"/home/user/storage/file1\",\n no_clobber=False)\n\n # File content after: \"hello world\"\n os.stat(\"/home/user/storage/file1\").st_mtime # 1540889799\n\n # Parameter: recursive and multi-threaded example:\n # Your local directory:\n # /home/user/storage/file1\n # ...\n # /home/user/storage/file1000\n # your-bucket before:\n # \"empty\"\n\n # Execute normal recursive copy in multiple threads.\n client.cp(src=\"/home/user/storage/\",\n dst=\"gs://your-bucket/local/\",\n recursive=True, multithreaded=True)\n # your-bucket after:\n # gs://your-bucket/local/storage/file1\n # ...\n # gs://your-bucket/local/storage/file1000\n\n # Parameter: preserve_posix example:\n # Your file before:\n # /home/user/storage/file1\n # e.g. file_mtime: 1547653413 equivalent to 2019-01-16 16:43:33\n\n client.cp(src=\"/home/user/storage/file1\",\n dst=\"gs://your-backup-bucket/file1\",\n preserve_posix=False)\n # your-backup-bucket after:\n # gs://your-backup-bucket/file1 e.g. \"no metadata file_mtime\"\n\n # Preserve the POSIX attributes. POSIX attributes are the metadata of a file.\n client.cp(src=\"/home/user/storage/file1\",\n dst=\"gs://your-backup-bucket/file1\",\n preserve_posix=True)\n # your-backup-bucket after:\n # gs://your-backup-bucket/file1 e.g. file_mtime: 2019-01-16 16:43:33\n\nPerform multiple copy operations in one call\n--------------------------------------------\n\n.. code-block:: python\n\n sources_destinations = [\n # Copy on Google Cloud Storage\n ('gs://your-bucket/your-dir/file',\n 'gs://your-bucket/backup-dir/file'),\n \n # Copy from gcs to local\n ('gs://your-bucket/your-dir/file',\n pathlib.Path('/home/user/storage/backup-file')),\n \n # Copy from local to gcs\n (pathlib.Path('/home/user/storage/new-file'),\n 'gs://your-bucket/your-dir/new-file'),\n \n # Copy locally\n (pathlib.Path('/home/user/storage/file'),\n pathlib.Path('/home/user/storage/new-file'))]\n\n client.cp_many_to_many(srcs_dsts=sources_destinations)\n\nRemove files from Google Cloud Storage\n--------------------------------------\n\n.. code-block:: python\n\n # your-bucket before:\n # gs://your-bucket/file\n client.rm(url=\"gs://your-bucket/file\")\n # your-bucket after:\n # \"empty\"\n\n # your-bucket before:\n # gs://your-bucket/file1\n # gs://your-bucket/your-dir/file2\n # gs://your-bucket/your-dir/sub-dir/file3\n client.rm(url=\"gs://your-bucket/your-dir\", recursive=True)\n # your-bucket after:\n # gs://your-bucket/file1\n\nRead and write files in Google Cloud Storage\n--------------------------------------------\n\n.. code-block:: python\n\n client.write_text(url=\"gs://your-bucket/file\",\n text=\"Hello, I'm text\",\n encoding='utf-8')\n\n client.read_text(url=\"gs://your-bucket/file\", \n encoding='utf-8')\n # Hello I'm text\n\n client.write_bytes(url=\"gs://your-bucket/data\",\n data=\"I'm important data\".encode('utf-8'))\n\n data = client.read_bytes(url=\"gs://your-bucket/data\")\n data.decode('utf-8')\n # I'm important data\n\nCopy os.stat() of a file or metadata of a blob\n----------------------------------------------\n\n.. note::\n\n POSIX attributes include meta information about a file. When copying a file\n locally or copying a file within Google Cloud Storage, the POSIX attributes\n are always preserved. On the other hand, when downloading or uploading file\n to Google Cloud Storage, the POSIX attributes is only preserved when\n **preserve_posix** is set to True.\n\n.. code-block:: python\n\n file = pathlib.Path('/home/user/storage/file')\n file.touch()\n print(file.stat())\n # os.stat_result(st_mode=33204, st_ino=19022665, st_dev=64769, st_nlink=1,\n # st_uid=1000, st_gid=1000, st_size=0, st_atime=1544015997,\n # st_mtime=1544015997, st_ctime=1544015997)\n\n # Upload does not preserve POSIX attributes.\n client.cp(src=pathlib.Path('/home/user/storage/file'),\n dst=\"gs://your-bucket/file\")\n\n stats = client.stat(url=\"gs://your-bucket/file\")\n stats.creation_time # 2018-11-21 13:27:46.255000+00:00\n stats.update_time # 2018-11-21 13:27:46.255000+00:00\n stats.content_length # 1024 [bytes]\n stats.storage_class # REGIONAL\n stats.file_atime # None\n stats.file_mtime # None\n stats.posix_uid # None\n stats.posix_gid # None\n stats.posix_mode # None\n stats.md5 # b'1B2M2Y8AsgTpgAmY7PhCfg=='\n stats.crc32c # b'AAAAAA=='\n\n # Upload with preserve_posix also copy POSIX attributes to blob.\n # POSIX attributes are the metadata of a file.\n # It also works for downloading.\n\n client.cp(src=pathlib.Path('/home/user/storage/file'),\n dst=\"gs://your-bucket/file\", preserve_posix=True)\n\n stats = client.stat(url=\"gs://your-bucket/file\")\n stats.creation_time # 2018-11-21 13:27:46.255000+00:00\n stats.update_time # 2018-11-21 13:27:46.255000+00:00\n stats.content_length # 1024 [bytes]\n stats.storage_class # REGIONAL\n stats.file_atime # 2018-11-21 13:27:46\n stats.file_mtime # 2018-11-21 13:27:46\n stats.posix_uid # 1000\n stats.posix_gid # 1000\n stats.posix_mode # 777\n stats.md5 # b'1B2M2Y8AsgTpgAmY7PhCfg=='\n stats.crc32c # b'AAAAAA=='\n\nCheck correctness of copied file\n--------------------------------\n\n.. code-block:: python\n\n # Check modification time when copied with preserve_posix.\n client.same_modtime(path='/home/user/storage/file',\n url='gs://your-bucket/file')\n\n # Check md5 hash to ensure content equality.\n client.same_md5(path='/home/user/storage/file', url='gs://your-bucket/file')\n\n # Retrieve hex digests of MD5 checksums for multiple URLs.\n urls = ['gs://your-bucket/file1', 'gs://your-bucket/file2']\n client.md5_hexdigests(urls=urls, multithreaded=False)\n\nDocumentation\n=============\nThe documentation is available on `readthedocs\n`_.\n\nSetup\n=====\n\nIn order to use this library, you need to go through the following steps:\n\n1. `Select or create a Cloud Platform project. `_\n2. `Enable billing for your project. `_\n3. `Enable the Google Cloud Storage API. `_\n4. `Setup Authentication using the Google Cloud SDK. `_\n\nInstallation\n============\n\n* Install gs-wrap with pip:\n\n.. code-block:: bash\n\n pip3 install gs-wrap\n\n\nDevelopment\n===========\n\n* Check out the repository.\n\n* In the repository root, create the virtual environment:\n\n.. code-block:: bash\n\n python3 -m venv venv3\n\n* Activate the virtual environment:\n\n.. code-block:: bash\n\n source venv3/bin/activate\n\n* Install the development dependencies:\n\n.. code-block:: bash\n\n pip3 install -e .[dev]\n\nWe use tox for testing and packaging the distribution. Assuming that the virtual\nenvironment has been activated and the development dependencies have been\ninstalled, run:\n\n.. code-block:: bash\n\n tox\n\n\nPre-commit Checks\n-----------------\n\nWe provide a set of pre-commit checks that lint and check code for formatting.\n\nNamely, we use:\n\n* `yapf `_ to check the formatting.\n* The style of the docstrings is checked with `pydocstyle `_.\n* Static type analysis is performed with `mypy `_.\n* `isort `_ to sort your imports for you.\n* Various linter checks are done with `pylint `_.\n* Doctests are executed using the Python `doctest module `_.\n* `pyicontract-lint `_ lints contracts \n in Python code defined with `icontract library `_.\n* `twine `_ to check the README for invalid markup \n which prevents it from rendering correctly on PyPI.\n\nRun the pre-commit checks locally from an activated virtual environment with\ndevelopment dependencies:\n\n.. code-block:: bash\n\n ./precommit.py\n\n* The pre-commit script can also automatically format the code:\n\n.. code-block:: bash\n\n ./precommit.py --overwrite\n\nBenchmarks\n----------\n\nAssuming that the virtual environment has been activated, the development\ndependencies have been installed and the ``PYTHONPATH`` has been set to the\nproject directory, run the benchmarks with:\n\n.. code-block:: bash\n\n ./benchmark/main.py *NAME OF YOUR GCS BUCKET*\n\nHere are some of our benchmark results:\n\nBenchmark list 10000 files:\n\n+------------+--------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+--------+---------+\n| gswrap | 3.22 s | \\- |\n+------------+--------+---------+\n| gsutilwrap | 3.98 s | 1.24 x |\n+------------+--------+---------+\n\nBenchmark upload 10000 files:\n\n+------------+---------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+---------+---------+\n| gswrap | 45.12 s | \\- |\n+------------+---------+---------+\n| gsutilwrap | 34.85 s | 0.77 x |\n+------------+---------+---------+\n\nBenchmark upload-many-to-many 500 files:\n\n+------------+--------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+--------+---------+\n| gswrap | 2.14 s | \\- |\n+------------+--------+---------+\n| gsutilwrap | 65.2 s | 30.49 x |\n+------------+--------+---------+\n\nBenchmark download 10000 files:\n\n+------------+---------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+---------+---------+\n| gswrap | 43.92 s | \\- |\n+------------+---------+---------+\n| gsutilwrap | 43.01 s | 0.98 x |\n+------------+---------+---------+\n\nBenchmark download-many-to-many 500 files:\n\n+------------+---------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+---------+---------+\n| gswrap | 5.85 s | \\- |\n+------------+---------+---------+\n| gsutilwrap | 62.93 s | 10.76 x |\n+------------+---------+---------+\n\nBenchmark copy on remote 1000 files:\n\n+------------+--------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+--------+---------+\n| gswrap | 5.09 s | \\- |\n+------------+--------+---------+\n| gsutilwrap | 4.47 s | 0.88 x |\n+------------+--------+---------+\n\nBenchmark copy-many-to-many-on-remote 500 files:\n\n+------------+---------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+---------+---------+\n| gswrap | 6.55 s | \\- |\n+------------+---------+---------+\n| gsutilwrap | 62.76 s | 9.57 x |\n+------------+---------+---------+\n\nBenchmark remove 1000 files:\n\n+------------+--------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+--------+---------+\n| gswrap | 3.16 s | \\- |\n+------------+--------+---------+\n| gsutilwrap | 3.66 s | 1.16 x |\n+------------+--------+---------+\n\nBenchmark read 100 files:\n\n+------------+---------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+---------+---------+\n| gswrap | 16.56 s | \\- |\n+------------+---------+---------+\n| gsutilwrap | 64.73 s | 3.91 x |\n+------------+---------+---------+\n\nBenchmark write 30 files:\n\n+------------+---------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+---------+---------+\n| gswrap | 2.67 s | \\- |\n+------------+---------+---------+\n| gsutilwrap | 32.55 s | 12.17 x |\n+------------+---------+---------+\n\nBenchmark stat 100 files:\n\n+------------+---------+---------+\n| TESTED | TIME | SPEEDUP |\n+------------+---------+---------+\n| gswrap | 6.39 s | \\- |\n+------------+---------+---------+\n| gsutilwrap | 48.15 s | 7.53 x |\n+------------+---------+---------+\n\n\nAll results of our benchmarks can be found `here\n`_.\n\nVersioning\n==========\nWe follow `Semantic Versioning `_.\nThe version X.Y.Z indicates:\n\n* X is the major version (backward-incompatible),\n* Y is the minor version (backward-compatible), and\n* Z is the patch version (backward-compatible bug fix).",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/Parquery/gs-wrap",
"keywords": "gsutil google cloud storage wrap",
"license": "License :: OSI Approved :: MIT License",
"maintainer": "",
"maintainer_email": "",
"name": "gs-wrap",
"package_url": "https://pypi.org/project/gs-wrap/",
"platform": "",
"project_url": "https://pypi.org/project/gs-wrap/",
"project_urls": {
"Homepage": "https://github.com/Parquery/gs-wrap"
},
"release_url": "https://pypi.org/project/gs-wrap/1.0.5/",
"requires_dist": null,
"requires_python": "",
"summary": "gs-wrap wraps Google Cloud Storage API for multi-threaded data manipulations.",
"version": "1.0.5"
},
"last_serial": 5410370,
"releases": {
"1.0.0": [
{
"comment_text": "",
"digests": {
"md5": "3265278bcaffe15196681bf164282382",
"sha256": "3fbcd9601c023e4751dbec825739112096d52ff06a85174affc4a68cc45613c1"
},
"downloads": -1,
"filename": "gs-wrap-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "3265278bcaffe15196681bf164282382",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 24843,
"upload_time": "2019-01-25T11:07:20",
"url": "https://files.pythonhosted.org/packages/f0/c6/73ad41db94b5587e037def5519e9878bf22db981a5a40e611b191d1227aa/gs-wrap-1.0.0.tar.gz"
}
],
"1.0.1": [
{
"comment_text": "",
"digests": {
"md5": "537001553a5bed26f7b0d1a65dafe895",
"sha256": "9bf48ce37f3733e263d6d41ac0d680217bd562f7a6b66b0f7ad624f5ceab14c5"
},
"downloads": -1,
"filename": "gs-wrap-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "537001553a5bed26f7b0d1a65dafe895",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 24860,
"upload_time": "2019-01-25T12:51:32",
"url": "https://files.pythonhosted.org/packages/4d/4c/8ca2121f0949a85894ec2eb64cd8c4441743e5e6da7c68349a66be3336d2/gs-wrap-1.0.1.tar.gz"
}
],
"1.0.2": [
{
"comment_text": "",
"digests": {
"md5": "9c0c8f91398fd9fa1a4ffe4c52ee936e",
"sha256": "c693e548c460037c692a07cb7f839031911d71aa2f344e79acb8537fe3f9b50e"
},
"downloads": -1,
"filename": "gs-wrap-1.0.2.tar.gz",
"has_sig": false,
"md5_digest": "9c0c8f91398fd9fa1a4ffe4c52ee936e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 24873,
"upload_time": "2019-02-08T08:59:50",
"url": "https://files.pythonhosted.org/packages/e5/bd/c9d3f34ce7dd367a557978495e4311197d38aed002651d33aca6e3b25d22/gs-wrap-1.0.2.tar.gz"
}
],
"1.0.3": [
{
"comment_text": "",
"digests": {
"md5": "f65eac05ee12dcbd62ff62c82d41b3fd",
"sha256": "277827207ff10a5fecd25ec80fe16f75b58242b2e583a5412f2f194a68da4ff7"
},
"downloads": -1,
"filename": "gs-wrap-1.0.3.tar.gz",
"has_sig": false,
"md5_digest": "f65eac05ee12dcbd62ff62c82d41b3fd",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 25449,
"upload_time": "2019-02-12T16:21:17",
"url": "https://files.pythonhosted.org/packages/b3/df/c6b480027e4a6f216ee6fff0786171a2f98be766ed7f8819f801c514a1ce/gs-wrap-1.0.3.tar.gz"
}
],
"1.0.4": [
{
"comment_text": "",
"digests": {
"md5": "5b51f542e676aa80900c484ce83bd3b8",
"sha256": "2a4bf3af09ba76328679318bed825055adf6f16837eb72e52721f7f4f353e357"
},
"downloads": -1,
"filename": "gs-wrap-1.0.4.tar.gz",
"has_sig": false,
"md5_digest": "5b51f542e676aa80900c484ce83bd3b8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 25456,
"upload_time": "2019-05-14T09:45:18",
"url": "https://files.pythonhosted.org/packages/34/9d/3758f0f7cecbbb97497845d5467b5889cc486112a5adc0328c9fed0141e0/gs-wrap-1.0.4.tar.gz"
}
],
"1.0.5": [
{
"comment_text": "",
"digests": {
"md5": "a5a9c86d665f1944676db0736c564431",
"sha256": "ab0cfa7c3a554a41847f33e0e7b1859a5e151d9014e2e84dd97db15caf54c661"
},
"downloads": -1,
"filename": "gs-wrap-1.0.5.tar.gz",
"has_sig": false,
"md5_digest": "a5a9c86d665f1944676db0736c564431",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 25451,
"upload_time": "2019-05-21T13:28:22",
"url": "https://files.pythonhosted.org/packages/a7/91/bdf2ae5eacf95ade786abf45611bd5a2dd8a7dc80d191ac40261ee4e3ece/gs-wrap-1.0.5.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "a5a9c86d665f1944676db0736c564431",
"sha256": "ab0cfa7c3a554a41847f33e0e7b1859a5e151d9014e2e84dd97db15caf54c661"
},
"downloads": -1,
"filename": "gs-wrap-1.0.5.tar.gz",
"has_sig": false,
"md5_digest": "a5a9c86d665f1944676db0736c564431",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 25451,
"upload_time": "2019-05-21T13:28:22",
"url": "https://files.pythonhosted.org/packages/a7/91/bdf2ae5eacf95ade786abf45611bd5a2dd8a7dc80d191ac40261ee4e3ece/gs-wrap-1.0.5.tar.gz"
}
]
}