{ "info": { "author": "complynx", "author_email": "complynx@yandex.ru", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: User Interfaces" ], "description": "EAsync -- easy async decorator and promises\r\n===========================================\r\n\r\n.. image:: https://travis-ci.org/complynx/easync.svg?branch=master\r\n :target: https://travis-ci.org/complynx/easync\r\n\r\n@async\r\n======\r\n\r\nDecorator around the functions for them to be asynchronous.\r\n\r\nUsed as a simple decorator or along with named arguments. Preserves function's or method's docs, names and other stuff\r\nusing ``functools.wraps``.\r\n\r\nWhen the wrapped function is called, it produces the Promise_ of itself.\r\n\r\nUsage:\r\n\r\n>>> from easync import async\r\n>>> @async\r\n>>> def f1():\r\n>>> # some heavy-weight code\r\n\r\n>>> @async(daemon=True, print_exception=False)\r\n>>> def f2():\r\n>>> # some faulty daemon we don't really care about\r\n\r\n>>> @async(print_exception=logging.WARNING)\r\n>>> def f3():\r\n>>> # setting another logging level\r\n\r\n>>> f1().wait()\r\n\r\n>>> f2() # started daemon\r\neasync.Promise(...)\r\n>>> f3().then(callback, error) # notice, here the print_exception is suppressed (see Promise doc for more)\r\neasync.Promise(...)\r\n\r\n:param function: The function to be decorated.\r\n:param Boolean daemon: Optional. Create the daemon thread, that will die when no other threads left.\r\n:param print_exception: Log level to log the exception, if any, or None to mute it. See ``logging``.\r\n:param no_promise: Will not return a thing, instead of returning Promise_, good for decorating, for example, Tornado\r\n handlers.\r\n:return: Wrapped function, Promise_ generator.\r\n\r\n\r\nPromise\r\n=======\r\n\r\nThis is a threading wrapper that performs asynchronous call of the provided function.\r\nThe behaviour is inspired by JavaScript Promises, but differs in several points.\r\n\r\nFirst, the resolution is based upon the return of the function beneath.\r\nOn successful return, the result is stored in `Promise.result`_\r\nOn exception, exception is stored in `Promise.exception`_\r\nMore about how Promise_ resolves in `Resolving Promises`_.\r\n\r\nYou can add callbacks by using methods `Promise.then`_\r\nor `Promise.catch`_\r\nThey will create a new `Promise`_\r\nresolving in either the resolution of the previous one or the underlying callback.\r\n\r\nIf at the final stage no exception is processed or were generated new ones, they will be printed.\r\n\r\nBasic usage:\r\n\r\n>>> from easync import Promise\r\n>>> promise = Promise(func)(...args)\r\n>>> promise.wait()\r\n # if any\r\n>>> promise.result\r\n # if any\r\n>>> promise.exception\r\nException # if something went wrong\r\n>>> promise.exc_info\r\nTuple (Exception.__class__, Exception, traceback) # if any\r\n\r\nCallbacks usage:\r\n\r\n>>> def callback(result):\r\n>>> do_stuff(result)\r\n>>> def error(exception, exc_info):\r\n>>> my_log(exception, exc_info=exc_info)\r\n>>> def some_final(_):\r\n>>> cleanup()\r\n>>> Promise(func)(...args).then(callback, error).then(some_final)\r\nPromise(...)\r\n\r\n.. _Promise.Callable:\r\n\r\nPromise.Callable\r\n The to-be-runned function.\r\n\r\n.. _Promise.result:\r\n\r\nPromise.result\r\n This parameter holds the result the underlying function returned.\r\n\r\n.. _Promise.exception:\r\n\r\nPromise.exception\r\n This parameter holds the exception if the underlying function fails.\r\n\r\n.. _Promise.exc_info:\r\n\r\nPromise.exc_info\r\n This parameter holds the exception information tuple if the underlying function fails and the tuple was recovered.\r\n In case of `Promise.reject`_, it will be None.\r\n\r\n.. _Promise.resolved:\r\n\r\nPromise.resolved\r\n True if the function resolves. More about it in `Resolving Promises`_.\r\n\r\n.. _Promise.started:\r\n\r\nPromise.started\r\n ``threading.Event``, sets when thread is started.\r\n\r\n.. _Promise.finished:\r\n\r\nPromise.finished\r\n ``threading.Event``, sets when thread is finished, no matter where had it been resolved or not.\r\n\r\n.. _Promise.print_exception:\r\n\r\nPromise.print_exception\r\n Enables exception printout if caught. Uses logging to do so and has to be one of logging levels. ``False`` or\r\n ``None`` disable. Also automatically sets to False when a dependent Promise_ created through `Promise.then`_ or\r\n `Promise.catch`_.\r\n\r\n\r\nResolving Promises\r\n------------------\r\n\r\n\r\nFunctions\r\n^^^^^^^^^\r\n\r\nIf the Promise_ is constructed with a function, it will resolve in it's return or reject with the caught exception.\r\n\r\nOther Promises\r\n^^^^^^^^^^^^^^\r\n\r\nJust resolves in the same way the other one is resolved. Printouts will be suppressed in the first one.\r\n\r\nEvents and Conditions\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf Promise_ is based on ``threading.Event`` or ``threading.Condition``, it is resolved when the underlying Event or\r\nCondition occurs. The type testing is duck-type for having the ``wait`` method, so anything using the interface of\r\nwaiting can be resolved, for example other Promises, or threads.\r\n\r\nThe resolving is based on testing `is_failed`_ on the object, and if that one returns, the Promise_ rejects. Otherwise,\r\nthe `get_result`_ is called to obtain the result. Both are duck-type thingeys.\r\n\r\nAnything else\r\n^^^^^^^^^^^^^\r\n\r\nResolves successfully with the result equals to the passed-in argument.\r\n\r\nPromise.__init__\r\n----------------\r\n\r\n``__init__(function[, daemon=False, print_exception=logging.ERROR])``\r\n\r\nThe constructor creates a ``threading.Thread`` wrapping the ``function``.\r\nTo start it, call the resulting object as a function with it's arguments. (Explained in `Promise.__call__`_)\r\n\r\n>>> promise = Promise(func, print_exception=None)\r\n>>> promise()\r\n\r\n:param function: Function, Event, Condition, or anything else to resolve.\r\n:param daemon: Sets up daemon flag in the thread. May be set later. Optional.\r\n:param print_exception: Sets up the final exception printing level. Pass ``False`` to suppress.\r\n\r\nPromise.__call__\r\n----------------\r\n\r\n``__call__(*args, **kwargs)``\r\n\r\nStarts the thread and passes the arguments of the function into it.\r\nReturns self, for simple adding `Promise.then`_, `Promise.wait`_ or `Promise.catch`_.\r\n\r\nPromise.wait\r\n------------\r\n\r\n``wait([timeout=None])``\r\n\r\nPauses the current thread to wait until the underlying promise resolves.\r\n\r\nIf ``timeout`` is set, raises ``easync.TimeoutError`` if it's reached.\r\n\r\nReturns result of the underlying function if there's any.\r\n\r\nPromise.then\r\n------------\r\n\r\n``then([resolved=None, rejected=None, print_exception=Promise.print_exception])``\r\n\r\nThis method sets callbacks for a Promise_.\r\n\r\n**NOTE** this method suppresses the Promise_ default error handling by setting `Promise.print_exception`_ to ``False``.\r\nYou can then re-enable printouts manually, overriding the `Promise.print_exception`_ yourself.\r\n\r\n**NOTE** calling this method twice on the same Promise_ object will result in duplicated exception printouts unless\r\nchanged.\r\n\r\nThe result is a new Promise_ which resolves in:\r\n\r\n:callback exception: If the called callback (either ``resolved`` or ``rejected``) failed or raised anything.\r\n:reject: If the underlying Promise_ rejected and no ``rejected`` callback was passed.\r\n:callback return: The result of the called callback.\r\n:resolve: The result of the underlying Promise_ if it resolves and no ``resolved`` callback was passed.\r\n\r\nThis is done to have this kind of behaviour:\r\n\r\n>>> Promise(action)(...args).then(parse_result).then(parse_one_more_result).catch(any_exception).then(cleanup)\r\n\r\n:function resolved(result): The positive callback for the Promise_. Has to accept one positional\r\n argument - the result.\r\n:function rejected(exception, exc_info): The negative callback for the Promise_. Has to accept two positional\r\n arguments - the caught exception and it's exc_info.\r\n:print_exception: Passed into the corresponding argument of the newly created Promise_.\r\n:return: New Promise_.\r\n\r\nPromise.catch\r\n-------------\r\n\r\n``catch([callback=None, print_exception=Promise.print_exception])``\r\n\r\nThe same as `Promise.then`_ (resolved=None, callback, print_exception).\r\n\r\nPromise static methods\r\n======================\r\n\r\nPromise.resolve\r\n---------------\r\n\r\n``Promise.resolve(thing)``\r\n\r\nResolves ``thing``, regardless of what it is, to result.\r\n\r\n:param thing: any\r\n:return: resolved Promise_ with the `Promise.result`_ equals to ``thing``.\r\n\r\nPromise.reject\r\n--------------\r\n\r\n``Promise.reject(thing)``\r\n\r\nRejects ``thing``, regardless of what it is.\r\n\r\n**NOTE**, if this will be called, the `Promise.exc_info`_ will be set None.\r\n\r\n:param thing: any\r\n:return: rejected Promise_ with the `Promise.exception`_ equals to ``thing``.\r\n\r\nPromise.all\r\n-----------\r\n\r\n``Promise.all(things)``\r\n\r\nResolves when *all* the items in the ``enumerate(things)`` are resolved.\r\nOr rejects when *any* of the items is rejected.\r\n\r\n:param things: ``list`` of things or anything to be ``enumerate``'d.\r\n:result: ``list`` of results of all the Promises for each of the items.\r\n:exception: first caught exception.\r\n\r\nPromise.race\r\n------------\r\n\r\n``Promise.race(things)``\r\n\r\nResolves when *any* of the items in the ``enumerate(things)`` is resolved.\r\nOr rejects when *any* of the items is rejected.\r\n\r\n:param things: ``list`` of things or anything to be ``enumerate``'d.\r\n:result: the result of the first resolved item.\r\n:exception: first caught exception.\r\n\r\nOther functions\r\n===============\r\n\r\nget_result\r\n----------\r\n\r\n``get_result(obj)``\r\n\r\nReturns the first found attribute of ``result`` or ``success`` of the object obj, if any. Otherwise returns ``None``.\r\n\r\nis_failed\r\n---------\r\n\r\n``is_failed(obj)``\r\n\r\nReturns:\r\n\r\n:found property: if one of ``error``, ``exception``, ``failure`` is found.\r\n:True: if one of ``failed`` or ``is_failed`` is true.\r\n:True: if ``success`` is present and is ``False``.\r\n:None: Otherwise\r\n\r\n\r\n\r\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/complynx/easync", "keywords": "async asynchronous decorator promise", "license": "", "maintainer": "", "maintainer_email": "", "name": "easync", "package_url": "https://pypi.org/project/easync/", "platform": "", "project_url": "https://pypi.org/project/easync/", "project_urls": { "Homepage": "https://github.com/complynx/easync" }, "release_url": "https://pypi.org/project/easync/1.2.0b2/", "requires_dist": [ "check-manifest; extra == 'dev'", "coverage; extra == 'test'" ], "requires_python": "", "summary": "An easy async decorators and promises.", "version": "1.2.0b2" }, "last_serial": 3524507, "releases": { "1.0.0b1": [ { "comment_text": "", "digests": { "md5": "7ce4f5ba16d284c4404f77239034893e", "sha256": "355ee4ffe1128e53e9ec3f59f200b2aebd0ced0a757db6f57758e6fb467ebc73" }, "downloads": -1, "filename": "easync-1.0.0b1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "7ce4f5ba16d284c4404f77239034893e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 6553, "upload_time": "2017-12-26T19:11:37", "url": "https://files.pythonhosted.org/packages/14/3e/1965e8d74a43eda30f320e863667ad3b69b4144a0e6f117314e00b7845e6/easync-1.0.0b1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0ae415ed2c1c7f52881d2c9dbb9c1ba9", "sha256": "c5fd72506ddcd136e8233215d441c7cdd183509009564af1708f173496f3d5cf" }, "downloads": -1, "filename": "easync-1.0.0b1.tar.gz", "has_sig": false, "md5_digest": "0ae415ed2c1c7f52881d2c9dbb9c1ba9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8054, "upload_time": "2017-12-26T19:11:39", "url": "https://files.pythonhosted.org/packages/dc/0f/25bc95e488fb6c2b5d3d655e91b0dcab9365a894295195dbd9292bca9941/easync-1.0.0b1.tar.gz" } ], "1.0.1b1": [ { "comment_text": "", "digests": { "md5": "a953d22d6ff56e7503b3271a36bf5be0", "sha256": "548544df67b3997ecbdd61dfc925060a8a4a3e6ace5e20d017f918851ad79f67" }, "downloads": -1, "filename": "easync-1.0.1b1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "a953d22d6ff56e7503b3271a36bf5be0", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12296, "upload_time": "2017-12-27T17:55:17", "url": "https://files.pythonhosted.org/packages/e2/d6/4a374ffdcd828309e23d988cc89c05a9ae885dede3e38b41d1230ff84383/easync-1.0.1b1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2dde44f0e2ec8567139c505df0070b64", "sha256": "92def6f4240e79cad1b32f59d3f6b6187a815f6bb4412e2fcdaa85eea8bdc03d" }, "downloads": -1, "filename": "easync-1.0.1b1.tar.gz", "has_sig": false, "md5_digest": "2dde44f0e2ec8567139c505df0070b64", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11555, "upload_time": "2017-12-27T17:55:19", "url": "https://files.pythonhosted.org/packages/5b/10/2975c93bbcb0ddb4871a55a42c266c94a919413dda84a779856c2b8e0cd5/easync-1.0.1b1.tar.gz" } ], "1.0.2b1": [ { "comment_text": "", "digests": { "md5": "b7a8cfcd31de5fe3d55c19644d322736", "sha256": "e2cb0f8e151885ac05614b03bb3e26803191b6abf6a2cf0713d2baed2fb807f6" }, "downloads": -1, "filename": "easync-1.0.2b1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "b7a8cfcd31de5fe3d55c19644d322736", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12293, "upload_time": "2017-12-27T18:03:46", "url": "https://files.pythonhosted.org/packages/d9/b1/2e76342b8ae2862e65a2d7a8fcbe1c37348775d7f8bcde2eadc5e1476ecd/easync-1.0.2b1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "38547824e748bbf0f478b623ca01e2c0", "sha256": "5d65f693e30563ed46a76a2457d91eafaddd9cf17f99ff3788ffc95bbdec2b75" }, "downloads": -1, "filename": "easync-1.0.2b1.tar.gz", "has_sig": false, "md5_digest": "38547824e748bbf0f478b623ca01e2c0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11553, "upload_time": "2017-12-27T18:03:48", "url": "https://files.pythonhosted.org/packages/bd/47/5c75ea13b24934d1554cf85a94735a72df9b22cd36f8d9fbe8dda87d9261/easync-1.0.2b1.tar.gz" } ], "1.0.3b1": [ { "comment_text": "", "digests": { "md5": "00bcba1e182d92bc8716181fba2e5634", "sha256": "f52b5cbbd888d58f4bd4b473f91a796d371297a38632814960bb3b1fdd9e607f" }, "downloads": -1, "filename": "easync-1.0.3b1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "00bcba1e182d92bc8716181fba2e5634", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12439, "upload_time": "2018-01-07T13:26:31", "url": "https://files.pythonhosted.org/packages/e4/13/91166ac8907c5742084626f63f0ade9807128b14928428931f99815caff1/easync-1.0.3b1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f6e80d7d196d2f3c4c3d311038ecaf1d", "sha256": "60f6b0662bd148604c524b12fca40e1c8569699e052c0a3d08f09fdf07753e95" }, "downloads": -1, "filename": "easync-1.0.3b1.tar.gz", "has_sig": false, "md5_digest": "f6e80d7d196d2f3c4c3d311038ecaf1d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11641, "upload_time": "2018-01-07T13:26:32", "url": "https://files.pythonhosted.org/packages/f1/08/825862c9d74f626d4039ec996f3c7f8d3a20b5354d4d8cc56cac720d9be0/easync-1.0.3b1.tar.gz" } ], "1.0.4b1": [ { "comment_text": "", "digests": { "md5": "16516bf473fe407e5a1ecb4f3bee163b", "sha256": "e00712b13ca5f4f5e31e933eeaf6f5958895b082368307605376279942be4c51" }, "downloads": -1, "filename": "easync-1.0.4b1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "16516bf473fe407e5a1ecb4f3bee163b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12440, "upload_time": "2018-01-07T13:36:34", "url": "https://files.pythonhosted.org/packages/39/a8/cdf6a6efcffd1c48686243cca3fbb8b36186d03f57023477ecb63b770260/easync-1.0.4b1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4f57a952401884575220959c5c52e391", "sha256": "7f55c90e83ab392e32f883c3beb2c1d503188a9932624eb5bce72abe742c10da" }, "downloads": -1, "filename": "easync-1.0.4b1.tar.gz", "has_sig": false, "md5_digest": "4f57a952401884575220959c5c52e391", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11637, "upload_time": "2018-01-07T13:36:37", "url": "https://files.pythonhosted.org/packages/73/6f/4f4de61403abb57f459a9ddef784c7fd208754b3747a3ee7ef54dce8a2ee/easync-1.0.4b1.tar.gz" } ], "1.0.5b1": [ { "comment_text": "", "digests": { "md5": "115baecfd860f736194d9d85657febf5", "sha256": "b68aed9a91050fafc78db549260b16ef328afc25eecc34d966b3db0e4de6f014" }, "downloads": -1, "filename": "easync-1.0.5b1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "115baecfd860f736194d9d85657febf5", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12546, "upload_time": "2018-01-13T19:28:37", "url": "https://files.pythonhosted.org/packages/38/07/c841e8f5129eb957d03197584b78781774f45fac8560e2fe15ebe7e63d97/easync-1.0.5b1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b3b5c246ae5b25b2468d74bc1f3ca5f8", "sha256": "f5a9ccad7a7b09ad805ca1cc1d5c025024144c0395c484174d8be99fb2ca11f5" }, "downloads": -1, "filename": "easync-1.0.5b1.tar.gz", "has_sig": false, "md5_digest": "b3b5c246ae5b25b2468d74bc1f3ca5f8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11809, "upload_time": "2018-01-13T19:28:38", "url": "https://files.pythonhosted.org/packages/dc/65/d26c58ca9f5fc21bd3343da226b3924425cb248efa0492b8306d4878139b/easync-1.0.5b1.tar.gz" } ], "1.1.0b2": [ { "comment_text": "", "digests": { "md5": "5a233f766f997514c7456863e7df15f3", "sha256": "d1e0eb6ae01e2cf9a379387a29acca0cccdf51abb2a30a2d8453fd1e432738a5" }, "downloads": -1, "filename": "easync-1.1.0b2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "5a233f766f997514c7456863e7df15f3", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16287, "upload_time": "2018-01-13T23:20:01", "url": "https://files.pythonhosted.org/packages/9e/72/36c148e37d7ac7553d89249f047453997bd0a123cfbec75db7d5d1ce4f02/easync-1.1.0b2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e53767a2ee7b0ac549c214b7cd17c6ab", "sha256": "fa3d5c1df9af6ab9fdc1f9d19e7305c1d16d051339a98eeeef9ed92390903050" }, "downloads": -1, "filename": "easync-1.1.0b2.tar.gz", "has_sig": false, "md5_digest": "e53767a2ee7b0ac549c214b7cd17c6ab", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12422, "upload_time": "2018-01-13T23:20:03", "url": "https://files.pythonhosted.org/packages/07/ac/6842eb4c606fa82e2341d7ddee44aa9c7fd0ac5dd9fec1b6cad2b24ff808/easync-1.1.0b2.tar.gz" } ], "1.2.0b2": [ { "comment_text": "", "digests": { "md5": "f0645c1d3c60ae902626fbf8625e7e0b", "sha256": "9f5b49cc8e0d612e1a383a697fcd90c8b61c4377f2605603d04b71b03bbecb2e" }, "downloads": -1, "filename": "easync-1.2.0b2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "f0645c1d3c60ae902626fbf8625e7e0b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 15227, "upload_time": "2018-01-26T15:14:11", "url": "https://files.pythonhosted.org/packages/5e/97/28016350031c2de32cb950522b22f24e4cb34ef4189cb914929fe2c424ed/easync-1.2.0b2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5129a9f64b148346be134e74380f2244", "sha256": "079f25d0d1c325e322473159fb9b1a77a24a1f84ebf143065eed3d418f0b0471" }, "downloads": -1, "filename": "easync-1.2.0b2.tar.gz", "has_sig": false, "md5_digest": "5129a9f64b148346be134e74380f2244", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12522, "upload_time": "2018-01-26T15:14:12", "url": "https://files.pythonhosted.org/packages/7b/d1/fc728d97a9f0c8e2e12f93097b1d1d1f9e1304f71a210bad99b1005bc3c3/easync-1.2.0b2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "f0645c1d3c60ae902626fbf8625e7e0b", "sha256": "9f5b49cc8e0d612e1a383a697fcd90c8b61c4377f2605603d04b71b03bbecb2e" }, "downloads": -1, "filename": "easync-1.2.0b2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "f0645c1d3c60ae902626fbf8625e7e0b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 15227, "upload_time": "2018-01-26T15:14:11", "url": "https://files.pythonhosted.org/packages/5e/97/28016350031c2de32cb950522b22f24e4cb34ef4189cb914929fe2c424ed/easync-1.2.0b2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5129a9f64b148346be134e74380f2244", "sha256": "079f25d0d1c325e322473159fb9b1a77a24a1f84ebf143065eed3d418f0b0471" }, "downloads": -1, "filename": "easync-1.2.0b2.tar.gz", "has_sig": false, "md5_digest": "5129a9f64b148346be134e74380f2244", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12522, "upload_time": "2018-01-26T15:14:12", "url": "https://files.pythonhosted.org/packages/7b/d1/fc728d97a9f0c8e2e12f93097b1d1d1f9e1304f71a210bad99b1005bc3c3/easync-1.2.0b2.tar.gz" } ] }