{ "info": { "author": "Kyle Lahnakoski", "author_email": "kyle@lahnakoski.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# More Threads!\n\n\n|Branch |Status |\n|------------|---------|\n|master | [![Build Status](https://travis-ci.org/klahnakoski/mo-threads.svg?branch=master)](https://travis-ci.org/klahnakoski/mo-threads) |\n|dev | [![Build Status](https://travis-ci.org/klahnakoski/mo-threads.svg?branch=dev)](https://travis-ci.org/klahnakoski/mo-threads) [![Coverage Status](https://coveralls.io/repos/github/klahnakoski/mo-threads/badge.svg?branch=dev)](https://coveralls.io/github/klahnakoski/mo-threads?branch=dev) |\n\n## Module `threads`\n\nThe main benefits over Python's threading library is:\n\n1. **Multi-threaded queues do not use serialization** - Serialization is \ngreat in the general case, where you may also be communicating between \nprocesses, but it is a needless overhead for single-process multi-threading. \nIt is left to the programmer to ensure the messages put on the queue are \nnot changed, which is not ominous demand.\n2. **Shutdown order is deterministic and explicit** - Python's threading \nlibrary is missing strict conventions for controlled and orderly shutdown. \nThese conventions eliminate the need for `interrupt()` and `abort()`, both of \nwhich are unstable idioms when using resources. Each thread can shutdown on \nits own terms, but is expected to do so expediently.\n * All threads are required to accept a `please_stop` signal; are \n expected to test it in a timely manner; and expected to exit when signalled.\n * All threads have a parent - The parent is responsible for ensuring their \n children get the `please_stop` signal, and are dead, before stopping \n themselves. This responsibility is baked into the thread spawning process, \n so you need not deal with it unless you want.\n3. Uses [**Signals**](#signal-class) to simplify logical \ndependencies among multiple threads, events, and timeouts.\n4. **Logging and Profiling is Integrated** - Logging and exception handling \nis seamlessly integrated: This means logs are centrally handled, and thread \nsafe. Parent threads have access to uncaught child thread exceptions, and \nthe cProfiler properly aggregates results from the multiple threads.\n\n\n### What's it used for\n\nA good amount of time is spent waiting for underlying C libraries and OS\nservices to respond to network and file access requests. Multiple\nthreads can make your code faster, despite the GIL, when dealing with those\nrequests. For example, by moving logging off the main thread, we can get\nup to 15% increase in overall speed because we no longer have the main thread\nwaiting for disk writes or remote logging posts. Please note, this level of\nspeed improvement can only be realized if there is no serialization happening\nat the multi-threaded queue. \n\n### Asynch vs. Actors\n\nMy personal belief is that [actors](http://en.wikipedia.org/wiki/Actor_model)\nare easier to reason about than [asynch tasks](https://docs.python.org/3/library/asyncio-task.html).\nMixing regular methods and co-routines (with their `yield from` pollution) is\ndangerous because:\n\n1. calling styles between methods and co-routines can be easily confused\n2. actors can use blocking methods, co-routines can not\n3. there is no way to manage resource priority with co-routines.\n4. stack traces are lost with co-routines\n5. asynch scope easily escapes lexical scope, which promotes bugs \n\nPython's asynch efforts are a still-immature re-invention of threading functionality by another name. Expect to experience a decade of problems that are already solved by threading; [here is an example](https://www.python.org/dev/peps/pep-0550/)\n\n### Synchronization Primitives\n\nThere are three major aspects of a synchronization primitive:\n\n* **Resource** - Monitors and locks can only be owned by one thread at a time\n* **Binary** - The primitive has only two states\n* **Irreversible** - The state of the primitive can only be set, or advanced, never reversed\n\nThe last, *irreversibility* is very useful, but ignored in many threading\nlibraries. The irreversibility allows us to model progression; and\nwe can allow threads to poll for progress, or be notified of progress. \n\nThese three aspects can be combined to give us 8 synchronization primitives:\n\n* `- - -` - Semaphore\n* `- B -` - Binary Semaphore\n* `R - -` - Monitor\n* `R B -` - **[Lock](#lock-class)**\n* `- - I` - Iterator/generator\n* `- B I` - **[Signal](#signal-class)** (or Promise)\n* `R - I` - Private Iterator \n* `R B I` - Private Signal (best implemented as `is_done` Boolean flag)\n\n## `Lock` Class\n\nLocks are identical to [threading monitors](https://en.wikipedia.org/wiki/Monitor_(synchronization)), except for two differences: \n\n1. The `wait()` method will **always acquire the lock before returning**. This is an important feature, it ensures every line inside a `with` block has lock acquisition, and is easier to reason about.\n2. Exiting a lock via `__exit__()` will **always** signal a waiting thread to resume. This ensures no signals are missed, and every thread gets an opportunity to react to possible change.\n\n```python\n lock = Lo\n while not please_stop:\n with lock:\n while not todo:\n lock.wait(seconds=1)\n # DO SOME WORK\n\n```\n\nIn this example, we look for stuff `todo`, and if there is none, we wait for a second. During that time others can acquire the `lock` and add `todo` items. Upon releasing the the `lock`, our example code will immediately resume to see what's available, waiting again if nothing is found.\n\n\n## `Signal` Class\n\n[The `Signal` class](mo_threads/signal.py) is a binary semaphore that can be signalled only once; subsequent signals have no effect. It can be signalled by any thread; any thread can wait on a `Signal`; and once signalled, all waiting threads are unblocked, including all subsequent waiting threads. A Signal's current state can be accessed by any thread without blocking. `Signal` is used to model thread-safe state advancement. It initializes to `False`, and when signalled (with `go()`) becomes `True`. It can not be reversed. \n\nSignals are like a Promise, but more explicit \n\n| Signal | Promise |\n|:----------:|:------------------:|\n| s.go() | s.resolve() |\n| s.then(f) | s.then(m) |\n| s.wait() | await s |\n| s & t | Promise.all(s, t) | \n| s | t | Promise.race(s, t) |\n\n```python\nis_done = Signal()\nyield is_done # give signal to another that wants to know when done\n# DO WORK\nis_done.go()\n```\n\nYou can attach methods to a `Signal`, which will be run, just once, upon `go()`. If already signalled, then the method is run immediately.\n\n```python\nis_done = Signal()\nis_done.then(lambda: print(\"done\"))\nreturn is_done\n```\n\nYou may also wait on a `Signal`, which will block the current thread until the `Signal` is a go\n\n```python\nis_done = worker_thread.stopped\nis_done.wait()\nis_done = print(\"worker thread is done\")\n```\n\n`Signals` are first class, they can be passed around and combined with other Signals. For example, using the `__or__` operator (`|`): `either = lhs | rhs`; `either` will be triggered when `lhs` or `rhs` is triggered.\n\n```python\ndef worker(please_stop):\n while not please_stop:\n #DO WORK \n\nuser_cancel = get_user_cancel_signal()\nworker(user_cancel | Till(seconds=360))\n```\n\n`Signal`s can also be combined using logical and (`&`): `both = lhs & rhs`; `both` is triggered only when both `lhs` and `rhs` are triggered:\n\n```python\n(workerA.stopped & workerB.stopped).wait()\nprint(\"both threads are done\")\n```\n\n## `Till` Class\n\n[The `Till` class](https://github.com/klahnakoski/pyLibrary/blob/dev/pyLibrary/thread/till.py) is a special `Signal` used to represent timeouts. \n\n```python\nTill(seconds=20).wait()\nTill(till=Date(\"21 Jan 2016\").unix).wait()\n```\n\nUse `Till` rather than `sleep()` because you can combine `Till` objects with other `Signals`. \n\n**Beware that all `Till` objects will be triggered before expiry when the main thread is asked to shutdown**", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/klahnakoski/mo-threads", "keywords": "", "license": "MPL 2.0", "maintainer": "", "maintainer_email": "", "name": "mo-threads", "package_url": "https://pypi.org/project/mo-threads/", "platform": "", "project_url": "https://pypi.org/project/mo-threads/", "project_urls": { "Homepage": "https://github.com/klahnakoski/mo-threads" }, "release_url": "https://pypi.org/project/mo-threads/2.53.19239/", "requires_dist": null, "requires_python": "", "summary": "More Threads! Simpler and faster threading.", "version": "2.53.19239" }, "last_serial": 5738226, "releases": { "1.0.17035": [ { "comment_text": "", "digests": { "md5": "46a6da388ad35040930680bf7d312532", "sha256": "56e7cddf3a8ee5fbdb00d5148b6adc0168f2830ae3f98162b34a7ad266f5df29" }, "downloads": -1, "filename": "mo_threads-1.0.17035-py2.7.egg", "has_sig": false, "md5_digest": "46a6da388ad35040930680bf7d312532", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 41163, "upload_time": "2017-02-03T20:43:20", "url": "https://files.pythonhosted.org/packages/dc/5c/0943319436bd0c0bba67ca63209b4c0326f3ad6dc5db1d08d0fa8f577ac2/mo_threads-1.0.17035-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "23b29de700685ae65f804950b1cb7750", "sha256": "91fa912ce9e1f85894ee89b82d86c870fb3c0417f6a145a57cddf435f3871e66" }, "downloads": -1, "filename": "mo-threads-1.0.17035.zip", "has_sig": false, "md5_digest": "23b29de700685ae65f804950b1cb7750", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28200, "upload_time": "2017-02-03T20:43:25", "url": "https://files.pythonhosted.org/packages/3a/94/b0afc69f0180219c178a3d01a9f3a8e3d6ae7acd876fe077806ba2bc1807/mo-threads-1.0.17035.zip" } ], "1.0.17039": [ { "comment_text": "", "digests": { "md5": "28c5192bd28038f7e1a28f108814a270", "sha256": "e441ecaef710ed5f6abb3806e44cb730a6dbecd1fd0b845d89818065c937d781" }, "downloads": -1, "filename": "mo_threads-1.0.17039-py2.7.egg", "has_sig": false, "md5_digest": "28c5192bd28038f7e1a28f108814a270", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 41167, "upload_time": "2017-02-07T14:59:32", "url": "https://files.pythonhosted.org/packages/82/9f/325ea82702c5f3fdd3c7374ea7d7216e8ca2d9ade159c8af0dafca9918cd/mo_threads-1.0.17039-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "73b4122fb221980f57d7e33e7ea078e2", "sha256": "eb195fae0858ec8f9b8e07a4fedb8d9bda20b9c30b6a854bd78969feb7571885" }, "downloads": -1, "filename": "mo-threads-1.0.17039.zip", "has_sig": false, "md5_digest": "73b4122fb221980f57d7e33e7ea078e2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28214, "upload_time": "2017-02-07T14:59:39", "url": "https://files.pythonhosted.org/packages/e9/bc/82080b7b7535215f4d8689239323439a8d303c94899975bb0571a2e64739/mo-threads-1.0.17039.zip" } ], "1.1.17040": [ { "comment_text": "", "digests": { "md5": "d351385240f7666c41c8cb435593eb6a", "sha256": "3b366917f3f1c1160768f27387452c66b68e6637a039f8de02d9fcaaf30978ca" }, "downloads": -1, "filename": "mo_threads-1.1.17040-py2.7.egg", "has_sig": false, "md5_digest": "d351385240f7666c41c8cb435593eb6a", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 41849, "upload_time": "2017-02-08T03:45:52", "url": "https://files.pythonhosted.org/packages/e3/4c/80843e4fbcfc7385cd9fcd43a6e9b493a4ab6c74a882fb65487d00edaa15/mo_threads-1.1.17040-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "68095b37edd513797753b6a5b74bf5c2", "sha256": "0eaaddea11d426690e1e79bc06ea58b654d42ed14aeecdca3c6d1031096231f0" }, "downloads": -1, "filename": "mo-threads-1.1.17040.zip", "has_sig": false, "md5_digest": "68095b37edd513797753b6a5b74bf5c2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28450, "upload_time": "2017-02-08T03:46:00", "url": "https://files.pythonhosted.org/packages/04/77/b77753e9ed8f92d48d500e7e785c7d3f25db12b5686efd5969bad56207ce/mo-threads-1.1.17040.zip" } ], "1.2.17040": [ { "comment_text": "", "digests": { "md5": "6c380f442d89ba58a46361a8eafa6b43", "sha256": "db290d35a0e6e4033a5ffc3c4fefacf38be56945e26ef29a67659788cf3bceb2" }, "downloads": -1, "filename": "mo_threads-1.2.17040-py2.7.egg", "has_sig": false, "md5_digest": "6c380f442d89ba58a46361a8eafa6b43", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 41631, "upload_time": "2017-02-08T16:19:20", "url": "https://files.pythonhosted.org/packages/75/93/0db96e003c71917166166359e2e30368315ac34f87f3c79dd295e0968ac0/mo_threads-1.2.17040-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "1250e0c2aa298ec72e3a4808290c7f55", "sha256": "e7631daad959db63d7c958cbda2e0746f435028ef4c49d005d430e1009897bf5" }, "downloads": -1, "filename": "mo-threads-1.2.17040.zip", "has_sig": false, "md5_digest": "1250e0c2aa298ec72e3a4808290c7f55", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28401, "upload_time": "2017-02-08T16:19:26", "url": "https://files.pythonhosted.org/packages/12/70/c1b8da080c40c2399b9fef7ff05d6dcc211c55968c19d22a5ff567f5ea70/mo-threads-1.2.17040.zip" } ], "1.2.17049": [ { "comment_text": "", "digests": { "md5": "67c213e39f74b227d5ed90085d3b18c7", "sha256": "b139ff986f7da3228dfa71a56d519acb72bc4b001600ac0869c813c9c4ee36d3" }, "downloads": -1, "filename": "mo_threads-1.2.17049-py2.7.egg", "has_sig": false, "md5_digest": "67c213e39f74b227d5ed90085d3b18c7", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 19887, "upload_time": "2017-02-18T02:08:16", "url": "https://files.pythonhosted.org/packages/da/86/76f138e640ce20309c16ddaecfe6f1744628c8ae4e443bf2a29a71009283/mo_threads-1.2.17049-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "97db8fc14a7455ff4480f5004f923f07", "sha256": "9bd7ef136c8f68f70149ea64c47ca8d6a733b3a49840ac052d80801552455430" }, "downloads": -1, "filename": "mo-threads-1.2.17049.zip", "has_sig": false, "md5_digest": "97db8fc14a7455ff4480f5004f923f07", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28356, "upload_time": "2017-02-18T02:08:20", "url": "https://files.pythonhosted.org/packages/45/96/7a43c5d3cbf243f3df9b41403195101a7244e9924f0fe8049291cd1013ad/mo-threads-1.2.17049.zip" } ], "1.2.17056": [ { "comment_text": "", "digests": { "md5": "ec4f379a2a46d2d8ef43cce42e03281e", "sha256": "32494312d9ae6b0ecaefa75317c2d748a7f9aa6b4a12d76d991b9e9885733ad0" }, "downloads": -1, "filename": "mo_threads-1.2.17056-py2.7.egg", "has_sig": false, "md5_digest": "ec4f379a2a46d2d8ef43cce42e03281e", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 19873, "upload_time": "2017-02-25T20:27:41", "url": "https://files.pythonhosted.org/packages/a8/36/4c409847b530333d0766863194ad361cc728b974cb4814f7824abf5fffbc/mo_threads-1.2.17056-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "0df0f271c0463feae52879fd5ddfb432", "sha256": "c8aaf14184b9bcbec972441ecc9258e1f5fcac388c558d272013dc47d61fca8a" }, "downloads": -1, "filename": "mo-threads-1.2.17056.zip", "has_sig": false, "md5_digest": "0df0f271c0463feae52879fd5ddfb432", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28343, "upload_time": "2017-02-25T20:27:47", "url": "https://files.pythonhosted.org/packages/57/30/37f7e451fca56d631f9f04028bc4c0de1542c76185851fde875967b82fce/mo-threads-1.2.17056.zip" } ], "1.2.17085": [ { "comment_text": "", "digests": { "md5": "6751df419f10810de216da1402fd80eb", "sha256": "85c968cba7a76bf519acbf11dae081bf863ca76dc2344d02890d0eac6cae705e" }, "downloads": -1, "filename": "mo_threads-1.2.17085-py2.7.egg", "has_sig": false, "md5_digest": "6751df419f10810de216da1402fd80eb", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 19955, "upload_time": "2017-03-26T12:35:40", "url": "https://files.pythonhosted.org/packages/91/c4/bdab92360833c3a0cb8dda33e3c89960f95321ca117a2524cade26ccd56f/mo_threads-1.2.17085-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "113c12249c22281c90149a30016b78ba", "sha256": "7d36763419c9363bf13e145c59f1feb64f3527fe98931ef28e96e5b8f494529a" }, "downloads": -1, "filename": "mo-threads-1.2.17085.zip", "has_sig": false, "md5_digest": "113c12249c22281c90149a30016b78ba", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28489, "upload_time": "2017-03-26T12:35:43", "url": "https://files.pythonhosted.org/packages/6e/5f/899d214aba5897d47caa4518ff974941b54fa38e30a8cb68defc4a024387/mo-threads-1.2.17085.zip" } ], "1.2.17131": [ { "comment_text": "", "digests": { "md5": "d08d1df4acd46b560a0b8bb541e15ddf", "sha256": "0c995aa600ed0c77af206d781a2688339fceb271585e17a6236de4d7ec6d2713" }, "downloads": -1, "filename": "mo-threads-1.2.17131.zip", "has_sig": false, "md5_digest": "d08d1df4acd46b560a0b8bb541e15ddf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33124, "upload_time": "2017-05-11T13:41:08", "url": "https://files.pythonhosted.org/packages/25/6d/70e71554419ec6dcc689f06dbd410b2a60757cbc230433a8b72d7904117e/mo-threads-1.2.17131.zip" } ], "1.2.17227": [ { "comment_text": "", "digests": { "md5": "93875b4a6717b52502555128abbd1da0", "sha256": "8845da88da6aff10b2bf9f4f9c325b1664bb1d8df6c177c8fe29109e3528193f" }, "downloads": -1, "filename": "mo-threads-1.2.17227.zip", "has_sig": false, "md5_digest": "93875b4a6717b52502555128abbd1da0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33173, "upload_time": "2017-08-15T12:55:27", "url": "https://files.pythonhosted.org/packages/a2/da/dfe96a55a53c10703e9da098ddf007d68f24923a718805ca30ac3d3a6d5d/mo-threads-1.2.17227.zip" } ], "1.7.17339": [ { "comment_text": "", "digests": { "md5": "e2721802d0b7c219eefae0537745de45", "sha256": "a236c41c28097278d28f533d6221a19e4d1ed464f62ee181e8974f556960d66f" }, "downloads": -1, "filename": "mo-threads-1.7.17339.tar.gz", "has_sig": false, "md5_digest": "e2721802d0b7c219eefae0537745de45", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19274, "upload_time": "2017-12-05T01:59:22", "url": "https://files.pythonhosted.org/packages/12/f1/3894f1fe6eae49ccc3e94345facc2ba3947efa0391ea8089f3006ab68404/mo-threads-1.7.17339.tar.gz" } ], "1.7.18043": [ { "comment_text": "", "digests": { "md5": "be76ae2216d21d2771e05772df8ad243", "sha256": "891e1ae81ded558b0f2dceb3ddb38fcde12591b2fe0fe7903bbf0ba737e84fc9" }, "downloads": -1, "filename": "mo-threads-1.7.18043.tar.gz", "has_sig": false, "md5_digest": "be76ae2216d21d2771e05772df8ad243", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19409, "upload_time": "2018-02-12T13:39:15", "url": "https://files.pythonhosted.org/packages/c5/74/c99c7f30a4cd57d90bdb9f5b5129c027711d36942a153d2e225f698bd8ca/mo-threads-1.7.18043.tar.gz" } ], "2.10.18154": [ { "comment_text": "", "digests": { "md5": "8035101bcbe4767f6b83156a3712a0c3", "sha256": "bb845bf637e1a44e4137f447f8b5dc1b462e2722323c472f80c90687e27a9424" }, "downloads": -1, "filename": "mo-threads-2.10.18154.tar.gz", "has_sig": false, "md5_digest": "8035101bcbe4767f6b83156a3712a0c3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22633, "upload_time": "2018-06-03T22:40:00", "url": "https://files.pythonhosted.org/packages/48/81/542ef56f9ad8ae90d20c20fa083d3d41b6cc7e2657beee00bbc389b08b33/mo-threads-2.10.18154.tar.gz" } ], "2.11.18154": [ { "comment_text": "", "digests": { "md5": "22f7fc8fee4bd14bcfbff1e6269d7110", "sha256": "e64141e06abed889b2a548cf43cbb0566cce5541794de8f11c52446ff529e8c0" }, "downloads": -1, "filename": "mo-threads-2.11.18154.tar.gz", "has_sig": false, "md5_digest": "22f7fc8fee4bd14bcfbff1e6269d7110", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22638, "upload_time": "2018-06-03T23:36:44", "url": "https://files.pythonhosted.org/packages/a4/fd/bec675442b53e33f6b6b25b97a7f23d07a54b3483e37522393705f98f007/mo-threads-2.11.18154.tar.gz" } ], "2.16.18199": [ { "comment_text": "", "digests": { "md5": "3eca3d73639cfefbe9e064bc13776c9f", "sha256": "aa1bf982a49fcfe1afb308d6a277c287bf951b47ea3e418638c6076079143347" }, "downloads": -1, "filename": "mo-threads-2.16.18199.tar.gz", "has_sig": false, "md5_digest": "3eca3d73639cfefbe9e064bc13776c9f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23013, "upload_time": "2018-07-18T12:17:15", "url": "https://files.pythonhosted.org/packages/61/b5/0f57c418c5fe241beddb149b8a9d724b518b1a5cb474e6d85dac4c9e9850/mo-threads-2.16.18199.tar.gz" } ], "2.18.18240": [ { "comment_text": "", "digests": { "md5": "d5e02824a234cc2ecfbf5c7026c53315", "sha256": "ab64431a51db1369ce0830debe6cf7bafd8547ea2bb83745ca5763e636c7114a" }, "downloads": -1, "filename": "mo-threads-2.18.18240.tar.gz", "has_sig": false, "md5_digest": "d5e02824a234cc2ecfbf5c7026c53315", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24872, "upload_time": "2018-08-28T13:13:15", "url": "https://files.pythonhosted.org/packages/18/4a/e02f55238e3c21cacb488b50ca269dc981f9fda3165474cdaa85d7a062c6/mo-threads-2.18.18240.tar.gz" } ], "2.26.18331": [ { "comment_text": "", "digests": { "md5": "461b45cfe867eda45df26a8199bffd7d", "sha256": "719154341386ccaa193ae1038de2ea152b07caad807d286e773707d554426414" }, "downloads": -1, "filename": "mo-threads-2.26.18331.tar.gz", "has_sig": false, "md5_digest": "461b45cfe867eda45df26a8199bffd7d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20794, "upload_time": "2018-11-27T14:29:29", "url": "https://files.pythonhosted.org/packages/54/f9/e0b7e9005db45bb792bf2266860a19297d3b779d6afedd21f85c36e58da1/mo-threads-2.26.18331.tar.gz" } ], "2.28.19015": [ { "comment_text": "", "digests": { "md5": "6ca99340b8bc16521c100e9de8c49b16", "sha256": "d6e29ded6143f3aaf2cfa0d847402ecc48a122eddca1d5368187df1e7789757f" }, "downloads": -1, "filename": "mo-threads-2.28.19015.tar.gz", "has_sig": false, "md5_digest": "6ca99340b8bc16521c100e9de8c49b16", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20832, "upload_time": "2019-01-15T14:02:07", "url": "https://files.pythonhosted.org/packages/63/bc/bfc85242d590ca6208ada6a418d3c6a69e9d6299e850b07f73f740968609/mo-threads-2.28.19015.tar.gz" } ], "2.31.19025": [ { "comment_text": "", "digests": { "md5": "8f23b2826cf9ef84e79e45843392a82f", "sha256": "c1246b3004b5cc920791eec328a15f9f656d28cc2d2a6da0dcd8c074dd03dc1d" }, "downloads": -1, "filename": "mo-threads-2.31.19025.tar.gz", "has_sig": false, "md5_digest": "8f23b2826cf9ef84e79e45843392a82f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20911, "upload_time": "2019-01-26T00:00:05", "url": "https://files.pythonhosted.org/packages/a0/86/4518c35236f6296cc1af99c730c1273468ebda779ac9f75002b0f452f18d/mo-threads-2.31.19025.tar.gz" } ], "2.43.19055": [ { "comment_text": "", "digests": { "md5": "0678aa26c2dc85dc38fd3bdaa6464798", "sha256": "8323b74879269262ebf399e521dffc1fdc2214ce6c54de064c840d64062c7c09" }, "downloads": -1, "filename": "mo-threads-2.43.19055.tar.gz", "has_sig": false, "md5_digest": "0678aa26c2dc85dc38fd3bdaa6464798", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21154, "upload_time": "2019-02-24T18:47:49", "url": "https://files.pythonhosted.org/packages/a0/a2/8dd7dedc6afa36cf528eb8e66a6a970a9c6f4a87b3683f0b8ecb158acefc/mo-threads-2.43.19055.tar.gz" } ], "2.53.19239": [ { "comment_text": "", "digests": { "md5": "60b2017f744938835542a4a9fd6e976f", "sha256": "66e84642effe66145311412d89d2a21965b0208bdb2e2a5800a9c5c0d46e0b82" }, "downloads": -1, "filename": "mo-threads-2.53.19239.tar.gz", "has_sig": false, "md5_digest": "60b2017f744938835542a4a9fd6e976f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21456, "upload_time": "2019-08-27T17:20:42", "url": "https://files.pythonhosted.org/packages/e3/ca/535170f38e3a06d2436d19d792b0f2f8e5c1f7eaf87bdd74f8b581a803b1/mo-threads-2.53.19239.tar.gz" } ], "2.8.18148": [ { "comment_text": "", "digests": { "md5": "337644a4beb6d4735d3cdef2fed9bd48", "sha256": "69f00fc7808399e4ac94ddb4cbd3780338b143dc518235e5dbca1cf19e3cfc01" }, "downloads": -1, "filename": "mo-threads-2.8.18148.tar.gz", "has_sig": false, "md5_digest": "337644a4beb6d4735d3cdef2fed9bd48", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22621, "upload_time": "2018-05-28T13:44:22", "url": "https://files.pythonhosted.org/packages/21/57/e9513938c3d40b5b540cc848cb7481d7a87bb8b96deac7a19ec36eb95b38/mo-threads-2.8.18148.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "60b2017f744938835542a4a9fd6e976f", "sha256": "66e84642effe66145311412d89d2a21965b0208bdb2e2a5800a9c5c0d46e0b82" }, "downloads": -1, "filename": "mo-threads-2.53.19239.tar.gz", "has_sig": false, "md5_digest": "60b2017f744938835542a4a9fd6e976f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21456, "upload_time": "2019-08-27T17:20:42", "url": "https://files.pythonhosted.org/packages/e3/ca/535170f38e3a06d2436d19d792b0f2f8e5c1f7eaf87bdd74f8b581a803b1/mo-threads-2.53.19239.tar.gz" } ] }